@antora/assembler 1.0.0-rc.5 → 1.0.0-rc.6
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/assemble-content.js +41 -27
- package/lib/configure.js +12 -11
- package/lib/filter-component-versions.js +1 -1
- package/lib/load-config.js +1 -1
- package/lib/produce-assembly-file.js +34 -19
- package/lib/produce-assembly-files.js +98 -94
- package/lib/util/rewriter.js +2 -2
- package/package.json +2 -2
package/lib/assemble-content.js
CHANGED
|
@@ -11,11 +11,12 @@ const produceAssemblyFiles = require('./produce-assembly-files')
|
|
|
11
11
|
const PromiseQueue = require('./util/promise-queue')
|
|
12
12
|
const runCommand = require('@antora/run-command-helper')
|
|
13
13
|
|
|
14
|
-
const invariably = {
|
|
14
|
+
const invariably = { new: () => ({}), null: () => null, void: () => undefined }
|
|
15
15
|
const PACKAGE_NAME = require('../package.json').name
|
|
16
16
|
const NEWLINE_RX = /(?:\r?\n)+/
|
|
17
17
|
|
|
18
|
-
async function assembleContent (playbook, contentCatalog, converter,
|
|
18
|
+
async function assembleContent (playbook, contentCatalog, converter, providers) {
|
|
19
|
+
const { configSource, navigationCatalog, componentVersionFilter } = providers
|
|
19
20
|
const {
|
|
20
21
|
convert = converter,
|
|
21
22
|
getDefaultCommand,
|
|
@@ -64,7 +65,8 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
64
65
|
loadAsciiDoc,
|
|
65
66
|
contentCatalog,
|
|
66
67
|
assemblerConfig,
|
|
67
|
-
generateSelectAssemblyProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog)
|
|
68
|
+
generateSelectAssemblyProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog),
|
|
69
|
+
typeof componentVersionFilter === 'function' ? componentVersionFilter : undefined
|
|
68
70
|
)
|
|
69
71
|
if (!(assemblyFiles.length && typeof convert === 'function')) return assemblyFiles
|
|
70
72
|
buildConfig.command ??= typeof getDefaultCommand === 'function' ? await getDefaultCommand(cwd, playbook) : null
|
|
@@ -77,8 +79,7 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
77
79
|
.add(
|
|
78
80
|
assemblyFiles.map((doc) => async () => {
|
|
79
81
|
const relativeToOutput = embedReferenceStyle === 'output-relative'
|
|
80
|
-
const convertAttributes = prepareConvertAttributes(doc, targetExtname, relativeToOutput, assemblerConfig)
|
|
81
|
-
if (buildConfig.mkdirs) await fsp.mkdir(convertAttributes.outdir, { recursive: true, force: true })
|
|
82
|
+
const convertAttributes = await prepareConvertAttributes(doc, targetExtname, relativeToOutput, assemblerConfig)
|
|
82
83
|
return boundConvert(doc, convertAttributes, buildConfig, helpers).then((result) =>
|
|
83
84
|
boundResolveFileOrContents(result, convertAttributes, buildConfig, loggerName).then((fileOrContents) =>
|
|
84
85
|
coerceToExportFormat(doc, targetBackend, targetExtname, targetMediaType, fileOrContents)
|
|
@@ -172,14 +173,14 @@ function generateSelectAssemblyProfile (context, contentCatalog, baseModel, intr
|
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assemblerConfig) {
|
|
176
|
+
async function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assemblerConfig) {
|
|
176
177
|
const {
|
|
177
178
|
asciidoc: { attributes: docAttributes } = { attributes: {} },
|
|
178
179
|
extname: docfilesuffix,
|
|
179
180
|
path: reldocfile,
|
|
180
181
|
src: { family, relative },
|
|
181
182
|
} = doc
|
|
182
|
-
const { cwd = process.cwd(), dir = cwd } = assemblerConfig.build
|
|
183
|
+
const { cwd = process.cwd(), dir = cwd, mkdirs } = assemblerConfig.build
|
|
183
184
|
const docname = family + '$' + relative.substring(0, relative.length - docfilesuffix.length)
|
|
184
185
|
const docfile = ospath.join(dir, reldocfile)
|
|
185
186
|
const outdir = ospath.dirname(docfile)
|
|
@@ -194,22 +195,23 @@ function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assembl
|
|
|
194
195
|
outdir,
|
|
195
196
|
outfile,
|
|
196
197
|
outfilesuffix: targetExtname,
|
|
197
|
-
toArgs (
|
|
198
|
+
toArgs (attributeOptionFlag, outputOptionFlag) {
|
|
198
199
|
const args = []
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
val = name
|
|
200
|
+
let requireAsciidoctorLogAdapter
|
|
201
|
+
for (const [name, val] of Object.entries(this)) {
|
|
202
|
+
let optionValue
|
|
203
|
+
if (name === 'asciidoctor-log-integration') {
|
|
204
|
+
if ((val ?? false) !== false) requireAsciidoctorLogAdapter = val
|
|
205
|
+
continue
|
|
206
|
+
} else if ((val ?? false) === false) {
|
|
207
|
+
optionValue = `${name}!${val === false ? '=@' : ''}`
|
|
208
208
|
} else {
|
|
209
|
-
|
|
209
|
+
optionValue = val === '' ? name : name + '=' + val
|
|
210
210
|
}
|
|
211
|
-
args.push(
|
|
211
|
+
args.push(attributeOptionFlag, optionValue)
|
|
212
212
|
}
|
|
213
|
+
if (requireAsciidoctorLogAdapter) args.push('-r', requireAsciidoctorLogAdapter)
|
|
214
|
+
if (outputOptionFlag) args.push(outputOptionFlag, this.outfile)
|
|
213
215
|
return args
|
|
214
216
|
},
|
|
215
217
|
})
|
|
@@ -224,7 +226,18 @@ function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assembl
|
|
|
224
226
|
this.outfile += value
|
|
225
227
|
},
|
|
226
228
|
})
|
|
227
|
-
|
|
229
|
+
Object.defineProperty(attributes, 'toArgs', { enumerable: false })
|
|
230
|
+
if (mkdirs) await fsp.mkdir(outdir, { recursive: true, force: true })
|
|
231
|
+
if (attributes['asciidoctor-log-integration'] != null) {
|
|
232
|
+
const scriptSourcePath = require.resolve('#asciidoctor-log-adapter')
|
|
233
|
+
let scriptTargetPath = scriptSourcePath
|
|
234
|
+
if (attributes['asciidoctor-log-integration'] === 'copy-to-build-dir') {
|
|
235
|
+
scriptTargetPath = ospath.join(dir, 'asciidoctor-log-adapter.rb')
|
|
236
|
+
await fsp.cp(scriptSourcePath, scriptTargetPath, { force: true, recursive: true })
|
|
237
|
+
}
|
|
238
|
+
attributes['asciidoctor-log-integration'] = scriptTargetPath
|
|
239
|
+
}
|
|
240
|
+
return attributes
|
|
228
241
|
}
|
|
229
242
|
|
|
230
243
|
function coerceToExportFormat (assemblyFile, targetBackend, targetExtname, targetMediaType, fileOrContents) {
|
|
@@ -237,7 +250,8 @@ function coerceToExportFormat (assemblyFile, targetBackend, targetExtname, targe
|
|
|
237
250
|
const { path: sourcePath, extname: sourceExtname } = file
|
|
238
251
|
const relativeWithoutExtname = file.src.relative.substring(0, file.src.relative.length - sourceExtname.length)
|
|
239
252
|
const newPath = sourcePath.substring(0, sourcePath.length - sourceExtname.length) + targetExtname
|
|
240
|
-
Object.assign(file, { mediaType: (file.src.mediaType = targetMediaType), path: newPath })
|
|
253
|
+
Object.assign(file, { mediaType: (file.src.mediaType = targetMediaType), path: (file.src.path = newPath) })
|
|
254
|
+
delete file.src.abspath
|
|
241
255
|
file.src.basename = path.basename((file.src.relative = relativeWithoutExtname + (file.src.extname = targetExtname)))
|
|
242
256
|
return file
|
|
243
257
|
}
|
|
@@ -260,8 +274,9 @@ function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
|
|
|
260
274
|
}
|
|
261
275
|
if (keepSource) {
|
|
262
276
|
for (const file of assemblyFiles) {
|
|
263
|
-
file.src.contents = file.contents
|
|
264
277
|
file.out = { path: file.path }
|
|
278
|
+
file.src.contents = file.contents
|
|
279
|
+
file.src.abspath = ospath.join(dir, file.path)
|
|
265
280
|
files.push(file)
|
|
266
281
|
}
|
|
267
282
|
}
|
|
@@ -272,7 +287,7 @@ function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
|
|
|
272
287
|
})
|
|
273
288
|
}
|
|
274
289
|
|
|
275
|
-
|
|
290
|
+
function resolveFileOrContents (convertResult, convertAttributes, buildConfig, loggerName) {
|
|
276
291
|
let fileOrContents
|
|
277
292
|
if (convertResult?.status != null) {
|
|
278
293
|
if ('file' in convertResult) {
|
|
@@ -312,12 +327,11 @@ async function resolveFileOrContents (convertResult, convertAttributes, buildCon
|
|
|
312
327
|
if (additionalLines.length > 1) logger.info({ command, file }, additionalLines.join('\n'))
|
|
313
328
|
}
|
|
314
329
|
} else if (convertResult !== undefined) {
|
|
315
|
-
return convertResult
|
|
330
|
+
return Promise.resolve(convertResult)
|
|
316
331
|
}
|
|
317
|
-
if (fileOrContents !== undefined) return fileOrContents
|
|
332
|
+
if (fileOrContents !== undefined) return Promise.resolve(fileOrContents)
|
|
318
333
|
const outfile = convertAttributes.outfile
|
|
319
|
-
|
|
320
|
-
return outfileExists ? new LazyReadable(() => fs.createReadStream(outfile)) : null
|
|
334
|
+
return fsp.access(outfile).then(() => new LazyReadable(() => fs.createReadStream(outfile)), invariably.null)
|
|
321
335
|
}
|
|
322
336
|
|
|
323
337
|
function isBound (obj) {
|
package/lib/configure.js
CHANGED
|
@@ -7,25 +7,26 @@ function configure (context, ...args) {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
function internalConfigure (converter, config = {}, providers = {}) {
|
|
10
|
-
this.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
if (!this.listeners('beforeProcess').includes(enableKeepSource)) {
|
|
11
|
+
this.once('componentsRegistered', ({ contentCatalog, assemblerProfiles }) => {
|
|
12
|
+
contentCatalog.publishableFamilies.add('export')
|
|
13
|
+
if (!assemblerProfiles) this.updateVariables({ assemblerProfiles: getAssemblerProfiles(contentCatalog) })
|
|
14
|
+
})
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
this.once('beforeProcess', enableKeepSource)
|
|
17
|
+
}
|
|
17
18
|
|
|
18
19
|
this.once('navigationBuilt', async ({ playbook, contentCatalog }) => {
|
|
19
|
-
const { assembleContent = require('./assemble-content'), ...
|
|
20
|
+
const { assembleContent = require('./assemble-content'), ...assembleContentProviders } = providers
|
|
20
21
|
if (config.configSource?.constructor === Object) {
|
|
21
|
-
|
|
22
|
-
await assembleContent.call(this, playbook, contentCatalog, converter,
|
|
22
|
+
assembleContentProviders.configSource = config.configSource
|
|
23
|
+
await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentProviders)
|
|
23
24
|
} else {
|
|
24
25
|
const singleConfig = !('configFiles' in config)
|
|
25
26
|
const configFiles = singleConfig ? config.configFile : config.configFiles
|
|
26
27
|
for (const configSource of Array.isArray(configFiles) ? configFiles : [configFiles]) {
|
|
27
|
-
const
|
|
28
|
-
await assembleContent.call(this, playbook, contentCatalog, converter,
|
|
28
|
+
const assembleContentProvidersWithConfigSource = Object.assign({}, assembleContentProviders, { configSource })
|
|
29
|
+
await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentProvidersWithConfigSource)
|
|
29
30
|
if (singleConfig) break
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -5,7 +5,7 @@ const { filterCollection, compilePattern } = require('./util/matcher')
|
|
|
5
5
|
const VERSION_SEPARATOR_RX = /@(?!\()(?=(\w)?)/g
|
|
6
6
|
const US = '\x1f'
|
|
7
7
|
|
|
8
|
-
function filterComponentVersions (
|
|
8
|
+
function filterComponentVersions ({ names: patterns, prereleases = true }, components) {
|
|
9
9
|
if (!patterns.length) return []
|
|
10
10
|
const candidateMap = components.reduce((accum, { name: componentName, latest, versions }) => {
|
|
11
11
|
for (const version of versions) {
|
package/lib/load-config.js
CHANGED
|
@@ -134,7 +134,7 @@ function loadConfig (configSource, playbook, preferredQualifier = '') {
|
|
|
134
134
|
})
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
function camelCaseKeys (o, stopPaths
|
|
137
|
+
function camelCaseKeys (o, stopPaths, p = undefined) {
|
|
138
138
|
if (Array.isArray(o)) return o.map((it) => camelCaseKeys(it, stopPaths, p))
|
|
139
139
|
if (o == null || o.constructor !== Object) return o
|
|
140
140
|
const pathPrefix = p ? p + '.' : ''
|
|
@@ -20,6 +20,7 @@ const DISCARD_ATTRIBUTE_NAMES = [
|
|
|
20
20
|
'doctype',
|
|
21
21
|
'leveloffset',
|
|
22
22
|
'preface-title',
|
|
23
|
+
'assembly-doctype',
|
|
23
24
|
'assembly-header-attributes',
|
|
24
25
|
'assembly-navtitle',
|
|
25
26
|
'assembly-slug',
|
|
@@ -39,8 +40,8 @@ function produceAssemblyFile (
|
|
|
39
40
|
assemblyModel
|
|
40
41
|
) {
|
|
41
42
|
const pagesByUrl = files.reduce((map, it) => (it.src.family === 'page' ? map.set(it.pub.url, it) : map), new Map())
|
|
42
|
-
if (outline.urlType === 'internal' && !pagesByUrl.get(outline.url) && !(outline.items || []).length) return
|
|
43
43
|
const pagesInOutline = selectPagesInOutline(outline, pagesByUrl)
|
|
44
|
+
if (outline.urlType === 'internal' && !pagesByUrl.has(outline.pathname) && !outline.items?.length) return
|
|
44
45
|
const { rootLevel, xmlIds } = assemblyModel
|
|
45
46
|
const idSeparators = {
|
|
46
47
|
prefix:
|
|
@@ -77,6 +78,7 @@ function produceAssemblyFile (
|
|
|
77
78
|
mutableAttributes,
|
|
78
79
|
assemblyModel
|
|
79
80
|
)
|
|
81
|
+
if (buffer[buffer.doctypeIdx] === ':doctype: article') buffer.splice(buffer.doctypeIdx, 1)
|
|
80
82
|
const stem =
|
|
81
83
|
(buffer.slug ? sanitizeSlug(buffer.slug) : generateSlug(rootLevel === 0 ? 'index' : buffer.navtitle)) ||
|
|
82
84
|
'export-' + (assemblyModel.stemSeq = (assemblyModel.exportSeq ?? 0) + 1)
|
|
@@ -88,7 +90,7 @@ function produceAssemblyFile (
|
|
|
88
90
|
src: { component, version, componentVersion, module: 'ROOT', family: 'export', relative: stem + '.adoc' },
|
|
89
91
|
pub: false,
|
|
90
92
|
})
|
|
91
|
-
file.path = file.out.path // use out path as
|
|
93
|
+
file.path = file.src.path = file.out.path // use out path as path so assets can be published to same hierarchy
|
|
92
94
|
delete file.out
|
|
93
95
|
return file
|
|
94
96
|
}
|
|
@@ -124,7 +126,6 @@ function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocCon
|
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
|
|
127
|
-
const doctype = assemblyModel.doctype ?? 'book'
|
|
128
129
|
const navtitlePlain = sanitize(navtitle)
|
|
129
130
|
const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
130
131
|
const doctitle = (componentVersion.title === navtitlePlain ? '' : componentVersion.title + ': ') + navtitleAsciiDoc
|
|
@@ -133,7 +134,7 @@ function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
|
|
|
133
134
|
const buffer = [
|
|
134
135
|
`= ${doctitle}`,
|
|
135
136
|
...(version ? [`:revnumber: ${displayVersion}`] : []),
|
|
136
|
-
|
|
137
|
+
`:doctype: ${assemblyModel.doctype ?? 'book'}`,
|
|
137
138
|
':underscore: _',
|
|
138
139
|
// Q: should we pass these via the CLI so they cannot be modified?
|
|
139
140
|
`:page-component-name: ${componentVersion.name}`,
|
|
@@ -142,17 +143,22 @@ function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
|
|
|
142
143
|
`:page-component-display-version: ${displayVersion}`,
|
|
143
144
|
`:page-component-title: ${componentVersion.title}`,
|
|
144
145
|
]
|
|
146
|
+
buffer.doctypeIdx = version ? 2 : 1
|
|
145
147
|
return Object.assign(buffer, { navtitle })
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
function selectPagesInOutline (outlineEntry, pagesByUrl, accum) {
|
|
149
151
|
accum ??= Object.assign(new Map(), { assembled: { pages: new Map(), assets: new Set() } })
|
|
150
|
-
const
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
const { urlType, url, hash, unresolved, items = [] } = outlineEntry
|
|
153
|
+
if (urlType === 'internal' && !unresolved) {
|
|
154
|
+
const pathname = (outlineEntry.pathname ??= hash ? url.substring(0, url.length - hash.length) : url)
|
|
155
|
+
let page
|
|
156
|
+
if (!accum.has(pathname) && (page = pagesByUrl.get(pathname))) {
|
|
157
|
+
accum.set(createResourceKey(page.src), page)
|
|
158
|
+
accum.set(pathname, page)
|
|
159
|
+
}
|
|
154
160
|
}
|
|
155
|
-
for (const item of
|
|
161
|
+
for (const item of items) selectPagesInOutline(item, pagesByUrl, accum)
|
|
156
162
|
return accum
|
|
157
163
|
}
|
|
158
164
|
|
|
@@ -172,21 +178,22 @@ function mergeAsciiDoc (
|
|
|
172
178
|
level = 0,
|
|
173
179
|
supportsParts = false
|
|
174
180
|
) {
|
|
175
|
-
const { items = [], roles = [], unresolved, urlType, url, hash } = outlineEntry
|
|
181
|
+
const { items = [], roles = [], unresolved, urlType, url, pathname, hash } = outlineEntry
|
|
176
182
|
// TODO: we could try to be smart about it and make sure the page with fragment is included at least once
|
|
177
|
-
if (
|
|
183
|
+
if (roles.includes('site-only')) {
|
|
178
184
|
buffer.inBody ??= false
|
|
179
185
|
return buffer
|
|
180
186
|
}
|
|
181
|
-
|
|
187
|
+
const assembled = pagesInOutline.assembled
|
|
188
|
+
// FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
|
|
189
|
+
const page = urlType === 'internal' && !unresolved ? pagesInOutline.get(pathname) : undefined
|
|
190
|
+
let navtitle = outlineEntry.navtitle ?? (page && hash ? page.asciidoc.navtitle : outlineEntry.content)
|
|
182
191
|
let navtitlePlain = sanitize(navtitle)
|
|
183
192
|
let navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
184
193
|
const { filetype, linkReferenceStyle, pubRoot, sectionMergeStrategy, siteRoot, logger, rootLevel } = assemblyModel
|
|
185
|
-
const
|
|
186
|
-
// FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
|
|
187
|
-
const page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
|
|
194
|
+
const doctype = assemblyModel.doctype ?? 'book'
|
|
188
195
|
const atDocumentRoot = !buffer.inBody
|
|
189
|
-
|
|
196
|
+
let atBookRoot = atDocumentRoot && !level && doctype === 'book' && (supportsParts = true)
|
|
190
197
|
const hasItems = items.length > 0
|
|
191
198
|
if (page && !assembled.pages.has(page)) {
|
|
192
199
|
const contents = page.src.contents
|
|
@@ -211,6 +218,11 @@ function mergeAsciiDoc (
|
|
|
211
218
|
doc.source_header_attributes ??= doc.parent.$to_h()
|
|
212
219
|
const isRootPage = atDocumentRoot && !level
|
|
213
220
|
const { component, version, module: module_, relative, origin } = page.src
|
|
221
|
+
const doctypeOverride = doc.getAttribute('assembly-doctype')
|
|
222
|
+
if (doctypeOverride) {
|
|
223
|
+
if (doctypeOverride !== doctype) buffer[buffer.doctypeIdx] = `:doctype: ${doctypeOverride}`
|
|
224
|
+
if (doctypeOverride !== 'book') atBookRoot = supportsParts = false
|
|
225
|
+
}
|
|
214
226
|
if (isRootPage && doc.hasAttribute('assembly-slug')) buffer.slug = doc.getAttribute('assembly-slug')
|
|
215
227
|
let hasAssemblyNavtitleAttr
|
|
216
228
|
if ((hasAssemblyNavtitleAttr = !!doc.getAttribute('assembly-navtitle'))) {
|
|
@@ -279,6 +291,7 @@ function mergeAsciiDoc (
|
|
|
279
291
|
for (const entry of processDocumentHeader(doc, lines, ignoreLines, isRootPage)) {
|
|
280
292
|
if (entry.type === 'author_line') {
|
|
281
293
|
buffer.splice(1, 0, entry.lines[0])
|
|
294
|
+
buffer.doctypeIdx += 1
|
|
282
295
|
buffer.endHeaderIdx += 1
|
|
283
296
|
} else if (entry.type === 'attribute_entry') {
|
|
284
297
|
const name = entry.name
|
|
@@ -371,6 +384,7 @@ function mergeAsciiDoc (
|
|
|
371
384
|
// Q: should we use htitleAsciiDoc to generate ID if not {empty} instead of pageStyle
|
|
372
385
|
pageFragment = `#_${idSeparators.coordinate}${pageIdLeader}${pageStyle}`
|
|
373
386
|
buffer.unshift(`[#${pageId}]`)
|
|
387
|
+
buffer.doctypeIdx += 1
|
|
374
388
|
} else {
|
|
375
389
|
pageFragment = `#${pageId}`
|
|
376
390
|
}
|
|
@@ -401,6 +415,7 @@ function mergeAsciiDoc (
|
|
|
401
415
|
buffer.push(`${'='.repeat(heading.level)} ${heading.title}`)
|
|
402
416
|
} else if (atDocumentRoot) {
|
|
403
417
|
buffer.unshift(`[#${pageId}]`)
|
|
418
|
+
buffer.doctypeIdx += 1
|
|
404
419
|
}
|
|
405
420
|
if (sectionMergeStrategy === 'enclose' && hasItems && hasSections && !(atDocumentRoot && heading)) {
|
|
406
421
|
// TODO: make overview section title configurable
|
|
@@ -610,7 +625,7 @@ function mergeAsciiDoc (
|
|
|
610
625
|
)
|
|
611
626
|
if (resolvedAttributeEntries.length > 1) safePush(buffer, resolvedAttributeEntries)
|
|
612
627
|
}
|
|
613
|
-
} else if (level) {
|
|
628
|
+
} else if (level && !hash) {
|
|
614
629
|
if (atDocumentRoot && rootLevel === 0 && navtitlePlain === componentVersion.title) {
|
|
615
630
|
buffer.inBody ??= false
|
|
616
631
|
level--
|
|
@@ -622,7 +637,7 @@ function mergeAsciiDoc (
|
|
|
622
637
|
if (urlType === 'external') {
|
|
623
638
|
sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
624
639
|
} else if (urlType === 'internal' && !unresolved) {
|
|
625
|
-
const resource = files.find((it) => it.pub.url ===
|
|
640
|
+
const resource = files.find((it) => it.pub.url === pathname)
|
|
626
641
|
if (resource) {
|
|
627
642
|
if (resource.src.family === 'page' && pagesInOutline.has(resource.pub.url)) {
|
|
628
643
|
const refid = generateScopedId(resource.src, componentVersion, idSeparators, filetype, true)
|
|
@@ -641,7 +656,7 @@ function mergeAsciiDoc (
|
|
|
641
656
|
if (hasItems) {
|
|
642
657
|
const nextLevel = level + 1
|
|
643
658
|
// NOTE: drop first child if same as parent; should we keep if content is different?
|
|
644
|
-
;(urlType === 'internal' &&
|
|
659
|
+
;(urlType === 'internal' && items[0].urlType === 'internal' && pathname === items[0].url && !items[0].items?.length
|
|
645
660
|
? items.slice(1)
|
|
646
661
|
: items
|
|
647
662
|
).forEach((item) => {
|
|
@@ -6,7 +6,13 @@ const selectMutableAttributes = require('./select-mutable-attributes')
|
|
|
6
6
|
|
|
7
7
|
const ATTR_REF_RX = /\\?\{(\w[\w-]*)\}/g
|
|
8
8
|
|
|
9
|
-
function produceAssemblyFiles (
|
|
9
|
+
function produceAssemblyFiles (
|
|
10
|
+
loadAsciiDoc,
|
|
11
|
+
contentCatalog,
|
|
12
|
+
assemblerConfig,
|
|
13
|
+
selectAssemblyProfile,
|
|
14
|
+
selectComponentVersions
|
|
15
|
+
) {
|
|
10
16
|
const { assembly: assemblyConfig } = assemblerConfig
|
|
11
17
|
selectAssemblyProfile ??= (componentVersion) => ({
|
|
12
18
|
attributes: assemblyConfig.attributes,
|
|
@@ -26,106 +32,104 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, se
|
|
|
26
32
|
const publishableFiles = contentCatalog.getFiles().filter((file) => file.out && file.pub)
|
|
27
33
|
let siteRoot
|
|
28
34
|
const configMdc = assemblerConfig.file ? { file: { path: assemblerConfig.file } } : {}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
selectComponentVersions ??= filterComponentVersions.bind(null, assemblerConfig.componentVersionFilter)
|
|
36
|
+
return selectComponentVersions(contentCatalog.getComponents()).reduce((accum, componentVersion) => {
|
|
37
|
+
const assemblyModel = selectAssemblyProfile(componentVersion)
|
|
38
|
+
const { attributes: assemblyAttributes, logger, navigation, rootLevel } = assemblyModel
|
|
39
|
+
if (!navigation) return accum
|
|
40
|
+
const contextualLogger = logger ? { warn: logger.warn.bind(logger, configMdc) } : undefined
|
|
41
|
+
const { name: componentName, version, title } = componentVersion
|
|
42
|
+
const componentVersionAsciiDocConfig = getAsciiDocConfigWithAsciidoctorReducerExtension(componentVersion)
|
|
43
|
+
let sourceHighlighter
|
|
44
|
+
if ('source-highlighter' in assemblyAttributes) {
|
|
45
|
+
sourceHighlighter = assemblyAttributes['source-highlighter']
|
|
46
|
+
delete assemblyAttributes['source-highlighter']
|
|
47
|
+
}
|
|
48
|
+
const mergedAsciiDocAttributes = collateAsciiDocAttributes(
|
|
49
|
+
Object.assign({}, componentVersionAsciiDocConfig.attributes),
|
|
50
|
+
assemblyAttributes,
|
|
51
|
+
contextualLogger
|
|
52
|
+
)
|
|
53
|
+
const mergedAsciiDocConfig = Object.assign({}, componentVersionAsciiDocConfig, {
|
|
54
|
+
attributes: mergedAsciiDocAttributes,
|
|
55
|
+
})
|
|
56
|
+
assemblyModel.filetype = baseAssemblyAttributes['assembler-filetype']
|
|
57
|
+
const outDirname = (assemblyModel.outDirname = contentCatalog.createFile({
|
|
58
|
+
src: {
|
|
59
|
+
component: componentName,
|
|
60
|
+
version,
|
|
61
|
+
componentVersion,
|
|
62
|
+
module: 'ROOT',
|
|
63
|
+
family: 'export',
|
|
64
|
+
relative: 'index.adoc',
|
|
65
|
+
},
|
|
66
|
+
}).out.dirname)
|
|
67
|
+
assemblyModel.pubRoot = outDirname ? '/' + outDirname : ''
|
|
68
|
+
assemblyModel.siteRoot =
|
|
69
|
+
siteRoot === undefined
|
|
70
|
+
? (siteRoot ??= ((val) => {
|
|
71
|
+
if (!val) return null
|
|
72
|
+
if (val.charAt(val.length - 1) === '/') val = val.substring(0, val.length - 1)
|
|
73
|
+
if (!val || val.charAt() === '/') return { path: val }
|
|
74
|
+
return { url: val, path: extractUrlPath(val) }
|
|
75
|
+
})(mergedAsciiDocAttributes['site-url'] || mergedAsciiDocAttributes['primary-site-url']))
|
|
76
|
+
: siteRoot
|
|
77
|
+
if (assemblyModel.filetype === 'html') {
|
|
78
|
+
let linkRefStyle = assemblyModel.linkReferenceStyle
|
|
79
|
+
if (linkRefStyle === 'absolute' && siteRoot?.url == null) linkRefStyle = 'root-relative'
|
|
80
|
+
if (linkRefStyle === 'root-relative' && siteRoot?.path == null) linkRefStyle = 'relative'
|
|
81
|
+
assemblyModel.linkReferenceStyle = linkRefStyle
|
|
82
|
+
} else if (!(assemblyModel.filetype === 'pdf' && assemblyModel.linkReferenceStyle === 'relative')) {
|
|
83
|
+
assemblyModel.linkReferenceStyle = 'absolute'
|
|
84
|
+
}
|
|
85
|
+
const rootEntry = { content: title }
|
|
86
|
+
let startPage =
|
|
87
|
+
'startPage' in componentVersion
|
|
88
|
+
? componentVersion.startPage
|
|
89
|
+
: contentCatalog.resolvePage('index.adoc', { component: componentName, version })
|
|
90
|
+
if (startPage && startPage.src.component === componentName && startPage.src.version === version) {
|
|
91
|
+
if (assemblyModel.insertStartPage) {
|
|
92
|
+
const navtitle = startPage.asciidoc?.navtitle || rootEntry.content
|
|
93
|
+
Object.assign(rootEntry, { navtitle, url: startPage.pub.url, urlType: 'internal', roles: ['page'] })
|
|
41
94
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
contextualLogger
|
|
46
|
-
)
|
|
47
|
-
const mergedAsciiDocConfig = Object.assign({}, componentVersionAsciiDocConfig, {
|
|
48
|
-
attributes: mergedAsciiDocAttributes,
|
|
49
|
-
})
|
|
50
|
-
assemblyModel.filetype = baseAssemblyAttributes['assembler-filetype']
|
|
51
|
-
const outDirname = (assemblyModel.outDirname = contentCatalog.createFile({
|
|
95
|
+
} else {
|
|
96
|
+
// Q: should we always use a reference page as startPage for computing mutableAttributes?
|
|
97
|
+
startPage = contentCatalog.createFile({
|
|
52
98
|
src: {
|
|
53
|
-
component:
|
|
54
|
-
version,
|
|
99
|
+
component: componentVersion.name,
|
|
100
|
+
version: componentVersion.version,
|
|
55
101
|
componentVersion,
|
|
56
102
|
module: 'ROOT',
|
|
57
|
-
family: '
|
|
103
|
+
family: 'page',
|
|
58
104
|
relative: 'index.adoc',
|
|
105
|
+
origin: (componentVersion.origins || [])[0],
|
|
59
106
|
},
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
'startPage' in componentVersion
|
|
82
|
-
? componentVersion.startPage
|
|
83
|
-
: contentCatalog.resolvePage('index.adoc', { component: componentName, version })
|
|
84
|
-
if (startPage && startPage.src.component === componentName && startPage.src.version === version) {
|
|
85
|
-
if (assemblyModel.insertStartPage) {
|
|
86
|
-
const navtitle = startPage.asciidoc?.navtitle || rootEntry.content
|
|
87
|
-
Object.assign(rootEntry, { navtitle, url: startPage.pub.url, urlType: 'internal', roles: ['page'] })
|
|
88
|
-
}
|
|
89
|
-
} else {
|
|
90
|
-
// Q: should we always use a reference page as startPage for computing mutableAttributes?
|
|
91
|
-
startPage = contentCatalog.createFile({
|
|
92
|
-
src: {
|
|
93
|
-
component: componentVersion.name,
|
|
94
|
-
version: componentVersion.version,
|
|
95
|
-
componentVersion,
|
|
96
|
-
module: 'ROOT',
|
|
97
|
-
family: 'page',
|
|
98
|
-
relative: 'index.adoc',
|
|
99
|
-
origin: (componentVersion.origins || [])[0],
|
|
100
|
-
},
|
|
101
|
-
})
|
|
102
|
-
startPage.path = ['modules', startPage.src.module, startPage.src.family + 's', startPage.src.relative].join('/')
|
|
107
|
+
})
|
|
108
|
+
startPage.path = ['modules', startPage.src.module, startPage.src.family + 's', startPage.src.relative].join('/')
|
|
109
|
+
}
|
|
110
|
+
const mutableAttributes = selectMutableAttributes(loadAsciiDoc, contentCatalog, startPage, mergedAsciiDocConfig)
|
|
111
|
+
prepareOutlines(navigation, rootEntry, rootLevel).reduce((any, outline) => {
|
|
112
|
+
const assemblyFile = produceAssemblyFile(
|
|
113
|
+
loadAsciiDoc,
|
|
114
|
+
contentCatalog,
|
|
115
|
+
componentVersion,
|
|
116
|
+
outline,
|
|
117
|
+
publishableFiles,
|
|
118
|
+
mergedAsciiDocConfig,
|
|
119
|
+
mutableAttributes,
|
|
120
|
+
assemblyModel
|
|
121
|
+
)
|
|
122
|
+
if (!assemblyFile) return any
|
|
123
|
+
// NOTE restore source highlighter for conversion if defined in Assembler config
|
|
124
|
+
if (sourceHighlighter !== undefined) {
|
|
125
|
+
assemblyFile.asciidoc.attributes['source-highlighter'] = sourceHighlighter
|
|
126
|
+
} else if (assemblyModel.filetype !== 'html') {
|
|
127
|
+
delete assemblyFile.asciidoc.attributes['source-highlighter']
|
|
103
128
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
contentCatalog,
|
|
109
|
-
componentVersion,
|
|
110
|
-
outline,
|
|
111
|
-
publishableFiles,
|
|
112
|
-
mergedAsciiDocConfig,
|
|
113
|
-
mutableAttributes,
|
|
114
|
-
assemblyModel
|
|
115
|
-
)
|
|
116
|
-
if (!assemblyFile) return any
|
|
117
|
-
// NOTE restore source highlighter for conversion if defined in Assembler config
|
|
118
|
-
if (sourceHighlighter !== undefined) {
|
|
119
|
-
assemblyFile.asciidoc.attributes['source-highlighter'] = sourceHighlighter
|
|
120
|
-
} else if (assemblyModel.filetype !== 'html') {
|
|
121
|
-
delete assemblyFile.asciidoc.attributes['source-highlighter']
|
|
122
|
-
}
|
|
123
|
-
return !!accum.push(assemblyFile)
|
|
124
|
-
}, false)
|
|
125
|
-
return accum
|
|
126
|
-
},
|
|
127
|
-
[]
|
|
128
|
-
)
|
|
129
|
+
return !!accum.push(assemblyFile)
|
|
130
|
+
}, false)
|
|
131
|
+
return accum
|
|
132
|
+
}, [])
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
function getAsciiDocConfigWithAsciidoctorReducerExtension (componentVersion) {
|
package/lib/util/rewriter.js
CHANGED
|
@@ -19,7 +19,7 @@ function rewriteXrefs (
|
|
|
19
19
|
doc,
|
|
20
20
|
sourceLocation
|
|
21
21
|
) {
|
|
22
|
-
return line.replace(/(?<![\\+])xref:((
|
|
22
|
+
return line.replace(/(?<![\\+])xref:((?:\.\/|:)?[\p{Alpha}0-9_/.{#].*?)\[(|[\s\S]*?[^\\])\]/gu, (_, target, text) =>
|
|
23
23
|
rewriteXref(
|
|
24
24
|
target,
|
|
25
25
|
text,
|
|
@@ -87,7 +87,7 @@ function rewriteXref (
|
|
|
87
87
|
const linkAttrlist = text
|
|
88
88
|
? (~text.indexOf(',') ? `"${text}"` : text) + ',role=unresolved'
|
|
89
89
|
: (~target.indexOf(':') ? `${rawTarget},` : '') + 'role=unresolved'
|
|
90
|
-
return `link:${rawTarget}[${linkAttrlist}]`
|
|
90
|
+
return `link:${rawTarget.charAt() === ':' ? resourceId.module + rawTarget : rawTarget}[${linkAttrlist}]`
|
|
91
91
|
}
|
|
92
92
|
if (fragment === resource.asciidoc.id) fragment = ''
|
|
93
93
|
if (
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/assembler",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.6",
|
|
4
4
|
"description": "A JavaScript library that merges AsciiDoc content from multiple pages in an Antora site into assembly files and delegates to an exporter to convert those files to another format, such as PDF.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"author": "OpenDevise Inc. (https://opendevise.com)",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"#unconvert-inline-asciidoc": "./lib/util/unconvert-inline-asciidoc.js"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@asciidoctor/reducer": "~1.1",
|
|
42
41
|
"@antora/expand-path-helper": "~3.0",
|
|
43
42
|
"@antora/run-command-helper": "~1.1",
|
|
43
|
+
"@asciidoctor/reducer": "~1.1",
|
|
44
44
|
"js-yaml": "~5.2"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|