@antora/content-classifier 3.1.2 → 3.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/classify-content.js +42 -26
- package/lib/content-catalog.js +146 -101
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -9,4 +9,4 @@ Its site generator aggregates documents from versioned content repositories and
|
|
|
9
9
|
|
|
10
10
|
Copyright (C) 2017-present [OpenDevise Inc.](https://opendevise.com) and the [Antora Project](https://antora.org).
|
|
11
11
|
|
|
12
|
-
Use of this software is granted under the terms of the [Mozilla Public License Version 2.0](https://www.mozilla.org/en-US/MPL/2.0/) (MPL-2.0).
|
|
12
|
+
Use of this software is granted under the terms of the [Mozilla Public License Version 2.0](https://www.mozilla.org/en-US/MPL/2.0/) (MPL-2.0).
|
package/lib/classify-content.js
CHANGED
|
@@ -8,42 +8,58 @@ const collateAsciiDocAttributes = require('@antora/asciidoc-loader/config/collat
|
|
|
8
8
|
*
|
|
9
9
|
* @memberof content-classifier
|
|
10
10
|
*
|
|
11
|
-
* @param {Object} playbook - The configuration object for Antora.
|
|
11
|
+
* @param {Object} playbook - The configuration object for Antora. See ContentCatalog constructor for relevant keys.
|
|
12
12
|
* @param {Object} playbook.site - Site-related configuration data.
|
|
13
13
|
* @param {String} playbook.site.startPage - The start page for the site; redirects from base URL.
|
|
14
|
-
* @param {Object} playbook.urls - URL settings for the site.
|
|
15
|
-
* @param {String} playbook.urls.htmlExtensionStyle - The style to use when computing page URLs.
|
|
16
14
|
* @param {Object} aggregate - The raw aggregate of virtual file objects to be classified.
|
|
17
15
|
* @param {Object} [siteAsciiDocConfig={}] - Site-wide AsciiDoc processor configuration options.
|
|
18
|
-
* @
|
|
16
|
+
* @param {Function} [onComponentsRegistered] - A function (optionally async) to invoke after components are
|
|
17
|
+
* registered. Must return an instance of ContentCatalog. If async, this function will also return a Promise.
|
|
18
|
+
*
|
|
19
|
+
* @returns {ContentCatalog} A structured catalog of content components, versions, and virtual content files.
|
|
19
20
|
*/
|
|
20
|
-
function classifyContent (playbook, aggregate, siteAsciiDocConfig = {}) {
|
|
21
|
-
const
|
|
22
|
-
aggregate
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.forEach((componentVersionData, componentVersion) => {
|
|
35
|
-
const { name, version } = componentVersion
|
|
36
|
-
const { files, nav, startPage } = componentVersionData
|
|
37
|
-
componentVersionData.files = undefined // clean up memory
|
|
38
|
-
files.forEach((file) => allocateSrc(file, name, version, nav) && contentCatalog.addFile(file))
|
|
39
|
-
contentCatalog.registerComponentVersionStartPage(name, componentVersion, startPage)
|
|
21
|
+
function classifyContent (playbook, aggregate, siteAsciiDocConfig = {}, onComponentsRegistered) {
|
|
22
|
+
const siteStartPage = playbook.site.startPage
|
|
23
|
+
let contentCatalog = registerComponentVersions(new ContentCatalog(playbook), aggregate, siteAsciiDocConfig)
|
|
24
|
+
return typeof onComponentsRegistered === 'function' &&
|
|
25
|
+
(contentCatalog = onComponentsRegistered(contentCatalog)) instanceof Promise
|
|
26
|
+
? contentCatalog.then((contentCatalogValue) => addFilesAndRegisterStartPages(contentCatalogValue, siteStartPage))
|
|
27
|
+
: addFilesAndRegisterStartPages(contentCatalog, siteStartPage)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function registerComponentVersions (contentCatalog, aggregate, siteAsciiDocConfig) {
|
|
31
|
+
for (const componentVersionBucket of aggregate) {
|
|
32
|
+
// advance files, nav, and startPage to component version to be used in later phase
|
|
33
|
+
const { name, version, files, nav, startPage, ...data } = Object.assign(componentVersionBucket, {
|
|
34
|
+
asciidoc: resolveAsciiDocConfig(siteAsciiDocConfig, componentVersionBucket),
|
|
40
35
|
})
|
|
41
|
-
|
|
36
|
+
Object.assign(contentCatalog.registerComponentVersion(name, version, data), { files, nav, startPage })
|
|
37
|
+
}
|
|
38
|
+
return contentCatalog
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function addFilesAndRegisterStartPages (contentCatalog, siteStartPage) {
|
|
42
|
+
for (const { versions: componentVersions } of contentCatalog.getComponents()) {
|
|
43
|
+
for (const componentVersion of componentVersions) {
|
|
44
|
+
const { name: component, version, files = [], nav, startPage } = componentVersion
|
|
45
|
+
for (let file, i = 0, len = files.length; i < len; i++) {
|
|
46
|
+
allocateSrc((file = files[i]), component, version, nav) && contentCatalog.addFile(file, componentVersion)
|
|
47
|
+
files[i] = undefined // free memory
|
|
48
|
+
}
|
|
49
|
+
contentCatalog.registerComponentVersionStartPage(component, componentVersion, startPage)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
contentCatalog.registerSiteStartPage(siteStartPage)
|
|
42
53
|
return contentCatalog
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
function allocateSrc (file, component, version, nav) {
|
|
46
|
-
const extname = file.src
|
|
57
|
+
const { extname, family } = file.src
|
|
58
|
+
if (family && family !== 'nav') {
|
|
59
|
+
Object.assign(file.src, { component, version })
|
|
60
|
+
file.src.moduleRootPath ??= calculateRootPath(file.src.relative.split('/').length)
|
|
61
|
+
return true
|
|
62
|
+
}
|
|
47
63
|
const filepath = file.path
|
|
48
64
|
const navInfo = nav && getNavInfo(filepath, nav)
|
|
49
65
|
const pathSegments = filepath.split('/')
|
package/lib/content-catalog.js
CHANGED
|
@@ -23,17 +23,17 @@ class ContentCatalog {
|
|
|
23
23
|
const urls = playbook.urls || {}
|
|
24
24
|
this.htmlUrlExtensionStyle = urls.htmlExtensionStyle || 'default'
|
|
25
25
|
this.urlRedirectFacility = urls.redirectFacility || 'static'
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
if (this.
|
|
29
|
-
this.
|
|
26
|
+
this.latestVersionSegment = urls.latestVersionSegment
|
|
27
|
+
this.latestPrereleaseVersionSegment = urls.latestPrereleaseVersionSegment
|
|
28
|
+
if (this.latestVersionSegment == null && this.latestPrereleaseVersionSegment == null) {
|
|
29
|
+
this.latestVersionSegmentStrategy = undefined
|
|
30
30
|
} else {
|
|
31
|
-
this.
|
|
32
|
-
if (this.
|
|
33
|
-
if (!this.
|
|
34
|
-
if (!this.
|
|
35
|
-
this.
|
|
36
|
-
if (!this.
|
|
31
|
+
this.latestVersionSegmentStrategy = urls.latestVersionSegmentStrategy || 'replace'
|
|
32
|
+
if (this.latestVersionSegmentStrategy === 'redirect:from') {
|
|
33
|
+
if (!this.latestVersionSegment) this.latestVersionSegment = undefined
|
|
34
|
+
if (!this.latestPrereleaseVersionSegment) {
|
|
35
|
+
this.latestPrereleaseVersionSegment = undefined
|
|
36
|
+
if (!this.latestVersionSegment) this.latestVersionSegmentStrategy = undefined
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -42,6 +42,8 @@ class ContentCatalog {
|
|
|
42
42
|
/**
|
|
43
43
|
* Registers a new component version with the content catalog. Also registers the component if it does not yet exist.
|
|
44
44
|
*
|
|
45
|
+
* Must be followed by a call to registerComponentVersionStartPage to finalize object.
|
|
46
|
+
*
|
|
45
47
|
* @param {String} name - The name of the component to which this component version belongs.
|
|
46
48
|
* @param {String} version - The version of the component to register.
|
|
47
49
|
* @param {Object} [descriptor={}] - The configuration data for the component version.
|
|
@@ -58,8 +60,9 @@ class ContentCatalog {
|
|
|
58
60
|
* @returns {Object} The constructed component version object.
|
|
59
61
|
*/
|
|
60
62
|
registerComponentVersion (name, version, descriptor = {}) {
|
|
61
|
-
const { asciidoc, displayVersion, prerelease, startPage: startPageSpec, title } = descriptor
|
|
63
|
+
const { asciidoc, displayVersion, prerelease, startPage: startPageSpec, title, versionSegment } = descriptor
|
|
62
64
|
const componentVersion = { displayVersion: displayVersion || version || 'default', title: title || name, version }
|
|
65
|
+
if (versionSegment != null) componentVersion.versionSegment = versionSegment
|
|
63
66
|
Object.defineProperty(componentVersion, 'name', { value: name, enumerable: true })
|
|
64
67
|
if (prerelease) {
|
|
65
68
|
componentVersion.prerelease = prerelease
|
|
@@ -127,14 +130,15 @@ class ContentCatalog {
|
|
|
127
130
|
)
|
|
128
131
|
}
|
|
129
132
|
if (startPageSpec) {
|
|
133
|
+
// @deprecated use separate call to register start page for component version
|
|
130
134
|
this.registerComponentVersionStartPage(name, componentVersion, startPageSpec === true ? undefined : startPageSpec)
|
|
131
135
|
}
|
|
132
136
|
return componentVersion
|
|
133
137
|
}
|
|
134
138
|
|
|
135
|
-
addFile (file) {
|
|
139
|
+
addFile (file, componentVersion) {
|
|
136
140
|
const src = file.src
|
|
137
|
-
let family = src
|
|
141
|
+
let { component, version, family } = src
|
|
138
142
|
let filesForFamily = this[$files].get(family)
|
|
139
143
|
if (!filesForFamily) this[$files].set(family, (filesForFamily = new Map()))
|
|
140
144
|
const key = generateKey(src)
|
|
@@ -146,7 +150,7 @@ class ContentCatalog {
|
|
|
146
150
|
.map((it, idx) => `${idx + 1}: ${getFileLocation(it)}`)
|
|
147
151
|
.join(LOG_WRAP)
|
|
148
152
|
if (family === 'nav') {
|
|
149
|
-
throw new Error(`Duplicate nav in ${
|
|
153
|
+
throw new Error(`Duplicate nav in ${version}@${component}: ${file.path}${LOG_WRAP}${details}`)
|
|
150
154
|
} else {
|
|
151
155
|
throw new Error(`Duplicate ${family}: ${generateResourceSpec(src)}${LOG_WRAP}${details}`)
|
|
152
156
|
}
|
|
@@ -162,16 +166,18 @@ class ContentCatalog {
|
|
|
162
166
|
file = new File(file)
|
|
163
167
|
}
|
|
164
168
|
if (family === 'alias') {
|
|
165
|
-
src.mediaType = 'text/asciidoc'
|
|
166
169
|
file.mediaType = 'text/html'
|
|
167
170
|
// NOTE: an alias masquerades as the target file
|
|
168
171
|
family = file.rel.src.family
|
|
169
|
-
//
|
|
172
|
+
// NOTE: short circuit in case of splat alias (alias -> alias)
|
|
173
|
+
if (family === 'alias' && (file.pub || {}).splat) return filesForFamily.set(key, file) && file
|
|
174
|
+
src.mediaType = 'text/asciidoc'
|
|
170
175
|
} else if (!(file.mediaType = src.mediaType) && !('mediaType' in src)) {
|
|
176
|
+
// QUESTION: should we preserve the mediaType property on file if already defined?
|
|
171
177
|
file.mediaType = src.mediaType = resolveMimeType(src.extname) || (family === 'page' ? 'text/asciidoc' : undefined)
|
|
172
178
|
}
|
|
173
179
|
let publishable
|
|
174
|
-
let
|
|
180
|
+
let activeVersionSegment
|
|
175
181
|
if (file.out) {
|
|
176
182
|
publishable = true
|
|
177
183
|
} else if ('out' in file) {
|
|
@@ -181,15 +187,18 @@ class ContentCatalog {
|
|
|
181
187
|
('/' + src.relative).indexOf('/_') < 0
|
|
182
188
|
) {
|
|
183
189
|
publishable = true
|
|
184
|
-
|
|
185
|
-
|
|
190
|
+
if (componentVersion == null) componentVersion = this.getComponentVersion(component, version) || { version }
|
|
191
|
+
activeVersionSegment = computeVersionSegment.call(this, componentVersion)
|
|
192
|
+
file.out = computeOut(src, family, activeVersionSegment, this.htmlUrlExtensionStyle)
|
|
186
193
|
}
|
|
187
194
|
if (!file.pub && (publishable || family === 'nav')) {
|
|
188
|
-
if (
|
|
189
|
-
|
|
195
|
+
if (activeVersionSegment == null) {
|
|
196
|
+
if (componentVersion == null) componentVersion = this.getComponentVersion(component, version) || { version }
|
|
197
|
+
activeVersionSegment = computeVersionSegment.call(this, componentVersion)
|
|
198
|
+
}
|
|
199
|
+
file.pub = computePub(src, file.out, family, activeVersionSegment, this.htmlUrlExtensionStyle)
|
|
190
200
|
}
|
|
191
|
-
filesForFamily.set(key, file)
|
|
192
|
-
return file
|
|
201
|
+
return filesForFamily.set(key, file) && file
|
|
193
202
|
}
|
|
194
203
|
|
|
195
204
|
removeFile (file) {
|
|
@@ -267,38 +276,48 @@ class ContentCatalog {
|
|
|
267
276
|
|
|
268
277
|
// TODO add `follow` argument to control whether alias is followed
|
|
269
278
|
getSiteStartPage () {
|
|
270
|
-
|
|
279
|
+
let file
|
|
280
|
+
if ((file = this.getById(ROOT_INDEX_PAGE_ID))) return file
|
|
281
|
+
if ((file = this.getById(ROOT_INDEX_ALIAS_ID))) return file.rel
|
|
282
|
+
const rootComponent = this.getComponent('ROOT')
|
|
283
|
+
if (!rootComponent) return
|
|
284
|
+
const version = rootComponent.versions.find(({ activeVersionSegment }) => activeVersionSegment === '')?.version
|
|
285
|
+
if (!version) return
|
|
286
|
+
if ((file = this.getById(Object.assign({}, ROOT_INDEX_PAGE_ID, { version })))) return file
|
|
287
|
+
if ((file = this.getById(Object.assign({}, ROOT_INDEX_ALIAS_ID, { version })))) return file.rel
|
|
271
288
|
}
|
|
272
289
|
|
|
273
290
|
registerComponentVersionStartPage (name, componentVersion, startPageSpec = undefined) {
|
|
291
|
+
const component = name
|
|
274
292
|
let version = componentVersion.version
|
|
275
293
|
if (version == null) {
|
|
276
294
|
// QUESTION: should we warn or throw error if component version cannot be found?
|
|
277
|
-
if (!(componentVersion = this.getComponentVersion(
|
|
295
|
+
if (!(componentVersion = this.getComponentVersion(component, componentVersion))) return
|
|
278
296
|
version = componentVersion.version
|
|
279
297
|
}
|
|
298
|
+
const activeVersionSegment = computeVersionSegment.call(this, componentVersion)
|
|
280
299
|
let startPage
|
|
281
300
|
let startPageSrc
|
|
282
|
-
const indexPageId = Object.assign({}, ROOT_INDEX_PAGE_ID, { component
|
|
301
|
+
const indexPageId = Object.assign({}, ROOT_INDEX_PAGE_ID, { component, version })
|
|
283
302
|
if (startPageSpec) {
|
|
284
303
|
if (
|
|
285
304
|
(startPage = this.resolvePage(startPageSpec, indexPageId)) &&
|
|
286
|
-
(startPageSrc = startPage.src).component ===
|
|
305
|
+
(startPageSrc = startPage.src).component === component &&
|
|
287
306
|
startPageSrc.version === version
|
|
288
307
|
) {
|
|
289
308
|
if (!this.getById(indexPageId)) {
|
|
290
|
-
const indexAliasId = Object.assign({}, ROOT_INDEX_ALIAS_ID, { component
|
|
309
|
+
const indexAliasId = Object.assign({}, ROOT_INDEX_ALIAS_ID, { component, version })
|
|
291
310
|
const indexAlias = this.getById(indexAliasId)
|
|
292
311
|
indexAlias
|
|
293
312
|
? indexAlias.synthetic && Object.assign(indexAlias, { rel: startPage })
|
|
294
|
-
: this.addFile({ src: indexAliasId, rel: startPage, synthetic: true })
|
|
313
|
+
: this.addFile({ src: indexAliasId, rel: startPage, synthetic: true }, componentVersion)
|
|
295
314
|
}
|
|
296
315
|
} else {
|
|
297
316
|
// TODO pass componentVersion as logObject
|
|
298
317
|
logger.warn(
|
|
299
318
|
'Start page specified for %s@%s %s: %s',
|
|
300
319
|
version,
|
|
301
|
-
|
|
320
|
+
component,
|
|
302
321
|
startPage === false ? 'has invalid syntax' : 'not found',
|
|
303
322
|
startPageSpec
|
|
304
323
|
)
|
|
@@ -309,25 +328,34 @@ class ContentCatalog {
|
|
|
309
328
|
}
|
|
310
329
|
if (startPage) {
|
|
311
330
|
componentVersion.url = startPage.pub.url
|
|
312
|
-
} else {
|
|
331
|
+
} else if (!componentVersion.url) {
|
|
313
332
|
// QUESTION: should we warn if the default start page cannot be found?
|
|
314
|
-
const versionSegment = computeVersionSegment.call(this, name, version)
|
|
315
333
|
componentVersion.url = computePub(
|
|
316
334
|
(startPageSrc = prepareSrc(Object.assign({}, indexPageId, { family: 'page' }))),
|
|
317
|
-
computeOut(startPageSrc, startPageSrc.family,
|
|
335
|
+
computeOut(startPageSrc, startPageSrc.family, activeVersionSegment, this.htmlUrlExtensionStyle),
|
|
318
336
|
startPageSrc.family,
|
|
319
|
-
|
|
337
|
+
activeVersionSegment,
|
|
320
338
|
this.htmlUrlExtensionStyle
|
|
321
339
|
).url
|
|
322
340
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
341
|
+
Object.defineProperties(componentVersion, {
|
|
342
|
+
activeVersionSegment:
|
|
343
|
+
activeVersionSegment === version
|
|
344
|
+
? { configurable: true, enumerable: false, get: getVersion }
|
|
345
|
+
: { configurable: true, enumerable: false, value: activeVersionSegment },
|
|
346
|
+
files: {
|
|
347
|
+
configurable: true,
|
|
348
|
+
enumerable: false,
|
|
349
|
+
get: getComponentVersionFiles.bind(this, { component, version }),
|
|
350
|
+
},
|
|
351
|
+
startPage: {
|
|
352
|
+
configurable: true,
|
|
353
|
+
enumerable: false,
|
|
354
|
+
get: getComponentVersionStartPage.bind(this, { component, version }),
|
|
355
|
+
},
|
|
356
|
+
})
|
|
357
|
+
addSymbolicVersionAlias.call(this, componentVersion)
|
|
358
|
+
return startPage
|
|
331
359
|
}
|
|
332
360
|
|
|
333
361
|
registerSiteStartPage (startPageSpec) {
|
|
@@ -335,9 +363,9 @@ class ContentCatalog {
|
|
|
335
363
|
const rel = this.resolvePage(startPageSpec)
|
|
336
364
|
if (rel) {
|
|
337
365
|
if (this.getById(ROOT_INDEX_PAGE_ID)) return
|
|
366
|
+
if (rel.pub.url === (this.htmlUrlExtensionStyle === 'default' ? '/index.html' : '/')) return
|
|
338
367
|
const rootIndexAlias = this.getById(ROOT_INDEX_ALIAS_ID)
|
|
339
368
|
if (rootIndexAlias) return rootIndexAlias.synthetic ? Object.assign(rootIndexAlias, { rel }) : undefined
|
|
340
|
-
if (rel.pub.url === (this.htmlUrlExtensionStyle === 'default' ? '/index.html' : '/')) return
|
|
341
369
|
const src = Object.assign({}, ROOT_INDEX_ALIAS_ID)
|
|
342
370
|
return this.addFile({ src, rel, synthetic: true }, { version: src.version })
|
|
343
371
|
} else if (rel === false) {
|
|
@@ -357,9 +385,14 @@ class ContentCatalog {
|
|
|
357
385
|
// QUESTION should we throw an error if alias is invalid?
|
|
358
386
|
if (!src || (inferredSpec && src.relative === '.adoc')) return
|
|
359
387
|
const component = this.getComponent(src.component)
|
|
388
|
+
let componentVersion
|
|
360
389
|
if (component) {
|
|
361
390
|
// NOTE version is not set when alias specifies a component, but not a version
|
|
362
|
-
if (src.version == null)
|
|
391
|
+
if (src.version == null) {
|
|
392
|
+
src.version = (componentVersion = component.latest).version
|
|
393
|
+
} else {
|
|
394
|
+
componentVersion = this.getComponentVersion(component, src.version)
|
|
395
|
+
}
|
|
363
396
|
const existingPage = this.getById(src)
|
|
364
397
|
if (existingPage) {
|
|
365
398
|
throw new Error(
|
|
@@ -384,12 +417,34 @@ class ContentCatalog {
|
|
|
384
417
|
)
|
|
385
418
|
}
|
|
386
419
|
// NOTE the redirect producer will populate contents when the redirect facility is 'static'
|
|
387
|
-
const alias = this.addFile({ src, rel: target })
|
|
420
|
+
const alias = this.addFile({ src, rel: target }, componentVersion)
|
|
388
421
|
// NOTE record the first alias this target claims as the preferred one
|
|
389
422
|
if (!target.rel) target.rel = alias
|
|
390
423
|
return alias
|
|
391
424
|
}
|
|
392
425
|
|
|
426
|
+
/**
|
|
427
|
+
* Adds a splat (directory) alias from the specified version segment in one component to the specified
|
|
428
|
+
* version segment in the same or different component.
|
|
429
|
+
*
|
|
430
|
+
* @returns {File} The virtual file that represents the splat alias.
|
|
431
|
+
*/
|
|
432
|
+
addSplatAlias (from, to) {
|
|
433
|
+
if (!from.versionSegment) throw new Error('cannot map splat alias from empty version segment')
|
|
434
|
+
const family = 'alias'
|
|
435
|
+
const baseSrc = { module: 'ROOT', family, relative: '', basename: '', stem: '', extname: '' }
|
|
436
|
+
const basePub = { splat: true }
|
|
437
|
+
const { component: fromComponent = to.component, versionSegment: fromVersionSegment } = from
|
|
438
|
+
const fromSrc = Object.assign({ component: fromComponent, version: fromVersionSegment }, baseSrc)
|
|
439
|
+
const fromPub = Object.assign(computePub(fromSrc, computeOut(fromSrc, family, fromVersionSegment), family), basePub)
|
|
440
|
+
const { component: toComponent, version: toVersion } = to
|
|
441
|
+
const toVersionSegment =
|
|
442
|
+
to.versionSegment ?? this.getComponentVersion(toComponent, toVersion)?.activeVersionSegment ?? toVersion
|
|
443
|
+
const toSrc = Object.assign({ component: toComponent, version: toVersion ?? toVersionSegment }, baseSrc)
|
|
444
|
+
const toPub = Object.assign(computePub(toSrc, computeOut(toSrc, family, toVersionSegment), family), basePub)
|
|
445
|
+
return this.addFile({ pub: fromPub, src: fromSrc, rel: { pub: toPub, src: toSrc } })
|
|
446
|
+
}
|
|
447
|
+
|
|
393
448
|
/**
|
|
394
449
|
* Attempts to resolve a string contextual page ID spec to a file in the catalog.
|
|
395
450
|
*
|
|
@@ -477,7 +532,7 @@ function prepareSrc (src) {
|
|
|
477
532
|
return update ? Object.assign(src, { basename, extname, stem }) : src
|
|
478
533
|
}
|
|
479
534
|
|
|
480
|
-
function computeOut (src, family,
|
|
535
|
+
function computeOut (src, family, versionSegment, htmlUrlExtensionStyle) {
|
|
481
536
|
let { component, module: module_, basename, extname, relative, stem } = src
|
|
482
537
|
if (component === 'ROOT') component = ''
|
|
483
538
|
if (module_ === 'ROOT') module_ = ''
|
|
@@ -497,7 +552,7 @@ function computeOut (src, family, version, htmlUrlExtensionStyle) {
|
|
|
497
552
|
familyPathSegment = '_attachments'
|
|
498
553
|
}
|
|
499
554
|
|
|
500
|
-
const modulePath = path.join(component,
|
|
555
|
+
const modulePath = path.join(component, versionSegment, module_)
|
|
501
556
|
const dirname = path.join(modulePath, familyPathSegment, path.dirname(relative), indexifyPathSegment)
|
|
502
557
|
const path_ = path.join(dirname, basename)
|
|
503
558
|
const moduleRootPath = path.relative(dirname, modulePath) || '.'
|
|
@@ -506,13 +561,13 @@ function computeOut (src, family, version, htmlUrlExtensionStyle) {
|
|
|
506
561
|
return { dirname, basename, path: path_, moduleRootPath, rootPath }
|
|
507
562
|
}
|
|
508
563
|
|
|
509
|
-
function computePub (src, out, family,
|
|
564
|
+
function computePub (src, out, family, versionSegment, htmlUrlExtensionStyle) {
|
|
510
565
|
const pub = {}
|
|
511
566
|
let url
|
|
512
567
|
if (family === 'nav') {
|
|
513
568
|
const component = src.component || 'ROOT'
|
|
514
569
|
const urlSegments = component === 'ROOT' ? [] : [component]
|
|
515
|
-
if (
|
|
570
|
+
if (versionSegment) urlSegments.push(versionSegment)
|
|
516
571
|
const module_ = src.module || 'ROOT'
|
|
517
572
|
if (module_ !== 'ROOT') urlSegments.push(module_)
|
|
518
573
|
if (urlSegments.length) urlSegments.push('')
|
|
@@ -531,71 +586,49 @@ function computePub (src, out, family, version, htmlUrlExtensionStyle) {
|
|
|
531
586
|
urlSegments[lastUrlSegmentIdx] = ''
|
|
532
587
|
}
|
|
533
588
|
url = '/' + urlSegments.join('/')
|
|
534
|
-
} else {
|
|
535
|
-
|
|
536
|
-
if (family === 'alias' && !src.relative) pub.splat = true
|
|
589
|
+
} else if ((url = '/' + out.path) === '/.') {
|
|
590
|
+
url = '/'
|
|
537
591
|
}
|
|
538
|
-
|
|
539
592
|
pub.url = ~url.indexOf(' ') ? url.replace(SPACE_RX, '%20') : url
|
|
593
|
+
return out ? Object.assign(pub, { moduleRootPath: out.moduleRootPath, rootPath: out.rootPath }) : pub
|
|
594
|
+
}
|
|
540
595
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
596
|
+
function addSymbolicVersionAlias (componentVersion) {
|
|
597
|
+
const { name: component, version } = componentVersion
|
|
598
|
+
const originalVersionSegment = computeVersionSegment.call(this, componentVersion, 'original')
|
|
599
|
+
const symbolicVersionSegment = computeVersionSegment.call(this, componentVersion, 'alias')
|
|
600
|
+
if (symbolicVersionSegment === originalVersionSegment || symbolicVersionSegment == null) return
|
|
601
|
+
const originalVersionSrc = { component, version, versionSegment: originalVersionSegment }
|
|
602
|
+
const symbolicVersionSrc = { component, version, versionSegment: symbolicVersionSegment }
|
|
603
|
+
return this.latestVersionSegmentStrategy === 'redirect:to'
|
|
604
|
+
? this.addSplatAlias(originalVersionSrc, symbolicVersionSrc)
|
|
605
|
+
: this.addSplatAlias(symbolicVersionSrc, originalVersionSrc)
|
|
547
606
|
}
|
|
548
607
|
|
|
549
|
-
function computeVersionSegment (
|
|
608
|
+
function computeVersionSegment (componentVersion, mode) {
|
|
609
|
+
const version = componentVersion.version
|
|
550
610
|
// special designation for master verson is @deprecated; special designation scheduled to be removed in Antora 4
|
|
551
|
-
|
|
552
|
-
const
|
|
553
|
-
if (
|
|
554
|
-
|
|
611
|
+
const normalizedVersion = version && version !== 'master' ? version : ''
|
|
612
|
+
const { versionSegment = normalizedVersion } = componentVersion
|
|
613
|
+
if (mode === 'original') return versionSegment
|
|
614
|
+
const strategy = this.latestVersionSegmentStrategy
|
|
615
|
+
if (!versionSegment) {
|
|
616
|
+
if (!mode) return ''
|
|
555
617
|
if (strategy === 'redirect:to') return
|
|
556
618
|
}
|
|
557
|
-
if (strategy === 'redirect:to' || strategy === (mode
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const segment =
|
|
619
|
+
if (strategy === 'redirect:to' || strategy === (mode ? 'redirect:from' : 'replace')) {
|
|
620
|
+
let component
|
|
621
|
+
if ((component = 'name' in componentVersion && this.getComponent(componentVersion.name))) {
|
|
622
|
+
const latestSegment =
|
|
562
623
|
componentVersion === component.latest
|
|
563
|
-
? this.
|
|
624
|
+
? this.latestVersionSegment
|
|
564
625
|
: componentVersion === component.latestPrerelease
|
|
565
|
-
? this.
|
|
626
|
+
? this.latestPrereleaseVersionSegment
|
|
566
627
|
: undefined
|
|
567
|
-
return
|
|
628
|
+
return latestSegment == null ? versionSegment : latestSegment
|
|
568
629
|
}
|
|
569
630
|
}
|
|
570
|
-
return
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
function createSymbolicVersionAlias (component, version, symbolicVersionSegment, strategy) {
|
|
574
|
-
if (symbolicVersionSegment == null || symbolicVersionSegment === version) return
|
|
575
|
-
const family = 'alias'
|
|
576
|
-
const baseVersionAliasSrc = { component, module: 'ROOT', family, relative: '', basename: '', stem: '', extname: '' }
|
|
577
|
-
const symbolicVersionAliasSrc = Object.assign({}, baseVersionAliasSrc, { version: symbolicVersionSegment })
|
|
578
|
-
const symbolicVersionAlias = {
|
|
579
|
-
src: symbolicVersionAliasSrc,
|
|
580
|
-
pub: computePub(
|
|
581
|
-
symbolicVersionAliasSrc,
|
|
582
|
-
computeOut(symbolicVersionAliasSrc, family, symbolicVersionSegment),
|
|
583
|
-
family
|
|
584
|
-
),
|
|
585
|
-
}
|
|
586
|
-
const originalVersionAliasSrc = Object.assign({}, baseVersionAliasSrc, { version })
|
|
587
|
-
const originalVersionSegment = computeVersionSegment(component, version, 'original')
|
|
588
|
-
const originalVersionAlias = {
|
|
589
|
-
src: originalVersionAliasSrc,
|
|
590
|
-
pub: computePub(
|
|
591
|
-
originalVersionAliasSrc,
|
|
592
|
-
computeOut(originalVersionAliasSrc, family, originalVersionSegment),
|
|
593
|
-
family
|
|
594
|
-
),
|
|
595
|
-
}
|
|
596
|
-
return strategy === 'redirect:to'
|
|
597
|
-
? Object.assign(originalVersionAlias, { out: undefined, rel: symbolicVersionAlias })
|
|
598
|
-
: Object.assign(symbolicVersionAlias, { out: undefined, rel: originalVersionAlias })
|
|
631
|
+
return versionSegment
|
|
599
632
|
}
|
|
600
633
|
|
|
601
634
|
function getFileLocation ({ path: path_, src: { abspath, origin } }) {
|
|
@@ -607,4 +640,16 @@ function getFileLocation ({ path: path_, src: { abspath, origin } }) {
|
|
|
607
640
|
return `${abspath || path.join(startPath, path_)} in ${'worktree' in origin ? worktree || gitdir : url} (${details})`
|
|
608
641
|
}
|
|
609
642
|
|
|
643
|
+
function getComponentVersionFiles (componentVersionId) {
|
|
644
|
+
return this.findBy(componentVersionId)
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function getComponentVersionStartPage (componentVersionId) {
|
|
648
|
+
return this.resolvePage('index.adoc', componentVersionId)
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function getVersion () {
|
|
652
|
+
return this.version
|
|
653
|
+
}
|
|
654
|
+
|
|
610
655
|
module.exports = ContentCatalog
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/content-classifier",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0-alpha.2",
|
|
4
4
|
"description": "Organizes aggregated content into a virtual file catalog for use in an Antora documentation pipeline.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"author": "OpenDevise Inc. (https://opendevise.com)",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"#constants": "./lib/constants.js"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@antora/asciidoc-loader": "3.
|
|
31
|
-
"@antora/logger": "3.
|
|
30
|
+
"@antora/asciidoc-loader": "3.2.0-alpha.2",
|
|
31
|
+
"@antora/logger": "3.2.0-alpha.2",
|
|
32
32
|
"mime-types": "~2.1",
|
|
33
33
|
"vinyl": "~2.2"
|
|
34
34
|
},
|