@antora/assembler 1.0.0-beta.6 → 1.0.0-beta.7
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 +68 -16
- package/lib/load-config.js +23 -1
- package/lib/produce-assembly-file.js +91 -29
- package/lib/produce-assembly-files.js +35 -0
- package/lib/util/compute-out.js +1 -1
- package/package.json +1 -1
package/lib/assemble-content.js
CHANGED
|
@@ -13,6 +13,7 @@ const { stringify: toJSON } = JSON
|
|
|
13
13
|
|
|
14
14
|
const invariably = { new: () => ({}), void: () => undefined }
|
|
15
15
|
const PACKAGE_NAME = require('../package.json').name
|
|
16
|
+
const NEWLINE_RX = /(?:\r?\n)+/
|
|
16
17
|
|
|
17
18
|
async function assembleContent (playbook, contentCatalog, converter, { configSource, navigationCatalog }) {
|
|
18
19
|
const assemblerConfig = await loadConfig(playbook, configSource)
|
|
@@ -26,10 +27,20 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
26
27
|
}
|
|
27
28
|
const generatorFunctions = context.getFunctions()
|
|
28
29
|
const { loadAsciiDoc = require('@antora/asciidoc-loader') } = generatorFunctions
|
|
29
|
-
const
|
|
30
|
+
const {
|
|
31
|
+
convert = converter,
|
|
32
|
+
getDefaultCommand,
|
|
33
|
+
extname: targetExtname = '',
|
|
34
|
+
backend: targetBackend = targetExtname.slice(1),
|
|
35
|
+
embedReferenceStyle = 'relative',
|
|
36
|
+
mediaType: targetMediaType,
|
|
37
|
+
loggerName = PACKAGE_NAME,
|
|
38
|
+
} = converter ?? {}
|
|
30
39
|
const { assembly: assemblyConfig, build: buildConfig } = assemblerConfig
|
|
40
|
+
assemblyConfig.embedReferenceStyle = embedReferenceStyle
|
|
31
41
|
const { profile = targetBackend } = assemblyConfig
|
|
32
42
|
const intrinsicAttributes = { 'loader-assembler': '' }
|
|
43
|
+
buildConfig.cwd ??= process.cwd()
|
|
33
44
|
if (profile) {
|
|
34
45
|
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), `build/assembler-${profile}`)
|
|
35
46
|
intrinsicAttributes[`assembler-profile-${profile}`] = ''
|
|
@@ -41,6 +52,11 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
41
52
|
} else {
|
|
42
53
|
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), 'build/assembler')
|
|
43
54
|
}
|
|
55
|
+
if (targetExtname) {
|
|
56
|
+
const targetFiletype = targetExtname.slice(1)
|
|
57
|
+
intrinsicAttributes[`assembler-filetype-${targetFiletype}`] = ''
|
|
58
|
+
intrinsicAttributes['assembler-filetype'] = targetFiletype
|
|
59
|
+
}
|
|
44
60
|
Object.assign(assemblerConfig.asciidoc.attributes, intrinsicAttributes)
|
|
45
61
|
const assemblyFiles = produceAssemblyFiles(
|
|
46
62
|
loadAsciiDoc,
|
|
@@ -48,21 +64,23 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
48
64
|
assemblerConfig,
|
|
49
65
|
createResolveAssemblyModel(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog)
|
|
50
66
|
)
|
|
51
|
-
|
|
52
|
-
if (
|
|
67
|
+
if (!(assemblyFiles.length && typeof convert === 'function')) return assemblyFiles
|
|
68
|
+
if (typeof getDefaultCommand === 'function') {
|
|
69
|
+
buildConfig.command = await getDefaultCommand(buildConfig.cwd)
|
|
70
|
+
}
|
|
53
71
|
const { publishSite: publishFiles = require('@antora/site-publisher') } = generatorFunctions
|
|
54
72
|
await prepareWorkspace(publishFiles, assemblyFiles, buildConfig)
|
|
55
73
|
const boundConvert = convert.bind(context)
|
|
56
74
|
return new PromiseQueue({ concurrency: buildConfig.processLimit })
|
|
57
75
|
.add(
|
|
58
76
|
assemblyFiles.map((doc) => async () => {
|
|
59
|
-
const
|
|
77
|
+
const relativeToOutput = embedReferenceStyle === 'output-relative'
|
|
78
|
+
const convertAttributes = prepareConvertAttributes(doc, targetExtname, relativeToOutput, buildConfig)
|
|
60
79
|
if (buildConfig.mkdirs) await fsp.mkdir(convertAttributes.outdir, { recursive: true, force: true })
|
|
61
|
-
return boundConvert(doc, convertAttributes, buildConfig).then(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
80
|
+
return boundConvert(doc, convertAttributes, buildConfig).then((result) => {
|
|
81
|
+
const fileOrContents = resolveFileOrContents.call(context, result, convertAttributes, buildConfig, loggerName)
|
|
82
|
+
return coerceToExportFormat(doc, targetBackend, targetExtname, targetMediaType, fileOrContents)
|
|
83
|
+
})
|
|
66
84
|
})
|
|
67
85
|
)
|
|
68
86
|
.toPromise()
|
|
@@ -99,14 +117,14 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
99
117
|
})
|
|
100
118
|
}
|
|
101
119
|
|
|
102
|
-
function createResolveAssemblyModel (context, contentCatalog,
|
|
103
|
-
const logger = context.getLogger
|
|
120
|
+
function createResolveAssemblyModel (context, contentCatalog, common, intrinsicAttributes, navigationCatalog) {
|
|
121
|
+
const logger = context.getLogger?.(PACKAGE_NAME)
|
|
104
122
|
const { assemblerProfiles } = context.getVariables()
|
|
105
123
|
if (!assemblerProfiles) {
|
|
106
124
|
return (componentVersion) => {
|
|
107
125
|
const navigation =
|
|
108
126
|
navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version) ?? componentVersion.navigation
|
|
109
|
-
return Object.assign({ logger },
|
|
127
|
+
return Object.assign({ logger }, common, { navigation })
|
|
110
128
|
}
|
|
111
129
|
}
|
|
112
130
|
const boundSendToLog = sendToLog.bind(logger)
|
|
@@ -116,7 +134,7 @@ function createResolveAssemblyModel (context, contentCatalog, shared, intrinsicA
|
|
|
116
134
|
const overrides =
|
|
117
135
|
componentVersionProfiles?.get(intrinsicAttributes['assembler-profile']) ?? componentVersionProfiles?.get() ?? {}
|
|
118
136
|
const { navFiles, messages } = overrides
|
|
119
|
-
const model = Object.assign({ logger },
|
|
137
|
+
const model = Object.assign({ logger }, common, overrides)
|
|
120
138
|
delete model.navFiles
|
|
121
139
|
delete model.messages
|
|
122
140
|
const navigationOverride = navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version)
|
|
@@ -145,7 +163,7 @@ function createResolveAssemblyModel (context, contentCatalog, shared, intrinsicA
|
|
|
145
163
|
}
|
|
146
164
|
}
|
|
147
165
|
|
|
148
|
-
function prepareConvertAttributes (doc, targetExtname, buildConfig) {
|
|
166
|
+
function prepareConvertAttributes (doc, targetExtname, relativeToOutput, buildConfig) {
|
|
149
167
|
const {
|
|
150
168
|
asciidoc: { attributes: docAttributes } = { attributes: {} },
|
|
151
169
|
extname: docfilesuffix,
|
|
@@ -155,7 +173,8 @@ function prepareConvertAttributes (doc, targetExtname, buildConfig) {
|
|
|
155
173
|
const { cwd = process.cwd(), dir = cwd } = buildConfig
|
|
156
174
|
const docname = family + '$' + relative.slice(0, relative.length - docfilesuffix.length)
|
|
157
175
|
const docfile = ospath.join(dir, reldocfile)
|
|
158
|
-
const
|
|
176
|
+
const outdir = ospath.dirname(docfile)
|
|
177
|
+
const docdir = relativeToOutput ? dir : outdir
|
|
159
178
|
const imagesdir = ''
|
|
160
179
|
const outfile = docfile.slice(0, docfile.length - docfilesuffix.length) + targetExtname
|
|
161
180
|
const attributes = Object.assign({}, docAttributes, {
|
|
@@ -164,7 +183,7 @@ function prepareConvertAttributes (doc, targetExtname, buildConfig) {
|
|
|
164
183
|
docfilesuffix,
|
|
165
184
|
'docname@': docname,
|
|
166
185
|
imagesdir,
|
|
167
|
-
outdir
|
|
186
|
+
outdir,
|
|
168
187
|
outfile,
|
|
169
188
|
outfilesuffix: targetExtname,
|
|
170
189
|
toArgs (optionFlag, command) {
|
|
@@ -222,6 +241,39 @@ function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
|
|
|
222
241
|
return publishFiles({ output: { clean, dir } }, { getFiles: () => files })
|
|
223
242
|
}
|
|
224
243
|
|
|
244
|
+
function resolveFileOrContents (convertResult, convertAttributes, buildConfig, loggerName) {
|
|
245
|
+
let fileOrContents
|
|
246
|
+
if (convertResult?.status != null) {
|
|
247
|
+
fileOrContents = 'file' in convertResult ? convertResult.file : convertResult.contents
|
|
248
|
+
let logger, match
|
|
249
|
+
if (buildConfig.stderrSink === 'log' && convertResult.stderr.length && (logger = this.getLogger(loggerName))) {
|
|
250
|
+
const docfile = convertAttributes.docfile
|
|
251
|
+
const command = buildConfig.command
|
|
252
|
+
const stderr = convertResult.stderr.toString().trimEnd()
|
|
253
|
+
stderr.split(NEWLINE_RX).forEach((line) => {
|
|
254
|
+
const ctx = { command, file: { path: docfile } }
|
|
255
|
+
if (line.charAt() === '{' && line.charAt(line.length - 1) === '}') {
|
|
256
|
+
const entry = JSON.parse(line)
|
|
257
|
+
if (entry.name) ctx.program = entry.name
|
|
258
|
+
if (entry.file?.line) ctx.line = entry.file.line
|
|
259
|
+
logger[entry.level](ctx, entry.msg)
|
|
260
|
+
} else if ((match = /^(.+):(\d+): warning: (.+)/.exec(line))) {
|
|
261
|
+
const [, scriptPath, lineno, msg] = match
|
|
262
|
+
ctx.stack = [{ file: { path: scriptPath }, line: parseInt(lineno, 10) }]
|
|
263
|
+
logger.warn(ctx, msg)
|
|
264
|
+
} else {
|
|
265
|
+
logger.info(ctx, line)
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
} else if (convertResult !== undefined) {
|
|
270
|
+
return convertResult
|
|
271
|
+
}
|
|
272
|
+
return fileOrContents === undefined
|
|
273
|
+
? new LazyReadable(() => fs.createReadStream(convertAttributes.outfile))
|
|
274
|
+
: fileOrContents
|
|
275
|
+
}
|
|
276
|
+
|
|
225
277
|
function isBound (obj) {
|
|
226
278
|
if (obj == null) return false
|
|
227
279
|
for (const _ in obj) return true
|
package/lib/load-config.js
CHANGED
|
@@ -5,6 +5,14 @@ const fsp = require('node:fs/promises')
|
|
|
5
5
|
const os = require('node:os')
|
|
6
6
|
const yaml = require('js-yaml')
|
|
7
7
|
|
|
8
|
+
const ASSEMBLY_KEYS = [
|
|
9
|
+
'rootLevel',
|
|
10
|
+
'insertStartPage',
|
|
11
|
+
'sectionMergeStrategy',
|
|
12
|
+
'linkReferenceStyle',
|
|
13
|
+
'dropExplicitXrefText',
|
|
14
|
+
]
|
|
15
|
+
|
|
8
16
|
function loadConfig (playbook, configSource = './antora-assembler.yml') {
|
|
9
17
|
return (
|
|
10
18
|
configSource.constructor === Object
|
|
@@ -46,7 +54,7 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
|
|
|
46
54
|
const remapAssemblyKeys = !('assembly' in config)
|
|
47
55
|
const assembly = (config.assembly ??= {})
|
|
48
56
|
if (remapAssemblyKeys) {
|
|
49
|
-
for (const key of
|
|
57
|
+
for (const key of ASSEMBLY_KEYS) {
|
|
50
58
|
if (!(key in config)) continue
|
|
51
59
|
assembly[key] = config[key]
|
|
52
60
|
delete config[key]
|
|
@@ -59,6 +67,12 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
|
|
|
59
67
|
if (['discrete', 'fuse', 'enclose'].indexOf(assembly.sectionMergeStrategy) < 0) {
|
|
60
68
|
assembly.sectionMergeStrategy = 'discrete'
|
|
61
69
|
}
|
|
70
|
+
if (['relative', 'root-relative', 'absolute'].indexOf(assembly.linkReferenceStyle) < 0) {
|
|
71
|
+
assembly.linkReferenceStyle = 'absolute'
|
|
72
|
+
}
|
|
73
|
+
if (['always', 'if-redundant', 'never'].indexOf(assembly.dropExplicitXrefText) < 0) {
|
|
74
|
+
assembly.dropExplicitXrefText = 'never'
|
|
75
|
+
}
|
|
62
76
|
const build = (config.build ??= {})
|
|
63
77
|
if (build.dir === '$' + '{playbook.output.dir}') {
|
|
64
78
|
throw new Error('Not implemented')
|
|
@@ -74,6 +88,14 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
|
|
|
74
88
|
if (!build.processLimit) {
|
|
75
89
|
build.processLimit = 'processLimit' in build ? Infinity : Math.round(os.cpus().length * 0.5)
|
|
76
90
|
}
|
|
91
|
+
if ('stderr' in build) {
|
|
92
|
+
if (build.stderr === 'log') {
|
|
93
|
+
build.stderr = 'buffer'
|
|
94
|
+
build.stderrSink = 'log'
|
|
95
|
+
} else if (!['ignore', 'print'].includes(build.stderr)) {
|
|
96
|
+
delete build.stderr
|
|
97
|
+
}
|
|
98
|
+
}
|
|
77
99
|
return config
|
|
78
100
|
})
|
|
79
101
|
}
|
|
@@ -114,16 +114,22 @@ function mergeAsciiDoc (
|
|
|
114
114
|
let navtitlePlain = sanitize(navtitle)
|
|
115
115
|
let navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
116
116
|
const { items = [], unresolved, urlType, url } = outlineEntry
|
|
117
|
+
const {
|
|
118
|
+
doctype,
|
|
119
|
+
filetype,
|
|
120
|
+
embedReferenceStyle: embedRefStyle,
|
|
121
|
+
linkReferenceStyle: linkRefStyle,
|
|
122
|
+
outDirname,
|
|
123
|
+
siteRoot,
|
|
124
|
+
xmlIds,
|
|
125
|
+
} = assemblyModel
|
|
117
126
|
// FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
|
|
118
127
|
const page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
|
|
119
128
|
const atDocumentRoot = !buffer.inBody
|
|
120
|
-
const atBookRoot = atDocumentRoot && !level &&
|
|
129
|
+
const atBookRoot = atDocumentRoot && !level && doctype === 'book' && (supportsParts = true)
|
|
121
130
|
const hasItems = items.length > 0
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
return val.charAt(val.length - 1) === '/' ? val.slice(0, val.length - 1) : val
|
|
125
|
-
})(asciidocConfig.attributes['primary-site-url'] || asciidocConfig.attributes['site-url'])
|
|
126
|
-
const idSeparator = assemblyModel.xmlIds ? '-' : ':'
|
|
131
|
+
const pubRoot = outDirname ? '/' + outDirname : ''
|
|
132
|
+
const idSeparator = xmlIds ? '-' : ':'
|
|
127
133
|
const idScopeSeparator = idSeparator.repeat(3)
|
|
128
134
|
const idCoordinateSeparator = idSeparator === '-' ? '----' : idSeparator
|
|
129
135
|
if (page && !pagesInOutline.assembled.pages.has(page)) {
|
|
@@ -141,7 +147,15 @@ function mergeAsciiDoc (
|
|
|
141
147
|
if (doc.hasAttribute('assembly-navtitle')) {
|
|
142
148
|
navtitleAsciiDoc = doc.getAttribute('assembly-navtitle')
|
|
143
149
|
navtitlePlain = sanitize((navtitle = doc.$apply_reftext_subs(navtitleAsciiDoc)))
|
|
144
|
-
if (buffer.inBody == null)
|
|
150
|
+
if (buffer.inBody == null) {
|
|
151
|
+
buffer.navtitle = navtitle
|
|
152
|
+
// Q do we need to assert !level here?
|
|
153
|
+
if (assemblyModel.rootLevel) {
|
|
154
|
+
let doctitle = navtitleAsciiDoc
|
|
155
|
+
if (navtitlePlain !== componentVersion.title) doctitle = `${componentVersion.title}: ${doctitle}`
|
|
156
|
+
buffer[0] = `= ${doctitle}`
|
|
157
|
+
}
|
|
158
|
+
}
|
|
145
159
|
}
|
|
146
160
|
if (atDocumentRoot) {
|
|
147
161
|
const authors = doc.getAuthors()
|
|
@@ -374,7 +388,7 @@ function mergeAsciiDoc (
|
|
|
374
388
|
if (~line.indexOf('xref:')) {
|
|
375
389
|
// Q: should we allow : as first character of target?
|
|
376
390
|
line = line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (m, target, text) => {
|
|
377
|
-
let relativePart, fragment,
|
|
391
|
+
let relativePart, fragment, resource, isPage
|
|
378
392
|
const hashIdx = target.indexOf('#')
|
|
379
393
|
const dollarIdx = target.indexOf('$')
|
|
380
394
|
if (~hashIdx) {
|
|
@@ -403,9 +417,9 @@ function mergeAsciiDoc (
|
|
|
403
417
|
if (~hashIdx && !relativePart.endsWith('.adoc')) relativePart += '.adoc'
|
|
404
418
|
}
|
|
405
419
|
if (!isPage || ~relativePart.indexOf('@') || /:.*:/.test(relativePart)) {
|
|
406
|
-
if (
|
|
407
|
-
text ||=
|
|
408
|
-
return `${
|
|
420
|
+
if (siteRoot && (resource = contentCatalog.resolveResource(relativePart, page.src, 'page'))?.pub) {
|
|
421
|
+
text ||= resource.asciidoc?.xreftext || target
|
|
422
|
+
return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkRefStyle)}${fragment && '#' + fragment}[${text}]`
|
|
409
423
|
}
|
|
410
424
|
// TODO: handle unresolved resource better
|
|
411
425
|
return m
|
|
@@ -420,14 +434,10 @@ function mergeAsciiDoc (
|
|
|
420
434
|
targetModule = module_
|
|
421
435
|
}
|
|
422
436
|
const pageResourceRef = targetModule === 'ROOT' ? relativePart : `${targetModule}:${relativePart}`
|
|
423
|
-
if (!(
|
|
424
|
-
if (
|
|
425
|
-
|
|
426
|
-
(
|
|
427
|
-
targetResource.out
|
|
428
|
-
) {
|
|
429
|
-
text ||= targetResource.asciidoc?.xreftext || target
|
|
430
|
-
return `${siteUrl}${targetResource.pub.url}${fragment && '#' + fragment}[${text}]`
|
|
437
|
+
if (!(resource = pagesInOutline.get(pageResourceRef))) {
|
|
438
|
+
if (siteRoot && (resource = contentCatalog.resolvePage(pageResourceRef, page.src)) && resource.out) {
|
|
439
|
+
text ||= resource.asciidoc?.xreftext || target
|
|
440
|
+
return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkRefStyle)}${fragment && '#' + fragment}[${text}]`
|
|
431
441
|
}
|
|
432
442
|
// TODO: handle unresolved page better
|
|
433
443
|
return m
|
|
@@ -437,13 +447,20 @@ function mergeAsciiDoc (
|
|
|
437
447
|
const refid = fragment
|
|
438
448
|
? `${relativePart}${idScopeSeparator}${fragment}`
|
|
439
449
|
: relativePart + (ReservedIdNames.includes(relativePart) ? idScopeSeparator : '')
|
|
440
|
-
|
|
450
|
+
if (
|
|
451
|
+
text &&
|
|
452
|
+
(assemblyModel.dropExplicitXrefText === 'always' ||
|
|
453
|
+
(assemblyModel.dropExplicitXrefText === 'if-redundant' && text === resource.title))
|
|
454
|
+
) {
|
|
455
|
+
text = ''
|
|
456
|
+
}
|
|
457
|
+
return `<<${refid}${text ? ',' + text.replace(/\\]/g, ']') : ''}>>`
|
|
441
458
|
})
|
|
442
459
|
}
|
|
443
460
|
if (~line.indexOf('link:{attachmentsdir}/')) {
|
|
444
461
|
line = line.replace(/(?<![\\+])link:\{attachmentsdir\}\/([^\s[]+)\[(|.*?[^\\])\]/g, (m, relative, text) => {
|
|
445
462
|
const attachment =
|
|
446
|
-
|
|
463
|
+
siteRoot &&
|
|
447
464
|
contentCatalog.getById({
|
|
448
465
|
component: componentVersion.name,
|
|
449
466
|
version: componentVersion.version,
|
|
@@ -452,7 +469,7 @@ function mergeAsciiDoc (
|
|
|
452
469
|
relative,
|
|
453
470
|
})
|
|
454
471
|
// TODO: handle unresolved attachment page
|
|
455
|
-
return attachment?.out ? `${
|
|
472
|
+
return attachment?.out ? `${resolveLinkTarget(attachment, siteRoot, pubRoot, linkRefStyle)}[${text}]` : m
|
|
456
473
|
})
|
|
457
474
|
}
|
|
458
475
|
if (~line.indexOf('image:') && !line.startsWith('image::')) {
|
|
@@ -460,9 +477,11 @@ function mergeAsciiDoc (
|
|
|
460
477
|
if (isResourceSpec(target)) {
|
|
461
478
|
const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
|
|
462
479
|
// TODO: handle (or report) unresolved image better
|
|
463
|
-
if (image?.out) {
|
|
480
|
+
if (image?.out && (filetype !== 'html' || siteRoot)) {
|
|
464
481
|
pagesInOutline.assembled.assets.add(image)
|
|
465
|
-
return
|
|
482
|
+
return filetype === 'html'
|
|
483
|
+
? `image:${resolveLinkTarget(image, siteRoot, pubRoot, linkRefStyle)}[${attrlist}]`
|
|
484
|
+
: `image:${resolveEmbedTarget(image, outDirname, embedRefStyle, true)}[${attrlist}]`
|
|
466
485
|
}
|
|
467
486
|
}
|
|
468
487
|
return m
|
|
@@ -516,10 +535,13 @@ function mergeAsciiDoc (
|
|
|
516
535
|
if (isResourceSpec(target)) {
|
|
517
536
|
const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
|
|
518
537
|
// FIXME: handle (or report) case when image is not resolved
|
|
519
|
-
if (image?.out) {
|
|
520
|
-
const
|
|
538
|
+
if (image?.out && (filetype !== 'html' || siteRoot)) {
|
|
539
|
+
const attrlist = line.slice(line.indexOf('[') + 1, -1)
|
|
521
540
|
pagesInOutline.assembled.assets.add(image)
|
|
522
|
-
lines[idx] =
|
|
541
|
+
lines[idx] =
|
|
542
|
+
filetype === 'html'
|
|
543
|
+
? `${prefix}image::${resolveLinkTarget(image, siteRoot, pubRoot, linkRefStyle, false)}[${attrlist}]`
|
|
544
|
+
: `${prefix}image::${resolveEmbedTarget(image, outDirname, embedRefStyle)}[${attrlist}]`
|
|
523
545
|
}
|
|
524
546
|
}
|
|
525
547
|
lastImageMacroAt = [idx, imageMacroOffset]
|
|
@@ -580,9 +602,18 @@ function mergeAsciiDoc (
|
|
|
580
602
|
let sectionTitle = navtitleAsciiDoc
|
|
581
603
|
if (urlType === 'external') {
|
|
582
604
|
sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
583
|
-
} else if (urlType === 'internal' && !unresolved
|
|
605
|
+
} else if (urlType === 'internal' && !unresolved) {
|
|
584
606
|
const resource = files.find((it) => it.pub.url === url)
|
|
585
|
-
if (resource)
|
|
607
|
+
if (resource) {
|
|
608
|
+
if (resource.src.family === 'page' && pagesInOutline.has(resource.pub.url)) {
|
|
609
|
+
let refid = resource.src.relative.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
|
|
610
|
+
if (refid.startsWith('./')) refid = topicPrefix + refid.slice(2)
|
|
611
|
+
if (resource.src.module !== 'ROOT') refid = `${resource.src.module}${idCoordinateSeparator}${refid}`
|
|
612
|
+
sectionTitle = `<<${refid},${navtitleAsciiDoc}>>`
|
|
613
|
+
} else if (siteRoot) {
|
|
614
|
+
sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot, linkRefStyle)}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
615
|
+
}
|
|
616
|
+
}
|
|
586
617
|
}
|
|
587
618
|
let hlevel = level + 1
|
|
588
619
|
if (hlevel > 6) {
|
|
@@ -787,4 +818,35 @@ function safePush (onto, entries) {
|
|
|
787
818
|
}
|
|
788
819
|
}
|
|
789
820
|
|
|
821
|
+
function resolveEmbedTarget (resource, outDirname, referenceStyle, escapeForInline) {
|
|
822
|
+
const target =
|
|
823
|
+
referenceStyle === 'output-relative' ? resource.out.path : path.relative(outDirname + '/', resource.out.path)
|
|
824
|
+
return escapeForInline ? target.replace(/_/g, '{underscore}') : target
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function resolveLinkTarget (resource, siteRoot, pubRoot, referenceStyle, escapeForInline = true) {
|
|
828
|
+
let target
|
|
829
|
+
if (resource.site?.url) {
|
|
830
|
+
target = ['', resource.pub.url]
|
|
831
|
+
} else {
|
|
832
|
+
switch (referenceStyle) {
|
|
833
|
+
case 'absolute':
|
|
834
|
+
target = ['', siteRoot.url + resource.pub.url]
|
|
835
|
+
break
|
|
836
|
+
case 'root-relative':
|
|
837
|
+
target = ['link:', siteRoot.path + resource.pub.url]
|
|
838
|
+
break
|
|
839
|
+
default:
|
|
840
|
+
target = ['link:', computeRelativeUrl(pubRoot + '/', resource.pub.url)]
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if (escapeForInline) target[1] = target[1].replace(/_/g, '{underscore}')
|
|
844
|
+
return target.join('')
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function computeRelativeUrl (from, to) {
|
|
848
|
+
const rel = path.relative(from, to)
|
|
849
|
+
return to.charAt(to.length - 1) === '/' ? rel + '/' : rel
|
|
850
|
+
}
|
|
851
|
+
|
|
790
852
|
module.exports = produceAssemblyFile
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const computeOut = require('./util/compute-out')
|
|
3
4
|
const createAsciiDocFile = require('./util/create-asciidoc-file')
|
|
4
5
|
const filterComponentVersions = require('./filter-component-versions')
|
|
5
6
|
const produceAssemblyFile = require('./produce-assembly-file')
|
|
@@ -17,12 +18,16 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
|
|
|
17
18
|
sectionMergeStrategy: assemblyConfig.sectionMergeStrategy,
|
|
18
19
|
navigation: componentVersion.navigation,
|
|
19
20
|
xmlIds: assemblyConfig.xmlIds,
|
|
21
|
+
embedReferenceStyle: assemblyConfig.embedReferenceStyle,
|
|
22
|
+
linkReferenceStyle: assemblyConfig.linkReferenceStyle,
|
|
23
|
+
dropExplicitXrefText: assemblyConfig.dropExplicitXrefText,
|
|
20
24
|
})
|
|
21
25
|
const assemblerAsciiDocAttributes = Object.assign({}, assemblerAsciiDocConfig.attributes)
|
|
22
26
|
const { revdate, 'source-highlighter': sourceHighlighter } = assemblerAsciiDocAttributes
|
|
23
27
|
delete assemblerAsciiDocAttributes.revdate
|
|
24
28
|
delete assemblerAsciiDocAttributes['source-highlighter']
|
|
25
29
|
const publishableFiles = contentCatalog.getFiles().filter((file) => file.out)
|
|
30
|
+
let siteRoot
|
|
26
31
|
const configMdc = assemblerConfig.file ? { file: { path: assemblerConfig.file } } : {}
|
|
27
32
|
return filterComponentVersions(contentCatalog.getComponents(), assemblerConfig.componentVersionFilter.names).reduce(
|
|
28
33
|
(accum, componentVersion) => {
|
|
@@ -38,6 +43,30 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
|
|
|
38
43
|
const mergedAsciiDocConfig = Object.assign({}, componentVersionAsciiDocConfig, {
|
|
39
44
|
attributes: mergedAsciiDocAttributes,
|
|
40
45
|
})
|
|
46
|
+
assemblyModel.outDirname = computeOut.call(contentCatalog, {
|
|
47
|
+
component: componentName,
|
|
48
|
+
version,
|
|
49
|
+
family: 'export',
|
|
50
|
+
relative: '.index.adoc',
|
|
51
|
+
}).dirname
|
|
52
|
+
assemblyModel.filetype = assemblerAsciiDocAttributes['assembler-filetype']
|
|
53
|
+
assemblyModel.siteRoot =
|
|
54
|
+
siteRoot === undefined
|
|
55
|
+
? (siteRoot ??= ((val) => {
|
|
56
|
+
if (!val) return null
|
|
57
|
+
if (val.charAt(val.length - 1) === '/') val = val.slice(0, val.length - 1)
|
|
58
|
+
if (!val || val.charAt() === '/') return { path: val }
|
|
59
|
+
return { url: val, path: extractUrlPath(val) }
|
|
60
|
+
})(mergedAsciiDocAttributes['site-url'] || mergedAsciiDocAttributes['primary-site-url']))
|
|
61
|
+
: siteRoot
|
|
62
|
+
if (assemblyModel.filetype === 'html') {
|
|
63
|
+
let linkRefStyle = assemblyModel.linkReferenceStyle
|
|
64
|
+
if (linkRefStyle === 'absolute' && siteRoot?.url == null) linkRefStyle = 'root-relative'
|
|
65
|
+
if (linkRefStyle === 'root-relative' && siteRoot?.path == null) linkRefStyle = 'relative'
|
|
66
|
+
assemblyModel.linkReferenceStyle = linkRefStyle
|
|
67
|
+
} else {
|
|
68
|
+
assemblyModel.linkReferenceStyle = 'absolute'
|
|
69
|
+
}
|
|
41
70
|
const auxiliaryImages = new Set()
|
|
42
71
|
Object.entries(mergedAsciiDocAttributes).forEach(([name, val]) => {
|
|
43
72
|
const match = name.endsWith('-image') && val.startsWith('image:') && IMAGE_MACRO_RX.exec(val)
|
|
@@ -177,4 +206,10 @@ function collateAsciiDocAttributes (collated, additional, { logger, mdc }) {
|
|
|
177
206
|
return collated
|
|
178
207
|
}
|
|
179
208
|
|
|
209
|
+
function extractUrlPath (url) {
|
|
210
|
+
if (!url) return ''
|
|
211
|
+
const urlPath = new URL(url).pathname
|
|
212
|
+
return urlPath === '/' ? '' : urlPath
|
|
213
|
+
}
|
|
214
|
+
|
|
180
215
|
module.exports = produceAssemblyFiles
|
package/lib/util/compute-out.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { posix: path } = require('node:path')
|
|
4
4
|
|
|
5
5
|
function computeOut (src) {
|
|
6
|
-
const { component, version, module: module_, family, relative } = src
|
|
6
|
+
const { component, version, module: module_ = 'ROOT', family, relative } = src
|
|
7
7
|
const outRelative = family === 'page' ? relative.replace(/\.adoc$/, '.html') : relative
|
|
8
8
|
const { dir: dirname, base: basename } = path.parse(outRelative)
|
|
9
9
|
const componentVersion = this.getComponentVersion(component, version)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/assembler",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.7",
|
|
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)",
|