16
16
17
17
package laika .rewrite
18
18
19
- import cats .syntax .all ._
19
+ import cats .syntax .all .*
20
20
import cats .data .NonEmptyChain
21
21
import laika .ast .Path
22
22
import laika .config .{
@@ -29,23 +29,57 @@ import laika.config.{
29
29
}
30
30
31
31
/** Configuration for a single version of the documentation.
32
- *
33
- * @param displayValue the description of the version to use in any UI (e.g. version dropdowns)
34
- * @param pathSegment the string to use as a path segments in URLs pointing to this version
35
- * @param fallbackLink the link target to use when switching to this version from a page that does not exist in this version
36
- * @param label an optional label that will be used in the UI (e.g. `Dev` or `Stable`)
37
- * @param canonical indicates whether this is the canonical version
38
32
*/
39
- case class Version (
40
- displayValue : String ,
41
- pathSegment : String ,
42
- fallbackLink : String = " index.html" ,
43
- label : Option [String ] = None ,
44
- canonical : Boolean = false
45
- )
33
+ sealed abstract class Version {
34
+
35
+ /** The description of the version to use in any UI (for example in version dropdowns).
36
+ */
37
+ def displayValue : String
38
+
39
+ /** The string to use as a path segments in URLs pointing to this version.
40
+ */
41
+ def pathSegment : String
42
+
43
+ /** The link target to use when switching to this version from a page that does not exist in this version.
44
+ *
45
+ * Default: `/index.html`
46
+ */
47
+ def fallbackLink : String
48
+
49
+ /** An optional label that will be used in the UI (e.g. `Dev` or `Stable`).
50
+ */
51
+ def label : Option [String ]
52
+
53
+ /** Indicates whether this is the canonical version.
54
+ *
55
+ * When using the Helium theme setting this flag results in canonical link references
56
+ * getting inserted into the HTML `head` section of the generated output.
57
+ */
58
+ def canonical : Boolean
59
+
60
+ def withFallbackLink (value : String ): Version
61
+ def withLabel (value : String ): Version
62
+ def setCanonical : Version
63
+ }
46
64
47
65
object Version {
48
66
67
+ def apply (displayValue : String , pathSegment : String ): Version =
68
+ Impl (displayValue, pathSegment, " index.html" , None , canonical = false )
69
+
70
+ private final case class Impl (
71
+ displayValue : String ,
72
+ pathSegment : String ,
73
+ fallbackLink : String ,
74
+ label : Option [String ],
75
+ canonical : Boolean
76
+ ) extends Version {
77
+ override def productPrefix = " Version"
78
+ def withFallbackLink (value : String ): Version = copy(fallbackLink = value)
79
+ def withLabel (value : String ): Version = copy(label = Some (value))
80
+ def setCanonical : Version = copy(canonical = true )
81
+ }
82
+
49
83
implicit val decoder : ConfigDecoder [Version ] = ConfigDecoder .config.flatMap { config =>
50
84
for {
51
85
displayName <- config.get[String ](" displayValue" )
@@ -54,7 +88,7 @@ object Version {
54
88
fallbackLink <- config.get[String ](" fallbackLink" , " index.html" )
55
89
label <- config.getOpt[String ](" label" )
56
90
} yield {
57
- Version (displayName, pathSegment, fallbackLink, label, canonical)
91
+ Impl (displayName, pathSegment, fallbackLink, label, canonical)
58
92
}
59
93
}
60
94
@@ -73,51 +107,127 @@ object Version {
73
107
/** Global configuration for versioned documentation.
74
108
*
75
109
* The order in the `Seq` properties will be used for any list views in the UI (e.g. for the version chooser dropdown).
76
- *
77
- * @param currentVersion the version that the sources of a transformation produce
78
- * @param olderVersions list of older versions that have previously been rendered (may be empty)
79
- * @param newerVersions list of newer versions that have previously been rendered (may be empty)
80
- * @param renderUnversioned indicates whether unversioned documents should be rendered
81
- * (setting this to false may be useful when re-rendering older versions)
82
- * @param scannerConfig optional configuration for scanning and indexing existing versions,
83
- * used by the Helium version switcher dropdown and by the preview server.
84
110
*/
85
- case class Versions (
86
- currentVersion : Version ,
87
- olderVersions : Seq [Version ],
88
- newerVersions : Seq [Version ] = Nil ,
89
- renderUnversioned : Boolean = true ,
90
- scannerConfig : Option [VersionScannerConfig ] = None
91
- ) {
111
+ sealed abstract class Versions {
92
112
93
- lazy val allVersions : Seq [Version ] = newerVersions ++: currentVersion +: olderVersions
113
+ /** The version that the sources of a transformation produce. */
114
+ def currentVersion : Version
115
+
116
+ /** List of older versions that have previously been rendered (may be empty). */
117
+ def olderVersions : Seq [Version ]
118
+
119
+ /** List of newer versions that have previously been rendered (may be empty). */
120
+ def newerVersions : Seq [Version ]
121
+
122
+ /** Indicates whether unversioned documents should be rendered
123
+ * (setting this to false may be useful when re-rendering older versions).
124
+ */
125
+ def renderUnversioned : Boolean
126
+
127
+ /** Optional configuration for scanning and indexing existing versions,
128
+ * used by the Helium version switcher dropdown and by the preview server..
129
+ */
130
+ def scannerConfig : Option [VersionScannerConfig ]
94
131
95
- /** Configures the version scanner to use during transformations.
96
- * These settings enable scanning and indexing existing versions during a transformation,
132
+ /** Specifies an absolute file path that points to a directory containing previously
133
+ * rendered Laika output.
134
+ * This enables scanning and indexing existing versions during a transformation,
97
135
* used by the Helium version switcher dropdown and by the preview server.
136
+ *
137
+ * This is optional, without infos about existing versions, the menu will simply switch
138
+ * to the landing page of the respective versions.
139
+ *
140
+ * See [[VersionScannerConfig ]] for details.
141
+ *
142
+ * @param rootDirectory the directory to scan
143
+ * @param exclude virtual paths inside that directory that should be ignored
98
144
*/
99
- def withVersionScanner (rootDirectory : String , exclude : Seq [Path ]): Versions =
100
- copy(scannerConfig = Some (VersionScannerConfig (rootDirectory, exclude)))
145
+ def withVersionScanner (rootDirectory : String , exclude : Seq [Path ] = Nil ): Versions
101
146
102
147
/** Validates this configuration instance and either returns a `Left` with a
103
148
* list of errors encountered or a `Right` containing this instance.
104
149
*/
105
- def validated : Either [NonEmptyChain [String ], Versions ] = {
150
+ def validated : Either [NonEmptyChain [String ], Versions ]
151
+
152
+ def withNewerVersions (versions : Version * ): Versions
153
+ def withOlderVersions (versions : Version * ): Versions
154
+
155
+ def withRenderUnversioned (value : Boolean ): Versions
156
+
157
+ lazy val allVersions : Seq [Version ] = newerVersions ++: currentVersion +: olderVersions
158
+ }
159
+
160
+ object Versions {
161
+
162
+ def forCurrentVersion (version : Version ): Versions =
163
+ Impl (version, Nil , Nil , renderUnversioned = true , None )
164
+
165
+ private final case class Impl (
166
+ currentVersion : Version ,
167
+ olderVersions : Seq [Version ],
168
+ newerVersions : Seq [Version ] = Nil ,
169
+ renderUnversioned : Boolean = true ,
170
+ scannerConfig : Option [VersionScannerConfig ] = None
171
+ ) extends Versions {
172
+
173
+ override def productPrefix = " Versions"
174
+
175
+ def withVersionScanner (rootDirectory : String , exclude : Seq [Path ] = Nil ): Versions =
176
+ copy(scannerConfig = Some (VersionScannerConfig (rootDirectory, exclude)))
106
177
107
- val dupSegments = allVersions.groupBy(_.pathSegment).filter(_._2.size > 1 ).keys.toList.sorted
108
- val dupSegmentsMsg =
109
- if (dupSegments.isEmpty) None
110
- else Some (s " Path segments used for more than one version: ${dupSegments.mkString(" , " )}" )
178
+ def validated : Either [NonEmptyChain [String ], Versions ] = {
111
179
112
- val dupCanonical = allVersions.filter (_.canonical).map (_.displayValue) .toList.sorted
113
- val dupCanonicalMsg =
114
- if (dupCanonical.size < 2 ) None
115
- else Some (s " More than one version marked as canonical : ${dupCanonical .mkString(" , " )}" )
180
+ val dupSegments = allVersions.groupBy (_.pathSegment).filter (_._2.size > 1 ).keys .toList.sorted
181
+ val dupSegmentsMsg =
182
+ if (dupSegments.isEmpty ) None
183
+ else Some (s " Path segments used for more than one version : ${dupSegments .mkString(" , " )}" )
116
184
117
- NonEmptyChain .fromSeq(dupSegmentsMsg.toList ++ dupCanonicalMsg.toList) match {
118
- case Some (chain) => Left (chain)
119
- case None => Right (this )
185
+ val dupCanonical = allVersions.filter(_.canonical).map(_.displayValue).toList.sorted
186
+ val dupCanonicalMsg =
187
+ if (dupCanonical.size < 2 ) None
188
+ else Some (s " More than one version marked as canonical: ${dupCanonical.mkString(" , " )}" )
189
+
190
+ NonEmptyChain .fromSeq(dupSegmentsMsg.toList ++ dupCanonicalMsg.toList) match {
191
+ case Some (chain) => Left (chain)
192
+ case None => Right (this )
193
+ }
120
194
}
195
+
196
+ def withNewerVersions (versions : Version * ): Versions = copy(newerVersions = versions)
197
+
198
+ def withOlderVersions (versions : Version * ): Versions = copy(olderVersions = versions)
199
+
200
+ def withRenderUnversioned (value : Boolean ): Versions = copy(renderUnversioned = value)
201
+ }
202
+
203
+ implicit val key : DefaultKey [Versions ] = DefaultKey (LaikaKeys .versions)
204
+
205
+ implicit val decoder : ConfigDecoder [Versions ] = ConfigDecoder .config.flatMap { config =>
206
+ for {
207
+ currentVersion <- config.get[Version ](" currentVersion" )
208
+ olderVersions <- config.get[Seq [Version ]](" olderVersions" , Nil )
209
+ newerVersions <- config.get[Seq [Version ]](" newerVersions" , Nil )
210
+ renderUnversioned <- config.get[Boolean ](" renderUnversioned" , false )
211
+ versionScanner <- config.getOpt[VersionScannerConfig ](" scannerConfig" )
212
+ result <- Impl (
213
+ currentVersion,
214
+ olderVersions,
215
+ newerVersions,
216
+ renderUnversioned,
217
+ versionScanner
218
+ )
219
+ .validated.leftMap(err => ConfigErrors (err.map(ValidationError (_))))
220
+ } yield result
221
+ }
222
+
223
+ implicit val encoder : ConfigEncoder [Versions ] = ConfigEncoder [Versions ] { versions =>
224
+ ConfigEncoder .ObjectBuilder .empty
225
+ .withValue(" currentVersion" , versions.currentVersion)
226
+ .withValue(" olderVersions" , versions.olderVersions)
227
+ .withValue(" newerVersions" , versions.newerVersions)
228
+ .withValue(" renderUnversioned" , versions.renderUnversioned)
229
+ .withValue(" scannerConfig" , versions.scannerConfig)
230
+ .build
121
231
}
122
232
123
233
}
@@ -138,22 +248,36 @@ case class Versions(
138
248
* The specified root directory is expected to match the structure of versioned documentation as rendered by Laika.
139
249
* This means that the root directory is expected to have immediate sub-directories with names that correspond
140
250
* to the `pathSegment` property of the configuration for that version.
141
- *
142
- * @param rootDirectory file system path that represents the root of existing versions.
143
- * @param exclude paths to be skipped when scanning the output directory for existing versions (e.g. for API docs),
144
- * interpreted from the root directory of each version.
145
251
*/
146
- case class VersionScannerConfig (rootDirectory : String , exclude : Seq [Path ] = Nil )
252
+ sealed abstract class VersionScannerConfig {
253
+
254
+ /** File system path that represents the root of existing versions.
255
+ */
256
+ def rootDirectory : String
257
+
258
+ /** Paths to be skipped when scanning the output directory for existing versions (for example for API docs),
259
+ * interpreted from the root directory of each version.
260
+ */
261
+ def exclude : Seq [Path ]
262
+
263
+ }
147
264
148
265
object VersionScannerConfig {
149
266
267
+ def apply (rootDirectory : String , exclude : Seq [Path ] = Nil ): VersionScannerConfig =
268
+ Impl (rootDirectory, exclude)
269
+
270
+ private final case class Impl (rootDirectory : String , exclude : Seq [Path ] = Nil ) extends VersionScannerConfig {
271
+ override def productPrefix = " VersionScannerConfig"
272
+ }
273
+
150
274
implicit val decoder : ConfigDecoder [VersionScannerConfig ] = ConfigDecoder .config.flatMap {
151
275
config =>
152
276
for {
153
277
rootDirectory <- config.get[String ](" rootDirectory" )
154
278
exclude <- config.get[Seq [Path ]](" exclude" , Nil )
155
279
} yield {
156
- VersionScannerConfig (rootDirectory, exclude)
280
+ Impl (rootDirectory, exclude)
157
281
}
158
282
}
159
283
@@ -166,37 +290,3 @@ object VersionScannerConfig {
166
290
}
167
291
168
292
}
169
-
170
- object Versions {
171
-
172
- implicit val key : DefaultKey [Versions ] = DefaultKey (LaikaKeys .versions)
173
-
174
- implicit val decoder : ConfigDecoder [Versions ] = ConfigDecoder .config.flatMap { config =>
175
- for {
176
- currentVersion <- config.get[Version ](" currentVersion" )
177
- olderVersions <- config.get[Seq [Version ]](" olderVersions" , Nil )
178
- newerVersions <- config.get[Seq [Version ]](" newerVersions" , Nil )
179
- renderUnversioned <- config.get[Boolean ](" renderUnversioned" , false )
180
- versionScanner <- config.getOpt[VersionScannerConfig ](" scannerConfig" )
181
- result <- Versions (
182
- currentVersion,
183
- olderVersions,
184
- newerVersions,
185
- renderUnversioned,
186
- versionScanner
187
- )
188
- .validated.leftMap(err => ConfigErrors (err.map(ValidationError (_))))
189
- } yield result
190
- }
191
-
192
- implicit val encoder : ConfigEncoder [Versions ] = ConfigEncoder [Versions ] { versions =>
193
- ConfigEncoder .ObjectBuilder .empty
194
- .withValue(" currentVersion" , versions.currentVersion)
195
- .withValue(" olderVersions" , versions.olderVersions)
196
- .withValue(" newerVersions" , versions.newerVersions)
197
- .withValue(" renderUnversioned" , versions.renderUnversioned)
198
- .withValue(" scannerConfig" , versions.scannerConfig)
199
- .build
200
- }
201
-
202
- }
0 commit comments