@antora/content-classifier 3.2.0-alpha.6 → 3.2.0-alpha.9

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.
@@ -20,7 +20,7 @@ const summarizeFileLocation = require('./util/summarize-file-location')
20
20
  *
21
21
  * @returns {ContentCatalog} A structured catalog of content components, versions, and virtual content files.
22
22
  */
23
- function classifyContent (playbook, aggregate, siteAsciiDocConfig = {}, onComponentsRegistered) {
23
+ function classifyContent (playbook, aggregate, siteAsciiDocConfig = {}, onComponentsRegistered = undefined) {
24
24
  const siteStartPage = playbook.site.startPage
25
25
  let contentCatalog = registerComponentVersions(new ContentCatalog(playbook), aggregate, siteAsciiDocConfig)
26
26
  return typeof onComponentsRegistered === 'function' &&
@@ -157,7 +157,7 @@ function getNavInfo (filepath, nav) {
157
157
  }
158
158
 
159
159
  function resolveAsciiDocConfig (siteAsciiDocConfig, { name, version, asciidoc, origins = [] }) {
160
- const scopedAttributes = (asciidoc || {}).attributes
160
+ const scopedAttributes = asciidoc?.attributes
161
161
  if (scopedAttributes) {
162
162
  const initial = Object.assign({}, siteAsciiDocConfig.attributes)
163
163
  initial['antora-component-name'] = name
@@ -11,7 +11,6 @@ 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
15
14
  const LOG_WRAP = '\n '
16
15
 
17
16
  const $components = Symbol('components')
@@ -61,8 +60,13 @@ class ContentCatalog {
61
60
  * @returns {Object} The constructed component version object.
62
61
  */
63
62
  registerComponentVersion (name, version, descriptor = {}) {
64
- const { asciidoc, displayVersion, prerelease, startPage: startPageSpec, title, versionSegment } = descriptor
65
- const componentVersion = { displayVersion: displayVersion || version || 'default', title: title || name, version }
63
+ const { asciidoc, displayVersion, prerelease, startPage: startPageRef, title, versionSegment, origins } = descriptor
64
+ const componentVersion = {
65
+ displayVersion: displayVersion || version || 'default',
66
+ title: title || name,
67
+ version,
68
+ origins: new Set(origins && typeof origins[Symbol.iterator] === 'function' ? origins : []),
69
+ }
66
70
  if (versionSegment != null) componentVersion.versionSegment = versionSegment
67
71
  Object.defineProperty(componentVersion, 'name', { value: name, enumerable: true })
68
72
  if (prerelease) {
@@ -87,10 +91,10 @@ class ContentCatalog {
87
91
  let lastVerdict
88
92
  const insertIdx = version
89
93
  ? componentVersions.findIndex(({ version: candidateVersion, prerelease: candidatePrerelease }) => {
90
- return (lastVerdict = versionCompare(candidateVersion, version)) > 1
91
- ? !!prerelease === !!candidatePrerelease
92
- : lastVerdict > 0 || (lastVerdict < -1 && prerelease && !candidatePrerelease)
93
- })
94
+ return (lastVerdict = versionCompare(candidateVersion, version)) > 1
95
+ ? !!prerelease === !!candidatePrerelease
96
+ : lastVerdict > 0 || (lastVerdict < -1 && prerelease && !candidatePrerelease)
97
+ })
94
98
  : prerelease
95
99
  ? -1
96
100
  : ~(~componentVersions.findIndex(({ prerelease: candidatePrerelease }) => !candidatePrerelease) || -1)
@@ -131,9 +135,9 @@ class ContentCatalog {
131
135
  )
132
136
  )
133
137
  }
134
- if (startPageSpec) {
138
+ if (startPageRef) {
135
139
  // @deprecated use separate call to register start page for component version
136
- this.registerComponentVersionStartPage(name, componentVersion, startPageSpec === true ? undefined : startPageSpec)
140
+ this.registerComponentVersionStartPage(name, componentVersion, startPageRef === true ? undefined : startPageRef)
137
141
  }
138
142
  return componentVersion
139
143
  }
@@ -147,22 +151,18 @@ class ContentCatalog {
147
151
  if (filesForFamily.has(key)) {
148
152
  if (family === 'alias') {
149
153
  throw new Error(`Duplicate alias: ${generateResourceSpec(src)}`)
150
- } else {
151
- const details = [filesForFamily.get(key), file]
152
- .map((it, idx) => `${idx + 1}: ${summarizeFileLocation(it)}`)
153
- .join(LOG_WRAP)
154
- if (family === 'nav') {
155
- throw new Error(`Duplicate nav file: ${file.path} in ${version}@${component}${LOG_WRAP}${details}`)
156
- } else {
157
- throw new Error(`Duplicate ${family}: ${generateResourceSpec(src)}${LOG_WRAP}${details}`)
158
- }
159
154
  }
155
+ const details = [filesForFamily.get(key), file]
156
+ .map((it, idx) => `${idx + 1}: ${summarizeFileLocation(it)}`)
157
+ .join(LOG_WRAP)
158
+ if (family === 'nav') {
159
+ throw new Error(`Duplicate nav file: ${file.path} in ${version}@${component}${LOG_WRAP}${details}`)
160
+ }
161
+ throw new Error(`Duplicate ${family}: ${generateResourceSpec(src)}${LOG_WRAP}${details}`)
160
162
  }
161
- // NOTE: if the path property is not set, assume the src likely needs to be prepared
162
- // another option is to assume that if the file is not a vinyl object, the src likely needs to be prepared
163
+ // NOTE: assume that if the file is not a vinyl object, the src likely needs to be prepared
163
164
  // a vinyl object is one indication the file was created and prepared by the content aggregator
164
- //if (!src.path) prepareSrc(src)
165
- //if (!File.isVinyl(file)) file = new File(file)
165
+ // an alternate approach would be to call prepareSrc if the path property is not set
166
166
  if (!File.isVinyl(file)) {
167
167
  prepareSrc(src)
168
168
  file = new File(file)
@@ -172,7 +172,7 @@ class ContentCatalog {
172
172
  // NOTE: an alias masquerades as the target file
173
173
  family = file.rel.src.family
174
174
  // NOTE: short circuit in case of splat alias (alias -> alias)
175
- if (family === 'alias' && (file.pub || {}).splat) return filesForFamily.set(key, file) && file
175
+ if (family === 'alias' && file.pub?.splat) return filesForFamily.set(key, file) && file
176
176
  src.mediaType = 'text/asciidoc'
177
177
  } else if (!(file.mediaType = src.mediaType) && !('mediaType' in src)) {
178
178
  // QUESTION: should we preserve the mediaType property on file if already defined?
@@ -185,18 +185,25 @@ class ContentCatalog {
185
185
  } else if ('out' in file) {
186
186
  delete file.out
187
187
  } else if (
188
+ !file.private &&
188
189
  (family === 'page' || family === 'image' || family === 'attachment') &&
189
- ('/' + src.relative).indexOf('/_') < 0
190
+ (file.private === false || ('/' + src.relative).indexOf('/_') < 0)
190
191
  ) {
191
192
  publishable = true
192
193
  if (componentVersion == null) componentVersion = this.getComponentVersion(component, version) || { version }
193
- activeVersionSegment = computeVersionSegment.call(this, componentVersion)
194
+ activeVersionSegment =
195
+ 'activeVersionSegment' in componentVersion
196
+ ? componentVersion.activeVersionSegment
197
+ : computeVersionSegment.call(this, componentVersion)
194
198
  file.out = computeOut(src, family, activeVersionSegment, this.htmlUrlExtensionStyle)
195
199
  }
196
200
  if (!file.pub && (publishable || family === 'nav')) {
197
201
  if (activeVersionSegment == null) {
198
202
  if (componentVersion == null) componentVersion = this.getComponentVersion(component, version) || { version }
199
- activeVersionSegment = computeVersionSegment.call(this, componentVersion)
203
+ activeVersionSegment =
204
+ 'activeVersionSegment' in componentVersion
205
+ ? componentVersion.activeVersionSegment
206
+ : computeVersionSegment.call(this, componentVersion)
200
207
  }
201
208
  file.pub = computePub(src, file.out, family, activeVersionSegment, this.htmlUrlExtensionStyle)
202
209
  }
@@ -243,7 +250,7 @@ class ContentCatalog {
243
250
  }
244
251
 
245
252
  getComponentVersion (component, version) {
246
- return (component.versions || (this.getComponent(component) || {}).versions || []).find(
253
+ return (component.versions || this.getComponent(component)?.versions || []).find(
247
254
  ({ version: candidate }) => candidate === version
248
255
  )
249
256
  }
@@ -271,9 +278,8 @@ class ContentCatalog {
271
278
  const accum = []
272
279
  for (const candidate of candidates.values()) filter(candidate) && accum.push(candidate)
273
280
  return accum
274
- } else {
275
- return [...candidates.values()]
276
281
  }
282
+ return [...candidates.values()]
277
283
  }
278
284
 
279
285
  // TODO add `follow` argument to control whether alias is followed
@@ -370,7 +376,8 @@ class ContentCatalog {
370
376
  if (rootIndexAlias) return rootIndexAlias.synthetic ? Object.assign(rootIndexAlias, { rel }) : undefined
371
377
  const src = Object.assign({}, ROOT_INDEX_ALIAS_ID)
372
378
  return this.addFile({ src, rel, synthetic: true }, { version: src.version })
373
- } else if (rel === false) {
379
+ }
380
+ if (rel === false) {
374
381
  logger.warn('Start page specified for site has invalid syntax: %s', startPageSpec)
375
382
  } else if (startPageSpec.lastIndexOf(':') > startPageSpec.indexOf(':')) {
376
383
  logger.warn('Start page specified for site not found: %s', startPageSpec)
@@ -400,10 +407,10 @@ class ContentCatalog {
400
407
  throw new Error(
401
408
  existingPage === target
402
409
  ? `Page cannot define alias that references itself: ${generateResourceSpec(src)}` +
403
- ` (specified as: ${spec})${LOG_WRAP}source: ${summarizeFileLocation(existingPage)}`
410
+ ` (specified as: ${spec})${LOG_WRAP}source: ${summarizeFileLocation(existingPage)}`
404
411
  : `Page alias cannot reference an existing page: ${generateResourceSpec(src)} (specified as: ${spec})` +
405
- `${LOG_WRAP}source: ${summarizeFileLocation(target)}` +
406
- `${LOG_WRAP}existing page: ${summarizeFileLocation(existingPage)}`
412
+ `${LOG_WRAP}source: ${summarizeFileLocation(target)}` +
413
+ `${LOG_WRAP}existing page: ${summarizeFileLocation(existingPage)}`
407
414
  )
408
415
  }
409
416
  } else if (src.version == null) {
@@ -448,26 +455,48 @@ class ContentCatalog {
448
455
  }
449
456
 
450
457
  /**
451
- * Attempts to resolve a string contextual page ID spec to a file in the catalog.
458
+ * Attempts to resolve a page reference within the given context to a page in the catalog.
452
459
  *
453
- * Parses the specified contextual page ID spec into a page ID object, then attempts to lookup a
454
- * file with this page ID in the catalog. If a component is specified, but not a version, the
455
- * latest version of the component stored in the catalog is used. If a page cannot be resolved,
456
- * the search is attempted again for an "alias". If neither a page or alias can be resolved, the
457
- * function returns undefined. If the spec does not match the page ID syntax, this function throws
458
- * an error.
460
+ * Parses the specified page reference (i.e., page ID spec) into a partial page ID, expands it
461
+ * using the provided context, then attempts to locate a file in the page family with that page ID
462
+ * in this catalog. The family segment is optional.
459
463
  *
460
- * @param {String} spec - The contextual page ID spec (e.g., version@component:module:topic/page.adoc).
461
- * @param {ContentCatalog} catalog - The content catalog in which to resolve the page file.
462
- * @param {Object} [ctx={}] - The context to use to qualified the contextual page ID.
464
+ * If a component is specified, but no version, the latest version of the component stored in the
465
+ * catalog is used. If a page cannot be resolved, the search is attempted again for an "alias". If
466
+ * neither a page or alias can be resolved, the function returns undefined. If the syntax of the
467
+ * reference is invalid, this function throws an error.
463
468
  *
464
- * @returns {File} The virtual file to which the contextual page ID spec refers, or undefined if the
465
- * file cannot be resolved.
469
+ * @param {String} spec - The contextual page reference (e.g., version@component:module:topic/page.adoc).
470
+ * @param {Object} [context={}] - The context to use to qualify the page reference.
471
+ *
472
+ * @returns {File} The virtual file to which the contextual page reference resolves, or undefined
473
+ * if the file cannot be resolved.
466
474
  */
467
475
  resolvePage (spec, context = {}) {
468
476
  return this.resolveResource(spec, context, 'page', ['page'])
469
477
  }
470
478
 
479
+ /**
480
+ * Attempts to resolve a resource reference within the given context to a file in the catalog.
481
+ *
482
+ * Parses the specified resource reference (i.e., resource ID spec) into a partial resource ID,
483
+ * expands it using the provided context, then attempts to locate a file with that resource ID in
484
+ * this catalog.
485
+ *
486
+ * If a component is specified, but no version, the latest version of the component stored in the
487
+ * catalog is used. If a defaultFamily is not specified, the family must be specified either by
488
+ * the reference or the context. If permittedFamilies are stated, the family must resolve to a
489
+ * family in this list. If a file cannot be resolved, the function returns undefined. If the
490
+ * syntax of the reference is invalid, this function throws an error.
491
+ *
492
+ * @param {String} spec - The contextual resource reference (e.g., version@component:module:image$topic/image.png).
493
+ * @param {Object} [context={}] - The context to use to qualify the resource reference.
494
+ * @param {String} [defaultFamily=undefined] - The default family to use if one is not provided.
495
+ * @param {Array<String>} [permittedFamilies=undefined] - A list of families that are permitted.
496
+ *
497
+ * @returns {File} The virtual file to which the contextual resource reference resolves, or
498
+ * undefined if the file cannot be resolved.
499
+ */
471
500
  resolveResource (spec, context = {}, defaultFamily = undefined, permittedFamilies = undefined) {
472
501
  return resolveResource(spec, this, context, defaultFamily, permittedFamilies)
473
502
  }
@@ -509,29 +538,25 @@ function generateResourceSpec ({ component, version, module: module_, family, re
509
538
 
510
539
  function prepareSrc (src) {
511
540
  let { basename, extname, stem } = src
512
- let update
513
541
  if (basename == null) {
514
- update = true
515
- basename = path.basename(src.relative)
542
+ basename = src.basename = path.basename(src.relative)
516
543
  }
517
544
  if (stem == null) {
518
- update = true
519
545
  if (extname == null) {
520
546
  if (~(extname = basename.lastIndexOf('.'))) {
521
- stem = basename.substr(0, extname)
522
- extname = basename.substr(extname)
547
+ src.stem = basename.substr(0, extname)
548
+ src.extname = basename.substr(extname)
523
549
  } else {
524
- stem = basename
525
- extname = ''
550
+ src.stem = basename
551
+ src.extname = ''
526
552
  }
527
553
  } else {
528
- stem = basename.substr(0, basename.length - extname.length)
554
+ src.stem = basename.substr(0, basename.length - extname.length)
529
555
  }
530
556
  } else if (extname == null) {
531
- update = true
532
- extname = basename.substr(stem.length)
557
+ src.extname = basename.substr(stem.length)
533
558
  }
534
- return update ? Object.assign(src, { basename, extname, stem }) : src
559
+ return src
535
560
  }
536
561
 
537
562
  function computeOut (src, family, versionSegment, htmlUrlExtensionStyle) {
@@ -591,7 +616,7 @@ function computePub (src, out, family, versionSegment, htmlUrlExtensionStyle) {
591
616
  } else if ((url = '/' + out.path) === '/.') {
592
617
  url = '/'
593
618
  }
594
- pub.url = ~url.indexOf(' ') ? url.replace(SPACE_RX, '%20') : url
619
+ pub.url = ~url.indexOf(' ') ? url.replaceAll(' ', '%20') : url
595
620
  return out ? Object.assign(pub, { moduleRootPath: out.moduleRootPath, rootPath: out.rootPath }) : pub
596
621
  }
597
622
 
@@ -26,7 +26,6 @@ const parseResourceId = require('./parse-resource-id')
26
26
  */
27
27
  function resolveResource (spec, catalog, ctx = {}, defaultFamily = undefined, permittedFamilies = undefined) {
28
28
  const id = parseResourceId(spec, ctx, defaultFamily, permittedFamilies)
29
-
30
29
  if (!id || !id.family) return false
31
30
  if (id.version == null) {
32
31
  const component = catalog.getComponent(id.component)
@@ -34,10 +33,9 @@ function resolveResource (spec, catalog, ctx = {}, defaultFamily = undefined, pe
34
33
  id.version = component.latest.version
35
34
  }
36
35
  if (!id.module) id.module = 'ROOT'
37
-
38
36
  return (
39
37
  catalog.getById(id) ||
40
- (id.family === 'page' ? (catalog.getById(Object.assign({}, id, { family: 'alias' })) || {}).rel : undefined)
38
+ (id.family === 'page' ? catalog.getById(Object.assign({}, id, { family: 'alias' }))?.rel : undefined)
41
39
  )
42
40
  }
43
41
 
@@ -24,11 +24,8 @@ function versionCompareDesc (a, b) {
24
24
  if (a && b) {
25
25
  const semverA = resolveSemver(a)
26
26
  const semverB = resolveSemver(b)
27
- if (semverA) {
28
- return semverB ? -semverCompare(semverA, semverB) : 1
29
- } else {
30
- return semverB ? -1 : -2 * a.localeCompare(b, 'en', { numeric: true })
31
- }
27
+ if (semverA) return semverB ? -semverCompare(semverA, semverB) : 1
28
+ return semverB ? -1 : -2 * a.localeCompare(b, 'en', { numeric: true })
32
29
  }
33
30
  return a ? 1 : -1
34
31
  }
@@ -64,23 +61,18 @@ function semverCompare (a, b) {
64
61
  const numsA = a.split('.')
65
62
  const numsB = b.split('.')
66
63
  for (let i = 0; i < 3; i++) {
67
- const numA = Number(numsA[i] || 0)
68
- const numB = Number(numsB[i] || 0)
69
- if (numA > numB) {
70
- return 1
71
- } else if (numB > numA) {
72
- return -1
73
- } else if (isNaN(numA)) {
74
- if (!isNaN(numB)) return -1
75
- } else if (isNaN(numB)) {
64
+ const numA = Number(numsA[i] ?? 0)
65
+ const numB = Number(numsB[i] ?? 0)
66
+ if (numA > numB) return 1
67
+ if (numB > numA) return -1
68
+ if (Number.isNaN(numA)) {
69
+ if (!Number.isNaN(numB)) return -1
70
+ } else if (Number.isNaN(numB)) {
76
71
  return 1
77
72
  }
78
73
  }
79
- if (preA == null) {
80
- return preB == null ? 0 : 1
81
- } else {
82
- return preB == null ? -1 : preA.localeCompare(preB, 'en', { numeric: true })
83
- }
74
+ if (preA == null) return preB == null ? 0 : 1
75
+ return preB == null ? -1 : preA.localeCompare(preB, 'en', { numeric: true })
84
76
  }
85
77
 
86
78
  module.exports = versionCompareDesc
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/content-classifier",
3
- "version": "3.2.0-alpha.6",
3
+ "version": "3.2.0-alpha.9",
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)",
@@ -10,7 +10,11 @@
10
10
  "Hubert SABLONNIÈRE <hubert.sablonniere@gmail.com>"
11
11
  ],
12
12
  "homepage": "https://antora.org",
13
- "repository": "gitlab:antora/antora",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://gitlab.com/antora/antora.git",
16
+ "directory": "packages/content-classifier"
17
+ },
14
18
  "bugs": {
15
19
  "url": "https://gitlab.com/antora/antora/issues"
16
20
  },
@@ -27,8 +31,8 @@
27
31
  "#constants": "./lib/constants.js"
28
32
  },
29
33
  "dependencies": {
30
- "@antora/asciidoc-loader": "3.2.0-alpha.6",
31
- "@antora/logger": "3.2.0-alpha.6",
34
+ "@antora/asciidoc-loader": "3.2.0-alpha.9",
35
+ "@antora/logger": "3.2.0-alpha.9",
32
36
  "mime-types": "~2.1",
33
37
  "vinyl": "~3.0"
34
38
  },