@antora/content-classifier 3.1.11 → 3.1.12

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.
@@ -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. See ContentCatalog constructor for relevant keys.
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
- * @param {Function} [onComponentsRegistered] - A function (optionally async) to invoke after components are
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 = {}, onComponentsRegistered = undefined) {
24
- const siteStartPage = playbook.site.startPage
25
- let contentCatalog = registerComponentVersions(new ContentCatalog(playbook), aggregate, siteAsciiDocConfig)
26
- return typeof onComponentsRegistered === 'function' &&
27
- (contentCatalog = onComponentsRegistered(contentCatalog)) instanceof Promise
28
- ? contentCatalog.then((contentCatalogValue) => addFilesAndRegisterStartPages(contentCatalogValue, siteStartPage))
29
- : addFilesAndRegisterStartPages(contentCatalog, siteStartPage)
30
- }
31
-
32
- function registerComponentVersions (contentCatalog, aggregate, siteAsciiDocConfig) {
33
- for (const componentVersionBucket of aggregate) {
34
- // advance files, nav, and startPage to component version to be used in later phase
35
- const { name, version, files, nav, startPage, ...data } = Object.assign(componentVersionBucket, {
36
- asciidoc: resolveAsciiDocConfig(siteAsciiDocConfig, componentVersionBucket),
37
- })
38
- Object.assign(contentCatalog.registerComponentVersion(name, version, data), { files, nav, startPage })
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
- for (let file, i = 0, len = files.length; i < len; i++) {
49
- allocateSrc((file = files[i]), component, version, nav) && contentCatalog.addFile(file, componentVersion)
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, component, loc, filepath)
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(component, componentVersion, startPage)
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 { extname, family } = file.src
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, { name, version, asciidoc, origins = [] }) {
143
+ function resolveAsciiDocConfig (siteAsciiDocConfig, { asciidoc, origins = [] }) {
160
144
  const scopedAttributes = asciidoc?.attributes
161
145
  if (scopedAttributes) {
162
- const initial = Object.assign({}, siteAsciiDocConfig.attributes)
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
  }
@@ -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('node:path')
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.latestVersionSegment = urls.latestVersionSegment
27
- this.latestPrereleaseVersionSegment = urls.latestPrereleaseVersionSegment
28
- if (this.latestVersionSegment == null && this.latestPrereleaseVersionSegment == null) {
29
- this.latestVersionSegmentStrategy = undefined
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.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
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, versionSegment } = descriptor
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, componentVersion) {
137
+ addFile (file) {
141
138
  const src = file.src
142
- let { component, version, family } = src
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 file: ${file.path} in ${version}@${component}${LOG_WRAP}${details}`)
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: assume that if the file is not a vinyl object, the src likely needs to be prepared
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
- // an alternate approach would be to call prepareSrc if the path property is not set
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 activeVersionSegment
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
- if (componentVersion == null) componentVersion = this.getComponentVersion(component, version) || { version }
188
- activeVersionSegment =
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 (activeVersionSegment == null) {
196
- if (componentVersion == null) componentVersion = this.getComponentVersion(component, version) || { version }
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
- return filesForFamily.set(key, file) && file
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
- let file
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(component, componentVersion))) return
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 === 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 }, componentVersion)
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
- component,
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 if (!componentVersion.url) {
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, activeVersionSegment, this.htmlUrlExtensionStyle),
316
+ computeOut(startPageSrc, startPageSrc.family, versionSegment, this.htmlUrlExtensionStyle),
338
317
  startPageSrc.family,
339
- activeVersionSegment,
318
+ versionSegment,
340
319
  this.htmlUrlExtensionStyle
341
320
  ).url
342
321
  }
343
- Object.defineProperties(componentVersion, {
344
- activeVersionSegment:
345
- activeVersionSegment === version
346
- ? { configurable: true, enumerable: false, get: getVersion }
347
- : { configurable: true, enumerable: false, value: activeVersionSegment },
348
- files: {
349
- configurable: true,
350
- enumerable: false,
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 }, componentVersion)
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
- basename = src.basename = path.basename(src.relative)
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
- src.stem = basename.substr(0, extname)
542
- src.extname = basename.substr(extname)
486
+ stem = basename.substr(0, extname)
487
+ extname = basename.substr(extname)
543
488
  } else {
544
- src.stem = basename
545
- src.extname = ''
489
+ stem = basename
490
+ extname = ''
546
491
  }
547
492
  } else {
548
- src.stem = basename.substr(0, basename.length - extname.length)
493
+ stem = basename.substr(0, basename.length - extname.length)
549
494
  }
550
495
  } else if (extname == null) {
551
- src.extname = basename.substr(stem.length)
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, versionSegment, htmlUrlExtensionStyle) {
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, versionSegment, module_)
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, versionSegment, htmlUrlExtensionStyle) {
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 (versionSegment) urlSegments.push(versionSegment)
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 if ((url = '/' + out.path) === '/.') {
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
- function addSymbolicVersionAlias (componentVersion) {
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 (componentVersion, mode) {
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
- const normalizedVersion = version && version !== 'master' ? version : ''
633
- const { versionSegment = normalizedVersion } = componentVersion
634
- if (mode === 'original') return versionSegment
635
- const strategy = this.latestVersionSegmentStrategy
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
- let component
642
- if ((component = 'name' in componentVersion && this.getComponent(componentVersion.name))) {
643
- const latestSegment =
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.latestVersionSegment
585
+ ? this.latestVersionUrlSegment
646
586
  : componentVersion === component.latestPrerelease
647
- ? this.latestPrereleaseVersionSegment
587
+ ? this.latestPrereleaseVersionUrlSegment
648
588
  : undefined
649
- return latestSegment == null ? versionSegment : latestSegment
589
+ return segment == null ? version : segment
650
590
  }
651
591
  }
652
- return versionSegment
653
- }
654
-
655
- function getComponentVersionFiles (componentVersionId) {
656
- return this.findBy(componentVersionId)
592
+ return version
657
593
  }
658
594
 
659
- function getComponentVersionStartPage (componentVersionId) {
660
- return this.resolvePage('index.adoc', componentVersionId)
661
- }
662
-
663
- function getVersion () {
664
- return this.version
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
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { posix: path } = require('node:path')
3
+ const { posix: path } = require('path')
4
4
 
5
5
  function summarizeFileLocation ({ path: path_, src: { abspath, origin } }) {
6
6
  if (!origin) return abspath || path_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/content-classifier",
3
- "version": "3.1.11",
3
+ "version": "3.1.12",
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.11",
35
- "@antora/logger": "3.1.11",
33
+ "@antora/asciidoc-loader": "3.1.12",
34
+ "@antora/logger": "3.1.12",
36
35
  "mime-types": "~2.1",
37
36
  "vinyl": "~3.0"
38
37
  },
39
38
  "engines": {
40
- "node": ">=18.0.0"
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@latest --prepublish",
56
- "postpublish": "npx -y downdoc@latest --postpublish"
54
+ "prepublishOnly": "npx -y downdoc --prepublish",
55
+ "postpublish": "npx -y downdoc --postpublish"
57
56
  }
58
57
  }