@antora/content-classifier 3.1.11 → 3.1.13
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/lib/classify-content.js +31 -53
- package/lib/content-catalog.js +118 -162
- package/lib/util/summarize-file-location.js +1 -1
- package/package.json +7 -8
package/lib/classify-content.js
CHANGED
|
@@ -10,66 +10,50 @@ const summarizeFileLocation = require('./util/summarize-file-location')
|
|
|
10
10
|
*
|
|
11
11
|
* @memberof content-classifier
|
|
12
12
|
*
|
|
13
|
-
* @param {Object} playbook - The configuration object for Antora.
|
|
13
|
+
* @param {Object} playbook - The configuration object for Antora.
|
|
14
14
|
* @param {Object} playbook.site - Site-related configuration data.
|
|
15
15
|
* @param {String} playbook.site.startPage - The start page for the site; redirects from base URL.
|
|
16
|
+
* @param {Object} playbook.urls - URL settings for the site.
|
|
17
|
+
* @param {String} playbook.urls.htmlExtensionStyle - The style to use when computing page URLs.
|
|
16
18
|
* @param {Object} aggregate - The raw aggregate of virtual file objects to be classified.
|
|
17
19
|
* @param {Object} [siteAsciiDocConfig={}] - Site-wide AsciiDoc processor configuration options.
|
|
18
|
-
* @
|
|
19
|
-
* registered. Must return an instance of ContentCatalog. If async, this function will also return a Promise.
|
|
20
|
-
*
|
|
21
|
-
* @returns {ContentCatalog} A structured catalog of content components, versions, and virtual content files.
|
|
20
|
+
* @returns {ContentCatalog} A structured catalog of content components and virtual content files.
|
|
22
21
|
*/
|
|
23
|
-
function classifyContent (playbook, aggregate, siteAsciiDocConfig = {}
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return contentCatalog
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function addFilesAndRegisterStartPages (contentCatalog, siteStartPage) {
|
|
44
|
-
for (const { versions: componentVersions } of contentCatalog.getComponents()) {
|
|
45
|
-
for (const componentVersion of componentVersions) {
|
|
46
|
-
const { name: component, version, files = [], nav, startPage } = componentVersion
|
|
22
|
+
function classifyContent (playbook, aggregate, siteAsciiDocConfig = {}) {
|
|
23
|
+
const contentCatalog = new ContentCatalog(playbook)
|
|
24
|
+
aggregate
|
|
25
|
+
.reduce((accum, componentVersionData) => {
|
|
26
|
+
// drop files since they aren't needed to register component version
|
|
27
|
+
// drop startPage to defer registration of start page
|
|
28
|
+
const { name, version, files, startPage, ...descriptor } = Object.assign({}, componentVersionData, {
|
|
29
|
+
asciidoc: resolveAsciiDocConfig(siteAsciiDocConfig, componentVersionData),
|
|
30
|
+
})
|
|
31
|
+
return new Map(accum).set(
|
|
32
|
+
contentCatalog.registerComponentVersion(name, version, descriptor),
|
|
33
|
+
componentVersionData
|
|
34
|
+
)
|
|
35
|
+
}, new Map())
|
|
36
|
+
.forEach((componentVersionData, componentVersion) => {
|
|
37
|
+
const { name, version } = componentVersion
|
|
38
|
+
const { files, nav, startPage } = componentVersionData
|
|
47
39
|
const navResolved = nav && (nav.resolved = new Set())
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
files[i] = undefined // free memory
|
|
51
|
-
}
|
|
40
|
+
componentVersionData.files = undefined // clean up memory
|
|
41
|
+
files.forEach((file) => allocateSrc(file, name, version, nav) && contentCatalog.addFile(file))
|
|
52
42
|
if (navResolved && nav.length > navResolved.size && new Set(nav).size > navResolved.size) {
|
|
53
43
|
const loc = summarizeFileLocation({ path: 'antora.yml', src: { origin: nav.origin } })
|
|
54
44
|
for (const filepath of nav) {
|
|
55
45
|
if (navResolved.has(filepath)) continue
|
|
56
|
-
logger.warn('Could not resolve nav entry for %s@%s defined in %s: %s', version,
|
|
46
|
+
logger.warn('Could not resolve nav entry for %s@%s defined in %s: %s', version, name, loc, filepath)
|
|
57
47
|
}
|
|
58
48
|
}
|
|
59
|
-
contentCatalog.registerComponentVersionStartPage(
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
contentCatalog.registerSiteStartPage(siteStartPage)
|
|
49
|
+
contentCatalog.registerComponentVersionStartPage(name, componentVersion, startPage)
|
|
50
|
+
})
|
|
51
|
+
contentCatalog.registerSiteStartPage(playbook.site.startPage)
|
|
63
52
|
return contentCatalog
|
|
64
53
|
}
|
|
65
54
|
|
|
66
55
|
function allocateSrc (file, component, version, nav) {
|
|
67
|
-
const
|
|
68
|
-
if (family && family !== 'nav') {
|
|
69
|
-
Object.assign(file.src, { component, version })
|
|
70
|
-
file.src.moduleRootPath ??= calculateRootPath(file.src.relative.split('/').length)
|
|
71
|
-
return true
|
|
72
|
-
}
|
|
56
|
+
const extname = file.src.extname
|
|
73
57
|
const filepath = file.path
|
|
74
58
|
const pathSegments = filepath.split('/')
|
|
75
59
|
let navInfo
|
|
@@ -156,19 +140,13 @@ function getNavInfo (filepath, nav) {
|
|
|
156
140
|
if (~index) return nav.resolved.add(filepath) && { index }
|
|
157
141
|
}
|
|
158
142
|
|
|
159
|
-
function resolveAsciiDocConfig (siteAsciiDocConfig, {
|
|
143
|
+
function resolveAsciiDocConfig (siteAsciiDocConfig, { asciidoc, origins = [] }) {
|
|
160
144
|
const scopedAttributes = asciidoc?.attributes
|
|
161
145
|
if (scopedAttributes) {
|
|
162
|
-
const initial =
|
|
163
|
-
initial['antora-component-name'] = name
|
|
164
|
-
initial['antora-component-version'] = version
|
|
146
|
+
const initial = siteAsciiDocConfig.attributes
|
|
165
147
|
const mdc = { file: { path: 'antora.yml', origin: origins[origins.length - 1] } }
|
|
166
148
|
const attributes = collateAsciiDocAttributes(scopedAttributes, { initial, mdc, merge: true })
|
|
167
|
-
if (attributes !== initial) {
|
|
168
|
-
delete attributes['antora-component-name']
|
|
169
|
-
delete attributes['antora-component-version']
|
|
170
|
-
return Object.assign({}, siteAsciiDocConfig, { attributes })
|
|
171
|
-
}
|
|
149
|
+
if (attributes !== initial) siteAsciiDocConfig = Object.assign({}, siteAsciiDocConfig, { attributes })
|
|
172
150
|
}
|
|
173
151
|
return siteAsciiDocConfig
|
|
174
152
|
}
|
package/lib/content-catalog.js
CHANGED
|
@@ -5,12 +5,13 @@ const invariably = { void: () => undefined }
|
|
|
5
5
|
const logger = require('./logger')
|
|
6
6
|
const { lookup: resolveMimeType } = require('./mime-types-with-asciidoc')
|
|
7
7
|
const parseResourceId = require('./util/parse-resource-id')
|
|
8
|
-
const { posix: path } = require('
|
|
8
|
+
const { posix: path } = require('path')
|
|
9
9
|
const resolveResource = require('./util/resolve-resource')
|
|
10
10
|
const summarizeFileLocation = require('./util/summarize-file-location')
|
|
11
11
|
const versionCompare = require('./util/version-compare-desc')
|
|
12
12
|
|
|
13
13
|
const { ROOT_INDEX_ALIAS_ID, ROOT_INDEX_PAGE_ID } = require('./constants')
|
|
14
|
+
const SPACE_RX = / /g
|
|
14
15
|
const LOG_WRAP = '\n '
|
|
15
16
|
|
|
16
17
|
const $components = Symbol('components')
|
|
@@ -23,17 +24,17 @@ class ContentCatalog {
|
|
|
23
24
|
const urls = playbook.urls || {}
|
|
24
25
|
this.htmlUrlExtensionStyle = urls.htmlExtensionStyle || 'default'
|
|
25
26
|
this.urlRedirectFacility = urls.redirectFacility || 'static'
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
if (this.
|
|
29
|
-
this.
|
|
27
|
+
this.latestVersionUrlSegment = urls.latestVersionSegment
|
|
28
|
+
this.latestPrereleaseVersionUrlSegment = urls.latestPrereleaseVersionSegment
|
|
29
|
+
if (this.latestVersionUrlSegment == null && this.latestPrereleaseVersionUrlSegment == null) {
|
|
30
|
+
this.latestVersionUrlSegmentStrategy = undefined
|
|
30
31
|
} else {
|
|
31
|
-
this.
|
|
32
|
-
if (this.
|
|
33
|
-
if (!this.
|
|
34
|
-
if (!this.
|
|
35
|
-
this.
|
|
36
|
-
if (!this.
|
|
32
|
+
this.latestVersionUrlSegmentStrategy = urls.latestVersionSegmentStrategy || 'replace'
|
|
33
|
+
if (this.latestVersionUrlSegmentStrategy === 'redirect:from') {
|
|
34
|
+
if (!this.latestVersionUrlSegment) this.latestVersionUrlSegment = undefined
|
|
35
|
+
if (!this.latestPrereleaseVersionUrlSegment) {
|
|
36
|
+
this.latestPrereleaseVersionUrlSegment = undefined
|
|
37
|
+
if (!this.latestVersionUrlSegment) this.latestVersionUrlSegmentStrategy = undefined
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
}
|
|
@@ -42,8 +43,6 @@ class ContentCatalog {
|
|
|
42
43
|
/**
|
|
43
44
|
* Registers a new component version with the content catalog. Also registers the component if it does not yet exist.
|
|
44
45
|
*
|
|
45
|
-
* Must be followed by a call to registerComponentVersionStartPage to finalize object.
|
|
46
|
-
*
|
|
47
46
|
* @param {String} name - The name of the component to which this component version belongs.
|
|
48
47
|
* @param {String} version - The version of the component to register.
|
|
49
48
|
* @param {Object} [descriptor={}] - The configuration data for the component version.
|
|
@@ -60,9 +59,8 @@ class ContentCatalog {
|
|
|
60
59
|
* @returns {Object} The constructed component version object.
|
|
61
60
|
*/
|
|
62
61
|
registerComponentVersion (name, version, descriptor = {}) {
|
|
63
|
-
const { asciidoc, displayVersion, prerelease, startPage: startPageSpec, title
|
|
62
|
+
const { asciidoc, displayVersion, prerelease, startPage: startPageSpec, title } = descriptor
|
|
64
63
|
const componentVersion = { displayVersion: displayVersion || version || 'default', title: title || name, version }
|
|
65
|
-
if (versionSegment != null) componentVersion.versionSegment = versionSegment
|
|
66
64
|
Object.defineProperty(componentVersion, 'name', { value: name, enumerable: true })
|
|
67
65
|
if (prerelease) {
|
|
68
66
|
componentVersion.prerelease = prerelease
|
|
@@ -131,15 +129,14 @@ class ContentCatalog {
|
|
|
131
129
|
)
|
|
132
130
|
}
|
|
133
131
|
if (startPageSpec) {
|
|
134
|
-
// @deprecated use separate call to register start page for component version
|
|
135
132
|
this.registerComponentVersionStartPage(name, componentVersion, startPageSpec === true ? undefined : startPageSpec)
|
|
136
133
|
}
|
|
137
134
|
return componentVersion
|
|
138
135
|
}
|
|
139
136
|
|
|
140
|
-
addFile (file
|
|
137
|
+
addFile (file) {
|
|
141
138
|
const src = file.src
|
|
142
|
-
let
|
|
139
|
+
let family = src.family
|
|
143
140
|
let filesForFamily = this[$files].get(family)
|
|
144
141
|
if (!filesForFamily) this[$files].set(family, (filesForFamily = new Map()))
|
|
145
142
|
const key = generateKey(src)
|
|
@@ -151,30 +148,30 @@ class ContentCatalog {
|
|
|
151
148
|
.map((it, idx) => `${idx + 1}: ${summarizeFileLocation(it)}`)
|
|
152
149
|
.join(LOG_WRAP)
|
|
153
150
|
if (family === 'nav') {
|
|
154
|
-
throw new Error(`Duplicate nav
|
|
151
|
+
throw new Error(`Duplicate nav in ${src.version}@${src.component}: ${file.path}${LOG_WRAP}${details}`)
|
|
155
152
|
}
|
|
156
153
|
throw new Error(`Duplicate ${family}: ${generateResourceSpec(src)}${LOG_WRAP}${details}`)
|
|
157
154
|
}
|
|
158
|
-
// NOTE:
|
|
155
|
+
// NOTE: if the path property is not set, assume the src likely needs to be prepared
|
|
156
|
+
// another option is to assume that if the file is not a vinyl object, the src likely needs to be prepared
|
|
159
157
|
// a vinyl object is one indication the file was created and prepared by the content aggregator
|
|
160
|
-
//
|
|
158
|
+
//if (!src.path) prepareSrc(src)
|
|
159
|
+
//if (!File.isVinyl(file)) file = new File(file)
|
|
161
160
|
if (!File.isVinyl(file)) {
|
|
162
161
|
prepareSrc(src)
|
|
163
162
|
file = new File(file)
|
|
164
163
|
}
|
|
165
164
|
if (family === 'alias') {
|
|
165
|
+
src.mediaType = 'text/asciidoc'
|
|
166
166
|
file.mediaType = 'text/html'
|
|
167
167
|
// NOTE: an alias masquerades as the target file
|
|
168
168
|
family = file.rel.src.family
|
|
169
|
-
// NOTE: short circuit in case of splat alias (alias -> alias)
|
|
170
|
-
if (family === 'alias' && file.pub?.splat) return filesForFamily.set(key, file) && file
|
|
171
|
-
src.mediaType = 'text/asciidoc'
|
|
172
|
-
} else if (!(file.mediaType = src.mediaType) && !('mediaType' in src)) {
|
|
173
169
|
// QUESTION: should we preserve the mediaType property on file if already defined?
|
|
170
|
+
} else if (!(file.mediaType = src.mediaType) && !('mediaType' in src)) {
|
|
174
171
|
file.mediaType = src.mediaType = resolveMimeType(src.extname) || (family === 'page' ? 'text/asciidoc' : undefined)
|
|
175
172
|
}
|
|
176
173
|
let publishable
|
|
177
|
-
let
|
|
174
|
+
let versionSegment
|
|
178
175
|
if (file.out) {
|
|
179
176
|
publishable = true
|
|
180
177
|
} else if ('out' in file) {
|
|
@@ -184,24 +181,15 @@ class ContentCatalog {
|
|
|
184
181
|
('/' + src.relative).indexOf('/_') < 0
|
|
185
182
|
) {
|
|
186
183
|
publishable = true
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
'activeVersionSegment' in componentVersion
|
|
190
|
-
? componentVersion.activeVersionSegment
|
|
191
|
-
: computeVersionSegment.call(this, componentVersion)
|
|
192
|
-
file.out = computeOut(src, family, activeVersionSegment, this.htmlUrlExtensionStyle)
|
|
184
|
+
versionSegment = computeVersionSegment.call(this, src.component, src.version)
|
|
185
|
+
file.out = computeOut(src, family, versionSegment, this.htmlUrlExtensionStyle)
|
|
193
186
|
}
|
|
194
187
|
if (!file.pub && (publishable || family === 'nav')) {
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
activeVersionSegment =
|
|
198
|
-
'activeVersionSegment' in componentVersion
|
|
199
|
-
? componentVersion.activeVersionSegment
|
|
200
|
-
: computeVersionSegment.call(this, componentVersion)
|
|
201
|
-
}
|
|
202
|
-
file.pub = computePub(src, file.out, family, activeVersionSegment, this.htmlUrlExtensionStyle)
|
|
188
|
+
if (versionSegment == null) versionSegment = computeVersionSegment.call(this, src.component, src.version)
|
|
189
|
+
file.pub = computePub(src, file.out, family, versionSegment, this.htmlUrlExtensionStyle)
|
|
203
190
|
}
|
|
204
|
-
|
|
191
|
+
filesForFamily.set(key, file)
|
|
192
|
+
return file
|
|
205
193
|
}
|
|
206
194
|
|
|
207
195
|
removeFile (file) {
|
|
@@ -278,48 +266,38 @@ class ContentCatalog {
|
|
|
278
266
|
|
|
279
267
|
// TODO add `follow` argument to control whether alias is followed
|
|
280
268
|
getSiteStartPage () {
|
|
281
|
-
|
|
282
|
-
if ((file = this.getById(ROOT_INDEX_PAGE_ID))) return file
|
|
283
|
-
if ((file = this.getById(ROOT_INDEX_ALIAS_ID))) return file.rel
|
|
284
|
-
const rootComponent = this.getComponent('ROOT')
|
|
285
|
-
if (!rootComponent) return
|
|
286
|
-
const version = rootComponent.versions.find(({ activeVersionSegment }) => activeVersionSegment === '')?.version
|
|
287
|
-
if (!version) return
|
|
288
|
-
if ((file = this.getById(Object.assign({}, ROOT_INDEX_PAGE_ID, { version })))) return file
|
|
289
|
-
if ((file = this.getById(Object.assign({}, ROOT_INDEX_ALIAS_ID, { version })))) return file.rel
|
|
269
|
+
return this.getById(ROOT_INDEX_PAGE_ID) || this.getById(ROOT_INDEX_ALIAS_ID)?.rel
|
|
290
270
|
}
|
|
291
271
|
|
|
292
272
|
registerComponentVersionStartPage (name, componentVersion, startPageSpec = undefined) {
|
|
293
|
-
const component = name
|
|
294
273
|
let version = componentVersion.version
|
|
295
274
|
if (version == null) {
|
|
296
275
|
// QUESTION: should we warn or throw error if component version cannot be found?
|
|
297
|
-
if (!(componentVersion = this.getComponentVersion(
|
|
276
|
+
if (!(componentVersion = this.getComponentVersion(name, componentVersion))) return
|
|
298
277
|
version = componentVersion.version
|
|
299
278
|
}
|
|
300
|
-
const activeVersionSegment = computeVersionSegment.call(this, componentVersion)
|
|
301
279
|
let startPage
|
|
302
280
|
let startPageSrc
|
|
303
|
-
const indexPageId = Object.assign({}, ROOT_INDEX_PAGE_ID, { component, version })
|
|
281
|
+
const indexPageId = Object.assign({}, ROOT_INDEX_PAGE_ID, { component: name, version })
|
|
304
282
|
if (startPageSpec) {
|
|
305
283
|
if (
|
|
306
284
|
(startPage = this.resolvePage(startPageSpec, indexPageId)) &&
|
|
307
|
-
(startPageSrc = startPage.src).component ===
|
|
285
|
+
(startPageSrc = startPage.src).component === name &&
|
|
308
286
|
startPageSrc.version === version
|
|
309
287
|
) {
|
|
310
288
|
if (!this.getById(indexPageId)) {
|
|
311
|
-
const indexAliasId = Object.assign({}, ROOT_INDEX_ALIAS_ID, { component, version })
|
|
289
|
+
const indexAliasId = Object.assign({}, ROOT_INDEX_ALIAS_ID, { component: name, version })
|
|
312
290
|
const indexAlias = this.getById(indexAliasId)
|
|
313
291
|
indexAlias
|
|
314
292
|
? indexAlias.synthetic && Object.assign(indexAlias, { rel: startPage })
|
|
315
|
-
: this.addFile({ src: indexAliasId, rel: startPage, synthetic: true }
|
|
293
|
+
: this.addFile({ src: indexAliasId, rel: startPage, synthetic: true })
|
|
316
294
|
}
|
|
317
295
|
} else {
|
|
318
296
|
// TODO pass componentVersion as logObject
|
|
319
297
|
logger.warn(
|
|
320
298
|
'Start page specified for %s@%s %s: %s',
|
|
321
299
|
version,
|
|
322
|
-
|
|
300
|
+
name,
|
|
323
301
|
startPage === false ? 'has invalid syntax' : 'not found',
|
|
324
302
|
startPageSpec
|
|
325
303
|
)
|
|
@@ -330,34 +308,25 @@ class ContentCatalog {
|
|
|
330
308
|
}
|
|
331
309
|
if (startPage) {
|
|
332
310
|
componentVersion.url = startPage.pub.url
|
|
333
|
-
} else
|
|
311
|
+
} else {
|
|
334
312
|
// QUESTION: should we warn if the default start page cannot be found?
|
|
313
|
+
const versionSegment = computeVersionSegment.call(this, name, version)
|
|
335
314
|
componentVersion.url = computePub(
|
|
336
315
|
(startPageSrc = prepareSrc(Object.assign({}, indexPageId, { family: 'page' }))),
|
|
337
|
-
computeOut(startPageSrc, startPageSrc.family,
|
|
316
|
+
computeOut(startPageSrc, startPageSrc.family, versionSegment, this.htmlUrlExtensionStyle),
|
|
338
317
|
startPageSrc.family,
|
|
339
|
-
|
|
318
|
+
versionSegment,
|
|
340
319
|
this.htmlUrlExtensionStyle
|
|
341
320
|
).url
|
|
342
321
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
get: getComponentVersionFiles.bind(this, { component, version }),
|
|
352
|
-
},
|
|
353
|
-
startPage: {
|
|
354
|
-
configurable: true,
|
|
355
|
-
enumerable: false,
|
|
356
|
-
get: getComponentVersionStartPage.bind(this, { component, version }),
|
|
357
|
-
},
|
|
358
|
-
})
|
|
359
|
-
addSymbolicVersionAlias.call(this, componentVersion)
|
|
360
|
-
return startPage
|
|
322
|
+
|
|
323
|
+
const symbolicVersionAlias = createSymbolicVersionAlias(
|
|
324
|
+
name,
|
|
325
|
+
version,
|
|
326
|
+
computeVersionSegment.call(this, name, version, 'alias'),
|
|
327
|
+
this.latestVersionUrlSegmentStrategy
|
|
328
|
+
)
|
|
329
|
+
if (symbolicVersionAlias) this.addFile(symbolicVersionAlias)
|
|
361
330
|
}
|
|
362
331
|
|
|
363
332
|
registerSiteStartPage (startPageSpec) {
|
|
@@ -365,9 +334,9 @@ class ContentCatalog {
|
|
|
365
334
|
const rel = this.resolvePage(startPageSpec)
|
|
366
335
|
if (rel) {
|
|
367
336
|
if (this.getById(ROOT_INDEX_PAGE_ID)) return
|
|
368
|
-
if (rel.pub.url === (this.htmlUrlExtensionStyle === 'default' ? '/index.html' : '/')) return
|
|
369
337
|
const rootIndexAlias = this.getById(ROOT_INDEX_ALIAS_ID)
|
|
370
338
|
if (rootIndexAlias) return rootIndexAlias.synthetic ? Object.assign(rootIndexAlias, { rel }) : undefined
|
|
339
|
+
if (rel.pub.url === (this.htmlUrlExtensionStyle === 'default' ? '/index.html' : '/')) return
|
|
371
340
|
const src = Object.assign({}, ROOT_INDEX_ALIAS_ID)
|
|
372
341
|
return this.addFile({ src, rel, synthetic: true }, { version: src.version })
|
|
373
342
|
}
|
|
@@ -388,14 +357,9 @@ class ContentCatalog {
|
|
|
388
357
|
// QUESTION should we throw an error if alias is invalid?
|
|
389
358
|
if (!src || (inferredSpec && src.relative === '.adoc')) return
|
|
390
359
|
const component = this.getComponent(src.component)
|
|
391
|
-
let componentVersion
|
|
392
360
|
if (component) {
|
|
393
361
|
// NOTE version is not set when alias specifies a component, but not a version
|
|
394
|
-
if (src.version == null)
|
|
395
|
-
src.version = (componentVersion = component.latest).version
|
|
396
|
-
} else {
|
|
397
|
-
componentVersion = this.getComponentVersion(component, src.version)
|
|
398
|
-
}
|
|
362
|
+
if (src.version == null) src.version = component.latest.version
|
|
399
363
|
const existingPage = this.getById(src)
|
|
400
364
|
if (existingPage) {
|
|
401
365
|
throw new Error(
|
|
@@ -420,34 +384,12 @@ class ContentCatalog {
|
|
|
420
384
|
)
|
|
421
385
|
}
|
|
422
386
|
// NOTE the redirect producer will populate contents when the redirect facility is 'static'
|
|
423
|
-
const alias = this.addFile({ src, rel: target }
|
|
387
|
+
const alias = this.addFile({ src, rel: target })
|
|
424
388
|
// NOTE record the first alias this target claims as the preferred one
|
|
425
389
|
if (!target.rel) target.rel = alias
|
|
426
390
|
return alias
|
|
427
391
|
}
|
|
428
392
|
|
|
429
|
-
/**
|
|
430
|
-
* Adds a splat (directory) alias from the specified version segment in one component to the specified
|
|
431
|
-
* version segment in the same or different component.
|
|
432
|
-
*
|
|
433
|
-
* @returns {File} The virtual file that represents the splat alias.
|
|
434
|
-
*/
|
|
435
|
-
addSplatAlias (from, to) {
|
|
436
|
-
if (!from.versionSegment) throw new Error('cannot map splat alias from empty version segment')
|
|
437
|
-
const family = 'alias'
|
|
438
|
-
const baseSrc = { module: 'ROOT', family, relative: '', basename: '', stem: '', extname: '' }
|
|
439
|
-
const basePub = { splat: true }
|
|
440
|
-
const { component: fromComponent = to.component, versionSegment: fromVersionSegment } = from
|
|
441
|
-
const fromSrc = Object.assign({ component: fromComponent, version: fromVersionSegment }, baseSrc)
|
|
442
|
-
const fromPub = Object.assign(computePub(fromSrc, computeOut(fromSrc, family, fromVersionSegment), family), basePub)
|
|
443
|
-
const { component: toComponent, version: toVersion } = to
|
|
444
|
-
const toVersionSegment =
|
|
445
|
-
to.versionSegment ?? this.getComponentVersion(toComponent, toVersion)?.activeVersionSegment ?? toVersion
|
|
446
|
-
const toSrc = Object.assign({ component: toComponent, version: toVersion ?? toVersionSegment }, baseSrc)
|
|
447
|
-
const toPub = Object.assign(computePub(toSrc, computeOut(toSrc, family, toVersionSegment), family), basePub)
|
|
448
|
-
return this.addFile({ pub: fromPub, src: fromSrc, rel: { pub: toPub, src: toSrc } })
|
|
449
|
-
}
|
|
450
|
-
|
|
451
393
|
/**
|
|
452
394
|
* Attempts to resolve a page reference within the given context to a page in the catalog.
|
|
453
395
|
*
|
|
@@ -532,28 +474,32 @@ function generateResourceSpec ({ component, version, module: module_, family, re
|
|
|
532
474
|
|
|
533
475
|
function prepareSrc (src) {
|
|
534
476
|
let { basename, extname, stem } = src
|
|
477
|
+
let update
|
|
535
478
|
if (basename == null) {
|
|
536
|
-
|
|
479
|
+
update = true
|
|
480
|
+
basename = path.basename(src.relative)
|
|
537
481
|
}
|
|
538
482
|
if (stem == null) {
|
|
483
|
+
update = true
|
|
539
484
|
if (extname == null) {
|
|
540
485
|
if (~(extname = basename.lastIndexOf('.'))) {
|
|
541
|
-
|
|
542
|
-
|
|
486
|
+
stem = basename.substr(0, extname)
|
|
487
|
+
extname = basename.substr(extname)
|
|
543
488
|
} else {
|
|
544
|
-
|
|
545
|
-
|
|
489
|
+
stem = basename
|
|
490
|
+
extname = ''
|
|
546
491
|
}
|
|
547
492
|
} else {
|
|
548
|
-
|
|
493
|
+
stem = basename.substr(0, basename.length - extname.length)
|
|
549
494
|
}
|
|
550
495
|
} else if (extname == null) {
|
|
551
|
-
|
|
496
|
+
update = true
|
|
497
|
+
extname = basename.substr(stem.length)
|
|
552
498
|
}
|
|
553
|
-
return src
|
|
499
|
+
return update ? Object.assign(src, { basename, extname, stem }) : src
|
|
554
500
|
}
|
|
555
501
|
|
|
556
|
-
function computeOut (src, family,
|
|
502
|
+
function computeOut (src, family, version, htmlUrlExtensionStyle) {
|
|
557
503
|
let { component, module: module_, basename, extname, relative, stem } = src
|
|
558
504
|
if (component === 'ROOT') component = ''
|
|
559
505
|
if (module_ === 'ROOT') module_ = ''
|
|
@@ -573,7 +519,7 @@ function computeOut (src, family, versionSegment, htmlUrlExtensionStyle) {
|
|
|
573
519
|
familyPathSegment = '_attachments'
|
|
574
520
|
}
|
|
575
521
|
|
|
576
|
-
const modulePath = path.join(component,
|
|
522
|
+
const modulePath = path.join(component, version, module_)
|
|
577
523
|
const dirname = path.join(modulePath, familyPathSegment, path.dirname(relative), indexifyPathSegment)
|
|
578
524
|
const path_ = path.join(dirname, basename)
|
|
579
525
|
const moduleRootPath = path.relative(dirname, modulePath) || '.'
|
|
@@ -582,13 +528,13 @@ function computeOut (src, family, versionSegment, htmlUrlExtensionStyle) {
|
|
|
582
528
|
return { dirname, basename, path: path_, moduleRootPath, rootPath }
|
|
583
529
|
}
|
|
584
530
|
|
|
585
|
-
function computePub (src, out, family,
|
|
531
|
+
function computePub (src, out, family, version, htmlUrlExtensionStyle) {
|
|
586
532
|
const pub = {}
|
|
587
533
|
let url
|
|
588
534
|
if (family === 'nav') {
|
|
589
535
|
const component = src.component || 'ROOT'
|
|
590
536
|
const urlSegments = component === 'ROOT' ? [] : [component]
|
|
591
|
-
if (
|
|
537
|
+
if (version) urlSegments.push(version)
|
|
592
538
|
const module_ = src.module || 'ROOT'
|
|
593
539
|
if (module_ !== 'ROOT') urlSegments.push(module_)
|
|
594
540
|
if (urlSegments.length) urlSegments.push('')
|
|
@@ -607,61 +553,71 @@ function computePub (src, out, family, versionSegment, htmlUrlExtensionStyle) {
|
|
|
607
553
|
urlSegments[lastUrlSegmentIdx] = ''
|
|
608
554
|
}
|
|
609
555
|
url = '/' + urlSegments.join('/')
|
|
610
|
-
} else
|
|
611
|
-
url = '/'
|
|
556
|
+
} else {
|
|
557
|
+
if ((url = '/' + out.path) === '/.') url = '/'
|
|
558
|
+
if (family === 'alias' && !src.relative) pub.splat = true
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
pub.url = ~url.indexOf(' ') ? url.replace(SPACE_RX, '%20') : url
|
|
562
|
+
|
|
563
|
+
if (out) {
|
|
564
|
+
pub.moduleRootPath = out.moduleRootPath
|
|
565
|
+
pub.rootPath = out.rootPath
|
|
612
566
|
}
|
|
613
|
-
pub.url = ~url.indexOf(' ') ? url.replaceAll(' ', '%20') : url
|
|
614
|
-
return out ? Object.assign(pub, { moduleRootPath: out.moduleRootPath, rootPath: out.rootPath }) : pub
|
|
615
|
-
}
|
|
616
567
|
|
|
617
|
-
|
|
618
|
-
const { name: component, version } = componentVersion
|
|
619
|
-
const originalVersionSegment = computeVersionSegment.call(this, componentVersion, 'original')
|
|
620
|
-
const symbolicVersionSegment = computeVersionSegment.call(this, componentVersion, 'alias')
|
|
621
|
-
if (symbolicVersionSegment === originalVersionSegment || symbolicVersionSegment == null) return
|
|
622
|
-
const originalVersionSrc = { component, version, versionSegment: originalVersionSegment }
|
|
623
|
-
const symbolicVersionSrc = { component, version, versionSegment: symbolicVersionSegment }
|
|
624
|
-
return this.latestVersionSegmentStrategy === 'redirect:to'
|
|
625
|
-
? this.addSplatAlias(originalVersionSrc, symbolicVersionSrc)
|
|
626
|
-
: this.addSplatAlias(symbolicVersionSrc, originalVersionSrc)
|
|
568
|
+
return pub
|
|
627
569
|
}
|
|
628
570
|
|
|
629
|
-
function computeVersionSegment (
|
|
630
|
-
const version = componentVersion.version
|
|
571
|
+
function computeVersionSegment (name, version, mode) {
|
|
631
572
|
// special designation for master version is @deprecated; special designation scheduled to be removed in Antora 4
|
|
632
|
-
|
|
633
|
-
const
|
|
634
|
-
if (
|
|
635
|
-
|
|
636
|
-
if (!versionSegment) {
|
|
637
|
-
if (!mode) return ''
|
|
573
|
+
if (mode === 'original') return !version || version === 'master' ? '' : version
|
|
574
|
+
const strategy = this.latestVersionUrlSegmentStrategy
|
|
575
|
+
if (!version || version === 'master') {
|
|
576
|
+
if (mode !== 'alias') return ''
|
|
638
577
|
if (strategy === 'redirect:to') return
|
|
639
578
|
}
|
|
640
|
-
if (strategy === 'redirect:to' || strategy === (mode ? 'redirect:from' : 'replace')) {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
579
|
+
if (strategy === 'redirect:to' || strategy === (mode === 'alias' ? 'redirect:from' : 'replace')) {
|
|
580
|
+
const component = this.getComponent(name)
|
|
581
|
+
const componentVersion = component && this.getComponentVersion(component, version)
|
|
582
|
+
if (componentVersion) {
|
|
583
|
+
const segment =
|
|
644
584
|
componentVersion === component.latest
|
|
645
|
-
? this.
|
|
585
|
+
? this.latestVersionUrlSegment
|
|
646
586
|
: componentVersion === component.latestPrerelease
|
|
647
|
-
? this.
|
|
587
|
+
? this.latestPrereleaseVersionUrlSegment
|
|
648
588
|
: undefined
|
|
649
|
-
return
|
|
589
|
+
return segment == null ? version : segment
|
|
650
590
|
}
|
|
651
591
|
}
|
|
652
|
-
return
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
function getComponentVersionFiles (componentVersionId) {
|
|
656
|
-
return this.findBy(componentVersionId)
|
|
592
|
+
return version
|
|
657
593
|
}
|
|
658
594
|
|
|
659
|
-
function
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
595
|
+
function createSymbolicVersionAlias (component, version, symbolicVersionSegment, strategy) {
|
|
596
|
+
if (symbolicVersionSegment == null || symbolicVersionSegment === version) return
|
|
597
|
+
const family = 'alias'
|
|
598
|
+
const baseVersionAliasSrc = { component, module: 'ROOT', family, relative: '', basename: '', stem: '', extname: '' }
|
|
599
|
+
const symbolicVersionAliasSrc = Object.assign({}, baseVersionAliasSrc, { version: symbolicVersionSegment })
|
|
600
|
+
const symbolicVersionAlias = {
|
|
601
|
+
src: symbolicVersionAliasSrc,
|
|
602
|
+
pub: computePub(
|
|
603
|
+
symbolicVersionAliasSrc,
|
|
604
|
+
computeOut(symbolicVersionAliasSrc, family, symbolicVersionSegment),
|
|
605
|
+
family
|
|
606
|
+
),
|
|
607
|
+
}
|
|
608
|
+
const originalVersionAliasSrc = Object.assign({}, baseVersionAliasSrc, { version })
|
|
609
|
+
const originalVersionSegment = computeVersionSegment(component, version, 'original')
|
|
610
|
+
const originalVersionAlias = {
|
|
611
|
+
src: originalVersionAliasSrc,
|
|
612
|
+
pub: computePub(
|
|
613
|
+
originalVersionAliasSrc,
|
|
614
|
+
computeOut(originalVersionAliasSrc, family, originalVersionSegment),
|
|
615
|
+
family
|
|
616
|
+
),
|
|
617
|
+
}
|
|
618
|
+
return strategy === 'redirect:to'
|
|
619
|
+
? Object.assign(originalVersionAlias, { out: undefined, rel: symbolicVersionAlias })
|
|
620
|
+
: Object.assign(symbolicVersionAlias, { out: undefined, rel: originalVersionAlias })
|
|
665
621
|
}
|
|
666
622
|
|
|
667
623
|
module.exports = ContentCatalog
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/content-classifier",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.13",
|
|
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)",
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
"homepage": "https://antora.org",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
|
-
"url": "git+https://gitlab.com/antora/antora.git"
|
|
16
|
-
"directory": "packages/content-classifier"
|
|
15
|
+
"url": "git+https://gitlab.com/antora/antora.git"
|
|
17
16
|
},
|
|
18
17
|
"bugs": {
|
|
19
18
|
"url": "https://gitlab.com/antora/antora/issues"
|
|
@@ -31,13 +30,13 @@
|
|
|
31
30
|
"#constants": "./lib/constants.js"
|
|
32
31
|
},
|
|
33
32
|
"dependencies": {
|
|
34
|
-
"@antora/asciidoc-loader": "3.1.
|
|
35
|
-
"@antora/logger": "3.1.
|
|
33
|
+
"@antora/asciidoc-loader": "3.1.13",
|
|
34
|
+
"@antora/logger": "3.1.13",
|
|
36
35
|
"mime-types": "~2.1",
|
|
37
36
|
"vinyl": "~3.0"
|
|
38
37
|
},
|
|
39
38
|
"engines": {
|
|
40
|
-
"node": ">=
|
|
39
|
+
"node": ">=16.0.0"
|
|
41
40
|
},
|
|
42
41
|
"files": [
|
|
43
42
|
"lib/"
|
|
@@ -52,7 +51,7 @@
|
|
|
52
51
|
],
|
|
53
52
|
"scripts": {
|
|
54
53
|
"test": "_mocha",
|
|
55
|
-
"prepublishOnly": "npx -y downdoc
|
|
56
|
-
"postpublish": "npx -y downdoc
|
|
54
|
+
"prepublishOnly": "npx -y downdoc --prepublish",
|
|
55
|
+
"postpublish": "npx -y downdoc --postpublish"
|
|
57
56
|
}
|
|
58
57
|
}
|