@antora/content-classifier 3.0.0-alpha.6 → 3.0.0-beta.1
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 +15 -11
- package/lib/content-catalog.js +57 -42
- package/lib/util/parse-resource-id.js +10 -3
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The Content Classifier is a component in Antora responsible for organizing aggregated content into a virtual file catalog for use in an Antora documentation pipeline.
|
|
4
4
|
|
|
5
5
|
[Antora](https://antora.org) is a modular static site generator designed for creating documentation sites from AsciiDoc documents.
|
|
6
|
-
Its site generator
|
|
6
|
+
Its site generator aggregates documents from versioned content repositories and processes them using [Asciidoctor](https://asciidoctor.org).
|
|
7
7
|
|
|
8
8
|
## Copyright and License
|
|
9
9
|
|
package/lib/classify-content.js
CHANGED
|
@@ -43,10 +43,12 @@ function classifyContent (playbook, aggregate, siteAsciiDocConfig = {}) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function allocateSrc (file, component, version, nav) {
|
|
46
|
+
const extname = file.src.extname
|
|
46
47
|
const filepath = file.path
|
|
47
|
-
const pathSegments = filepath.split('/')
|
|
48
48
|
const navInfo = nav && getNavInfo(filepath, nav)
|
|
49
|
+
const pathSegments = filepath.split('/')
|
|
49
50
|
if (navInfo) {
|
|
51
|
+
if (extname !== '.adoc') return // ignore file
|
|
50
52
|
file.nav = navInfo
|
|
51
53
|
file.src.family = 'nav'
|
|
52
54
|
if (pathSegments[0] === 'modules' && pathSegments.length > 2) {
|
|
@@ -67,45 +69,47 @@ function allocateSrc (file, component, version, nav) {
|
|
|
67
69
|
file.src.family = 'partial'
|
|
68
70
|
// relative to modules/<module>/pages/_partials (deprecated)
|
|
69
71
|
file.src.relative = pathSegments.slice(4).join('/')
|
|
70
|
-
} else if (
|
|
72
|
+
} else if (extname === '.adoc') {
|
|
71
73
|
file.src.family = 'page'
|
|
72
74
|
// relative to modules/<module>/pages
|
|
73
75
|
file.src.relative = pathSegments.slice(3).join('/')
|
|
74
76
|
} else {
|
|
75
|
-
// ignore file
|
|
76
|
-
return
|
|
77
|
+
return // ignore file
|
|
77
78
|
}
|
|
78
79
|
break
|
|
79
80
|
case 'assets':
|
|
80
81
|
switch ((familyFolder = pathSegments[3])) {
|
|
81
82
|
case 'attachments':
|
|
82
83
|
case 'images':
|
|
84
|
+
if (!extname) return // ignore file
|
|
83
85
|
file.src.family = familyFolder.substr(0, familyFolder.length - 1)
|
|
84
86
|
// relative to modules/<module>/assets/<family>s
|
|
85
87
|
file.src.relative = pathSegments.slice(4).join('/')
|
|
86
88
|
break
|
|
87
89
|
default:
|
|
88
|
-
// ignore file
|
|
89
|
-
return
|
|
90
|
+
return // ignore file
|
|
90
91
|
}
|
|
91
92
|
break
|
|
92
93
|
case 'attachments':
|
|
93
|
-
case 'examples':
|
|
94
94
|
case 'images':
|
|
95
|
+
if (!extname) return
|
|
96
|
+
file.src.family = familyFolder.substr(0, familyFolder.length - 1)
|
|
97
|
+
// relative to modules/<module>/<family>s
|
|
98
|
+
file.src.relative = pathSegments.slice(3).join('/')
|
|
99
|
+
break
|
|
100
|
+
case 'examples':
|
|
95
101
|
case 'partials':
|
|
96
102
|
file.src.family = familyFolder.substr(0, familyFolder.length - 1)
|
|
97
103
|
// relative to modules/<module>/<family>s
|
|
98
104
|
file.src.relative = pathSegments.slice(3).join('/')
|
|
99
105
|
break
|
|
100
106
|
default:
|
|
101
|
-
// ignore file
|
|
102
|
-
return
|
|
107
|
+
return // ignore file
|
|
103
108
|
}
|
|
104
109
|
file.src.module = pathSegments[1]
|
|
105
110
|
file.src.moduleRootPath = calculateRootPath(pathSegments.length - 3)
|
|
106
111
|
} else {
|
|
107
|
-
// ignore file
|
|
108
|
-
return
|
|
112
|
+
return // ignore file
|
|
109
113
|
}
|
|
110
114
|
file.src.component = component
|
|
111
115
|
file.src.version = version
|
package/lib/content-catalog.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const File = require('./file')
|
|
4
|
+
const invariably = { void: () => undefined }
|
|
4
5
|
const logger = require('./logger')
|
|
5
6
|
const { lookup: resolveMimeType } = require('./mime-types-with-asciidoc')
|
|
6
7
|
const parseResourceId = require('./util/parse-resource-id')
|
|
@@ -59,7 +60,7 @@ class ContentCatalog {
|
|
|
59
60
|
Object.defineProperty(componentVersion, 'name', { value: name, enumerable: true })
|
|
60
61
|
if (prerelease) {
|
|
61
62
|
componentVersion.prerelease = prerelease
|
|
62
|
-
if (!displayVersion &&
|
|
63
|
+
if (!displayVersion && prerelease.constructor === String) {
|
|
63
64
|
if (version) {
|
|
64
65
|
const ch0 = prerelease.charAt()
|
|
65
66
|
componentVersion.displayVersion = `${version}${ch0 === '-' || ch0 === '.' ? '' : ' '}${prerelease}`
|
|
@@ -72,15 +73,17 @@ class ContentCatalog {
|
|
|
72
73
|
const component = this[$components].get(name)
|
|
73
74
|
if (component) {
|
|
74
75
|
const componentVersions = component.versions
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return versionCompare(candidate, version) > 0
|
|
78
|
-
})
|
|
79
|
-
if (~insertIdx) {
|
|
80
|
-
componentVersions.splice(insertIdx, 0, componentVersion)
|
|
81
|
-
} else {
|
|
82
|
-
componentVersions.push(componentVersion)
|
|
76
|
+
if (componentVersions.find(({ version: candidate }) => candidate === version)) {
|
|
77
|
+
throw new Error(`Duplicate version detected for component ${name}: ${version}`)
|
|
83
78
|
}
|
|
79
|
+
const insertIdx = prerelease
|
|
80
|
+
? componentVersions.findIndex(({ version: candidateVersion, prerelease: candidatePrerelease }) =>
|
|
81
|
+
candidatePrerelease ? versionCompare(candidateVersion, version) > 0 : true
|
|
82
|
+
)
|
|
83
|
+
: componentVersions.findIndex(({ version: candidateVersion, prerelease: candidatePrerelease }) =>
|
|
84
|
+
candidatePrerelease ? false : versionCompare(candidateVersion, version) > 0
|
|
85
|
+
)
|
|
86
|
+
~insertIdx ? componentVersions.splice(insertIdx, 0, componentVersion) : componentVersions.push(componentVersion)
|
|
84
87
|
if ((component.latest = componentVersions.find((candidate) => !candidate.prerelease))) {
|
|
85
88
|
if (componentVersions[0] !== component.latest) component.latestPrerelease = componentVersions[0]
|
|
86
89
|
} else {
|
|
@@ -120,12 +123,16 @@ class ContentCatalog {
|
|
|
120
123
|
addFile (file) {
|
|
121
124
|
const src = file.src
|
|
122
125
|
let family = src.family
|
|
126
|
+
let filesForFamily = this[$files].get(family)
|
|
127
|
+
if (!filesForFamily) this[$files].set(family, (filesForFamily = new Map()))
|
|
123
128
|
const key = generateKey(src)
|
|
124
|
-
if (
|
|
129
|
+
if (filesForFamily.has(key)) {
|
|
125
130
|
if (family === 'alias') {
|
|
126
131
|
throw new Error(`Duplicate alias: ${generateResourceSpec(src)}`)
|
|
127
132
|
} else {
|
|
128
|
-
const details = [
|
|
133
|
+
const details = [filesForFamily.get(key), file]
|
|
134
|
+
.map((it, idx) => ` ${idx + 1}: ${getFileLocation(it)}`)
|
|
135
|
+
.join('\n')
|
|
129
136
|
if (family === 'nav') {
|
|
130
137
|
throw new Error(`Duplicate nav in ${src.version}@${src.component}: ${file.path}\n${details}`)
|
|
131
138
|
} else {
|
|
@@ -162,35 +169,42 @@ class ContentCatalog {
|
|
|
162
169
|
('/' + src.relative).indexOf('/_') < 0
|
|
163
170
|
) {
|
|
164
171
|
publishable = true
|
|
165
|
-
versionSegment = computeVersionSegment.
|
|
172
|
+
versionSegment = computeVersionSegment.call(this, src.component, src.version)
|
|
166
173
|
file.out = computeOut(src, family, versionSegment, this.htmlUrlExtensionStyle)
|
|
167
174
|
}
|
|
168
175
|
if (!file.pub && (publishable || family === 'nav')) {
|
|
169
|
-
if (versionSegment == null) versionSegment = computeVersionSegment.
|
|
176
|
+
if (versionSegment == null) versionSegment = computeVersionSegment.call(this, src.component, src.version)
|
|
170
177
|
file.pub = computePub(src, file.out, family, versionSegment, this.htmlUrlExtensionStyle)
|
|
171
178
|
}
|
|
172
|
-
|
|
179
|
+
filesForFamily.set(key, file)
|
|
173
180
|
return file
|
|
174
181
|
}
|
|
175
182
|
|
|
176
183
|
findBy (criteria) {
|
|
177
184
|
const criteriaEntries = Object.entries(criteria)
|
|
185
|
+
const family = criteria.family
|
|
186
|
+
if (criteriaEntries.length === 1 && family) {
|
|
187
|
+
const filesForFamily = this[$files].get(family)
|
|
188
|
+
return filesForFamily ? [...filesForFamily.values()] : []
|
|
189
|
+
}
|
|
178
190
|
const accum = []
|
|
179
|
-
for (const
|
|
180
|
-
const
|
|
181
|
-
|
|
191
|
+
for (const filesForFamily of this[$files].values()) {
|
|
192
|
+
for (const candidate of filesForFamily.values()) {
|
|
193
|
+
const candidateSrc = candidate.src
|
|
194
|
+
if (criteriaEntries.every(([key, val]) => candidateSrc[key] === val)) accum.push(candidate)
|
|
195
|
+
}
|
|
182
196
|
}
|
|
183
197
|
return accum
|
|
184
198
|
}
|
|
185
199
|
|
|
186
|
-
getById (
|
|
187
|
-
return this[$files].get(
|
|
200
|
+
getById (id) {
|
|
201
|
+
return (this[$files].get(id.family) || { get: invariably.void }).get(generateKey(id))
|
|
188
202
|
}
|
|
189
203
|
|
|
190
204
|
getByPath ({ component, version, path: path_ }) {
|
|
191
|
-
for (const
|
|
192
|
-
|
|
193
|
-
return
|
|
205
|
+
for (const filesForFamily of this[$files].values()) {
|
|
206
|
+
for (const it of filesForFamily.values()) {
|
|
207
|
+
if (it.path === path_ && it.src.component === component && it.src.version === version) return it
|
|
194
208
|
}
|
|
195
209
|
}
|
|
196
210
|
}
|
|
@@ -214,21 +228,23 @@ class ContentCatalog {
|
|
|
214
228
|
}
|
|
215
229
|
|
|
216
230
|
getFiles () {
|
|
217
|
-
|
|
231
|
+
const accum = []
|
|
232
|
+
for (const filesForFamily of this[$files].values()) {
|
|
233
|
+
for (const file of filesForFamily.values()) accum.push(file)
|
|
234
|
+
}
|
|
235
|
+
return accum
|
|
218
236
|
}
|
|
219
237
|
|
|
220
238
|
getPages (filter) {
|
|
221
|
-
const
|
|
239
|
+
const candidates = this[$files].get('page')
|
|
240
|
+
if (!candidates) return []
|
|
222
241
|
if (filter) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
242
|
+
const accum = []
|
|
243
|
+
for (const candidate of candidates.values()) filter(candidate) && accum.push(candidate)
|
|
244
|
+
return accum
|
|
226
245
|
} else {
|
|
227
|
-
|
|
228
|
-
if (candidate.src.family === 'page') accum.push(candidate)
|
|
229
|
-
}
|
|
246
|
+
return [...candidates.values()]
|
|
230
247
|
}
|
|
231
|
-
return accum
|
|
232
248
|
}
|
|
233
249
|
|
|
234
250
|
// TODO add `follow` argument to control whether alias is followed
|
|
@@ -273,7 +289,7 @@ class ContentCatalog {
|
|
|
273
289
|
componentVersion.url = startPage.pub.url
|
|
274
290
|
} else {
|
|
275
291
|
// QUESTION: should we warn if the default start page cannot be found?
|
|
276
|
-
const versionSegment = computeVersionSegment.
|
|
292
|
+
const versionSegment = computeVersionSegment.call(this, name, version)
|
|
277
293
|
componentVersion.url = computePub(
|
|
278
294
|
(startPageSrc = prepareSrc(Object.assign({}, indexPageId, { family: 'page' }))),
|
|
279
295
|
computeOut(startPageSrc, startPageSrc.family, versionSegment, this.htmlUrlExtensionStyle),
|
|
@@ -286,7 +302,7 @@ class ContentCatalog {
|
|
|
286
302
|
const symbolicVersionAlias = createSymbolicVersionAlias(
|
|
287
303
|
name,
|
|
288
304
|
version,
|
|
289
|
-
computeVersionSegment.
|
|
305
|
+
computeVersionSegment.call(this, name, version, 'alias'),
|
|
290
306
|
this.latestVersionUrlSegmentStrategy
|
|
291
307
|
)
|
|
292
308
|
if (symbolicVersionAlias) this.addFile(symbolicVersionAlias)
|
|
@@ -328,18 +344,17 @@ class ContentCatalog {
|
|
|
328
344
|
` existing page: ${getFileLocation(existingPage)}`
|
|
329
345
|
)
|
|
330
346
|
}
|
|
331
|
-
const existingAlias = this.getById(Object.assign({}, src, { family: 'alias' }))
|
|
332
|
-
if (existingAlias) {
|
|
333
|
-
throw new Error(
|
|
334
|
-
`Duplicate alias: ${generateResourceSpec(src)} (specified as: ${spec})\n` +
|
|
335
|
-
` source: ${getFileLocation(target)}`
|
|
336
|
-
)
|
|
337
|
-
}
|
|
338
347
|
} else if (src.version == null) {
|
|
339
348
|
// QUESTION should we skip registering alias in this case?
|
|
340
349
|
src.version = ''
|
|
341
350
|
}
|
|
342
351
|
src.family = 'alias'
|
|
352
|
+
const existingAlias = this.getById(src)
|
|
353
|
+
if (existingAlias) {
|
|
354
|
+
throw new Error(
|
|
355
|
+
`Duplicate alias: ${generateResourceSpec(src)} (specified as: ${spec})\n source: ${getFileLocation(target)}`
|
|
356
|
+
)
|
|
357
|
+
}
|
|
343
358
|
// NOTE the redirect producer will populate contents when the redirect facility is 'static'
|
|
344
359
|
const alias = this.addFile({ src, rel: target })
|
|
345
360
|
// NOTE record the first alias this target claims as the preferred one
|
|
@@ -394,8 +409,8 @@ class ContentCatalog {
|
|
|
394
409
|
*/
|
|
395
410
|
ContentCatalog.prototype.getAll = ContentCatalog.prototype.getFiles
|
|
396
411
|
|
|
397
|
-
function generateKey ({ component, version, module: module_,
|
|
398
|
-
return `${version}@${component}:${module_}:${
|
|
412
|
+
function generateKey ({ component, version, module: module_, relative }) {
|
|
413
|
+
return `${version}@${component}:${module_}:${relative}`
|
|
399
414
|
}
|
|
400
415
|
|
|
401
416
|
function generateResourceSpec ({ component, version, module: module_, family, relative }, shorthand = true) {
|
|
@@ -56,9 +56,16 @@ function parseResourceId (spec, ctx = {}, defaultFamily = 'page', permittedFamil
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
if (~relative.indexOf('/')) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const relativeSegments = relative.split('/')
|
|
60
|
+
let from
|
|
61
|
+
if (relativeSegments[0] === '.' && (from = ctx.relative)) {
|
|
62
|
+
relativeSegments[0] = from.substr(0, (from.lastIndexOf('/') + 1 || 1) - 1)
|
|
63
|
+
}
|
|
64
|
+
relative = relativeSegments
|
|
65
|
+
.reduce((accum, segment) => {
|
|
66
|
+
segment === '..' ? accum.pop() : (segment || '.') !== '.' && accum.push(segment)
|
|
67
|
+
return accum
|
|
68
|
+
}, [])
|
|
62
69
|
.join('/')
|
|
63
70
|
}
|
|
64
71
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/content-classifier",
|
|
3
|
-
"version": "3.0.0-
|
|
3
|
+
"version": "3.0.0-beta.1",
|
|
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)",
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
},
|
|
17
17
|
"main": "lib/index.js",
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@antora/logger": "3.0.0-
|
|
19
|
+
"@antora/logger": "3.0.0-beta.1",
|
|
20
20
|
"mime-types": "~2.1",
|
|
21
21
|
"vinyl": "~2.2"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
|
-
"node": ">=
|
|
24
|
+
"node": ">=12.21.0"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"lib/"
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"static site",
|
|
35
35
|
"web publishing"
|
|
36
36
|
],
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "7c5ef1ea93dd489af533c80a936c736013c41769"
|
|
38
38
|
}
|