@antora/assembler 1.0.0-beta.18 → 1.0.0-beta.19
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 +29 -20
- package/lib/configure.js +15 -4
- package/lib/load-config.js +20 -9
- package/lib/log-command.js +18 -0
- package/lib/produce-assembly-file.js +225 -106
- package/lib/produce-assembly-files.js +7 -30
- package/lib/select-mutable-attributes.js +1 -0
- package/lib/util/generate-scoped-id.js +12 -12
- package/lib/util/rewriter.js +25 -30
- package/lib/util/rx.js +2 -2
- package/package.json +3 -2
package/lib/assemble-content.js
CHANGED
|
@@ -5,10 +5,12 @@ const fs = require('node:fs')
|
|
|
5
5
|
const { promises: fsp } = fs
|
|
6
6
|
const LazyReadable = require('./util/lazy-readable')
|
|
7
7
|
const loadConfig = require('./load-config')
|
|
8
|
+
const logCommand = require('./log-command')
|
|
8
9
|
const ospath = require('node:path')
|
|
9
10
|
const { posix: path } = ospath
|
|
10
11
|
const produceAssemblyFiles = require('./produce-assembly-files')
|
|
11
12
|
const PromiseQueue = require('./util/promise-queue')
|
|
13
|
+
const runCommand = require('@antora/run-command-helper')
|
|
12
14
|
const { stringify: toJSON } = JSON
|
|
13
15
|
|
|
14
16
|
const invariably = { new: () => ({}), void: () => undefined }
|
|
@@ -16,17 +18,6 @@ const PACKAGE_NAME = require('../package.json').name
|
|
|
16
18
|
const NEWLINE_RX = /(?:\r?\n)+/
|
|
17
19
|
|
|
18
20
|
async function assembleContent (playbook, contentCatalog, converter, { configSource, navigationCatalog }) {
|
|
19
|
-
const context = isBound(this)
|
|
20
|
-
? this
|
|
21
|
-
: {
|
|
22
|
-
getFunctions: invariably.new,
|
|
23
|
-
getLogger: invariably.void,
|
|
24
|
-
getVariables: invariably.new,
|
|
25
|
-
}
|
|
26
|
-
const assemblerConfig = await loadConfig.call(context, playbook, configSource)
|
|
27
|
-
if (assemblerConfig.enabled === false) return []
|
|
28
|
-
const generatorFunctions = context.getFunctions()
|
|
29
|
-
const { loadAsciiDoc = require('@antora/asciidoc-loader') } = generatorFunctions
|
|
30
21
|
const {
|
|
31
22
|
convert = converter,
|
|
32
23
|
getDefaultCommand,
|
|
@@ -36,13 +27,24 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
36
27
|
mediaType: targetMediaType,
|
|
37
28
|
loggerName = PACKAGE_NAME,
|
|
38
29
|
} = converter ?? {}
|
|
30
|
+
const assemblerConfig = await loadConfig.call(this, playbook, configSource, '-' + targetBackend)
|
|
31
|
+
if (assemblerConfig.enabled === false) return []
|
|
32
|
+
const context = isBound(this)
|
|
33
|
+
? this
|
|
34
|
+
: {
|
|
35
|
+
getFunctions: invariably.new,
|
|
36
|
+
getLogger: invariably.void,
|
|
37
|
+
getVariables: invariably.new,
|
|
38
|
+
}
|
|
39
|
+
const generatorFunctions = context.getFunctions()
|
|
40
|
+
const { loadAsciiDoc = require('@antora/asciidoc-loader') } = generatorFunctions
|
|
39
41
|
const { assembly: assemblyConfig, build: buildConfig } = assemblerConfig
|
|
40
42
|
assemblyConfig.embedReferenceStyle = embedReferenceStyle
|
|
41
43
|
const { profile = targetBackend } = assemblyConfig
|
|
42
44
|
const intrinsicAttributes = { 'loader-assembler': '' }
|
|
43
45
|
buildConfig.cwd ??= process.cwd()
|
|
44
46
|
if (profile) {
|
|
45
|
-
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), `build/assembler
|
|
47
|
+
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), `build/assembler/${profile}`)
|
|
46
48
|
intrinsicAttributes[`assembler-profile-${profile}`] = ''
|
|
47
49
|
intrinsicAttributes['assembler-profile'] = profile
|
|
48
50
|
if (targetBackend) {
|
|
@@ -50,14 +52,14 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
50
52
|
intrinsicAttributes['assembler-backend'] = targetBackend
|
|
51
53
|
}
|
|
52
54
|
} else {
|
|
53
|
-
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), 'build/assembler')
|
|
55
|
+
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), 'build/assembler/_')
|
|
54
56
|
}
|
|
55
57
|
if (targetExtname) {
|
|
56
58
|
const targetFiletype = targetExtname.slice(1)
|
|
57
59
|
intrinsicAttributes[`assembler-filetype-${targetFiletype}`] = ''
|
|
58
60
|
intrinsicAttributes['assembler-filetype'] = targetFiletype
|
|
59
61
|
}
|
|
60
|
-
Object.assign(
|
|
62
|
+
Object.assign(assemblyConfig.attributes, intrinsicAttributes)
|
|
61
63
|
const assemblyFiles = produceAssemblyFiles(
|
|
62
64
|
loadAsciiDoc,
|
|
63
65
|
contentCatalog,
|
|
@@ -70,14 +72,15 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
70
72
|
}
|
|
71
73
|
const { publishSite: publishFiles = require('@antora/site-publisher') } = generatorFunctions
|
|
72
74
|
await prepareWorkspace(publishFiles, assemblyFiles, buildConfig)
|
|
75
|
+
const helpers = { logCommand: logCommand.bind(context, loggerName), runCommand }
|
|
73
76
|
const boundConvert = convert.bind(context)
|
|
74
77
|
return new PromiseQueue({ concurrency: buildConfig.processLimit })
|
|
75
78
|
.add(
|
|
76
79
|
assemblyFiles.map((doc) => async () => {
|
|
77
80
|
const relativeToOutput = embedReferenceStyle === 'output-relative'
|
|
78
|
-
const convertAttributes = prepareConvertAttributes(doc, targetExtname, relativeToOutput,
|
|
81
|
+
const convertAttributes = prepareConvertAttributes(doc, targetExtname, relativeToOutput, assemblerConfig)
|
|
79
82
|
if (buildConfig.mkdirs) await fsp.mkdir(convertAttributes.outdir, { recursive: true, force: true })
|
|
80
|
-
return boundConvert(doc, convertAttributes, buildConfig).then((result) => {
|
|
83
|
+
return boundConvert(doc, convertAttributes, buildConfig, helpers).then((result) => {
|
|
81
84
|
const fileOrContents = resolveFileOrContents.call(context, result, convertAttributes, buildConfig, loggerName)
|
|
82
85
|
return coerceToExportFormat(doc, targetBackend, targetExtname, targetMediaType, fileOrContents)
|
|
83
86
|
})
|
|
@@ -178,21 +181,21 @@ function generateSelectAssemblyProfile (context, contentCatalog, base, intrinsic
|
|
|
178
181
|
}
|
|
179
182
|
}
|
|
180
183
|
|
|
181
|
-
function prepareConvertAttributes (doc, targetExtname, relativeToOutput,
|
|
184
|
+
function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assemblerConfig) {
|
|
182
185
|
const {
|
|
183
186
|
asciidoc: { attributes: docAttributes } = { attributes: {} },
|
|
184
187
|
extname: docfilesuffix,
|
|
185
188
|
path: reldocfile,
|
|
186
189
|
src: { family, relative },
|
|
187
190
|
} = doc
|
|
188
|
-
const { cwd = process.cwd(), dir = cwd } =
|
|
191
|
+
const { cwd = process.cwd(), dir = cwd } = assemblerConfig.build
|
|
189
192
|
const docname = family + '$' + relative.slice(0, relative.length - docfilesuffix.length)
|
|
190
193
|
const docfile = ospath.join(dir, reldocfile)
|
|
191
194
|
const outdir = ospath.dirname(docfile)
|
|
192
195
|
const docdir = relativeToOutput ? dir : outdir
|
|
193
196
|
const imagesdir = ''
|
|
194
197
|
const outfile = docfile.slice(0, docfile.length - docfilesuffix.length) + targetExtname
|
|
195
|
-
const attributes = Object.assign({}, docAttributes, {
|
|
198
|
+
const attributes = Object.assign({ revdate: `${assemblerConfig.assembly.revdate}@` }, docAttributes, {
|
|
196
199
|
docdir,
|
|
197
200
|
docfile,
|
|
198
201
|
docfilesuffix,
|
|
@@ -256,7 +259,13 @@ function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
|
|
|
256
259
|
outPaths.add(asset.out.path)
|
|
257
260
|
}
|
|
258
261
|
}
|
|
259
|
-
if (keepSource)
|
|
262
|
+
if (keepSource) {
|
|
263
|
+
for (const file of assemblyFiles) {
|
|
264
|
+
file.src.contents = file.contents
|
|
265
|
+
file.out = { path: file.path }
|
|
266
|
+
files.push(file)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
260
269
|
return publishFiles({ output: { clean, dir } }, { getFiles: () => files })
|
|
261
270
|
}
|
|
262
271
|
|
package/lib/configure.js
CHANGED
|
@@ -10,9 +10,7 @@ function internalConfigure (converter, config = {}, providers = {}) {
|
|
|
10
10
|
this.updateVariables({ assemblerProfiles: getAssemblerProfiles(contentCatalog) })
|
|
11
11
|
})
|
|
12
12
|
|
|
13
|
-
this.once('beforeProcess',
|
|
14
|
-
siteAsciiDocConfig.keepSource = true
|
|
15
|
-
})
|
|
13
|
+
this.once('beforeProcess', enableKeepSource)
|
|
16
14
|
|
|
17
15
|
this.once('navigationBuilt', async ({ playbook, contentCatalog }) => {
|
|
18
16
|
const { assembleContent = require('./assemble-content'), ...assembleContentConfig } = providers
|
|
@@ -30,10 +28,23 @@ function internalConfigure (converter, config = {}, providers = {}) {
|
|
|
30
28
|
})
|
|
31
29
|
}
|
|
32
30
|
|
|
31
|
+
function enableKeepSource ({ siteAsciiDocConfig }) {
|
|
32
|
+
if (siteAsciiDocConfig.keepSource instanceof Boolean) return
|
|
33
|
+
siteAsciiDocConfig.keepSource = Object.assign(new Boolean(true), { oldValue: siteAsciiDocConfig.keepSource })
|
|
34
|
+
this.once('navigationBuilt', restoreKeepSource)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function restoreKeepSource ({ siteAsciiDocConfig, contentCatalog }) {
|
|
38
|
+
if (!(siteAsciiDocConfig.keepSource instanceof Boolean)) return
|
|
39
|
+
if ((siteAsciiDocConfig.keepSource = siteAsciiDocConfig.keepSource.oldValue)) return
|
|
40
|
+
contentCatalog.getPages((page) => delete page.src.contents)
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
|
|
34
44
|
contentCatalog.getComponents().forEach((component) => {
|
|
35
45
|
component.versions.forEach((componentVersion) => {
|
|
36
|
-
const source =
|
|
46
|
+
const source =
|
|
47
|
+
componentVersion.nav?.origin ?? componentVersion.origins?.find((it) => it.descriptor?.ext?.assembler)
|
|
37
48
|
const assemblerConfig = getAssemblerConfigFromDescriptor(source?.descriptor)
|
|
38
49
|
if (!assemblerConfig) return
|
|
39
50
|
const componentVersionRef = `${componentVersion.version}@${componentVersion.name}`
|
package/lib/load-config.js
CHANGED
|
@@ -5,8 +5,6 @@ const fsp = require('node:fs/promises')
|
|
|
5
5
|
const os = require('node:os')
|
|
6
6
|
const yaml = require('js-yaml')
|
|
7
7
|
|
|
8
|
-
const PACKAGE_NAME = require('../package.json').name
|
|
9
|
-
|
|
10
8
|
const ASSEMBLY_KEYS = [
|
|
11
9
|
'rootLevel',
|
|
12
10
|
'insertStartPage',
|
|
@@ -15,18 +13,24 @@ const ASSEMBLY_KEYS = [
|
|
|
15
13
|
'dropExplicitXrefText',
|
|
16
14
|
]
|
|
17
15
|
const CAMEL_CASE_STOP_PATHS = ['asciidoc.attributes', 'assembly.attributes']
|
|
16
|
+
const PACKAGE_NAME = require('../package.json').name
|
|
18
17
|
|
|
19
|
-
function loadConfig (playbook, configSource) {
|
|
18
|
+
function loadConfig (playbook, configSource, preferredQualifier = '') {
|
|
20
19
|
let resolvedConfigSource
|
|
21
20
|
return (
|
|
22
21
|
configSource?.constructor === Object
|
|
23
22
|
? Promise.resolve(configSource)
|
|
24
|
-
:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
23
|
+
: fileExists(
|
|
24
|
+
(resolvedConfigSource = expandPath(configSource ?? `./antora-assembler${preferredQualifier}.yml`, {
|
|
25
|
+
dot: playbook.dir,
|
|
26
|
+
}))
|
|
27
|
+
)
|
|
28
|
+
.then((exists) => {
|
|
29
|
+
if (exists || configSource || !preferredQualifier) return exists
|
|
30
|
+
return fileExists(
|
|
31
|
+
(resolvedConfigSource = resolvedConfigSource.slice(0, -(preferredQualifier.length + 4)) + '.yml')
|
|
32
|
+
)
|
|
33
|
+
})
|
|
30
34
|
.then((exists) => {
|
|
31
35
|
if (!exists) {
|
|
32
36
|
let logger
|
|
@@ -134,6 +138,13 @@ function camelCaseKeys (o, stopPaths = [], p = undefined) {
|
|
|
134
138
|
return accum
|
|
135
139
|
}
|
|
136
140
|
|
|
141
|
+
function fileExists (path) {
|
|
142
|
+
return fsp.access(path).then(
|
|
143
|
+
() => true,
|
|
144
|
+
() => false
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
137
148
|
function getLocalDate (now = new Date()) {
|
|
138
149
|
return new Date(now - now.getTimezoneOffset() * 60000).toISOString().split('T')[0]
|
|
139
150
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function logCommand (loggerName, command, file, convertAttributes, attributeOptionFlagOrArgs) {
|
|
4
|
+
const logger = this?.getLogger(loggerName)
|
|
5
|
+
if (!logger?.isLevelEnabled('debug')) return
|
|
6
|
+
const { docfile, 'assembler-filetype': filetype } = convertAttributes
|
|
7
|
+
const args = Array.isArray(attributeOptionFlagOrArgs)
|
|
8
|
+
? attributeOptionFlagOrArgs
|
|
9
|
+
: attributeOptionFlagOrArgs
|
|
10
|
+
? convertAttributes.toArgs(attributeOptionFlagOrArgs, command)
|
|
11
|
+
: []
|
|
12
|
+
const ctx = { command: [command].concat(args).join(' '), file: { path: docfile } }
|
|
13
|
+
const msg = `Running external command to export assembly in %s to %s: %s`
|
|
14
|
+
const componentVersionStr = file.src.version ? `${file.src.version}@${file.src.component}` : file.src.component
|
|
15
|
+
logger.debug(ctx, msg, componentVersionStr, filetype, file.src.relative)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = logCommand
|
|
@@ -4,15 +4,28 @@ const createAsciiDocFile = require('./util/create-asciidoc-file')
|
|
|
4
4
|
const createResourceKey = require('./util/create-resource-key')
|
|
5
5
|
const generateScopedId = require('./util/generate-scoped-id')
|
|
6
6
|
const { resolveLinkTarget } = require('./util/resolver')
|
|
7
|
-
const {
|
|
8
|
-
|
|
7
|
+
const {
|
|
8
|
+
rewriteXrefs,
|
|
9
|
+
rewriteImageAttr,
|
|
10
|
+
rewriteImageRef,
|
|
11
|
+
rewriteInlineImages,
|
|
12
|
+
rewriteStyleAttribute,
|
|
13
|
+
} = require('./util/rewriter')
|
|
9
14
|
const sanitize = require('./util/sanitize')
|
|
10
15
|
const unconvertInlineAsciiDoc = require('./util/unconvert-inline-asciidoc')
|
|
11
16
|
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
17
|
+
const ATTR_ENTRY_RX = /^:(!?[\p{Alpha}0-9_][^:]*):(?: |$)/u
|
|
18
|
+
const BUILT_IN_NAMED_ENTITIES = { amp: '&', apos: "'", gt: '>', lt: '<', nbsp: ' ', quot: '"' }
|
|
19
|
+
const CHAR_REF_RX = /&(?:([a-z][a-z]+\d{0,2})|#(?:(\d{2,6})|x([a-z\d]{2,5})));/g
|
|
20
|
+
const DISCARD_ATTRIBUTE_NAMES = [
|
|
21
|
+
'doctype',
|
|
22
|
+
'leveloffset',
|
|
23
|
+
'assembly-header-attributes',
|
|
24
|
+
'assembly-navtitle',
|
|
25
|
+
'assembly-style',
|
|
26
|
+
'underscore',
|
|
27
|
+
]
|
|
28
|
+
const { NAMED_ID_ATTR_RX } = require('./util/rx')
|
|
16
29
|
|
|
17
30
|
function produceAssemblyFile (
|
|
18
31
|
loadAsciiDoc,
|
|
@@ -27,12 +40,28 @@ function produceAssemblyFile (
|
|
|
27
40
|
const pagesByUrl = files.reduce((map, it) => (it.src.family === 'page' ? map.set(it.pub.url, it) : map), new Map())
|
|
28
41
|
if (outline.urlType === 'internal' && !pagesByUrl.get(outline.url) && !(outline.items || []).length) return
|
|
29
42
|
const pagesInOutline = selectPagesInOutline(outline, pagesByUrl)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
const { rootLevel, xmlIds } = assemblyModel
|
|
44
|
+
const idSeparators = {
|
|
45
|
+
prefix:
|
|
46
|
+
'assembler-idprefix' in asciidocConfig.attributes ? (asciidocConfig.attributes['assembler-idprefix'] ?? '') : '_',
|
|
47
|
+
scope: xmlIds ? '---' : ':::',
|
|
48
|
+
coordinate: xmlIds ? '----' : ':',
|
|
49
|
+
generateIdFromTitle: generateIdFromTitle.bind(
|
|
50
|
+
loadAsciiDoc(
|
|
51
|
+
{ contents: Buffer.alloc(0), src: { family: 'page', relative: 'generate-id-from-title.adoc' } },
|
|
52
|
+
undefined,
|
|
53
|
+
asciidocConfig
|
|
54
|
+
)
|
|
55
|
+
),
|
|
56
|
+
}
|
|
57
|
+
const { name: component, version } = componentVersion
|
|
58
|
+
asciidocConfig = prepareAsciiDocConfig(
|
|
59
|
+
contentCatalog,
|
|
60
|
+
{ component, version },
|
|
61
|
+
pagesInOutline,
|
|
62
|
+
asciidocConfig,
|
|
63
|
+
assemblyModel,
|
|
64
|
+
idSeparators
|
|
36
65
|
)
|
|
37
66
|
const buffer = mergeAsciiDoc(
|
|
38
67
|
loadAsciiDoc,
|
|
@@ -42,29 +71,51 @@ function produceAssemblyFile (
|
|
|
42
71
|
outline,
|
|
43
72
|
files,
|
|
44
73
|
pagesInOutline,
|
|
74
|
+
idSeparators,
|
|
45
75
|
asciidocConfig,
|
|
46
76
|
mutableAttributes,
|
|
47
77
|
assemblyModel
|
|
48
78
|
)
|
|
49
|
-
const rootLevel = assemblyModel.rootLevel
|
|
50
79
|
const stem = rootLevel === 0 ? 'index' : generateSlug(buffer.navtitle)
|
|
51
|
-
const downloadStem = [
|
|
52
|
-
.filter((it) => it)
|
|
53
|
-
.join('-')
|
|
80
|
+
const downloadStem = [component, version, rootLevel === 0 ? '' : stem].filter((it) => it).join('-')
|
|
54
81
|
return createAsciiDocFile(contentCatalog, {
|
|
55
82
|
asciidoc: asciidocConfig,
|
|
56
83
|
assembler: { assembled: pagesInOutline.assembled, downloadStem, rootLevel },
|
|
57
84
|
contents: Buffer.from(buffer.join('\n') + '\n'),
|
|
58
|
-
src: {
|
|
59
|
-
component: componentVersion.name,
|
|
60
|
-
version: componentVersion.version,
|
|
61
|
-
module: 'ROOT',
|
|
62
|
-
family: 'export',
|
|
63
|
-
relative: stem + '.adoc',
|
|
64
|
-
},
|
|
85
|
+
src: { component, version, module: 'ROOT', family: 'export', relative: stem + '.adoc' },
|
|
65
86
|
})
|
|
66
87
|
}
|
|
67
88
|
|
|
89
|
+
function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocConfig, assemblyModel, idSeparators) {
|
|
90
|
+
let attributesModified
|
|
91
|
+
const configShared = asciidocConfig.$shared
|
|
92
|
+
const sharedAttributes = asciidocConfig.attributes
|
|
93
|
+
if (!configShared) {
|
|
94
|
+
if (configShared == null) {
|
|
95
|
+
const assets = pagesInOutline.assembled.assets
|
|
96
|
+
for (const [name, val] of Object.entries(sharedAttributes)) {
|
|
97
|
+
if (!(typeof val === 'string' && ~val.indexOf(':'))) continue
|
|
98
|
+
let newVal
|
|
99
|
+
if (!name.endsWith('-image') || !(newVal = rewriteImageAttr(val, contentCatalog, assemblyModel, ctx, assets))) {
|
|
100
|
+
if (~(newVal = val).indexOf('image:')) {
|
|
101
|
+
newVal = rewriteInlineImages(newVal, contentCatalog, assemblyModel, ctx, assets)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (newVal !== val) sharedAttributes[name] = newVal
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (const [name, val] of Object.entries(sharedAttributes)) {
|
|
108
|
+
if (!(typeof val === 'string' && ~val.indexOf(':'))) continue
|
|
109
|
+
if (~val.indexOf('xref:')) {
|
|
110
|
+
const newVal = rewriteXrefs(val, contentCatalog, assemblyModel, ctx, false, pagesInOutline, idSeparators)
|
|
111
|
+
if (newVal !== val) (attributesModified ??= {})[name] = newVal
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (configShared == null) Object.defineProperty(asciidocConfig, '$shared', { value: attributesModified == null })
|
|
115
|
+
}
|
|
116
|
+
return Object.assign({}, asciidocConfig, { attributes: Object.assign({}, sharedAttributes, attributesModified) })
|
|
117
|
+
}
|
|
118
|
+
|
|
68
119
|
function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
|
|
69
120
|
const doctype = assemblyModel.doctype ?? 'book'
|
|
70
121
|
const navtitlePlain = sanitize(navtitle)
|
|
@@ -107,6 +158,7 @@ function mergeAsciiDoc (
|
|
|
107
158
|
outlineEntry,
|
|
108
159
|
files,
|
|
109
160
|
pagesInOutline,
|
|
161
|
+
idSeparators,
|
|
110
162
|
asciidocConfig,
|
|
111
163
|
mutableAttributes,
|
|
112
164
|
assemblyModel,
|
|
@@ -123,18 +175,14 @@ function mergeAsciiDoc (
|
|
|
123
175
|
let navtitlePlain = sanitize(navtitle)
|
|
124
176
|
let navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
125
177
|
const { items = [], unresolved, urlType, url } = outlineEntry
|
|
126
|
-
const { doctype, filetype, linkReferenceStyle, pubRoot, siteRoot,
|
|
178
|
+
const { doctype, filetype, linkReferenceStyle, pubRoot, siteRoot, logger } = assemblyModel
|
|
127
179
|
const assembled = pagesInOutline.assembled
|
|
128
180
|
// FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
|
|
129
181
|
const page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
|
|
130
182
|
const atDocumentRoot = !buffer.inBody
|
|
183
|
+
const isRootPage = page && atDocumentRoot && !level
|
|
131
184
|
const atBookRoot = atDocumentRoot && !level && doctype === 'book' && (supportsParts = true)
|
|
132
185
|
const hasItems = items.length > 0
|
|
133
|
-
const idPrefix =
|
|
134
|
-
asciidocConfig.attributes['assembler-idprefix'] ?? ('assembler-idprefix' in asciidocConfig.attributes ? '' : '_')
|
|
135
|
-
const idSeparator = xmlIds ? '-' : ':'
|
|
136
|
-
const idScopeSeparator = idSeparator.repeat(3)
|
|
137
|
-
const idCoordinateSeparator = idSeparator === '-' ? '----' : idSeparator
|
|
138
186
|
if (page && !assembled.pages.has(page)) {
|
|
139
187
|
const contents = page.src.contents
|
|
140
188
|
if (contents == null) {
|
|
@@ -155,6 +203,7 @@ function mergeAsciiDoc (
|
|
|
155
203
|
},
|
|
156
204
|
})
|
|
157
205
|
: doc.getLogger()
|
|
206
|
+
doc.source_header_attributes ??= doc.parent.$to_h()
|
|
158
207
|
if (doc.hasAttribute('assembly-navtitle')) {
|
|
159
208
|
navtitleAsciiDoc = doc.getAttribute('assembly-navtitle')
|
|
160
209
|
navtitlePlain = sanitize((navtitle = doc.$apply_reftext_subs(navtitleAsciiDoc)))
|
|
@@ -168,31 +217,21 @@ function mergeAsciiDoc (
|
|
|
168
217
|
}
|
|
169
218
|
}
|
|
170
219
|
}
|
|
171
|
-
if (atDocumentRoot) {
|
|
172
|
-
const authors = doc.getAuthors()
|
|
173
|
-
if (authors.length) {
|
|
174
|
-
const authorLine = authors
|
|
175
|
-
.map((author) => {
|
|
176
|
-
const email = author.getEmail()
|
|
177
|
-
return email ? `${author.getName()} <${author.getEmail()}>` : author.getName()
|
|
178
|
-
})
|
|
179
|
-
.join('; ')
|
|
180
|
-
buffer.splice(1, 0, authorLine)
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
220
|
// NOTE: in Antora, docname is relative src path from module without file extension
|
|
184
221
|
const docname = doc.getAttribute('docname')
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
componentVersion,
|
|
188
|
-
idCoordinateSeparator,
|
|
189
|
-
idScopeSeparator,
|
|
190
|
-
idPrefix,
|
|
191
|
-
filetype
|
|
192
|
-
)
|
|
222
|
+
const pageId = generateScopedId(page.src, componentVersion, idSeparators, filetype)
|
|
223
|
+
const pageIdLeader = pageId + idSeparators.scope
|
|
193
224
|
let pageFragment = ''
|
|
194
225
|
let pageRoles = ''
|
|
195
226
|
let pageStyle = doc.getAttribute('assembly-style', '')
|
|
227
|
+
if (
|
|
228
|
+
!pageStyle &&
|
|
229
|
+
atBookRoot &&
|
|
230
|
+
doc.isAttribute('preface-title') &&
|
|
231
|
+
!('assembly-style' in doc.source_header_attributes.$$smap)
|
|
232
|
+
) {
|
|
233
|
+
pageStyle = 'preface'
|
|
234
|
+
}
|
|
196
235
|
let part
|
|
197
236
|
if ((part = pageStyle === 'part')) {
|
|
198
237
|
pageStyle = ''
|
|
@@ -204,7 +243,10 @@ function mergeAsciiDoc (
|
|
|
204
243
|
let nextSectionLevel = 1
|
|
205
244
|
const lines = doc.getSourceLines()
|
|
206
245
|
const ignoreLines = []
|
|
207
|
-
buffer.inBody
|
|
246
|
+
if (!buffer.inBody) {
|
|
247
|
+
buffer.inBody = true
|
|
248
|
+
buffer.endHeaderIdx = buffer.length - 1
|
|
249
|
+
}
|
|
208
250
|
buffer.push('')
|
|
209
251
|
buffer.push(`:docname: ${docname}`)
|
|
210
252
|
if (component !== lastComponentVersion.name) {
|
|
@@ -228,7 +270,63 @@ function mergeAsciiDoc (
|
|
|
228
270
|
buffer.push(`:page-origin-refname: ${origin.branch || origin.tag}`)
|
|
229
271
|
buffer.push(`:page-origin-reftype: ${origin.branch ? 'branch' : 'tag'}`)
|
|
230
272
|
buffer.push(`:page-origin-refhash: ${origin.worktree ? '(worktree)' : origin.refhash}`)
|
|
231
|
-
if (doc.hasHeader())
|
|
273
|
+
if (doc.hasHeader()) {
|
|
274
|
+
for (const entry of processDocumentHeader(doc, lines, ignoreLines, isRootPage)) {
|
|
275
|
+
if (entry.type === 'author_line') {
|
|
276
|
+
buffer.splice(1, 0, entry.lines[0])
|
|
277
|
+
buffer.endHeaderIdx += 1
|
|
278
|
+
} else if (entry.type === 'attribute_entry') {
|
|
279
|
+
const name = entry.name
|
|
280
|
+
if (!entry.negated && !doc.isAttributeLocked(name)) {
|
|
281
|
+
let val, newVal
|
|
282
|
+
if (
|
|
283
|
+
name.endsWith('-image') &&
|
|
284
|
+
(val = doc.getAttribute(name)) &&
|
|
285
|
+
(newVal = rewriteImageAttr(val, contentCatalog, assemblyModel, page.src, assembled.assets))
|
|
286
|
+
) {
|
|
287
|
+
if (newVal !== val) entry.lines = [`:${name}: ${newVal}`]
|
|
288
|
+
} else if (~(val = entry.lines.join('\n').slice(name.length + 3)).indexOf(':')) {
|
|
289
|
+
newVal = val
|
|
290
|
+
if (~newVal.indexOf('image:')) {
|
|
291
|
+
newVal = rewriteInlineImages(
|
|
292
|
+
newVal,
|
|
293
|
+
contentCatalog,
|
|
294
|
+
assemblyModel,
|
|
295
|
+
page.src,
|
|
296
|
+
assembled.assets,
|
|
297
|
+
false,
|
|
298
|
+
doc
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
if (~newVal.indexOf('xref:')) {
|
|
302
|
+
newVal = rewriteXrefs(
|
|
303
|
+
newVal,
|
|
304
|
+
contentCatalog,
|
|
305
|
+
assemblyModel,
|
|
306
|
+
page.src,
|
|
307
|
+
false,
|
|
308
|
+
pagesInOutline,
|
|
309
|
+
idSeparators,
|
|
310
|
+
pageIdLeader,
|
|
311
|
+
doc,
|
|
312
|
+
{ file: relative, lineno: entry.lineno }
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
if (newVal !== val) entry.lines = `:${entry.name}: ${newVal}`.split('\n')
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (entry.promote) {
|
|
319
|
+
buffer.splice(buffer.endHeaderIdx + 1, 0, ...entry.lines)
|
|
320
|
+
buffer.endHeaderIdx += entry.lines.length
|
|
321
|
+
} else {
|
|
322
|
+
buffer.push(...entry.lines)
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
buffer.push(...entry.lines)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
pageRoles = doc.getRoles().reduce((accum, role) => `${accum}.${role}`, '')
|
|
329
|
+
}
|
|
232
330
|
let heading
|
|
233
331
|
if (pageStyle && (part && level === 1 ? (level = 0) : level) === 0) {
|
|
234
332
|
let htitleAsciiDoc = navtitleAsciiDoc
|
|
@@ -245,10 +343,10 @@ function mergeAsciiDoc (
|
|
|
245
343
|
assemblyModel = Object.assign({}, assemblyModel, { sectionMergeStrategy: 'discrete' })
|
|
246
344
|
} else {
|
|
247
345
|
if (atDocumentRoot) {
|
|
248
|
-
pageFragment = `#${
|
|
249
|
-
buffer.unshift(`[#${
|
|
346
|
+
pageFragment = `#${pageIdLeader}${doc.getId() ?? pageStyle}`
|
|
347
|
+
buffer.unshift(`[#${pageId}]`)
|
|
250
348
|
} else {
|
|
251
|
-
pageFragment = `#${
|
|
349
|
+
pageFragment = `#${pageId}`
|
|
252
350
|
}
|
|
253
351
|
heading = { title: htitleAsciiDoc, level: part ? 1 : 2 }
|
|
254
352
|
nextSectionLevel++
|
|
@@ -257,7 +355,7 @@ function mergeAsciiDoc (
|
|
|
257
355
|
if (atDocumentRoot && navtitlePlain === componentVersion.title) {
|
|
258
356
|
level--
|
|
259
357
|
} else {
|
|
260
|
-
pageFragment = `#${
|
|
358
|
+
pageFragment = `#${pageId}`
|
|
261
359
|
if (part && level === 1) level--
|
|
262
360
|
if ((heading = { title: navtitleAsciiDoc, level: level + 1 }).level > 6) {
|
|
263
361
|
Object.assign(heading, { level: 6, style: `discrete.h${heading.level}` })
|
|
@@ -268,7 +366,7 @@ function mergeAsciiDoc (
|
|
|
268
366
|
buffer.push(`[${heading.style ?? pageStyle}${pageFragment}${pageRoles}]`)
|
|
269
367
|
buffer.push(`${'='.repeat(heading.level)} ${heading.title}`)
|
|
270
368
|
} else if (atDocumentRoot) {
|
|
271
|
-
buffer.unshift(`[#${
|
|
369
|
+
buffer.unshift(`[#${pageId}]`)
|
|
272
370
|
}
|
|
273
371
|
let enclosed
|
|
274
372
|
if (assemblyModel.sectionMergeStrategy === 'enclose' && hasItems && doc.hasSections()) {
|
|
@@ -278,7 +376,7 @@ function mergeAsciiDoc (
|
|
|
278
376
|
//if (overviewTitle === navtitle) overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
279
377
|
const overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
280
378
|
buffer.push('')
|
|
281
|
-
const syntheticId =
|
|
379
|
+
const syntheticId = idSeparators.generateIdFromTitle(navtitleAsciiDoc, idSeparators)
|
|
282
380
|
let hlevel = level + 2
|
|
283
381
|
if (hlevel > 6) {
|
|
284
382
|
const blockStyle = `discrete.h${hlevel}`
|
|
@@ -363,7 +461,7 @@ function mergeAsciiDoc (
|
|
|
363
461
|
if (
|
|
364
462
|
line.charAt() === ':' &&
|
|
365
463
|
~line.indexOf(':', 2) &&
|
|
366
|
-
(line.match(
|
|
464
|
+
(line.match(ATTR_ENTRY_RX) || ['', ''])[1].replace('!', '') === 'leveloffset'
|
|
367
465
|
) {
|
|
368
466
|
if (lines[idx - 1] === '') lines[idx - 1] = undefined
|
|
369
467
|
lines[idx] = undefined
|
|
@@ -375,13 +473,13 @@ function mergeAsciiDoc (
|
|
|
375
473
|
if (!refs['$key?'](refid) && (~refid.indexOf(' ') || refid.toLowerCase() !== refid)) {
|
|
376
474
|
if ((refid = doc.$resolve_id(refid))['$nil?']()) return m
|
|
377
475
|
}
|
|
378
|
-
return `<<${
|
|
476
|
+
return `<<${pageIdLeader}${refid}${text ? ',' + text : ''}>>`
|
|
379
477
|
})
|
|
380
478
|
}
|
|
381
479
|
// NOTE: the next check takes care of inline and block anchors
|
|
382
480
|
if (~line.indexOf('[[')) {
|
|
383
481
|
line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, (_, refid, text) => {
|
|
384
|
-
return `[[${
|
|
482
|
+
return `[[${pageIdLeader}${refid}${text}]]`
|
|
385
483
|
})
|
|
386
484
|
}
|
|
387
485
|
if (~line.indexOf('xref:')) {
|
|
@@ -391,17 +489,15 @@ function mergeAsciiDoc (
|
|
|
391
489
|
assemblyModel,
|
|
392
490
|
page.src,
|
|
393
491
|
true,
|
|
394
|
-
idCoordinateSeparator,
|
|
395
|
-
idScopeSeparator,
|
|
396
|
-
idPrefix,
|
|
397
|
-
idScopeLeader,
|
|
398
492
|
pagesInOutline,
|
|
493
|
+
idSeparators,
|
|
494
|
+
pageIdLeader,
|
|
399
495
|
doc,
|
|
400
496
|
{ file: relative, lineno: idx + 1 }
|
|
401
497
|
)
|
|
402
498
|
}
|
|
403
499
|
if (~line.indexOf('image:') && !line.startsWith('image::')) {
|
|
404
|
-
line = rewriteInlineImages(line, contentCatalog, assemblyModel, page.src, assembled.assets, true)
|
|
500
|
+
line = rewriteInlineImages(line, contentCatalog, assemblyModel, page.src, assembled.assets, true, doc)
|
|
405
501
|
}
|
|
406
502
|
lines[idx] = line
|
|
407
503
|
}
|
|
@@ -427,7 +523,7 @@ function mergeAsciiDoc (
|
|
|
427
523
|
return '='.repeat(targetMarkerLength) + ' ' + rest
|
|
428
524
|
})
|
|
429
525
|
// NOTE: ID will be undefined if sectids are turned off
|
|
430
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
526
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, pageIdLeader, blockStyle)
|
|
431
527
|
} else {
|
|
432
528
|
if (context === 'image') {
|
|
433
529
|
let line = lines[idx] || ''
|
|
@@ -456,14 +552,14 @@ function mergeAsciiDoc (
|
|
|
456
552
|
// nested document
|
|
457
553
|
idx = (block.getHeader().getLineNumber() || idx + 1) - 1
|
|
458
554
|
}
|
|
459
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
555
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, pageIdLeader)
|
|
460
556
|
}
|
|
461
557
|
})
|
|
462
558
|
safePush(
|
|
463
559
|
buffer,
|
|
464
560
|
lines.filter((it) => it !== undefined)
|
|
465
561
|
)
|
|
466
|
-
const attributeEntries = Object.entries(doc.source_header_attributes
|
|
562
|
+
const attributeEntries = Object.entries(doc.source_header_attributes.$$smap)
|
|
467
563
|
if (attributeEntries.length) {
|
|
468
564
|
const resolvedAttributeEntries = attributeEntries.reduce(
|
|
469
565
|
(accum, [name, val]) => {
|
|
@@ -475,7 +571,7 @@ function mergeAsciiDoc (
|
|
|
475
571
|
} else if (val !== initialVal) {
|
|
476
572
|
accum.push(`:${name}:${initialVal ? ' ' + initialVal : ''}`)
|
|
477
573
|
}
|
|
478
|
-
} else if (!(val == null || doc.isAttributeLocked(name) ||
|
|
574
|
+
} else if (!(val == null || doc.isAttributeLocked(name) || DISCARD_ATTRIBUTE_NAMES.includes(name))) {
|
|
479
575
|
accum.push(`:!${name}:`)
|
|
480
576
|
}
|
|
481
577
|
return accum
|
|
@@ -491,7 +587,7 @@ function mergeAsciiDoc (
|
|
|
491
587
|
} else {
|
|
492
588
|
buffer.inBody = true
|
|
493
589
|
buffer.push('')
|
|
494
|
-
const syntheticId =
|
|
590
|
+
const syntheticId = idSeparators.generateIdFromTitle(navtitleAsciiDoc, idSeparators)
|
|
495
591
|
let sectionTitle = navtitleAsciiDoc
|
|
496
592
|
if (urlType === 'external') {
|
|
497
593
|
sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
@@ -499,15 +595,7 @@ function mergeAsciiDoc (
|
|
|
499
595
|
const resource = files.find((it) => it.pub.url === url)
|
|
500
596
|
if (resource) {
|
|
501
597
|
if (resource.src.family === 'page' && pagesInOutline.has(resource.pub.url)) {
|
|
502
|
-
const
|
|
503
|
-
resource.src,
|
|
504
|
-
componentVersion,
|
|
505
|
-
idCoordinateSeparator,
|
|
506
|
-
idScopeSeparator,
|
|
507
|
-
idPrefix,
|
|
508
|
-
filetype,
|
|
509
|
-
true
|
|
510
|
-
)
|
|
598
|
+
const refid = generateScopedId(resource.src, componentVersion, idSeparators, filetype, true)
|
|
511
599
|
sectionTitle = `xref:${refid}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
512
600
|
} else if (siteRoot) {
|
|
513
601
|
sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, true)}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
@@ -524,7 +612,6 @@ function mergeAsciiDoc (
|
|
|
524
612
|
buffer.push(`${'='.repeat(hlevel)} ${sectionTitle}`)
|
|
525
613
|
}
|
|
526
614
|
}
|
|
527
|
-
|
|
528
615
|
if (hasItems) {
|
|
529
616
|
const nextLevel = level + 1
|
|
530
617
|
// NOTE: drop first child if same as parent; should we keep if content is different?
|
|
@@ -540,6 +627,7 @@ function mergeAsciiDoc (
|
|
|
540
627
|
item,
|
|
541
628
|
files,
|
|
542
629
|
pagesInOutline,
|
|
630
|
+
idSeparators,
|
|
543
631
|
asciidocConfig,
|
|
544
632
|
mutableAttributes,
|
|
545
633
|
assemblyModel,
|
|
@@ -552,11 +640,32 @@ function mergeAsciiDoc (
|
|
|
552
640
|
return buffer
|
|
553
641
|
}
|
|
554
642
|
|
|
555
|
-
function processDocumentHeader (doc, lines,
|
|
643
|
+
function processDocumentHeader (doc, lines, ignoreLines, isRootPage) {
|
|
644
|
+
const entries = []
|
|
645
|
+
let assemblyHeaderAttributes = isRootPage && doc.getAttribute('assembly-header-attributes', '%authors')
|
|
646
|
+
assemblyHeaderAttributes = new Set(assemblyHeaderAttributes ? assemblyHeaderAttributes.split(/, */) : undefined)
|
|
647
|
+
if (assemblyHeaderAttributes.size) {
|
|
648
|
+
if (assemblyHeaderAttributes.has('%authors')) {
|
|
649
|
+
const authors = doc.getAuthors()
|
|
650
|
+
if (authors.length) {
|
|
651
|
+
entries.push({
|
|
652
|
+
type: 'author_line',
|
|
653
|
+
promote: true,
|
|
654
|
+
lines: [
|
|
655
|
+
authors
|
|
656
|
+
.map((author) => (author.getEmail() ? `${author.getName()} <${author.getEmail()}>` : author.getName()))
|
|
657
|
+
.join('; '),
|
|
658
|
+
],
|
|
659
|
+
})
|
|
660
|
+
}
|
|
661
|
+
assemblyHeaderAttributes.delete('%authors')
|
|
662
|
+
}
|
|
663
|
+
const headerAttributes = doc.source_header_attributes
|
|
664
|
+
for (const name of assemblyHeaderAttributes) headerAttributes.$delete(name)
|
|
665
|
+
}
|
|
556
666
|
const doctitleIdx = doc.getHeader().getLineNumber() - 1
|
|
557
667
|
const end = doc.getBlocks()[0]?.getLineNumber() ?? lines.length
|
|
558
|
-
let belowDoctitle
|
|
559
|
-
let open
|
|
668
|
+
let belowDoctitle, current, open
|
|
560
669
|
const implicitLines = []
|
|
561
670
|
for (let idx = 0; idx < end; idx++) {
|
|
562
671
|
if (idx === doctitleIdx) {
|
|
@@ -567,38 +676,51 @@ function processDocumentHeader (doc, lines, buffer, ignoreLines) {
|
|
|
567
676
|
}
|
|
568
677
|
const line = lines[idx]
|
|
569
678
|
if (open === ':' || open === '-:') {
|
|
570
|
-
if (open === ':')
|
|
571
|
-
if (!line || !line.endsWith(' \\')) open = undefined
|
|
679
|
+
if (open === ':') current.lines.push(line)
|
|
680
|
+
if (!line || !line.endsWith(' \\')) current = open = undefined
|
|
572
681
|
} else if (line) {
|
|
573
|
-
const chr0 = line.charAt()
|
|
574
682
|
let attributeEntryMatch
|
|
683
|
+
const chr0 = line.charAt()
|
|
575
684
|
if (chr0 === '/' && line.charAt(1) === '/') {
|
|
576
|
-
if (line.startsWith('////')) {
|
|
577
|
-
|
|
578
|
-
|
|
685
|
+
if (line.startsWith('////') && line === '/'.repeat(line.length)) {
|
|
686
|
+
if (open) {
|
|
687
|
+
current.lines.push(line)
|
|
688
|
+
if (open === line) current = open = undefined
|
|
689
|
+
} else {
|
|
690
|
+
entries.push((current = { type: 'block_comment', lines: [(open = line)] }))
|
|
691
|
+
}
|
|
692
|
+
} else if (open) {
|
|
693
|
+
current.lines.push(line)
|
|
694
|
+
} else if (belowDoctitle && line.charAt(2) === '/') {
|
|
579
695
|
break
|
|
696
|
+
} else {
|
|
697
|
+
entries.push({ type: 'line_comment', lines: [line] })
|
|
698
|
+
current = undefined
|
|
580
699
|
}
|
|
581
|
-
buffer.push(line)
|
|
582
700
|
} else if (open) {
|
|
583
|
-
|
|
584
|
-
} else if (chr0 === ':' && ~line.indexOf(':', 2) && (attributeEntryMatch = line.match(
|
|
585
|
-
|
|
586
|
-
|
|
701
|
+
current.lines.push(line)
|
|
702
|
+
} else if (chr0 === ':' && ~line.indexOf(':', 2) && (attributeEntryMatch = line.match(ATTR_ENTRY_RX))) {
|
|
703
|
+
let name = attributeEntryMatch[1]
|
|
704
|
+
const negated = name.charAt() === '!' || name.charAt(name.length - 1) === '!'
|
|
705
|
+
if (negated) name = name.replace('!', '')
|
|
706
|
+
if (DISCARD_ATTRIBUTE_NAMES.includes(name)) {
|
|
587
707
|
if (line.endsWith(' \\')) open = '-:' // disallow value continuation
|
|
588
708
|
} else {
|
|
589
709
|
if (line.endsWith(' \\')) open = ':'
|
|
590
|
-
|
|
710
|
+
entries.push((current = { type: 'attribute_entry', name, negated, lines: [line], lineno: idx + 1 }))
|
|
711
|
+
if (assemblyHeaderAttributes.has(name)) current.promote = true
|
|
591
712
|
}
|
|
592
713
|
} else if (belowDoctitle) {
|
|
593
714
|
if (implicitLines.length === 2 || !/[\p{Alpha}0-9]/u.test(chr0)) break
|
|
594
715
|
implicitLines.push(line)
|
|
595
716
|
} else if (chr0 === '[' && line.charAt(line.length - 1) === ']') {
|
|
717
|
+
current = undefined
|
|
596
718
|
const attrlist = line
|
|
597
719
|
.slice(1, -1)
|
|
598
720
|
.trim()
|
|
599
|
-
.replace(/(?:^\w[\w-]*(?!=|[\w-]))?([
|
|
600
|
-
.replace(
|
|
601
|
-
if (attrlist)
|
|
721
|
+
.replace(/(?:^\w[\w-]*(?!=|[\w-]))?([#.]\w[\w-]*)*/, '')
|
|
722
|
+
.replace(NAMED_ID_ATTR_RX, '')
|
|
723
|
+
if (attrlist) entries.push({ type: 'attrlist', lines: ['[' + attrlist + ']'] })
|
|
602
724
|
}
|
|
603
725
|
} else if (belowDoctitle) {
|
|
604
726
|
break
|
|
@@ -606,18 +728,15 @@ function processDocumentHeader (doc, lines, buffer, ignoreLines) {
|
|
|
606
728
|
lines[idx] = undefined
|
|
607
729
|
ignoreLines.push(idx)
|
|
608
730
|
}
|
|
609
|
-
return
|
|
610
|
-
.getRoles()
|
|
611
|
-
.map((role) => '.' + role)
|
|
612
|
-
.join('')
|
|
731
|
+
return entries
|
|
613
732
|
}
|
|
614
733
|
|
|
615
734
|
function generateSlug (title) {
|
|
616
735
|
return title
|
|
617
736
|
.toLowerCase()
|
|
618
737
|
.replace(/<[^>]+>/g, '')
|
|
619
|
-
.replace(
|
|
620
|
-
if (name) return
|
|
738
|
+
.replace(CHAR_REF_RX, (_, name, dec, hex) => {
|
|
739
|
+
if (name) return BUILT_IN_NAMED_ENTITIES[name] ?? '?'
|
|
621
740
|
return String.fromCharCode(dec ? parseInt(dec, 10) : parseInt(hex, 16))
|
|
622
741
|
})
|
|
623
742
|
.replace(/[\x27\u2019]/g, '')
|
|
@@ -655,11 +774,11 @@ function safePush (onto, entries) {
|
|
|
655
774
|
}
|
|
656
775
|
}
|
|
657
776
|
|
|
658
|
-
function generateIdFromTitle (titleAsciiDoc,
|
|
777
|
+
function generateIdFromTitle (titleAsciiDoc, idSeparators) {
|
|
659
778
|
const Section = this.$class().$const_get('::Asciidoctor::Section')
|
|
660
779
|
const baseId = Section.$generate_id(titleAsciiDoc, this)
|
|
661
780
|
this.getCatalog().refs['$[]='](baseId, true)
|
|
662
|
-
return `_${
|
|
781
|
+
return `_${idSeparators.coordinate}${baseId}`
|
|
663
782
|
}
|
|
664
783
|
|
|
665
784
|
module.exports = produceAssemblyFile
|
|
@@ -4,7 +4,6 @@ const computeOut = require('./util/compute-out')
|
|
|
4
4
|
const createAsciiDocFile = require('./util/create-asciidoc-file')
|
|
5
5
|
const filterComponentVersions = require('./filter-component-versions')
|
|
6
6
|
const produceAssemblyFile = require('./produce-assembly-file')
|
|
7
|
-
const { rewriteImageAttr, rewriteInlineImages, rewriteXrefs } = require('./util/rewriter')
|
|
8
7
|
const selectMutableAttributes = require('./select-mutable-attributes')
|
|
9
8
|
|
|
10
9
|
const ATTR_REF_RX = /\\?\{(\w[\w-]*)\}/g
|
|
@@ -43,7 +42,7 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, se
|
|
|
43
42
|
delete assemblyAttributes['source-highlighter']
|
|
44
43
|
}
|
|
45
44
|
const mergedAsciiDocAttributes = collateAsciiDocAttributes(
|
|
46
|
-
Object.assign({
|
|
45
|
+
Object.assign({}, componentVersionAsciiDocConfig.attributes),
|
|
47
46
|
assemblyAttributes,
|
|
48
47
|
contextualLogger
|
|
49
48
|
)
|
|
@@ -75,22 +74,6 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, se
|
|
|
75
74
|
} else if (!(assemblyModel.filetype === 'pdf' && assemblyModel.linkReferenceStyle === 'relative')) {
|
|
76
75
|
assemblyModel.linkReferenceStyle = 'absolute'
|
|
77
76
|
}
|
|
78
|
-
const auxiliaryImages = new Set()
|
|
79
|
-
const ctx = { component: componentName, version }
|
|
80
|
-
Object.entries(mergedAsciiDocAttributes).forEach(([name, val]) => {
|
|
81
|
-
if (!(typeof val === 'string' && ~val.indexOf(':'))) return
|
|
82
|
-
if (name.endsWith('-image')) {
|
|
83
|
-
const newVal = rewriteImageAttr(val, contentCatalog, assemblyModel, ctx, auxiliaryImages)
|
|
84
|
-
if (newVal) {
|
|
85
|
-
mergedAsciiDocAttributes[name] = newVal
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
const oldVal = val
|
|
90
|
-
if (~val.indexOf('image:')) val = rewriteInlineImages(val, contentCatalog, assemblyModel, ctx, auxiliaryImages)
|
|
91
|
-
if (~val.indexOf('xref:')) val = rewriteXrefs(val, contentCatalog, assemblyModel, ctx)
|
|
92
|
-
if (val !== oldVal) mergedAsciiDocAttributes[name] = val
|
|
93
|
-
})
|
|
94
77
|
const rootEntry = { content: title }
|
|
95
78
|
let startPage =
|
|
96
79
|
'startPage' in componentVersion
|
|
@@ -115,7 +98,6 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, se
|
|
|
115
98
|
})
|
|
116
99
|
}
|
|
117
100
|
const mutableAttributes = selectMutableAttributes(loadAsciiDoc, contentCatalog, startPage, mergedAsciiDocConfig)
|
|
118
|
-
delete mutableAttributes.doctype // Q: should this be in selectMutableAttributes?
|
|
119
101
|
prepareOutlines(navigation, rootEntry, rootLevel).reduce((any, outline) => {
|
|
120
102
|
const assemblyFile = produceAssemblyFile(
|
|
121
103
|
loadAsciiDoc,
|
|
@@ -128,19 +110,14 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, se
|
|
|
128
110
|
assemblyModel
|
|
129
111
|
)
|
|
130
112
|
if (!assemblyFile) return any
|
|
131
|
-
if
|
|
132
|
-
|
|
133
|
-
|
|
113
|
+
// NOTE restore source highlighter for conversion if defined in Assembler config
|
|
114
|
+
if (sourceHighlighter !== undefined) {
|
|
115
|
+
assemblyFile.asciidoc.attributes['source-highlighter'] = sourceHighlighter
|
|
116
|
+
} else if (assemblyModel.filetype !== 'html') {
|
|
117
|
+
delete assemblyFile.asciidoc.attributes['source-highlighter']
|
|
134
118
|
}
|
|
135
|
-
accum.push(assemblyFile)
|
|
136
|
-
return true
|
|
119
|
+
return !!accum.push(assemblyFile)
|
|
137
120
|
}, false)
|
|
138
|
-
// NOTE restore source highlighter for conversion if defined in Assembler config
|
|
139
|
-
if (sourceHighlighter !== undefined) {
|
|
140
|
-
mergedAsciiDocAttributes['source-highlighter'] = sourceHighlighter
|
|
141
|
-
} else if (assemblyModel.filetype !== 'html') {
|
|
142
|
-
delete mergedAsciiDocAttributes['source-highlighter']
|
|
143
|
-
}
|
|
144
121
|
return accum
|
|
145
122
|
},
|
|
146
123
|
[]
|
|
@@ -17,6 +17,7 @@ function selectMutableAttributes (loadAsciiDoc, contentCatalog, referencePage, a
|
|
|
17
17
|
]
|
|
18
18
|
// we could consider using an Asciidoctor extension here to grab attributes passed via the API instead
|
|
19
19
|
const immutableAttributeNames = doc.attribute_overrides.$keys()['$-'](additionalMutableNames)
|
|
20
|
+
immutableAttributeNames.push('doctype')
|
|
20
21
|
return Object.entries(doc.getAttributes()).reduce((accum, [name, val]) => {
|
|
21
22
|
if (!immutableAttributeNames.includes(name)) accum[name] = val
|
|
22
23
|
return accum
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const HTML_RESERVED_ID_NAMES = 'content header footnotes footer footer-text premable toc toctitle'.split(' ')
|
|
4
4
|
|
|
5
|
-
function generateScopedId (componentSrc, componentVersion,
|
|
5
|
+
function generateScopedId (componentSrc, componentVersion, separators, filetype, asXrefTarget) {
|
|
6
6
|
let { component, module: mod, relative } = componentSrc
|
|
7
7
|
let id = relative.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
|
|
8
|
+
let { coordinate: coordinateSeparator, prefix: prefixSeparator } = separators
|
|
8
9
|
if (component !== componentVersion.name) {
|
|
9
|
-
id = [component, mod === 'ROOT' ? '' : mod, id].join(
|
|
10
|
+
id = [component, mod === 'ROOT' ? '' : mod, id].join(coordinateSeparator)
|
|
10
11
|
} else if (mod !== 'ROOT') {
|
|
11
|
-
if (asXrefTarget &&
|
|
12
|
-
|
|
12
|
+
if (asXrefTarget && coordinateSeparator === ':' && /(?:pass|stem)$/.test(mod) && /^[a-z]+(?:,[a-z-]+)*$/.test(id)) {
|
|
13
|
+
prefixSeparator = ''
|
|
13
14
|
mod = mod.replace(/(?:pass|stem)$/, '\\$&')
|
|
14
15
|
}
|
|
15
|
-
id = mod +
|
|
16
|
-
} else if (filetype === 'html' &&
|
|
17
|
-
id =
|
|
18
|
-
|
|
16
|
+
id = mod + coordinateSeparator + id
|
|
17
|
+
} else if (filetype === 'html' && HTML_RESERVED_ID_NAMES.includes(id)) {
|
|
18
|
+
id = prefixSeparator + id
|
|
19
|
+
prefixSeparator = ''
|
|
19
20
|
}
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
return { idLeader, id }
|
|
21
|
+
if (prefixSeparator && !/^[\p{Alpha}_:]/u.test(id)) id = prefixSeparator + id
|
|
22
|
+
return id
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
module.exports = generateScopedId
|
package/lib/util/rewriter.js
CHANGED
|
@@ -4,7 +4,8 @@ const createResourceKey = require('./create-resource-key')
|
|
|
4
4
|
const generateScopedId = require('./generate-scoped-id')
|
|
5
5
|
const parseResourceRef = require('./parse-resource-ref')
|
|
6
6
|
const { resolveEmbedTarget, resolveLinkTarget } = require('./resolver')
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
const { NAMED_ID_ATTR_RX } = require('./rx')
|
|
8
9
|
|
|
9
10
|
function rewriteXrefs (
|
|
10
11
|
line,
|
|
@@ -12,15 +13,13 @@ function rewriteXrefs (
|
|
|
12
13
|
assemblyModel,
|
|
13
14
|
ctx,
|
|
14
15
|
escapeForInline,
|
|
15
|
-
idCoordinateSep,
|
|
16
|
-
idScopeSep,
|
|
17
|
-
idPrefix,
|
|
18
|
-
idLeader,
|
|
19
16
|
pagesInOutline,
|
|
17
|
+
idSeparators,
|
|
18
|
+
idLeader,
|
|
20
19
|
doc,
|
|
21
20
|
sourceLocation
|
|
22
21
|
) {
|
|
23
|
-
return line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(
|
|
22
|
+
return line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|[\s\S]*?[^\\])\]/gu, (_, target, text) =>
|
|
24
23
|
rewriteXref(
|
|
25
24
|
target,
|
|
26
25
|
text,
|
|
@@ -28,11 +27,9 @@ function rewriteXrefs (
|
|
|
28
27
|
assemblyModel,
|
|
29
28
|
ctx,
|
|
30
29
|
escapeForInline,
|
|
31
|
-
idCoordinateSep,
|
|
32
|
-
idScopeSep,
|
|
33
|
-
idPrefix,
|
|
34
|
-
idLeader,
|
|
35
30
|
pagesInOutline,
|
|
31
|
+
idSeparators,
|
|
32
|
+
idLeader,
|
|
36
33
|
doc,
|
|
37
34
|
sourceLocation
|
|
38
35
|
)
|
|
@@ -46,11 +43,9 @@ function rewriteXref (
|
|
|
46
43
|
assemblyModel,
|
|
47
44
|
ctx,
|
|
48
45
|
escapeForInline,
|
|
49
|
-
idCoordinateSep,
|
|
50
|
-
idScopeSep,
|
|
51
|
-
idPrefix,
|
|
52
|
-
idLeader,
|
|
53
46
|
pagesInOutline,
|
|
47
|
+
idSeparators,
|
|
48
|
+
idLeader,
|
|
54
49
|
doc,
|
|
55
50
|
sourceLocation
|
|
56
51
|
) {
|
|
@@ -67,7 +62,7 @@ function rewriteXref (
|
|
|
67
62
|
} else {
|
|
68
63
|
fragment = target
|
|
69
64
|
}
|
|
70
|
-
if (!resourceRef) return `xref:${idLeader
|
|
65
|
+
if (!resourceRef) return `xref:${idLeader == null ? rawTarget : idLeader + fragment}[${text}]`
|
|
71
66
|
const resourceId = parseResourceRef(resourceRef, ctx, 'page', contentCatalog)
|
|
72
67
|
const family = resourceId.family
|
|
73
68
|
let resource
|
|
@@ -76,7 +71,8 @@ function rewriteXref (
|
|
|
76
71
|
const { linkReferenceStyle, pubRoot, siteRoot } = assemblyModel
|
|
77
72
|
text ||= resource.asciidoc?.xreftext || rawTarget
|
|
78
73
|
if (siteRoot || linkReferenceStyle === 'relative') {
|
|
79
|
-
|
|
74
|
+
const hash = fragment && !(family === 'page' && fragment === resource.asciidoc.id) ? '#' + fragment : ''
|
|
75
|
+
return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, escapeForInline)}${hash}[${text}]`
|
|
80
76
|
}
|
|
81
77
|
if (doc) {
|
|
82
78
|
const msg = `Cannot create external ${family} reference in assembly because site URL is unknown: ${rawTarget}`
|
|
@@ -97,42 +93,41 @@ function rewriteXref (
|
|
|
97
93
|
text = ''
|
|
98
94
|
}
|
|
99
95
|
const componentVersionCtx = { name: ctx.component, version: ctx.version }
|
|
100
|
-
const
|
|
96
|
+
const refid = generateScopedId(
|
|
101
97
|
resource.src,
|
|
102
98
|
componentVersionCtx,
|
|
103
|
-
|
|
104
|
-
idScopeSep,
|
|
105
|
-
idPrefix,
|
|
99
|
+
idSeparators,
|
|
106
100
|
assemblyModel.filetype,
|
|
107
101
|
text.length > 0
|
|
108
102
|
)
|
|
109
|
-
return `xref:${fragment ?
|
|
103
|
+
return `xref:${fragment ? refid + idSeparators.scope + fragment : refid}[${text}]`
|
|
110
104
|
}
|
|
111
105
|
|
|
112
106
|
function rewriteImageAttr (val, contentCatalog, assemblyModel, ctx, assets) {
|
|
113
|
-
const match = val.startsWith('image:') && /^image::?(.+?)\[(.*?)\]$/.exec(val)
|
|
107
|
+
const match = val.startsWith('image:') && /^image::?(.+?)\[(.*?)\](@|)$/.exec(val)
|
|
114
108
|
if (!match) return
|
|
115
109
|
const newTarget = rewriteImageRef(match[1], contentCatalog, assemblyModel, ctx, assets)
|
|
116
|
-
return newTarget ? `image:${newTarget}[${match[2]}]` : val
|
|
110
|
+
return newTarget ? `image:${newTarget}[${match[2]}]${match[3]}` : val
|
|
117
111
|
}
|
|
118
112
|
|
|
119
|
-
function rewriteInlineImages (line, contentCatalog, assemblyModel, ctx, assets, escapeForInline) {
|
|
120
|
-
return line.replace(/(?<![\\+])image:([^:\s[](?:[^[]*[^\s[])?)\[([
|
|
121
|
-
const newTarget = rewriteImageRef(target, contentCatalog, assemblyModel, ctx, assets, escapeForInline)
|
|
113
|
+
function rewriteInlineImages (line, contentCatalog, assemblyModel, ctx, assets, escapeForInline, doc) {
|
|
114
|
+
return line.replace(/(?<![\\+])image:([^:\s[](?:[^[]*[^\s[])?)\[(|[\s\S]*?[^\\])\]/g, (m, target, attrlist) => {
|
|
115
|
+
const newTarget = rewriteImageRef(target, contentCatalog, assemblyModel, ctx, assets, escapeForInline, doc)
|
|
122
116
|
return newTarget ? `image:${newTarget}[${attrlist}]` : m
|
|
123
117
|
})
|
|
124
118
|
}
|
|
125
119
|
|
|
126
|
-
function rewriteImageRef (target, contentCatalog, assemblyModel, ctx, assets, escapeForInline
|
|
120
|
+
function rewriteImageRef (target, contentCatalog, assemblyModel, ctx, assets, escapeForInline, doc) {
|
|
121
|
+
if (doc && ~target.indexOf('{')) target = doc.$sub_attributes(target, global.Opal.hash({ attribute_missing: 'skip' }))
|
|
127
122
|
const image = isResourceRef(target) && contentCatalog.resolveResource(target, ctx, 'image', ['image'])
|
|
128
123
|
if (!image?.out) return
|
|
129
124
|
let newTarget
|
|
130
125
|
const { filetype, embedReferenceStyle, linkReferenceStyle, outDirname, pubRoot, siteRoot } = assemblyModel
|
|
131
126
|
if (filetype !== 'html') {
|
|
132
|
-
newTarget = resolveEmbedTarget(image, outDirname, embedReferenceStyle, escapeForInline)
|
|
127
|
+
newTarget = resolveEmbedTarget(image, outDirname, embedReferenceStyle, escapeForInline ?? false)
|
|
133
128
|
assets.add(image)
|
|
134
129
|
} else if (siteRoot || linkReferenceStyle === 'relative') {
|
|
135
|
-
newTarget = resolveLinkTarget(image, siteRoot, pubRoot, linkReferenceStyle, escapeForInline, false)
|
|
130
|
+
newTarget = resolveLinkTarget(image, siteRoot, pubRoot, linkReferenceStyle, escapeForInline ?? false, false)
|
|
136
131
|
if (linkReferenceStyle === 'relative') assets.add(image)
|
|
137
132
|
}
|
|
138
133
|
if (!newTarget) return
|
|
@@ -162,7 +157,7 @@ function rewriteStyleAttribute (block, lines, idx, idLeader, replacementStyle =
|
|
|
162
157
|
prevLine = cellSpec[2]
|
|
163
158
|
cellSpec = cellSpec[1]
|
|
164
159
|
} else if (~prevLine.indexOf('id=')) {
|
|
165
|
-
prevLine = `[${prevLine.slice(1, -1).replace(
|
|
160
|
+
prevLine = `[${prevLine.slice(1, -1).replace(NAMED_ID_ATTR_RX, '')}]`
|
|
166
161
|
}
|
|
167
162
|
let rawStyle
|
|
168
163
|
const commaIdx = prevLine.indexOf(',')
|
package/lib/util/rx.js
CHANGED
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.19",
|
|
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)",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
".": "./lib/index.js",
|
|
28
28
|
"./filter-component-versions": "./lib/filter-component-versions.js",
|
|
29
29
|
"./load-config": "./lib/load-config.js",
|
|
30
|
+
"./log-command": "./lib/log-command.js",
|
|
30
31
|
"./parse-resource-ref": "./lib/util/parse-resource-ref.js",
|
|
31
32
|
"./produce-assembly-file": "./lib/produce-assembly-file.js",
|
|
32
33
|
"./produce-assembly-files": "./lib/produce-assembly-files.js",
|
|
@@ -35,12 +36,12 @@
|
|
|
35
36
|
},
|
|
36
37
|
"imports": {
|
|
37
38
|
"#asciidoctor-log-adapter": "./adapters/asciidoctor/jsonl-logger.rb",
|
|
38
|
-
"#run-command": "@antora/run-command-helper",
|
|
39
39
|
"#unconvert-inline-asciidoc": "./lib/util/unconvert-inline-asciidoc.js"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@asciidoctor/reducer": "~1.1",
|
|
43
43
|
"@antora/expand-path-helper": "~3.0",
|
|
44
|
+
"@antora/run-command-helper": "~1.0",
|
|
44
45
|
"braces": "~3.0",
|
|
45
46
|
"picomatch": "~3.0",
|
|
46
47
|
"js-yaml": "~4.1"
|