diff --git a/core/shared/src/main/scala/laika/rewrite/TemplateRewriter.scala b/core/shared/src/main/scala/laika/rewrite/TemplateRewriter.scala index cad83c83f..ab13deb1a 100644 --- a/core/shared/src/main/scala/laika/rewrite/TemplateRewriter.scala +++ b/core/shared/src/main/scala/laika/rewrite/TemplateRewriter.scala @@ -153,15 +153,32 @@ private[laika] object TemplateRewriter extends TemplateRewriter * the output of documents to certain target formats. * It is not always identical to the fileSuffix used for the specific format. */ -case class OutputContext(fileSuffix: String, formatSelector: String) +sealed abstract class OutputContext { + + /** The suffix to be used for file names for this output format. + */ + def fileSuffix: String + + /** Identifier that matches configured formats in `TargetFormats`, + * used to filter content for specific output formats only. + * + * @return + */ + def formatSelector: String +} object OutputContext { - def apply(format: String): OutputContext = apply(format, format) + + private final case class Impl(fileSuffix: String, formatSelector: String) + extends OutputContext + + private[laika] def apply(fileSuffix: String, formatSelector: String): OutputContext = + Impl(fileSuffix, formatSelector) def apply(format: RenderFormat[_]): OutputContext = - apply(format.fileSuffix, format.description.toLowerCase) + Impl(format.fileSuffix, format.description.toLowerCase) def apply(format: TwoPhaseRenderFormat[_, _]): OutputContext = - apply(format.interimFormat.fileSuffix, format.description.toLowerCase) + Impl(format.interimFormat.fileSuffix, format.description.toLowerCase) } diff --git a/core/shared/src/main/scala/laika/rewrite/Version.scala b/core/shared/src/main/scala/laika/rewrite/Version.scala index 4fd0a1fea..e951cd8aa 100644 --- a/core/shared/src/main/scala/laika/rewrite/Version.scala +++ b/core/shared/src/main/scala/laika/rewrite/Version.scala @@ -16,7 +16,7 @@ package laika.rewrite -import cats.syntax.all._ +import cats.syntax.all.* import cats.data.NonEmptyChain import laika.ast.Path import laika.config.{ @@ -29,23 +29,57 @@ import laika.config.{ } /** Configuration for a single version of the documentation. - * - * @param displayValue the description of the version to use in any UI (e.g. version dropdowns) - * @param pathSegment the string to use as a path segments in URLs pointing to this version - * @param fallbackLink the link target to use when switching to this version from a page that does not exist in this version - * @param label an optional label that will be used in the UI (e.g. `Dev` or `Stable`) - * @param canonical indicates whether this is the canonical version */ -case class Version( - displayValue: String, - pathSegment: String, - fallbackLink: String = "index.html", - label: Option[String] = None, - canonical: Boolean = false -) +sealed abstract class Version { + + /** The description of the version to use in any UI (for example in version dropdowns). + */ + def displayValue: String + + /** The string to use as a path segments in URLs pointing to this version. + */ + def pathSegment: String + + /** The link target to use when switching to this version from a page that does not exist in this version. + * + * Default: `/index.html` + */ + def fallbackLink: String + + /** An optional label that will be used in the UI (e.g. `Dev` or `Stable`). + */ + def label: Option[String] + + /** Indicates whether this is the canonical version. + * + * When using the Helium theme setting this flag results in canonical link references + * getting inserted into the HTML `head` section of the generated output. + */ + def canonical: Boolean + + def withFallbackLink(value: String): Version + def withLabel(value: String): Version + def setCanonical: Version +} object Version { + def apply(displayValue: String, pathSegment: String): Version = + Impl(displayValue, pathSegment, "index.html", None, canonical = false) + + private final case class Impl( + displayValue: String, + pathSegment: String, + fallbackLink: String, + label: Option[String], + canonical: Boolean + ) extends Version { + override def productPrefix = "Version" + def withFallbackLink(value: String): Version = copy(fallbackLink = value) + def withLabel(value: String): Version = copy(label = Some(value)) + def setCanonical: Version = copy(canonical = true) + } + implicit val decoder: ConfigDecoder[Version] = ConfigDecoder.config.flatMap { config => for { displayName <- config.get[String]("displayValue") @@ -54,7 +88,7 @@ object Version { fallbackLink <- config.get[String]("fallbackLink", "index.html") label <- config.getOpt[String]("label") } yield { - Version(displayName, pathSegment, fallbackLink, label, canonical) + Impl(displayName, pathSegment, fallbackLink, label, canonical) } } @@ -73,51 +107,127 @@ object Version { /** Global configuration for versioned documentation. * * The order in the `Seq` properties will be used for any list views in the UI (e.g. for the version chooser dropdown). - * - * @param currentVersion the version that the sources of a transformation produce - * @param olderVersions list of older versions that have previously been rendered (may be empty) - * @param newerVersions list of newer versions that have previously been rendered (may be empty) - * @param renderUnversioned indicates whether unversioned documents should be rendered - * (setting this to false may be useful when re-rendering older versions) - * @param scannerConfig optional configuration for scanning and indexing existing versions, - * used by the Helium version switcher dropdown and by the preview server. */ -case class Versions( - currentVersion: Version, - olderVersions: Seq[Version], - newerVersions: Seq[Version] = Nil, - renderUnversioned: Boolean = true, - scannerConfig: Option[VersionScannerConfig] = None -) { +sealed abstract class Versions { - lazy val allVersions: Seq[Version] = newerVersions ++: currentVersion +: olderVersions + /** The version that the sources of a transformation produce. */ + def currentVersion: Version + + /** List of older versions that have previously been rendered (may be empty). */ + def olderVersions: Seq[Version] + + /** List of newer versions that have previously been rendered (may be empty). */ + def newerVersions: Seq[Version] + + /** Indicates whether unversioned documents should be rendered + * (setting this to false may be useful when re-rendering older versions). + */ + def renderUnversioned: Boolean + + /** Optional configuration for scanning and indexing existing versions, + * used by the Helium version switcher dropdown and by the preview server.. + */ + def scannerConfig: Option[VersionScannerConfig] - /** Configures the version scanner to use during transformations. - * These settings enable scanning and indexing existing versions during a transformation, + /** Specifies an absolute file path that points to a directory containing previously + * rendered Laika output. + * This enables scanning and indexing existing versions during a transformation, * used by the Helium version switcher dropdown and by the preview server. + * + * This is optional, without infos about existing versions, the menu will simply switch + * to the landing page of the respective versions. + * + * See [[VersionScannerConfig]] for details. + * + * @param rootDirectory the directory to scan + * @param exclude virtual paths inside that directory that should be ignored */ - def withVersionScanner(rootDirectory: String, exclude: Seq[Path]): Versions = - copy(scannerConfig = Some(VersionScannerConfig(rootDirectory, exclude))) + def withVersionScanner(rootDirectory: String, exclude: Seq[Path] = Nil): Versions /** Validates this configuration instance and either returns a `Left` with a * list of errors encountered or a `Right` containing this instance. */ - def validated: Either[NonEmptyChain[String], Versions] = { + def validated: Either[NonEmptyChain[String], Versions] + + def withNewerVersions(versions: Version*): Versions + def withOlderVersions(versions: Version*): Versions + + def withRenderUnversioned(value: Boolean): Versions + + lazy val allVersions: Seq[Version] = newerVersions ++: currentVersion +: olderVersions +} + +object Versions { + + def forCurrentVersion(version: Version): Versions = + Impl(version, Nil, Nil, renderUnversioned = true, None) + + private final case class Impl( + currentVersion: Version, + olderVersions: Seq[Version], + newerVersions: Seq[Version] = Nil, + renderUnversioned: Boolean = true, + scannerConfig: Option[VersionScannerConfig] = None + ) extends Versions { + + override def productPrefix = "Versions" + + def withVersionScanner(rootDirectory: String, exclude: Seq[Path] = Nil): Versions = + copy(scannerConfig = Some(VersionScannerConfig(rootDirectory, exclude))) - val dupSegments = allVersions.groupBy(_.pathSegment).filter(_._2.size > 1).keys.toList.sorted - val dupSegmentsMsg = - if (dupSegments.isEmpty) None - else Some(s"Path segments used for more than one version: ${dupSegments.mkString(", ")}") + def validated: Either[NonEmptyChain[String], Versions] = { - val dupCanonical = allVersions.filter(_.canonical).map(_.displayValue).toList.sorted - val dupCanonicalMsg = - if (dupCanonical.size < 2) None - else Some(s"More than one version marked as canonical: ${dupCanonical.mkString(", ")}") + val dupSegments = allVersions.groupBy(_.pathSegment).filter(_._2.size > 1).keys.toList.sorted + val dupSegmentsMsg = + if (dupSegments.isEmpty) None + else Some(s"Path segments used for more than one version: ${dupSegments.mkString(", ")}") - NonEmptyChain.fromSeq(dupSegmentsMsg.toList ++ dupCanonicalMsg.toList) match { - case Some(chain) => Left(chain) - case None => Right(this) + val dupCanonical = allVersions.filter(_.canonical).map(_.displayValue).toList.sorted + val dupCanonicalMsg = + if (dupCanonical.size < 2) None + else Some(s"More than one version marked as canonical: ${dupCanonical.mkString(", ")}") + + NonEmptyChain.fromSeq(dupSegmentsMsg.toList ++ dupCanonicalMsg.toList) match { + case Some(chain) => Left(chain) + case None => Right(this) + } } + + def withNewerVersions(versions: Version*): Versions = copy(newerVersions = versions) + + def withOlderVersions(versions: Version*): Versions = copy(olderVersions = versions) + + def withRenderUnversioned(value: Boolean): Versions = copy(renderUnversioned = value) + } + + implicit val key: DefaultKey[Versions] = DefaultKey(LaikaKeys.versions) + + implicit val decoder: ConfigDecoder[Versions] = ConfigDecoder.config.flatMap { config => + for { + currentVersion <- config.get[Version]("currentVersion") + olderVersions <- config.get[Seq[Version]]("olderVersions", Nil) + newerVersions <- config.get[Seq[Version]]("newerVersions", Nil) + renderUnversioned <- config.get[Boolean]("renderUnversioned", false) + versionScanner <- config.getOpt[VersionScannerConfig]("scannerConfig") + result <- Impl( + currentVersion, + olderVersions, + newerVersions, + renderUnversioned, + versionScanner + ) + .validated.leftMap(err => ConfigErrors(err.map(ValidationError(_)))) + } yield result + } + + implicit val encoder: ConfigEncoder[Versions] = ConfigEncoder[Versions] { versions => + ConfigEncoder.ObjectBuilder.empty + .withValue("currentVersion", versions.currentVersion) + .withValue("olderVersions", versions.olderVersions) + .withValue("newerVersions", versions.newerVersions) + .withValue("renderUnversioned", versions.renderUnversioned) + .withValue("scannerConfig", versions.scannerConfig) + .build } } @@ -138,22 +248,37 @@ case class Versions( * The specified root directory is expected to match the structure of versioned documentation as rendered by Laika. * This means that the root directory is expected to have immediate sub-directories with names that correspond * to the `pathSegment` property of the configuration for that version. - * - * @param rootDirectory file system path that represents the root of existing versions. - * @param exclude paths to be skipped when scanning the output directory for existing versions (e.g. for API docs), - * interpreted from the root directory of each version. */ -case class VersionScannerConfig(rootDirectory: String, exclude: Seq[Path] = Nil) +sealed abstract class VersionScannerConfig { + + /** File system path that represents the root of existing versions. + */ + def rootDirectory: String + + /** Paths to be skipped when scanning the output directory for existing versions (for example for API docs), + * interpreted from the root directory of each version. + */ + def exclude: Seq[Path] + +} object VersionScannerConfig { + def apply(rootDirectory: String, exclude: Seq[Path] = Nil): VersionScannerConfig = + Impl(rootDirectory, exclude) + + private final case class Impl(rootDirectory: String, exclude: Seq[Path] = Nil) + extends VersionScannerConfig { + override def productPrefix = "VersionScannerConfig" + } + implicit val decoder: ConfigDecoder[VersionScannerConfig] = ConfigDecoder.config.flatMap { config => for { rootDirectory <- config.get[String]("rootDirectory") exclude <- config.get[Seq[Path]]("exclude", Nil) } yield { - VersionScannerConfig(rootDirectory, exclude) + Impl(rootDirectory, exclude) } } @@ -166,37 +291,3 @@ object VersionScannerConfig { } } - -object Versions { - - implicit val key: DefaultKey[Versions] = DefaultKey(LaikaKeys.versions) - - implicit val decoder: ConfigDecoder[Versions] = ConfigDecoder.config.flatMap { config => - for { - currentVersion <- config.get[Version]("currentVersion") - olderVersions <- config.get[Seq[Version]]("olderVersions", Nil) - newerVersions <- config.get[Seq[Version]]("newerVersions", Nil) - renderUnversioned <- config.get[Boolean]("renderUnversioned", false) - versionScanner <- config.getOpt[VersionScannerConfig]("scannerConfig") - result <- Versions( - currentVersion, - olderVersions, - newerVersions, - renderUnversioned, - versionScanner - ) - .validated.leftMap(err => ConfigErrors(err.map(ValidationError(_)))) - } yield result - } - - implicit val encoder: ConfigEncoder[Versions] = ConfigEncoder[Versions] { versions => - ConfigEncoder.ObjectBuilder.empty - .withValue("currentVersion", versions.currentVersion) - .withValue("olderVersions", versions.olderVersions) - .withValue("newerVersions", versions.newerVersions) - .withValue("renderUnversioned", versions.renderUnversioned) - .withValue("scannerConfig", versions.scannerConfig) - .build - } - -} diff --git a/core/shared/src/test/scala/laika/config/ConfigCodecSpec.scala b/core/shared/src/test/scala/laika/config/ConfigCodecSpec.scala index 08e98400f..416195729 100644 --- a/core/shared/src/test/scala/laika/config/ConfigCodecSpec.scala +++ b/core/shared/src/test/scala/laika/config/ConfigCodecSpec.scala @@ -20,7 +20,7 @@ import cats.data.NonEmptyChain import laika.ast.{ DocumentMetadata, ExternalTarget, IconGlyph, IconStyle, InternalTarget } import laika.ast.Path.Root import laika.ast.RelativePath.CurrentTree -import laika.rewrite.{ Version, VersionScannerConfig, Versions } +import laika.rewrite.{ Version, Versions } import laika.rewrite.link.{ ApiLinks, IconRegistry, @@ -350,18 +350,17 @@ class ConfigCodecSpec extends FunSuite { object versions { - val testInstance = Versions( - Version("0.42.x", "0.42", canonical = true), - Seq( + val testInstance = Versions + .forCurrentVersion(Version("0.42.x", "0.42").setCanonical) + .withOlderVersions( Version("0.41.x", "0.41"), - Version("0.40.x", "0.40", fallbackLink = "toc.html") - ), - Seq( - Version("0.43.x", "0.43", label = Some("dev")) - ), - renderUnversioned = false, - scannerConfig = Some(VersionScannerConfig("/path/to/versions", Seq(Root / "api"))) - ) + Version("0.40.x", "0.40").withFallbackLink("toc.html") + ) + .withNewerVersions( + Version("0.43.x", "0.43").withLabel("dev") + ) + .withRenderUnversioned(false) + .withVersionScanner("/path/to/versions", Seq(Root / "api")) } diff --git a/core/shared/src/test/scala/laika/render/HTMLRendererSpec.scala b/core/shared/src/test/scala/laika/render/HTMLRendererSpec.scala index fdb66a1be..022dc40ce 100644 --- a/core/shared/src/test/scala/laika/render/HTMLRendererSpec.scala +++ b/core/shared/src/test/scala/laika/render/HTMLRendererSpec.scala @@ -36,7 +36,7 @@ import munit.FunSuite class HTMLRendererSpec extends FunSuite with ParagraphCompanionShortcuts with TestSourceBuilders { - private val versions = Versions(Version("0.42", "0.42"), Nil) + private val versions = Versions.forCurrentVersion(Version("0.42", "0.42")) private val defaultRenderer = Renderer.of(HTML).build diff --git a/core/shared/src/test/scala/laika/rewrite/PathTranslatorSpec.scala b/core/shared/src/test/scala/laika/rewrite/PathTranslatorSpec.scala index 48c846f48..6cd913e51 100644 --- a/core/shared/src/test/scala/laika/rewrite/PathTranslatorSpec.scala +++ b/core/shared/src/test/scala/laika/rewrite/PathTranslatorSpec.scala @@ -18,10 +18,10 @@ package laika.rewrite import laika.ast.Path.Root import laika.ast.RelativePath.CurrentTree -import laika.ast._ +import laika.ast.* import laika.ast.sample.{ SampleConfig, SampleTrees } import laika.config.LaikaKeys -import laika.format.HTML +import laika.format.{ HTML, XSLFO } import laika.rewrite.nav.{ ConfigurablePathTranslator, TargetFormats, @@ -34,7 +34,7 @@ class PathTranslatorSpec extends FunSuite { private val rootCursor = { - val versions = Versions(Version("0.42.x", "0.42"), Nil) + val versions = Versions.forCurrentVersion(Version("0.42", "0.42")) val doc2: Seq[Block] = Seq( Header(1, "Title").withOptions(Id("ref")), @@ -72,7 +72,7 @@ class PathTranslatorSpec extends FunSuite { val epubRef = ConfigurablePathTranslator( translatorConfig, - OutputContext("epub.xhtml", "epub"), + OutputContext(XSLFO), Root / "tree-1" / "doc-3.md", lookup ) @@ -138,8 +138,8 @@ class PathTranslatorSpec extends FunSuite { val input = ResolvedInternalTarget(Root / "tree-2" / "doc-5.md", RelativePath.parse("../tree-2/doc-5.md")) val expected = ResolvedInternalTarget( - Root / "tree-2" / "doc-5.epub.xhtml", - RelativePath.parse("../tree-2/doc-5.epub.xhtml") + Root / "tree-2" / "doc-5.fo", + RelativePath.parse("../tree-2/doc-5.fo") ) assertEquals(epubRef.translate(input), expected) } diff --git a/docs/src/03-preparing-content/01-directory-structure.md b/docs/src/03-preparing-content/01-directory-structure.md index e22c4f6cb..9131e8318 100644 --- a/docs/src/03-preparing-content/01-directory-structure.md +++ b/docs/src/03-preparing-content/01-directory-structure.md @@ -319,16 +319,15 @@ This is a global configuration artifact that you can define with the Helium conf ```scala mdoc:silent import laika.rewrite.{ Version, Versions } -val versions = Versions( - currentVersion = Version("0.42.x", "0.42", canonical = true), - olderVersions = Seq( +val versions = Versions + .forCurrentVersion(Version("0.42.x", "0.42").setCanonical) + .withOlderVersions( Version("0.41.x", "0.41"), - Version("0.40.x", "0.40", fallbackLink = "toc.html") - ), - newerVersions = Seq( - Version("0.43.x", "0.43", label = Some("dev")) + Version("0.40.x", "0.40").withFallbackLink("toc.html") + ) + .withNewerVersions( + Version("0.43.x", "0.43").withLabel("dev") ) -) Helium.defaults.site.versions(versions) ``` @@ -340,7 +339,7 @@ like `EOL`, `Stable` or `Dev` with each version. Those three values come with default styles in the Helium CSS, but you can define additional labels if you manually include the CSS for those. -Secondly, `canonical` is a boolean that allows to mark one version as the canonical version. +Secondly, `setCanonical` allows to mark one version as the canonical version. When using Helium this will trigger the automatic insertion of a `` element in case the canonical version has a page with the same path. For all other cases the canonical link can alternatively be set manually, @@ -390,20 +389,21 @@ The index for this flexible switching can be built up in two different ways, dep and the configuration for the version scanner itself: ```scala mdoc:nest - val versions = Versions( - currentVersion = Version("0.42.x", "0.42"), - olderVersions = Seq(Version("0.41.x", "0.41", label = Some("EOL"))), - scannerConfig = Some(VersionScannerConfig( + val versions = Versions + .forCurrentVersion(Version("0.42.x", "0.42").setCanonical) + .withOlderVersions( + Version("0.41.x", "0.41").withLabel("EOL") + ) + .withVersionScanner( rootDirectory = "/path/to/old/site-output", - exclude = Seq(Root / "api") - )) - ) + exclude = Seq(Root / "api") + ) Helium.defaults.site.versions(versions) ``` The transformer will scan the `rootDirectory` and index all sub-directories on the top level where the directory name corresponds to the configured `pathSegment` of a version. - The `exclude` property is a path within each version that will not be scanned + The `exclude` parameter is a path within each version that will not be scanned (API documentation, for example, would bloat the generated JSON file even though the version switcher does not need those paths). @@ -429,14 +429,13 @@ You can achieve this by setting the `renderUnversioned` flag to `false` in your the maintenance branch: ```scala mdoc:nest:silent -val versions = Versions( - currentVersion = Version("0.42.x", "0.42"), - olderVersions = Seq(), - newerVersions = Seq( - Version("0.43.x", "0.43", label = Some("dev")) - ), - renderUnversioned = false -) +val versions = Versions + .forCurrentVersion(Version("0.42.x", "0.42")) + .withNewerVersions( + Version("0.43.x", "0.43").withLabel("dev") + ) + .withRenderUnversioned(false) + Helium.defaults.site.versions(versions) ``` diff --git a/io/src/test/scala/laika/helium/HeliumHTMLHeadSpec.scala b/io/src/test/scala/laika/helium/HeliumHTMLHeadSpec.scala index a5629e6e5..d3b2862c5 100644 --- a/io/src/test/scala/laika/helium/HeliumHTMLHeadSpec.scala +++ b/io/src/test/scala/laika/helium/HeliumHTMLHeadSpec.scala @@ -24,15 +24,15 @@ import laika.format.{ HTML, Markdown } import laika.helium.config.Favicon import laika.io.api.{ TreeParser, TreeRenderer, TreeTransformer } import laika.io.helper.{ InputBuilder, ResultExtractor, StringOps, TestThemeBuilder } -import laika.io.implicits._ +import laika.io.implicits.* import laika.io.model.{ InputTree, StringTreeOutput } import laika.rewrite.link.LinkValidation -import laika.rewrite.{ Version, Versions } -import laika.theme._ +import laika.theme.ThemeProvider import laika.theme.config.{ Font, FontDefinition, FontStyle, FontWeight } import munit.CatsEffectSuite class HeliumHTMLHeadSpec extends CatsEffectSuite with InputBuilder with ResultExtractor + with TestVersions with StringOps { val parser: Resource[IO, TreeParser[IO]] = MarkupParser @@ -100,17 +100,6 @@ class HeliumHTMLHeadSpec extends CatsEffectSuite with InputBuilder with ResultEx | |""".stripMargin - val versions = Versions( - Version("0.42.x", "0.42"), - Seq( - Version("0.41.x", "0.41"), - Version("0.40.x", "0.40", fallbackLink = "toc.html") - ), - Seq( - Version("0.43.x", "0.43") - ) - ) - val heliumBase = Helium.defaults.site.landingPage() def transformAndExtractHead(inputs: Seq[(Path, String)]): IO[String] = @@ -365,16 +354,6 @@ class HeliumHTMLHeadSpec extends CatsEffectSuite with InputBuilder with ResultEx } test("version menu on a versioned page") { - val versions = Versions( - Version("0.42.x", "0.42"), - Seq( - Version("0.41.x", "0.41"), - Version("0.40.x", "0.40", fallbackLink = "toc.html") - ), - Seq( - Version("0.43.x", "0.43") - ) - ) val helium = heliumBase.site.versions(versions).site.baseURL("https://foo.org/") val expected = meta ++ """ @@ -393,16 +372,6 @@ class HeliumHTMLHeadSpec extends CatsEffectSuite with InputBuilder with ResultEx } test("version menu on an unversioned page") { - val versions = Versions( - Version("0.42.x", "0.42"), - Seq( - Version("0.41.x", "0.41"), - Version("0.40.x", "0.40", fallbackLink = "toc.html") - ), - Seq( - Version("0.43.x", "0.43") - ) - ) val helium = heliumBase.site.versions(versions) val expected = meta ++ """ diff --git a/io/src/test/scala/laika/helium/HeliumHTMLNavSpec.scala b/io/src/test/scala/laika/helium/HeliumHTMLNavSpec.scala index 025886dfe..d37d6c45b 100644 --- a/io/src/test/scala/laika/helium/HeliumHTMLNavSpec.scala +++ b/io/src/test/scala/laika/helium/HeliumHTMLNavSpec.scala @@ -21,29 +21,18 @@ import laika.api.Transformer import laika.ast.{ Image, Path } import laika.ast.Path.Root import laika.format.{ HTML, Markdown } -import laika.helium.config._ +import laika.helium.config.* import laika.io.api.TreeTransformer import laika.io.helper.{ InputBuilder, ResultExtractor, StringOps } -import laika.io.implicits._ +import laika.io.implicits.* import laika.io.model.StringTreeOutput -import laika.rewrite.{ Version, Versions } -import laika.theme._ +import laika.theme.* import munit.CatsEffectSuite class HeliumHTMLNavSpec extends CatsEffectSuite with InputBuilder with ResultExtractor + with TestVersions with StringOps { - private val versions = Versions( - Version("0.42.x", "0.42"), - Seq( - Version("0.41.x", "0.41"), - Version("0.40.x", "0.40", fallbackLink = "toc.html") - ), - Seq( - Version("0.43.x", "0.43") - ) - ) - def transformer(theme: ThemeProvider): Resource[IO, TreeTransformer[IO]] = Transformer .from(Markdown) diff --git a/io/src/test/scala/laika/helium/HeliumLandingPageSpec.scala b/io/src/test/scala/laika/helium/HeliumLandingPageSpec.scala index 1989e39d3..1d67195ab 100644 --- a/io/src/test/scala/laika/helium/HeliumLandingPageSpec.scala +++ b/io/src/test/scala/laika/helium/HeliumLandingPageSpec.scala @@ -22,30 +22,19 @@ import laika.api.Transformer import laika.ast.{ Image, Path } import laika.ast.Path.Root import laika.format.{ HTML, Markdown } -import laika.helium.config._ +import laika.helium.config.* import laika.io.api.TreeTransformer import laika.io.helper.{ InputBuilder, ResultExtractor, StringOps } -import laika.io.implicits._ +import laika.io.implicits.* import laika.io.model.StringTreeOutput -import laika.rewrite.{ Version, Versions } -import laika.theme._ +import laika.theme.* import laika.theme.config.Color import munit.CatsEffectSuite class HeliumLandingPageSpec extends CatsEffectSuite with InputBuilder with ResultExtractor + with TestVersions with StringOps { - private val versions = Versions( - Version("0.42.x", "0.42"), - Seq( - Version("0.41.x", "0.41"), - Version("0.40.x", "0.40", fallbackLink = "toc.html") - ), - Seq( - Version("0.43.x", "0.43") - ) - ) - def transformer(theme: ThemeProvider): Resource[IO, TreeTransformer[IO]] = Transformer .from(Markdown) .to(HTML) diff --git a/io/src/test/scala/laika/helium/TestVersions.scala b/io/src/test/scala/laika/helium/TestVersions.scala new file mode 100644 index 000000000..73bf6c54f --- /dev/null +++ b/io/src/test/scala/laika/helium/TestVersions.scala @@ -0,0 +1,17 @@ +package laika.helium + +import laika.rewrite.{ Version, Versions } + +trait TestVersions { + + val versions: Versions = Versions + .forCurrentVersion(Version("0.42.x", "0.42")) + .withOlderVersions( + Version("0.41.x", "0.41"), + Version("0.40.x", "0.40").withFallbackLink("toc.html") + ) + .withNewerVersions( + Version("0.43.x", "0.43") + ) + +} diff --git a/io/src/test/scala/laika/io/TreeParserFileIOSpec.scala b/io/src/test/scala/laika/io/TreeParserFileIOSpec.scala index 88964b4d4..13656c843 100644 --- a/io/src/test/scala/laika/io/TreeParserFileIOSpec.scala +++ b/io/src/test/scala/laika/io/TreeParserFileIOSpec.scala @@ -365,16 +365,11 @@ class TreeParserFileIOSpec private val doc8 = Document(Root / "tree-3" / "doc-8.md", RootElement("Doc8")) private val doc9 = Document(Root / "doc-9.md", RootElement("Doc9")) + val (start, end) = baseTree.allDocuments.splitAt(2) + private val expectedDocs = start ++ Seq(doc9) ++ end ++ Seq(doc7, doc8) + val expected: DocumentTreeRoot = DocumentTree.builder - .addDocument(baseTree.allDocuments.head) - .addDocument(baseTree.allDocuments(1)) - .addDocument(doc9) - .addDocument(baseTree.allDocuments(2)) - .addDocument(baseTree.allDocuments(3)) - .addDocument(baseTree.allDocuments(4)) - .addDocument(baseTree.allDocuments(5)) - .addDocument(doc7) - .addDocument(doc8) + .addDocuments(expectedDocs.toList) .buildRoot } @@ -403,16 +398,11 @@ class TreeParserFileIOSpec val doc8 = Document(Root / "tree-1" / "tree-3" / "doc-8.md", RootElement("Doc8")) val doc9 = Document(Root / "tree-1" / "doc-9.md", RootElement("Doc9")) + val (start, end) = baseTree.allDocuments.splitAt(4) + val expectedDocs = start ++ Seq(doc9, doc7, doc8) ++ end + val expected: DocumentTreeRoot = DocumentTree.builder - .addDocument(baseTree.allDocuments.head) - .addDocument(baseTree.allDocuments(1)) - .addDocument(baseTree.allDocuments(2)) - .addDocument(baseTree.allDocuments(3)) - .addDocument(doc9) - .addDocument(doc7) - .addDocument(doc8) - .addDocument(baseTree.allDocuments(4)) - .addDocument(baseTree.allDocuments(5)) + .addDocuments(expectedDocs.toList) .buildRoot defaultParser.use(_.fromInput(treeInput).parse).map(_.root).assertEquals(expected) diff --git a/io/src/test/scala/laika/io/TreeRendererSpec.scala b/io/src/test/scala/laika/io/TreeRendererSpec.scala index 7782785f8..04489dd99 100644 --- a/io/src/test/scala/laika/io/TreeRendererSpec.scala +++ b/io/src/test/scala/laika/io/TreeRendererSpec.scala @@ -47,7 +47,7 @@ import laika.render.fo.TestTheme import laika.render.fo.TestTheme.staticHTMLPaths import laika.rewrite.ReferenceResolver.CursorKeys import laika.rewrite.nav.{ NoOpPathTranslator, PrettyURLs, TargetFormats } -import laika.rewrite.{ DefaultTemplatePath, OutputContext, Version, VersionScannerConfig, Versions } +import laika.rewrite.{ DefaultTemplatePath, OutputContext, Version, Versions } import munit.CatsEffectSuite import scala.io.Codec @@ -925,8 +925,8 @@ class TreeRendererSpec extends CatsEffectSuite } test("render tree while excluding all unversioned documents, based on configuration") { - val versions = Versions(Version("0.4.x", "0.4"), Seq(), Seq(), renderUnversioned = false) - val input = SampleTrees.sixDocuments + val versions = Versions.forCurrentVersion(Version("0.4.x", "0.4")).withRenderUnversioned(false) + val input = SampleTrees.sixDocuments .root.config(_.withValue(versions)) .tree1.config(SampleConfig.versioned(true)) .tree2.config(SampleConfig.versioned(true)) @@ -1088,16 +1088,19 @@ class TreeRendererSpec extends CatsEffectSuite .parallel[IO] .build - def versions(scannerRoot: Option[String] = None): Versions = Versions( - Version("0.4.x", "0.4", canonical = true), - Seq( - Version("0.3.x", "0.3"), - Version("0.2.x", "0.2"), - Version("0.1.x", "0.1", fallbackLink = "toc.html") - ), - Seq(Version("0.5.x", "0.5")), - scannerConfig = scannerRoot.map(VersionScannerConfig.apply(_)) - ) + def versions(scannerRoot: Option[String] = None): Versions = { + val versions = Versions + .forCurrentVersion(Version("0.4.x", "0.4").setCanonical) + .withOlderVersions( + Version("0.3.x", "0.3"), + Version("0.2.x", "0.2"), + Version("0.1.x", "0.1").withFallbackLink("toc.html") + ) + .withNewerVersions( + Version("0.5.x", "0.5") + ) + scannerRoot.fold(versions)(versions.withVersionScanner(_)) + } def versionedInput(scannerRoot: Option[String] = None): DocumentTreeRoot = SampleTrees.sixDocuments diff --git a/io/src/test/scala/laika/render/epub/InputTreeBuilder.scala b/io/src/test/scala/laika/render/epub/InputTreeBuilder.scala index ed4c80a2c..fa48b2661 100644 --- a/io/src/test/scala/laika/render/epub/InputTreeBuilder.scala +++ b/io/src/test/scala/laika/render/epub/InputTreeBuilder.scala @@ -19,8 +19,9 @@ package laika.render.epub import cats.effect.IO import laika.config.{ Config, ConfigBuilder, LaikaKeys } import laika.ast.Path.Root -import laika.ast._ -import laika.io.model._ +import laika.ast.* +import laika.format.EPUB +import laika.io.model.* import laika.io.helper.InputBuilder import laika.rewrite.OutputContext import laika.rewrite.nav.NoOpPathTranslator @@ -69,7 +70,7 @@ trait InputTreeBuilder extends InputBuilder { cover: Option[RenderedDocument], docs: Seq[RenderContent] ): RenderedTreeRoot[IO] = { - val outputContext = OutputContext("ignored") + val outputContext = OutputContext(EPUB) // ignored new RenderedTreeRoot( tree(path, titleNum, docs: _*), TemplateRoot.empty, diff --git a/io/src/test/scala/laika/theme/ThemeBundleSpec.scala b/io/src/test/scala/laika/theme/ThemeBundleSpec.scala index e2faec69e..eb1769982 100644 --- a/io/src/test/scala/laika/theme/ThemeBundleSpec.scala +++ b/io/src/test/scala/laika/theme/ThemeBundleSpec.scala @@ -120,7 +120,7 @@ class ThemeBundleSpec extends FunSuite { val testTree = DocumentTree.builder.addDocument(Document(Root / "doc.md", RootElement.empty)).buildRoot val compoundTranslator = config(themeBundles, appBundles) - .pathTranslatorFor(testTree, OutputContext("html")) + .pathTranslatorFor(testTree, OutputContext(HTML)) .getOrElse(NoOpPathTranslator) assertEquals(compoundTranslator.translate(Root / "doc.md"), Root / "doc-theme-app.html") }