@antora/content-classifier 3.2.0-alpha.8 → 3.2.0-rc.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/lib/classify-content.js
CHANGED
|
@@ -4,6 +4,7 @@ const ContentCatalog = require('./content-catalog')
|
|
|
4
4
|
const collateAsciiDocAttributes = require('@antora/asciidoc-loader/config/collate-asciidoc-attributes')
|
|
5
5
|
const logger = require('./logger')
|
|
6
6
|
const summarizeFileLocation = require('./util/summarize-file-location')
|
|
7
|
+
const families = { attachments: true, examples: true, images: true, pages: true, partials: true }
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Organizes the raw aggregate of virtual files into a {ContentCatalog}.
|
|
@@ -45,9 +46,21 @@ function addFilesAndRegisterStartPages (contentCatalog, siteStartPage) {
|
|
|
45
46
|
for (const componentVersion of componentVersions) {
|
|
46
47
|
const { name: component, version, files = [], nav, startPage } = componentVersion
|
|
47
48
|
const navResolved = nav && (nav.resolved = new Set())
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
const rootFiles = []
|
|
50
|
+
for (let file, i = 0; (file = files[i]); i++) {
|
|
50
51
|
files[i] = undefined // free memory
|
|
52
|
+
const outcome = allocateSrc(file, component, version, componentVersion, nav)
|
|
53
|
+
if (outcome) {
|
|
54
|
+
contentCatalog.addFile(file)
|
|
55
|
+
} else if (outcome == null) {
|
|
56
|
+
rootFiles.push(file)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (rootFiles.length) {
|
|
60
|
+
for (let file, i = 0; (file = rootFiles[i]); i++) {
|
|
61
|
+
if (file.src.origin.hasModulesDir) continue
|
|
62
|
+
allocateSrc(file, component, version, componentVersion, null, true) && contentCatalog.addFile(file)
|
|
63
|
+
}
|
|
51
64
|
}
|
|
52
65
|
if (navResolved && nav.length > navResolved.size && new Set(nav).size > navResolved.size) {
|
|
53
66
|
const loc = summarizeFileLocation({ path: 'antora.yml', src: { origin: nav.origin } })
|
|
@@ -63,21 +76,24 @@ function addFilesAndRegisterStartPages (contentCatalog, siteStartPage) {
|
|
|
63
76
|
return contentCatalog
|
|
64
77
|
}
|
|
65
78
|
|
|
66
|
-
function allocateSrc (file, component, version, nav) {
|
|
67
|
-
const { extname, family } = file.src
|
|
79
|
+
function allocateSrc (file, component, version, componentVersion, nav, implicitRoot) {
|
|
80
|
+
const { extname, family, origin } = file.src
|
|
68
81
|
if (family && family !== 'nav') {
|
|
69
|
-
Object.assign(file.src, { component, version })
|
|
82
|
+
Object.assign(file.src, { component, version, componentVersion })
|
|
83
|
+
if (!('private' in file) && ~('/' + file.src.relative).indexOf('/_')) file.private = true
|
|
70
84
|
file.src.moduleRootPath ??= calculateRootPath(file.src.relative.split('/').length)
|
|
71
85
|
return true
|
|
72
86
|
}
|
|
73
87
|
const filepath = file.path
|
|
74
|
-
const pathSegments = filepath.split('/')
|
|
88
|
+
const pathSegments = implicitRoot ? ['modules', 'ROOT'].concat(filepath.split('/')) : filepath.split('/')
|
|
89
|
+
const inModules = pathSegments[0] === 'modules'
|
|
90
|
+
if (!implicitRoot && origin) origin.hasModulesDir ||= inModules
|
|
75
91
|
let navInfo
|
|
76
92
|
if (nav && (navInfo = getNavInfo(filepath, nav))) {
|
|
77
|
-
if (extname !== '.adoc') return // ignore file
|
|
93
|
+
if (extname !== '.adoc') return false // ignore file
|
|
78
94
|
file.nav = navInfo
|
|
79
95
|
file.src.family = 'nav'
|
|
80
|
-
if (
|
|
96
|
+
if (inModules && pathSegments.length > 2) {
|
|
81
97
|
file.src.module = pathSegments[1]
|
|
82
98
|
// relative to modules/<module>
|
|
83
99
|
file.src.relative = pathSegments.slice(2).join('/')
|
|
@@ -86,7 +102,7 @@ function allocateSrc (file, component, version, nav) {
|
|
|
86
102
|
// relative to content source root
|
|
87
103
|
file.src.relative = filepath
|
|
88
104
|
}
|
|
89
|
-
} else if (
|
|
105
|
+
} else if (inModules) {
|
|
90
106
|
let familyFolder = pathSegments[2]
|
|
91
107
|
switch (familyFolder) {
|
|
92
108
|
case 'pages':
|
|
@@ -100,45 +116,47 @@ function allocateSrc (file, component, version, nav) {
|
|
|
100
116
|
// relative to modules/<module>/pages
|
|
101
117
|
file.src.relative = pathSegments.slice(3).join('/')
|
|
102
118
|
} else {
|
|
103
|
-
return // ignore file
|
|
119
|
+
return false // ignore file
|
|
104
120
|
}
|
|
105
121
|
break
|
|
106
122
|
case 'assets':
|
|
107
123
|
switch ((familyFolder = pathSegments[3])) {
|
|
108
124
|
case 'attachments':
|
|
109
125
|
case 'images':
|
|
110
|
-
if (!extname) return // ignore file
|
|
111
|
-
file.src.family = familyFolder.
|
|
126
|
+
if (!extname && familyFolder === 'images') return false // ignore file
|
|
127
|
+
file.src.family = familyFolder.substring(0, familyFolder.length - 1)
|
|
112
128
|
// relative to modules/<module>/assets/<family>s
|
|
113
129
|
file.src.relative = pathSegments.slice(4).join('/')
|
|
114
130
|
break
|
|
115
131
|
default:
|
|
116
|
-
return // ignore file
|
|
132
|
+
return false // ignore file
|
|
117
133
|
}
|
|
118
134
|
break
|
|
119
|
-
case 'attachments':
|
|
120
135
|
case 'images':
|
|
121
|
-
if (!extname) return
|
|
122
|
-
file.src.family = familyFolder.
|
|
136
|
+
if (!extname) return false
|
|
137
|
+
file.src.family = familyFolder.substring(0, familyFolder.length - 1)
|
|
123
138
|
// relative to modules/<module>/<family>s
|
|
124
139
|
file.src.relative = pathSegments.slice(3).join('/')
|
|
125
140
|
break
|
|
141
|
+
case 'attachments':
|
|
126
142
|
case 'examples':
|
|
127
143
|
case 'partials':
|
|
128
|
-
file.src.family = familyFolder.
|
|
144
|
+
file.src.family = familyFolder.substring(0, familyFolder.length - 1)
|
|
129
145
|
// relative to modules/<module>/<family>s
|
|
130
146
|
file.src.relative = pathSegments.slice(3).join('/')
|
|
131
147
|
break
|
|
132
148
|
default:
|
|
133
|
-
return // ignore file
|
|
149
|
+
return false // ignore file
|
|
134
150
|
}
|
|
135
151
|
file.src.module = pathSegments[1]
|
|
136
152
|
file.src.moduleRootPath = calculateRootPath(pathSegments.length - 3)
|
|
153
|
+
if (!('private' in file) && ~('/' + file.src.relative).indexOf('/_')) file.private = true
|
|
154
|
+
} else if (origin && pathSegments[0] in families) {
|
|
155
|
+
return // defer file
|
|
137
156
|
} else {
|
|
138
|
-
return // ignore file
|
|
157
|
+
return false // ignore file
|
|
139
158
|
}
|
|
140
|
-
file.src
|
|
141
|
-
file.src.version = version
|
|
159
|
+
Object.assign(file.src, { component, version, componentVersion })
|
|
142
160
|
return true
|
|
143
161
|
}
|
|
144
162
|
|
|
@@ -152,7 +170,7 @@ function allocateSrc (file, component, version, nav) {
|
|
|
152
170
|
* index, if this file is a navigation file, or undefined if it's not.
|
|
153
171
|
*/
|
|
154
172
|
function getNavInfo (filepath, nav) {
|
|
155
|
-
const index = nav.
|
|
173
|
+
const index = nav.indexOf(filepath)
|
|
156
174
|
if (~index) return nav.resolved.add(filepath) && { index }
|
|
157
175
|
}
|
|
158
176
|
|
package/lib/content-catalog.js
CHANGED
|
@@ -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')
|
|
@@ -21,6 +20,7 @@ class ContentCatalog {
|
|
|
21
20
|
constructor (playbook = {}) {
|
|
22
21
|
this[$components] = new Map()
|
|
23
22
|
this[$files] = new Map()
|
|
23
|
+
this.publishableFamilies = new Set(['page', 'image', 'attachment'])
|
|
24
24
|
const urls = playbook.urls || {}
|
|
25
25
|
this.htmlUrlExtensionStyle = urls.htmlExtensionStyle || 'default'
|
|
26
26
|
this.urlRedirectFacility = urls.redirectFacility || 'static'
|
|
@@ -61,8 +61,13 @@ class ContentCatalog {
|
|
|
61
61
|
* @returns {Object} The constructed component version object.
|
|
62
62
|
*/
|
|
63
63
|
registerComponentVersion (name, version, descriptor = {}) {
|
|
64
|
-
const { asciidoc, displayVersion, prerelease, startPage:
|
|
65
|
-
const componentVersion = {
|
|
64
|
+
const { asciidoc, displayVersion, prerelease, startPage: startPageRef, title, versionSegment, origins } = descriptor
|
|
65
|
+
const componentVersion = {
|
|
66
|
+
displayVersion: displayVersion || version || 'default',
|
|
67
|
+
title: title || name,
|
|
68
|
+
version,
|
|
69
|
+
origins: new Set(origins && typeof origins[Symbol.iterator] === 'function' ? origins : []),
|
|
70
|
+
}
|
|
66
71
|
if (versionSegment != null) componentVersion.versionSegment = versionSegment
|
|
67
72
|
Object.defineProperty(componentVersion, 'name', { value: name, enumerable: true })
|
|
68
73
|
if (prerelease) {
|
|
@@ -131,18 +136,18 @@ class ContentCatalog {
|
|
|
131
136
|
)
|
|
132
137
|
)
|
|
133
138
|
}
|
|
134
|
-
if (
|
|
139
|
+
if (startPageRef) {
|
|
135
140
|
// @deprecated use separate call to register start page for component version
|
|
136
|
-
this.registerComponentVersionStartPage(name, componentVersion,
|
|
141
|
+
this.registerComponentVersionStartPage(name, componentVersion, startPageRef === true ? undefined : startPageRef)
|
|
137
142
|
}
|
|
138
143
|
return componentVersion
|
|
139
144
|
}
|
|
140
145
|
|
|
141
|
-
addFile (file
|
|
146
|
+
addFile (file) {
|
|
142
147
|
const src = file.src
|
|
143
|
-
|
|
144
|
-
let filesForFamily
|
|
145
|
-
if (
|
|
148
|
+
const family = src.family
|
|
149
|
+
let filesForFamily
|
|
150
|
+
if ((filesForFamily = this[$files].get(family)) == null) this[$files].set(family, (filesForFamily = new Map()))
|
|
146
151
|
const key = generateKey(src)
|
|
147
152
|
if (filesForFamily.has(key)) {
|
|
148
153
|
if (family === 'alias') {
|
|
@@ -152,53 +157,73 @@ class ContentCatalog {
|
|
|
152
157
|
.map((it, idx) => `${idx + 1}: ${summarizeFileLocation(it)}`)
|
|
153
158
|
.join(LOG_WRAP)
|
|
154
159
|
if (family === 'nav') {
|
|
155
|
-
throw new Error(`Duplicate nav file: ${file.path} in ${version}@${component}${LOG_WRAP}${details}`)
|
|
160
|
+
throw new Error(`Duplicate nav file: ${file.path} in ${src.version}@${src.component}${LOG_WRAP}${details}`)
|
|
156
161
|
}
|
|
157
162
|
throw new Error(`Duplicate ${family}: ${generateResourceSpec(src)}${LOG_WRAP}${details}`)
|
|
158
163
|
}
|
|
159
|
-
|
|
160
|
-
|
|
164
|
+
if (!(file.created && File.isVinyl(file))) file = this.createFile(file)
|
|
165
|
+
filesForFamily.set(key, file)
|
|
166
|
+
return file
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
createFile (file) {
|
|
170
|
+
const src = file.src
|
|
171
|
+
let { componentVersion, family } = src
|
|
172
|
+
if (componentVersion) delete src.componentVersion
|
|
173
|
+
// NOTE: assume that if the file is not a vinyl object, the src likely needs to be prepared
|
|
161
174
|
// a vinyl object is one indication the file was created and prepared by the content aggregator
|
|
162
|
-
//if
|
|
163
|
-
//if (!File.isVinyl(file)) file = new File(file)
|
|
175
|
+
// an alternate approach would be to call inflateSrc if the path property is not set
|
|
164
176
|
if (!File.isVinyl(file)) {
|
|
165
|
-
|
|
177
|
+
inflateSrc(src)
|
|
166
178
|
file = new File(file)
|
|
167
179
|
}
|
|
180
|
+
Object.defineProperty(file, 'created', { configurable: true, writable: true, value: true })
|
|
168
181
|
if (family === 'alias') {
|
|
169
182
|
file.mediaType = 'text/html'
|
|
170
|
-
// NOTE: an alias masquerades as the target file
|
|
171
|
-
family = file.rel.src.family
|
|
172
183
|
// NOTE: short circuit in case of splat alias (alias -> alias)
|
|
173
|
-
if (family === 'alias' && file.pub?.splat) return
|
|
184
|
+
if (file.rel.src.family === 'alias' && file.pub?.splat) return file
|
|
185
|
+
// NOTE the effective family of an alias is a page, which redirects to the target file
|
|
186
|
+
family = 'page'
|
|
174
187
|
src.mediaType = 'text/asciidoc'
|
|
175
188
|
} else if (!(file.mediaType = src.mediaType) && !('mediaType' in src)) {
|
|
176
189
|
// QUESTION: should we preserve the mediaType property on file if already defined?
|
|
177
|
-
file.mediaType = src.mediaType =
|
|
190
|
+
file.mediaType = src.mediaType = src.extname
|
|
191
|
+
? resolveMimeType(src.extname) || (family === 'page' ? 'text/asciidoc' : undefined)
|
|
192
|
+
: 'text/plain'
|
|
178
193
|
}
|
|
179
|
-
let
|
|
180
|
-
let
|
|
194
|
+
let activeVersionSegment = false
|
|
195
|
+
let publishable, referenceable
|
|
181
196
|
if (file.out) {
|
|
182
197
|
publishable = true
|
|
183
198
|
} else if ('out' in file) {
|
|
184
199
|
delete file.out
|
|
185
|
-
} else if (
|
|
186
|
-
(family === 'page' || family === 'image' || family === 'attachment') &&
|
|
187
|
-
('/' + src.relative).indexOf('/_') < 0
|
|
188
|
-
) {
|
|
200
|
+
} else if (!file.private && this.publishableFamilies.has(family)) {
|
|
189
201
|
publishable = true
|
|
190
|
-
|
|
191
|
-
activeVersionSegment =
|
|
192
|
-
|
|
202
|
+
componentVersion ??= this.getComponentVersion(src.component, src.version) || { version: src.version }
|
|
203
|
+
activeVersionSegment =
|
|
204
|
+
'activeVersionSegment' in componentVersion
|
|
205
|
+
? componentVersion.activeVersionSegment
|
|
206
|
+
: computeVersionSegment.call(this, componentVersion)
|
|
207
|
+
file.out = computeOut.call(this, src, family, activeVersionSegment)
|
|
208
|
+
}
|
|
209
|
+
if (!file.pub) {
|
|
210
|
+
if ('pub' in file) {
|
|
211
|
+
delete file.pub
|
|
212
|
+
} else if (publishable || family === 'nav') {
|
|
213
|
+
referenceable = true
|
|
214
|
+
}
|
|
193
215
|
}
|
|
194
|
-
if (
|
|
195
|
-
if (activeVersionSegment
|
|
196
|
-
|
|
197
|
-
activeVersionSegment =
|
|
216
|
+
if (referenceable) {
|
|
217
|
+
if (activeVersionSegment === false) {
|
|
218
|
+
componentVersion ??= this.getComponentVersion(src.component, src.version) || { version: src.version }
|
|
219
|
+
activeVersionSegment =
|
|
220
|
+
'activeVersionSegment' in componentVersion
|
|
221
|
+
? componentVersion.activeVersionSegment
|
|
222
|
+
: computeVersionSegment.call(this, componentVersion)
|
|
198
223
|
}
|
|
199
|
-
file.pub = computePub(src, file.out, family, activeVersionSegment
|
|
224
|
+
file.pub = computePub.call(this, src, file.out, family, activeVersionSegment)
|
|
200
225
|
}
|
|
201
|
-
return
|
|
226
|
+
return file
|
|
202
227
|
}
|
|
203
228
|
|
|
204
229
|
removeFile (file) {
|
|
@@ -254,21 +279,27 @@ class ContentCatalog {
|
|
|
254
279
|
return this.getComponents().sort((a, b) => a[property].localeCompare(b[property]))
|
|
255
280
|
}
|
|
256
281
|
|
|
257
|
-
getFiles () {
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
for (const
|
|
282
|
+
getFiles (filter) {
|
|
283
|
+
const files = []
|
|
284
|
+
if (filter) {
|
|
285
|
+
for (const filesForFamily of this[$files].values()) {
|
|
286
|
+
for (const candidate of filesForFamily.values()) filter(candidate) && files.push(candidate)
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
for (const filesForFamily of this[$files].values()) {
|
|
290
|
+
for (const file of filesForFamily.values()) files.push(file)
|
|
291
|
+
}
|
|
261
292
|
}
|
|
262
|
-
return
|
|
293
|
+
return files
|
|
263
294
|
}
|
|
264
295
|
|
|
265
296
|
getPages (filter) {
|
|
266
297
|
const candidates = this[$files].get('page')
|
|
267
298
|
if (!candidates) return []
|
|
268
299
|
if (filter) {
|
|
269
|
-
const
|
|
270
|
-
for (const candidate of candidates.values()) filter(candidate) &&
|
|
271
|
-
return
|
|
300
|
+
const pages = []
|
|
301
|
+
for (const candidate of candidates.values()) filter(candidate) && pages.push(candidate)
|
|
302
|
+
return pages
|
|
272
303
|
}
|
|
273
304
|
return [...candidates.values()]
|
|
274
305
|
}
|
|
@@ -286,17 +317,14 @@ class ContentCatalog {
|
|
|
286
317
|
if ((file = this.getById(Object.assign({}, ROOT_INDEX_ALIAS_ID, { version })))) return file.rel
|
|
287
318
|
}
|
|
288
319
|
|
|
289
|
-
registerComponentVersionStartPage (
|
|
290
|
-
|
|
291
|
-
let version = componentVersion.version
|
|
292
|
-
if (version == null) {
|
|
320
|
+
registerComponentVersionStartPage (component, componentVersion, startPageSpec = undefined) {
|
|
321
|
+
if (componentVersion.constructor === String) {
|
|
293
322
|
// QUESTION: should we warn or throw error if component version cannot be found?
|
|
294
323
|
if (!(componentVersion = this.getComponentVersion(component, componentVersion))) return
|
|
295
|
-
version = componentVersion.version
|
|
296
324
|
}
|
|
325
|
+
const version = componentVersion.version
|
|
297
326
|
const activeVersionSegment = computeVersionSegment.call(this, componentVersion)
|
|
298
|
-
let startPage
|
|
299
|
-
let startPageSrc
|
|
327
|
+
let startPage, startPageSrc
|
|
300
328
|
const indexPageId = Object.assign({}, ROOT_INDEX_PAGE_ID, { component, version })
|
|
301
329
|
if (startPageSpec) {
|
|
302
330
|
if (
|
|
@@ -305,11 +333,11 @@ class ContentCatalog {
|
|
|
305
333
|
startPageSrc.version === version
|
|
306
334
|
) {
|
|
307
335
|
if (!this.getById(indexPageId)) {
|
|
308
|
-
const indexAliasId = Object.assign({}, ROOT_INDEX_ALIAS_ID, { component, version })
|
|
336
|
+
const indexAliasId = Object.assign({}, ROOT_INDEX_ALIAS_ID, { component, version, componentVersion })
|
|
309
337
|
const indexAlias = this.getById(indexAliasId)
|
|
310
338
|
indexAlias
|
|
311
339
|
? indexAlias.synthetic && Object.assign(indexAlias, { rel: startPage })
|
|
312
|
-
: this.addFile({ src: indexAliasId, rel: startPage, synthetic: true }
|
|
340
|
+
: this.addFile({ src: indexAliasId, rel: startPage, synthetic: true })
|
|
313
341
|
}
|
|
314
342
|
} else {
|
|
315
343
|
// TODO pass componentVersion as logObject
|
|
@@ -328,20 +356,21 @@ class ContentCatalog {
|
|
|
328
356
|
if (startPage) {
|
|
329
357
|
componentVersion.url = startPage.pub.url
|
|
330
358
|
} else if (!componentVersion.url) {
|
|
359
|
+
startPageSrc = inflateSrc(Object.assign({}, indexPageId, { family: 'page' }))
|
|
331
360
|
// QUESTION: should we warn if the default start page cannot be found?
|
|
332
|
-
componentVersion.url = computePub(
|
|
333
|
-
|
|
334
|
-
|
|
361
|
+
componentVersion.url = computePub.call(
|
|
362
|
+
this,
|
|
363
|
+
startPageSrc,
|
|
364
|
+
computeOut.call(this, startPageSrc, startPageSrc.family, activeVersionSegment),
|
|
335
365
|
startPageSrc.family,
|
|
336
|
-
activeVersionSegment
|
|
337
|
-
this.htmlUrlExtensionStyle
|
|
366
|
+
activeVersionSegment
|
|
338
367
|
).url
|
|
339
368
|
}
|
|
340
369
|
Object.defineProperties(componentVersion, {
|
|
341
370
|
activeVersionSegment:
|
|
342
371
|
activeVersionSegment === version
|
|
343
|
-
? { configurable: true,
|
|
344
|
-
: { configurable: true,
|
|
372
|
+
? { configurable: true, get: getVersion }
|
|
373
|
+
: { configurable: true, value: activeVersionSegment },
|
|
345
374
|
files: {
|
|
346
375
|
configurable: true,
|
|
347
376
|
enumerable: false,
|
|
@@ -366,7 +395,7 @@ class ContentCatalog {
|
|
|
366
395
|
const rootIndexAlias = this.getById(ROOT_INDEX_ALIAS_ID)
|
|
367
396
|
if (rootIndexAlias) return rootIndexAlias.synthetic ? Object.assign(rootIndexAlias, { rel }) : undefined
|
|
368
397
|
const src = Object.assign({}, ROOT_INDEX_ALIAS_ID)
|
|
369
|
-
return this.addFile({ src, rel, synthetic: true }
|
|
398
|
+
return this.addFile({ src, rel, synthetic: true })
|
|
370
399
|
}
|
|
371
400
|
if (rel === false) {
|
|
372
401
|
logger.warn('Start page specified for site has invalid syntax: %s', startPageSpec)
|
|
@@ -385,14 +414,9 @@ class ContentCatalog {
|
|
|
385
414
|
// QUESTION should we throw an error if alias is invalid?
|
|
386
415
|
if (!src || (inferredSpec && src.relative === '.adoc')) return
|
|
387
416
|
const component = this.getComponent(src.component)
|
|
388
|
-
let componentVersion
|
|
389
417
|
if (component) {
|
|
390
418
|
// NOTE version is not set when alias specifies a component, but not a version
|
|
391
|
-
|
|
392
|
-
src.version = (componentVersion = component.latest).version
|
|
393
|
-
} else {
|
|
394
|
-
componentVersion = this.getComponentVersion(component, src.version)
|
|
395
|
-
}
|
|
419
|
+
src.version ??= (src.componentVersion = component.latest).version
|
|
396
420
|
const existingPage = this.getById(src)
|
|
397
421
|
if (existingPage) {
|
|
398
422
|
throw new Error(
|
|
@@ -417,7 +441,7 @@ class ContentCatalog {
|
|
|
417
441
|
)
|
|
418
442
|
}
|
|
419
443
|
// NOTE the redirect producer will populate contents when the redirect facility is 'static'
|
|
420
|
-
const alias = this.addFile({ src, rel: target }
|
|
444
|
+
const alias = this.addFile({ src, rel: target })
|
|
421
445
|
// NOTE record the first alias this target claims as the preferred one
|
|
422
446
|
if (!target.rel) target.rel = alias
|
|
423
447
|
return alias
|
|
@@ -436,36 +460,64 @@ class ContentCatalog {
|
|
|
436
460
|
const basePub = { splat: true }
|
|
437
461
|
const { component: fromComponent = to.component, versionSegment: fromVersionSegment } = from
|
|
438
462
|
const fromSrc = Object.assign({ component: fromComponent, version: fromVersionSegment }, baseSrc)
|
|
439
|
-
const fromPub = Object.assign(
|
|
463
|
+
const fromPub = Object.assign(
|
|
464
|
+
computePub.call(this, fromSrc, computeOut.call(this, fromSrc, family, fromVersionSegment), family),
|
|
465
|
+
basePub
|
|
466
|
+
)
|
|
440
467
|
const { component: toComponent, version: toVersion } = to
|
|
441
468
|
const toVersionSegment =
|
|
442
469
|
to.versionSegment ?? this.getComponentVersion(toComponent, toVersion)?.activeVersionSegment ?? toVersion
|
|
443
470
|
const toSrc = Object.assign({ component: toComponent, version: toVersion ?? toVersionSegment }, baseSrc)
|
|
444
|
-
const toPub = Object.assign(
|
|
471
|
+
const toPub = Object.assign(
|
|
472
|
+
computePub.call(this, toSrc, computeOut.call(this, toSrc, family, toVersionSegment), family),
|
|
473
|
+
basePub
|
|
474
|
+
)
|
|
445
475
|
return this.addFile({ pub: fromPub, src: fromSrc, rel: { pub: toPub, src: toSrc } })
|
|
446
476
|
}
|
|
447
477
|
|
|
448
478
|
/**
|
|
449
|
-
* Attempts to resolve a
|
|
479
|
+
* Attempts to resolve a page reference within the given context to a page in the catalog.
|
|
480
|
+
*
|
|
481
|
+
* Parses the specified page reference (i.e., page ID spec) into a partial page ID, expands it
|
|
482
|
+
* using the provided context, then attempts to locate a file in the page family with that page ID
|
|
483
|
+
* in this catalog. The family segment is optional.
|
|
450
484
|
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
* function returns undefined. If the spec does not match the page ID syntax, this function throws
|
|
456
|
-
* an error.
|
|
485
|
+
* If a component is specified, but no version, the latest version of the component stored in the
|
|
486
|
+
* catalog is used. If a page cannot be resolved, the search is attempted again for an "alias". If
|
|
487
|
+
* neither a page or alias can be resolved, the function returns undefined. If the syntax of the
|
|
488
|
+
* reference is invalid, this function throws an error.
|
|
457
489
|
*
|
|
458
|
-
* @param {String} spec - The contextual page
|
|
459
|
-
* @param {
|
|
460
|
-
* @param {Object} [ctx={}] - The context to use to qualified the contextual page ID.
|
|
490
|
+
* @param {String} spec - The contextual page reference (e.g., version@component:module:topic/page.adoc).
|
|
491
|
+
* @param {Object} [context={}] - The context to use to qualify the page reference.
|
|
461
492
|
*
|
|
462
|
-
* @returns {File} The virtual file to which the contextual page
|
|
463
|
-
* file cannot be resolved.
|
|
493
|
+
* @returns {File} The virtual file to which the contextual page reference resolves, or undefined
|
|
494
|
+
* if the file cannot be resolved.
|
|
464
495
|
*/
|
|
465
496
|
resolvePage (spec, context = {}) {
|
|
466
497
|
return this.resolveResource(spec, context, 'page', ['page'])
|
|
467
498
|
}
|
|
468
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Attempts to resolve a resource reference within the given context to a file in the catalog.
|
|
502
|
+
*
|
|
503
|
+
* Parses the specified resource reference (i.e., resource ID spec) into a partial resource ID,
|
|
504
|
+
* expands it using the provided context, then attempts to locate a file with that resource ID in
|
|
505
|
+
* this catalog.
|
|
506
|
+
*
|
|
507
|
+
* If a component is specified, but no version, the latest version of the component stored in the
|
|
508
|
+
* catalog is used. If a defaultFamily is not specified, the family must be specified either by
|
|
509
|
+
* the reference or the context. If permittedFamilies are stated, the family must resolve to a
|
|
510
|
+
* family in this list. If a file cannot be resolved, the function returns undefined. If the
|
|
511
|
+
* syntax of the reference is invalid, this function throws an error.
|
|
512
|
+
*
|
|
513
|
+
* @param {String} spec - The contextual resource reference (e.g., version@component:module:image$topic/image.png).
|
|
514
|
+
* @param {Object} [context={}] - The context to use to qualify the resource reference.
|
|
515
|
+
* @param {String} [defaultFamily=undefined] - The default family to use if one is not provided.
|
|
516
|
+
* @param {Array<String>} [permittedFamilies=undefined] - A list of families that are permitted.
|
|
517
|
+
*
|
|
518
|
+
* @returns {File} The virtual file to which the contextual resource reference resolves, or
|
|
519
|
+
* undefined if the file cannot be resolved.
|
|
520
|
+
*/
|
|
469
521
|
resolveResource (spec, context = {}, defaultFamily = undefined, permittedFamilies = undefined) {
|
|
470
522
|
return resolveResource(spec, this, context, defaultFamily, permittedFamilies)
|
|
471
523
|
}
|
|
@@ -505,34 +557,30 @@ function generateResourceSpec ({ component, version, module: module_, family, re
|
|
|
505
557
|
)
|
|
506
558
|
}
|
|
507
559
|
|
|
508
|
-
function
|
|
560
|
+
function inflateSrc (src) {
|
|
509
561
|
let { basename, extname, stem } = src
|
|
510
|
-
let update
|
|
511
562
|
if (basename == null) {
|
|
512
|
-
|
|
513
|
-
basename = path.basename(src.relative)
|
|
563
|
+
basename = src.basename = path.basename(src.relative)
|
|
514
564
|
}
|
|
515
565
|
if (stem == null) {
|
|
516
|
-
update = true
|
|
517
566
|
if (extname == null) {
|
|
518
567
|
if (~(extname = basename.lastIndexOf('.'))) {
|
|
519
|
-
stem = basename.
|
|
520
|
-
extname = basename.
|
|
568
|
+
src.stem = basename.substring(0, extname)
|
|
569
|
+
src.extname = basename.substring(extname)
|
|
521
570
|
} else {
|
|
522
|
-
stem = basename
|
|
523
|
-
extname = ''
|
|
571
|
+
src.stem = basename
|
|
572
|
+
src.extname = ''
|
|
524
573
|
}
|
|
525
574
|
} else {
|
|
526
|
-
stem = basename.
|
|
575
|
+
src.stem = basename.substring(0, basename.length - extname.length)
|
|
527
576
|
}
|
|
528
577
|
} else if (extname == null) {
|
|
529
|
-
|
|
530
|
-
extname = basename.substr(stem.length)
|
|
578
|
+
src.extname = basename.substring(stem.length)
|
|
531
579
|
}
|
|
532
|
-
return
|
|
580
|
+
return src
|
|
533
581
|
}
|
|
534
582
|
|
|
535
|
-
function computeOut (src, family, versionSegment
|
|
583
|
+
function computeOut (src, family, versionSegment) {
|
|
536
584
|
let { component, module: module_, basename, extname, relative, stem } = src
|
|
537
585
|
if (component === 'ROOT') component = ''
|
|
538
586
|
if (module_ === 'ROOT') module_ = ''
|
|
@@ -540,16 +588,14 @@ function computeOut (src, family, versionSegment, htmlUrlExtensionStyle) {
|
|
|
540
588
|
let familyPathSegment = ''
|
|
541
589
|
|
|
542
590
|
if (family === 'page') {
|
|
543
|
-
if (stem !== 'index' && htmlUrlExtensionStyle === 'indexify') {
|
|
591
|
+
if (stem !== 'index' && this.htmlUrlExtensionStyle === 'indexify') {
|
|
544
592
|
basename = 'index.html'
|
|
545
593
|
indexifyPathSegment = stem
|
|
546
594
|
} else if (extname === '.adoc') {
|
|
547
595
|
basename = stem + '.html'
|
|
548
596
|
}
|
|
549
|
-
} else if (family
|
|
550
|
-
familyPathSegment = '
|
|
551
|
-
} else if (family === 'attachment') {
|
|
552
|
-
familyPathSegment = '_attachments'
|
|
597
|
+
} else if (this.publishableFamilies.has(family)) {
|
|
598
|
+
familyPathSegment = '_' + family + 's'
|
|
553
599
|
}
|
|
554
600
|
|
|
555
601
|
const modulePath = path.join(component, versionSegment, module_)
|
|
@@ -561,7 +607,7 @@ function computeOut (src, family, versionSegment, htmlUrlExtensionStyle) {
|
|
|
561
607
|
return { dirname, basename, path: path_, moduleRootPath, rootPath }
|
|
562
608
|
}
|
|
563
609
|
|
|
564
|
-
function computePub (src, out, family, versionSegment
|
|
610
|
+
function computePub (src, out, family, versionSegment) {
|
|
565
611
|
const pub = {}
|
|
566
612
|
let url
|
|
567
613
|
if (family === 'nav') {
|
|
@@ -577,19 +623,19 @@ function computePub (src, out, family, versionSegment, htmlUrlExtensionStyle) {
|
|
|
577
623
|
} else if (family === 'page') {
|
|
578
624
|
const urlSegments = out.path.split('/')
|
|
579
625
|
const lastUrlSegmentIdx = urlSegments.length - 1
|
|
580
|
-
if (htmlUrlExtensionStyle === 'drop') {
|
|
626
|
+
if (this.htmlUrlExtensionStyle === 'drop') {
|
|
581
627
|
// drop just the .html extension or, if the filename is index.html, the whole segment
|
|
582
628
|
const lastUrlSegment = urlSegments[lastUrlSegmentIdx]
|
|
583
629
|
urlSegments[lastUrlSegmentIdx] =
|
|
584
|
-
lastUrlSegment === 'index.html' ? '' : lastUrlSegment.
|
|
585
|
-
} else if (htmlUrlExtensionStyle === 'indexify') {
|
|
630
|
+
lastUrlSegment === 'index.html' ? '' : lastUrlSegment.substring(0, lastUrlSegment.length - 5)
|
|
631
|
+
} else if (this.htmlUrlExtensionStyle === 'indexify') {
|
|
586
632
|
urlSegments[lastUrlSegmentIdx] = ''
|
|
587
633
|
}
|
|
588
634
|
url = '/' + urlSegments.join('/')
|
|
589
635
|
} else if ((url = '/' + out.path) === '/.') {
|
|
590
636
|
url = '/'
|
|
591
637
|
}
|
|
592
|
-
pub.url = ~url.indexOf(' ') ? url.
|
|
638
|
+
pub.url = ~url.indexOf(' ') ? url.replaceAll(' ', '%20') : url
|
|
593
639
|
return out ? Object.assign(pub, { moduleRootPath: out.moduleRootPath, rootPath: out.rootPath }) : pub
|
|
594
640
|
}
|
|
595
641
|
|
|
@@ -59,7 +59,7 @@ function parseResourceId (spec, ctx = {}, defaultFamily = 'page', permittedFamil
|
|
|
59
59
|
const relativeSegments = relative.split('/')
|
|
60
60
|
let from
|
|
61
61
|
if (relativeSegments[0] === '.' && (from = ctx.relative)) {
|
|
62
|
-
relativeSegments[0] = from.
|
|
62
|
+
relativeSegments[0] = from.substring(0, (from.lastIndexOf('/') + 1 || 1) - 1)
|
|
63
63
|
}
|
|
64
64
|
relative = relativeSegments
|
|
65
65
|
.reduce((accum, segment) => {
|
|
@@ -26,7 +26,7 @@ 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
|
-
if (!id
|
|
29
|
+
if (!id?.family) return false
|
|
30
30
|
if (id.version == null) {
|
|
31
31
|
const component = catalog.getComponent(id.component)
|
|
32
32
|
if (!component) return
|
|
@@ -33,7 +33,7 @@ function versionCompareDesc (a, b) {
|
|
|
33
33
|
function resolveSemver (str) {
|
|
34
34
|
const chr0 = str.charAt()
|
|
35
35
|
if (chr0 === 'v') {
|
|
36
|
-
if (isDigit(str.charAt(1)) && (str = str.
|
|
36
|
+
if (isDigit(str.charAt(1)) && (str = str.substring(1)) && (~str.indexOf('.') || isDigit(str.charAt()))) {
|
|
37
37
|
return str
|
|
38
38
|
}
|
|
39
39
|
} else if (isDigit(chr0) && (~str.indexOf('.') || isInteger(Number(str)))) {
|
|
@@ -51,12 +51,12 @@ function semverCompare (a, b) {
|
|
|
51
51
|
const preOffsetA = a.indexOf('-')
|
|
52
52
|
const preOffsetB = b.indexOf('-')
|
|
53
53
|
if (~preOffsetA) {
|
|
54
|
-
preA = a.
|
|
55
|
-
a = a.
|
|
54
|
+
preA = a.substring(preOffsetA + 1)
|
|
55
|
+
a = a.substring(0, preOffsetA)
|
|
56
56
|
}
|
|
57
57
|
if (~preOffsetB) {
|
|
58
|
-
preB = b.
|
|
59
|
-
b = b.
|
|
58
|
+
preB = b.substring(preOffsetB + 1)
|
|
59
|
+
b = b.substring(0, preOffsetB)
|
|
60
60
|
}
|
|
61
61
|
const numsA = a.split('.')
|
|
62
62
|
const numsB = b.split('.')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/content-classifier",
|
|
3
|
-
"version": "3.2.0-
|
|
3
|
+
"version": "3.2.0-rc.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)",
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
"#constants": "./lib/constants.js"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@antora/asciidoc-loader": "3.2.0-
|
|
35
|
-
"@antora/logger": "3.2.0-
|
|
36
|
-
"mime-types": "~
|
|
34
|
+
"@antora/asciidoc-loader": "3.2.0-rc.1",
|
|
35
|
+
"@antora/logger": "3.2.0-rc.1",
|
|
36
|
+
"mime-types": "~3.0",
|
|
37
37
|
"vinyl": "~3.0"
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|
|
40
|
-
"node": ">=
|
|
40
|
+
"node": ">=20.0.0"
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
43
43
|
"lib/"
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"web publishing"
|
|
52
52
|
],
|
|
53
53
|
"scripts": {
|
|
54
|
-
"test": "
|
|
54
|
+
"test": "node --test",
|
|
55
55
|
"prepublishOnly": "npx -y downdoc@latest --prepublish",
|
|
56
56
|
"postpublish": "npx -y downdoc@latest --postpublish"
|
|
57
57
|
}
|