@antora/assembler 1.0.0-beta.17 → 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 +26 -6
- package/lib/load-config.js +20 -9
- package/lib/log-command.js +18 -0
- package/lib/produce-assembly-file.js +242 -132
- package/lib/produce-assembly-files.js +7 -30
- package/lib/select-mutable-attributes.js +1 -0
- package/lib/util/generate-scoped-id.js +25 -0
- package/lib/util/rewriter.js +31 -33
- package/lib/util/rx.js +5 -0
- package/package.json +3 -2
- package/lib/util/generate-id.js +0 -25
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,21 +10,41 @@ 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
|
|
19
|
-
assembleContentConfig.configSource
|
|
20
|
-
|
|
17
|
+
if (assembleContentConfig.configSource?.constructor === Object) {
|
|
18
|
+
await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentConfig)
|
|
19
|
+
} else {
|
|
20
|
+
const singleConfig = !('configFiles' in config)
|
|
21
|
+
const configFiles = singleConfig ? config.configFile : config.configFiles
|
|
22
|
+
for (const configSource of Array.isArray(configFiles) ? configFiles : [configFiles]) {
|
|
23
|
+
const assembleContentConfigWithConfigSource = Object.assign({}, assembleContentConfig, { configSource })
|
|
24
|
+
await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentConfigWithConfigSource)
|
|
25
|
+
if (singleConfig) break
|
|
26
|
+
}
|
|
27
|
+
}
|
|
21
28
|
})
|
|
22
29
|
}
|
|
23
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
|
+
|
|
24
43
|
function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
|
|
25
44
|
contentCatalog.getComponents().forEach((component) => {
|
|
26
45
|
component.versions.forEach((componentVersion) => {
|
|
27
|
-
const source =
|
|
46
|
+
const source =
|
|
47
|
+
componentVersion.nav?.origin ?? componentVersion.origins?.find((it) => it.descriptor?.ext?.assembler)
|
|
28
48
|
const assemblerConfig = getAssemblerConfigFromDescriptor(source?.descriptor)
|
|
29
49
|
if (!assemblerConfig) return
|
|
30
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
|
|
@@ -2,16 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
const createAsciiDocFile = require('./util/create-asciidoc-file')
|
|
4
4
|
const createResourceKey = require('./util/create-resource-key')
|
|
5
|
-
const
|
|
5
|
+
const generateScopedId = require('./util/generate-scoped-id')
|
|
6
6
|
const { resolveLinkTarget } = require('./util/resolver')
|
|
7
|
-
const {
|
|
7
|
+
const {
|
|
8
|
+
rewriteXrefs,
|
|
9
|
+
rewriteImageAttr,
|
|
10
|
+
rewriteImageRef,
|
|
11
|
+
rewriteInlineImages,
|
|
12
|
+
rewriteStyleAttribute,
|
|
13
|
+
} = require('./util/rewriter')
|
|
8
14
|
const sanitize = require('./util/sanitize')
|
|
9
15
|
const unconvertInlineAsciiDoc = require('./util/unconvert-inline-asciidoc')
|
|
10
16
|
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
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')
|
|
15
29
|
|
|
16
30
|
function produceAssemblyFile (
|
|
17
31
|
loadAsciiDoc,
|
|
@@ -25,7 +39,30 @@ function produceAssemblyFile (
|
|
|
25
39
|
) {
|
|
26
40
|
const pagesByUrl = files.reduce((map, it) => (it.src.family === 'page' ? map.set(it.pub.url, it) : map), new Map())
|
|
27
41
|
if (outline.urlType === 'internal' && !pagesByUrl.get(outline.url) && !(outline.items || []).length) return
|
|
28
|
-
const pagesInOutline = selectPagesInOutline(outline, pagesByUrl
|
|
42
|
+
const pagesInOutline = selectPagesInOutline(outline, pagesByUrl)
|
|
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
|
|
65
|
+
)
|
|
29
66
|
const buffer = mergeAsciiDoc(
|
|
30
67
|
loadAsciiDoc,
|
|
31
68
|
contentCatalog,
|
|
@@ -34,29 +71,51 @@ function produceAssemblyFile (
|
|
|
34
71
|
outline,
|
|
35
72
|
files,
|
|
36
73
|
pagesInOutline,
|
|
74
|
+
idSeparators,
|
|
37
75
|
asciidocConfig,
|
|
38
76
|
mutableAttributes,
|
|
39
77
|
assemblyModel
|
|
40
78
|
)
|
|
41
|
-
const rootLevel = assemblyModel.rootLevel
|
|
42
79
|
const stem = rootLevel === 0 ? 'index' : generateSlug(buffer.navtitle)
|
|
43
|
-
const downloadStem = [
|
|
44
|
-
.filter((it) => it)
|
|
45
|
-
.join('-')
|
|
80
|
+
const downloadStem = [component, version, rootLevel === 0 ? '' : stem].filter((it) => it).join('-')
|
|
46
81
|
return createAsciiDocFile(contentCatalog, {
|
|
47
82
|
asciidoc: asciidocConfig,
|
|
48
83
|
assembler: { assembled: pagesInOutline.assembled, downloadStem, rootLevel },
|
|
49
84
|
contents: Buffer.from(buffer.join('\n') + '\n'),
|
|
50
|
-
src: {
|
|
51
|
-
component: componentVersion.name,
|
|
52
|
-
version: componentVersion.version,
|
|
53
|
-
module: 'ROOT',
|
|
54
|
-
family: 'export',
|
|
55
|
-
relative: stem + '.adoc',
|
|
56
|
-
},
|
|
85
|
+
src: { component, version, module: 'ROOT', family: 'export', relative: stem + '.adoc' },
|
|
57
86
|
})
|
|
58
87
|
}
|
|
59
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
|
+
|
|
60
119
|
function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
|
|
61
120
|
const doctype = assemblyModel.doctype ?? 'book'
|
|
62
121
|
const navtitlePlain = sanitize(navtitle)
|
|
@@ -64,29 +123,30 @@ function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
|
|
|
64
123
|
let doctitle = navtitleAsciiDoc
|
|
65
124
|
if (navtitlePlain !== componentVersion.title) doctitle = `${componentVersion.title}: ${doctitle}`
|
|
66
125
|
const version = componentVersion.version === 'master' ? '' : componentVersion.version
|
|
126
|
+
const displayVersion = componentVersion.displayVersion
|
|
67
127
|
const buffer = [
|
|
68
128
|
`= ${doctitle}`,
|
|
69
|
-
...(version ? [`:revnumber: ${
|
|
129
|
+
...(version ? [`:revnumber: ${displayVersion}`] : []),
|
|
70
130
|
...(doctype === 'article' ? [] : [`:doctype: ${doctype ?? 'book'}`]),
|
|
71
131
|
':underscore: _',
|
|
72
132
|
// Q: should we pass these via the CLI so they cannot be modified?
|
|
73
133
|
`:page-component-name: ${componentVersion.name}`,
|
|
74
134
|
`:page-component-version:${version ? ' ' + version : ''}`,
|
|
75
135
|
':page-version: {page-component-version}',
|
|
76
|
-
`:page-component-display-version: ${
|
|
136
|
+
`:page-component-display-version: ${displayVersion}`,
|
|
77
137
|
`:page-component-title: ${componentVersion.title}`,
|
|
78
138
|
]
|
|
79
139
|
return Object.assign(buffer, { navtitle })
|
|
80
140
|
}
|
|
81
141
|
|
|
82
|
-
function selectPagesInOutline (outlineEntry, pagesByUrl,
|
|
142
|
+
function selectPagesInOutline (outlineEntry, pagesByUrl, accum) {
|
|
83
143
|
accum ??= Object.assign(new Map(), { assembled: { pages: new Map(), assets: new Set() } })
|
|
84
144
|
const page = outlineEntry.urlType === 'internal' ? pagesByUrl.get(outlineEntry.url) : undefined
|
|
85
145
|
if (page) {
|
|
86
146
|
accum.set(createResourceKey(page.src), page)
|
|
87
147
|
accum.set(outlineEntry.url, page)
|
|
88
148
|
}
|
|
89
|
-
for (const item of outlineEntry.items || []) selectPagesInOutline(item, pagesByUrl,
|
|
149
|
+
for (const item of outlineEntry.items || []) selectPagesInOutline(item, pagesByUrl, accum)
|
|
90
150
|
return accum
|
|
91
151
|
}
|
|
92
152
|
|
|
@@ -98,6 +158,7 @@ function mergeAsciiDoc (
|
|
|
98
158
|
outlineEntry,
|
|
99
159
|
files,
|
|
100
160
|
pagesInOutline,
|
|
161
|
+
idSeparators,
|
|
101
162
|
asciidocConfig,
|
|
102
163
|
mutableAttributes,
|
|
103
164
|
assemblyModel,
|
|
@@ -114,18 +175,14 @@ function mergeAsciiDoc (
|
|
|
114
175
|
let navtitlePlain = sanitize(navtitle)
|
|
115
176
|
let navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
116
177
|
const { items = [], unresolved, urlType, url } = outlineEntry
|
|
117
|
-
const { doctype, linkReferenceStyle, pubRoot, siteRoot,
|
|
178
|
+
const { doctype, filetype, linkReferenceStyle, pubRoot, siteRoot, logger } = assemblyModel
|
|
118
179
|
const assembled = pagesInOutline.assembled
|
|
119
180
|
// FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
|
|
120
181
|
const page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
|
|
121
182
|
const atDocumentRoot = !buffer.inBody
|
|
183
|
+
const isRootPage = page && atDocumentRoot && !level
|
|
122
184
|
const atBookRoot = atDocumentRoot && !level && doctype === 'book' && (supportsParts = true)
|
|
123
185
|
const hasItems = items.length > 0
|
|
124
|
-
const idPrefix =
|
|
125
|
-
asciidocConfig.attributes['assembler-idprefix'] ?? ('assembler-idprefix' in asciidocConfig.attributes ? '' : '_')
|
|
126
|
-
const idSeparator = xmlIds ? '-' : ':'
|
|
127
|
-
const idScopeSeparator = idSeparator.repeat(3)
|
|
128
|
-
const idCoordinateSeparator = idSeparator === '-' ? '----' : idSeparator
|
|
129
186
|
if (page && !assembled.pages.has(page)) {
|
|
130
187
|
const contents = page.src.contents
|
|
131
188
|
if (contents == null) {
|
|
@@ -146,6 +203,7 @@ function mergeAsciiDoc (
|
|
|
146
203
|
},
|
|
147
204
|
})
|
|
148
205
|
: doc.getLogger()
|
|
206
|
+
doc.source_header_attributes ??= doc.parent.$to_h()
|
|
149
207
|
if (doc.hasAttribute('assembly-navtitle')) {
|
|
150
208
|
navtitleAsciiDoc = doc.getAttribute('assembly-navtitle')
|
|
151
209
|
navtitlePlain = sanitize((navtitle = doc.$apply_reftext_subs(navtitleAsciiDoc)))
|
|
@@ -159,30 +217,21 @@ function mergeAsciiDoc (
|
|
|
159
217
|
}
|
|
160
218
|
}
|
|
161
219
|
}
|
|
162
|
-
if (atDocumentRoot) {
|
|
163
|
-
const authors = doc.getAuthors()
|
|
164
|
-
if (authors.length) {
|
|
165
|
-
const authorLine = authors
|
|
166
|
-
.map((author) => {
|
|
167
|
-
const email = author.getEmail()
|
|
168
|
-
return email ? `${author.getName()} <${author.getEmail()}>` : author.getName()
|
|
169
|
-
})
|
|
170
|
-
.join('; ')
|
|
171
|
-
buffer.splice(1, 0, authorLine)
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
220
|
// NOTE: in Antora, docname is relative src path from module without file extension
|
|
175
221
|
const docname = doc.getAttribute('docname')
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
componentVersion,
|
|
179
|
-
idCoordinateSeparator,
|
|
180
|
-
idScopeSeparator,
|
|
181
|
-
idPrefix
|
|
182
|
-
)
|
|
222
|
+
const pageId = generateScopedId(page.src, componentVersion, idSeparators, filetype)
|
|
223
|
+
const pageIdLeader = pageId + idSeparators.scope
|
|
183
224
|
let pageFragment = ''
|
|
184
225
|
let pageRoles = ''
|
|
185
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
|
+
}
|
|
186
235
|
let part
|
|
187
236
|
if ((part = pageStyle === 'part')) {
|
|
188
237
|
pageStyle = ''
|
|
@@ -194,7 +243,10 @@ function mergeAsciiDoc (
|
|
|
194
243
|
let nextSectionLevel = 1
|
|
195
244
|
const lines = doc.getSourceLines()
|
|
196
245
|
const ignoreLines = []
|
|
197
|
-
buffer.inBody
|
|
246
|
+
if (!buffer.inBody) {
|
|
247
|
+
buffer.inBody = true
|
|
248
|
+
buffer.endHeaderIdx = buffer.length - 1
|
|
249
|
+
}
|
|
198
250
|
buffer.push('')
|
|
199
251
|
buffer.push(`:docname: ${docname}`)
|
|
200
252
|
if (component !== lastComponentVersion.name) {
|
|
@@ -218,7 +270,63 @@ function mergeAsciiDoc (
|
|
|
218
270
|
buffer.push(`:page-origin-refname: ${origin.branch || origin.tag}`)
|
|
219
271
|
buffer.push(`:page-origin-reftype: ${origin.branch ? 'branch' : 'tag'}`)
|
|
220
272
|
buffer.push(`:page-origin-refhash: ${origin.worktree ? '(worktree)' : origin.refhash}`)
|
|
221
|
-
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
|
+
}
|
|
222
330
|
let heading
|
|
223
331
|
if (pageStyle && (part && level === 1 ? (level = 0) : level) === 0) {
|
|
224
332
|
let htitleAsciiDoc = navtitleAsciiDoc
|
|
@@ -235,10 +343,10 @@ function mergeAsciiDoc (
|
|
|
235
343
|
assemblyModel = Object.assign({}, assemblyModel, { sectionMergeStrategy: 'discrete' })
|
|
236
344
|
} else {
|
|
237
345
|
if (atDocumentRoot) {
|
|
238
|
-
pageFragment = `#${
|
|
239
|
-
buffer.unshift(`[#${
|
|
346
|
+
pageFragment = `#${pageIdLeader}${doc.getId() ?? pageStyle}`
|
|
347
|
+
buffer.unshift(`[#${pageId}]`)
|
|
240
348
|
} else {
|
|
241
|
-
pageFragment = `#${
|
|
349
|
+
pageFragment = `#${pageId}`
|
|
242
350
|
}
|
|
243
351
|
heading = { title: htitleAsciiDoc, level: part ? 1 : 2 }
|
|
244
352
|
nextSectionLevel++
|
|
@@ -247,7 +355,7 @@ function mergeAsciiDoc (
|
|
|
247
355
|
if (atDocumentRoot && navtitlePlain === componentVersion.title) {
|
|
248
356
|
level--
|
|
249
357
|
} else {
|
|
250
|
-
pageFragment = `#${
|
|
358
|
+
pageFragment = `#${pageId}`
|
|
251
359
|
if (part && level === 1) level--
|
|
252
360
|
if ((heading = { title: navtitleAsciiDoc, level: level + 1 }).level > 6) {
|
|
253
361
|
Object.assign(heading, { level: 6, style: `discrete.h${heading.level}` })
|
|
@@ -258,7 +366,7 @@ function mergeAsciiDoc (
|
|
|
258
366
|
buffer.push(`[${heading.style ?? pageStyle}${pageFragment}${pageRoles}]`)
|
|
259
367
|
buffer.push(`${'='.repeat(heading.level)} ${heading.title}`)
|
|
260
368
|
} else if (atDocumentRoot) {
|
|
261
|
-
buffer.unshift(`[#${
|
|
369
|
+
buffer.unshift(`[#${pageId}]`)
|
|
262
370
|
}
|
|
263
371
|
let enclosed
|
|
264
372
|
if (assemblyModel.sectionMergeStrategy === 'enclose' && hasItems && doc.hasSections()) {
|
|
@@ -268,26 +376,16 @@ function mergeAsciiDoc (
|
|
|
268
376
|
//if (overviewTitle === navtitle) overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
269
377
|
const overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
270
378
|
buffer.push('')
|
|
271
|
-
|
|
272
|
-
let toggleSectids, syntheticId
|
|
273
|
-
if (doc.isAttribute('sectids')) {
|
|
274
|
-
if (doc.isAttributeLocked('sectids')) {
|
|
275
|
-
syntheticId = `__object-id-${getObjectId(outlineEntry)}`
|
|
276
|
-
} else {
|
|
277
|
-
buffer.push(':!sectids:')
|
|
278
|
-
toggleSectids = true
|
|
279
|
-
}
|
|
280
|
-
}
|
|
379
|
+
const syntheticId = idSeparators.generateIdFromTitle(navtitleAsciiDoc, idSeparators)
|
|
281
380
|
let hlevel = level + 2
|
|
282
381
|
if (hlevel > 6) {
|
|
283
382
|
const blockStyle = `discrete.h${hlevel}`
|
|
284
383
|
hlevel = 6
|
|
285
|
-
buffer.push(
|
|
286
|
-
} else
|
|
384
|
+
buffer.push(`[${blockStyle}#${syntheticId}]`)
|
|
385
|
+
} else {
|
|
287
386
|
buffer.push(`[#${syntheticId}]`)
|
|
288
387
|
}
|
|
289
388
|
buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
|
|
290
|
-
if (toggleSectids) buffer.push(':sectids:')
|
|
291
389
|
}
|
|
292
390
|
assembled.pages.set(page, pageFragment)
|
|
293
391
|
if (doc.hasSections()) {
|
|
@@ -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
|
-
idLeader,
|
|
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,20 +587,7 @@ function mergeAsciiDoc (
|
|
|
491
587
|
} else {
|
|
492
588
|
buffer.inBody = true
|
|
493
589
|
buffer.push('')
|
|
494
|
-
|
|
495
|
-
// Q: should we unset docname, page-module, etc?
|
|
496
|
-
let toggleSectids, syntheticId
|
|
497
|
-
if (!('sectids' in asciidocConfig.attributes)) {
|
|
498
|
-
buffer.push(':!sectids:')
|
|
499
|
-
toggleSectids = true
|
|
500
|
-
} else if (typeof asciidocConfig.attributes.sectids === 'string') {
|
|
501
|
-
if ('sectids' in mutableAttributes) {
|
|
502
|
-
buffer.push(':!sectids:')
|
|
503
|
-
toggleSectids = true
|
|
504
|
-
} else {
|
|
505
|
-
syntheticId = `__object-id-${global.Opal.hash(outlineEntry).$object_id()}`
|
|
506
|
-
}
|
|
507
|
-
}
|
|
590
|
+
const syntheticId = idSeparators.generateIdFromTitle(navtitleAsciiDoc, idSeparators)
|
|
508
591
|
let sectionTitle = navtitleAsciiDoc
|
|
509
592
|
if (urlType === 'external') {
|
|
510
593
|
sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
@@ -512,14 +595,7 @@ function mergeAsciiDoc (
|
|
|
512
595
|
const resource = files.find((it) => it.pub.url === url)
|
|
513
596
|
if (resource) {
|
|
514
597
|
if (resource.src.family === 'page' && pagesInOutline.has(resource.pub.url)) {
|
|
515
|
-
const refid =
|
|
516
|
-
resource.src,
|
|
517
|
-
componentVersion,
|
|
518
|
-
idCoordinateSeparator,
|
|
519
|
-
idScopeSeparator,
|
|
520
|
-
idPrefix,
|
|
521
|
-
true
|
|
522
|
-
).id
|
|
598
|
+
const refid = generateScopedId(resource.src, componentVersion, idSeparators, filetype, true)
|
|
523
599
|
sectionTitle = `xref:${refid}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
524
600
|
} else if (siteRoot) {
|
|
525
601
|
sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, true)}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
@@ -529,15 +605,13 @@ function mergeAsciiDoc (
|
|
|
529
605
|
let hlevel = level + 1
|
|
530
606
|
if (hlevel > 6) {
|
|
531
607
|
hlevel = 6
|
|
532
|
-
buffer.push(
|
|
533
|
-
} else
|
|
608
|
+
buffer.push(`[discrete#${syntheticId}]`)
|
|
609
|
+
} else {
|
|
534
610
|
buffer.push(`[#${syntheticId}]`)
|
|
535
611
|
}
|
|
536
612
|
buffer.push(`${'='.repeat(hlevel)} ${sectionTitle}`)
|
|
537
|
-
if (toggleSectids) buffer.push(':sectids:')
|
|
538
613
|
}
|
|
539
614
|
}
|
|
540
|
-
|
|
541
615
|
if (hasItems) {
|
|
542
616
|
const nextLevel = level + 1
|
|
543
617
|
// NOTE: drop first child if same as parent; should we keep if content is different?
|
|
@@ -553,6 +627,7 @@ function mergeAsciiDoc (
|
|
|
553
627
|
item,
|
|
554
628
|
files,
|
|
555
629
|
pagesInOutline,
|
|
630
|
+
idSeparators,
|
|
556
631
|
asciidocConfig,
|
|
557
632
|
mutableAttributes,
|
|
558
633
|
assemblyModel,
|
|
@@ -565,11 +640,32 @@ function mergeAsciiDoc (
|
|
|
565
640
|
return buffer
|
|
566
641
|
}
|
|
567
642
|
|
|
568
|
-
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
|
+
}
|
|
569
666
|
const doctitleIdx = doc.getHeader().getLineNumber() - 1
|
|
570
667
|
const end = doc.getBlocks()[0]?.getLineNumber() ?? lines.length
|
|
571
|
-
let belowDoctitle
|
|
572
|
-
let open
|
|
668
|
+
let belowDoctitle, current, open
|
|
573
669
|
const implicitLines = []
|
|
574
670
|
for (let idx = 0; idx < end; idx++) {
|
|
575
671
|
if (idx === doctitleIdx) {
|
|
@@ -580,37 +676,51 @@ function processDocumentHeader (doc, lines, buffer, ignoreLines) {
|
|
|
580
676
|
}
|
|
581
677
|
const line = lines[idx]
|
|
582
678
|
if (open === ':' || open === '-:') {
|
|
583
|
-
if (open === ':')
|
|
584
|
-
if (!line || !line.endsWith(' \\')) open = undefined
|
|
679
|
+
if (open === ':') current.lines.push(line)
|
|
680
|
+
if (!line || !line.endsWith(' \\')) current = open = undefined
|
|
585
681
|
} else if (line) {
|
|
586
|
-
const chr0 = line.charAt()
|
|
587
682
|
let attributeEntryMatch
|
|
683
|
+
const chr0 = line.charAt()
|
|
588
684
|
if (chr0 === '/' && line.charAt(1) === '/') {
|
|
589
|
-
if (line.startsWith('////')) {
|
|
590
|
-
|
|
591
|
-
|
|
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) === '/') {
|
|
592
695
|
break
|
|
696
|
+
} else {
|
|
697
|
+
entries.push({ type: 'line_comment', lines: [line] })
|
|
698
|
+
current = undefined
|
|
593
699
|
}
|
|
594
|
-
buffer.push(line)
|
|
595
700
|
} else if (open) {
|
|
596
|
-
|
|
597
|
-
} else if (chr0 === ':' && ~line.indexOf(':', 2) && (attributeEntryMatch = line.match(
|
|
598
|
-
|
|
599
|
-
|
|
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)) {
|
|
600
707
|
if (line.endsWith(' \\')) open = '-:' // disallow value continuation
|
|
601
708
|
} else {
|
|
602
709
|
if (line.endsWith(' \\')) open = ':'
|
|
603
|
-
|
|
710
|
+
entries.push((current = { type: 'attribute_entry', name, negated, lines: [line], lineno: idx + 1 }))
|
|
711
|
+
if (assemblyHeaderAttributes.has(name)) current.promote = true
|
|
604
712
|
}
|
|
605
713
|
} else if (belowDoctitle) {
|
|
606
714
|
if (implicitLines.length === 2 || !/[\p{Alpha}0-9]/u.test(chr0)) break
|
|
607
715
|
implicitLines.push(line)
|
|
608
716
|
} else if (chr0 === '[' && line.charAt(line.length - 1) === ']') {
|
|
717
|
+
current = undefined
|
|
609
718
|
const attrlist = line
|
|
610
719
|
.slice(1, -1)
|
|
611
720
|
.trim()
|
|
612
|
-
.replace(/(?:^\w[\w-]*(?!=|[\w-]))?([
|
|
613
|
-
|
|
721
|
+
.replace(/(?:^\w[\w-]*(?!=|[\w-]))?([#.]\w[\w-]*)*/, '')
|
|
722
|
+
.replace(NAMED_ID_ATTR_RX, '')
|
|
723
|
+
if (attrlist) entries.push({ type: 'attrlist', lines: ['[' + attrlist + ']'] })
|
|
614
724
|
}
|
|
615
725
|
} else if (belowDoctitle) {
|
|
616
726
|
break
|
|
@@ -618,18 +728,15 @@ function processDocumentHeader (doc, lines, buffer, ignoreLines) {
|
|
|
618
728
|
lines[idx] = undefined
|
|
619
729
|
ignoreLines.push(idx)
|
|
620
730
|
}
|
|
621
|
-
return
|
|
622
|
-
.getRoles()
|
|
623
|
-
.map((role) => '.' + role)
|
|
624
|
-
.join('')
|
|
731
|
+
return entries
|
|
625
732
|
}
|
|
626
733
|
|
|
627
734
|
function generateSlug (title) {
|
|
628
735
|
return title
|
|
629
736
|
.toLowerCase()
|
|
630
737
|
.replace(/<[^>]+>/g, '')
|
|
631
|
-
.replace(
|
|
632
|
-
if (name) return
|
|
738
|
+
.replace(CHAR_REF_RX, (_, name, dec, hex) => {
|
|
739
|
+
if (name) return BUILT_IN_NAMED_ENTITIES[name] ?? '?'
|
|
633
740
|
return String.fromCharCode(dec ? parseInt(dec, 10) : parseInt(hex, 16))
|
|
634
741
|
})
|
|
635
742
|
.replace(/[\x27\u2019]/g, '')
|
|
@@ -645,10 +752,6 @@ function fixSectionLevels (sections, expectedLevel) {
|
|
|
645
752
|
})
|
|
646
753
|
}
|
|
647
754
|
|
|
648
|
-
function getObjectId (obj) {
|
|
649
|
-
return global.Opal.id(obj)
|
|
650
|
-
}
|
|
651
|
-
|
|
652
755
|
// NOTE: blank lines at top and bottom of document create mismatch when using line numbers to navigate source lines
|
|
653
756
|
// IMPORTANT: this must not leave behind lines the parser will drop!
|
|
654
757
|
// IDEA: another option is to capture initial lineno of reader and use as offset (but preseves those blank lines)
|
|
@@ -671,4 +774,11 @@ function safePush (onto, entries) {
|
|
|
671
774
|
}
|
|
672
775
|
}
|
|
673
776
|
|
|
777
|
+
function generateIdFromTitle (titleAsciiDoc, idSeparators) {
|
|
778
|
+
const Section = this.$class().$const_get('::Asciidoctor::Section')
|
|
779
|
+
const baseId = Section.$generate_id(titleAsciiDoc, this)
|
|
780
|
+
this.getCatalog().refs['$[]='](baseId, true)
|
|
781
|
+
return `_${idSeparators.coordinate}${baseId}`
|
|
782
|
+
}
|
|
783
|
+
|
|
674
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
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const HTML_RESERVED_ID_NAMES = 'content header footnotes footer footer-text premable toc toctitle'.split(' ')
|
|
4
|
+
|
|
5
|
+
function generateScopedId (componentSrc, componentVersion, separators, filetype, asXrefTarget) {
|
|
6
|
+
let { component, module: mod, relative } = componentSrc
|
|
7
|
+
let id = relative.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
|
|
8
|
+
let { coordinate: coordinateSeparator, prefix: prefixSeparator } = separators
|
|
9
|
+
if (component !== componentVersion.name) {
|
|
10
|
+
id = [component, mod === 'ROOT' ? '' : mod, id].join(coordinateSeparator)
|
|
11
|
+
} else if (mod !== 'ROOT') {
|
|
12
|
+
if (asXrefTarget && coordinateSeparator === ':' && /(?:pass|stem)$/.test(mod) && /^[a-z]+(?:,[a-z-]+)*$/.test(id)) {
|
|
13
|
+
prefixSeparator = ''
|
|
14
|
+
mod = mod.replace(/(?:pass|stem)$/, '\\$&')
|
|
15
|
+
}
|
|
16
|
+
id = mod + coordinateSeparator + id
|
|
17
|
+
} else if (filetype === 'html' && HTML_RESERVED_ID_NAMES.includes(id)) {
|
|
18
|
+
id = prefixSeparator + id
|
|
19
|
+
prefixSeparator = ''
|
|
20
|
+
}
|
|
21
|
+
if (prefixSeparator && !/^[\p{Alpha}_:]/u.test(id)) id = prefixSeparator + id
|
|
22
|
+
return id
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = generateScopedId
|
package/lib/util/rewriter.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const createResourceKey = require('./create-resource-key')
|
|
4
|
-
const
|
|
5
|
-
const { resolveEmbedTarget, resolveLinkTarget } = require('./resolver')
|
|
4
|
+
const generateScopedId = require('./generate-scoped-id')
|
|
6
5
|
const parseResourceRef = require('./parse-resource-ref')
|
|
6
|
+
const { resolveEmbedTarget, resolveLinkTarget } = require('./resolver')
|
|
7
|
+
|
|
8
|
+
const { NAMED_ID_ATTR_RX } = require('./rx')
|
|
7
9
|
|
|
8
10
|
function rewriteXrefs (
|
|
9
11
|
line,
|
|
@@ -11,15 +13,13 @@ function rewriteXrefs (
|
|
|
11
13
|
assemblyModel,
|
|
12
14
|
ctx,
|
|
13
15
|
escapeForInline,
|
|
14
|
-
idCoordinateSep,
|
|
15
|
-
idScopeSep,
|
|
16
|
-
idPrefix,
|
|
17
|
-
idLeader,
|
|
18
16
|
pagesInOutline,
|
|
17
|
+
idSeparators,
|
|
18
|
+
idLeader,
|
|
19
19
|
doc,
|
|
20
20
|
sourceLocation
|
|
21
21
|
) {
|
|
22
|
-
return line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(
|
|
22
|
+
return line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|[\s\S]*?[^\\])\]/gu, (_, target, text) =>
|
|
23
23
|
rewriteXref(
|
|
24
24
|
target,
|
|
25
25
|
text,
|
|
@@ -27,11 +27,9 @@ function rewriteXrefs (
|
|
|
27
27
|
assemblyModel,
|
|
28
28
|
ctx,
|
|
29
29
|
escapeForInline,
|
|
30
|
-
idCoordinateSep,
|
|
31
|
-
idScopeSep,
|
|
32
|
-
idPrefix,
|
|
33
|
-
idLeader,
|
|
34
30
|
pagesInOutline,
|
|
31
|
+
idSeparators,
|
|
32
|
+
idLeader,
|
|
35
33
|
doc,
|
|
36
34
|
sourceLocation
|
|
37
35
|
)
|
|
@@ -45,11 +43,9 @@ function rewriteXref (
|
|
|
45
43
|
assemblyModel,
|
|
46
44
|
ctx,
|
|
47
45
|
escapeForInline,
|
|
48
|
-
idCoordinateSep,
|
|
49
|
-
idScopeSep,
|
|
50
|
-
idPrefix,
|
|
51
|
-
idLeader,
|
|
52
46
|
pagesInOutline,
|
|
47
|
+
idSeparators,
|
|
48
|
+
idLeader,
|
|
53
49
|
doc,
|
|
54
50
|
sourceLocation
|
|
55
51
|
) {
|
|
@@ -66,7 +62,7 @@ function rewriteXref (
|
|
|
66
62
|
} else {
|
|
67
63
|
fragment = target
|
|
68
64
|
}
|
|
69
|
-
if (!resourceRef) return `xref:${idLeader
|
|
65
|
+
if (!resourceRef) return `xref:${idLeader == null ? rawTarget : idLeader + fragment}[${text}]`
|
|
70
66
|
const resourceId = parseResourceRef(resourceRef, ctx, 'page', contentCatalog)
|
|
71
67
|
const family = resourceId.family
|
|
72
68
|
let resource
|
|
@@ -75,7 +71,8 @@ function rewriteXref (
|
|
|
75
71
|
const { linkReferenceStyle, pubRoot, siteRoot } = assemblyModel
|
|
76
72
|
text ||= resource.asciidoc?.xreftext || rawTarget
|
|
77
73
|
if (siteRoot || linkReferenceStyle === 'relative') {
|
|
78
|
-
|
|
74
|
+
const hash = fragment && !(family === 'page' && fragment === resource.asciidoc.id) ? '#' + fragment : ''
|
|
75
|
+
return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, escapeForInline)}${hash}[${text}]`
|
|
79
76
|
}
|
|
80
77
|
if (doc) {
|
|
81
78
|
const msg = `Cannot create external ${family} reference in assembly because site URL is unknown: ${rawTarget}`
|
|
@@ -96,42 +93,41 @@ function rewriteXref (
|
|
|
96
93
|
text = ''
|
|
97
94
|
}
|
|
98
95
|
const componentVersionCtx = { name: ctx.component, version: ctx.version }
|
|
99
|
-
const refid =
|
|
96
|
+
const refid = generateScopedId(
|
|
100
97
|
resource.src,
|
|
101
98
|
componentVersionCtx,
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
).id
|
|
108
|
-
return `xref:${refid}[${text}]`
|
|
99
|
+
idSeparators,
|
|
100
|
+
assemblyModel.filetype,
|
|
101
|
+
text.length > 0
|
|
102
|
+
)
|
|
103
|
+
return `xref:${fragment ? refid + idSeparators.scope + fragment : refid}[${text}]`
|
|
109
104
|
}
|
|
110
105
|
|
|
111
106
|
function rewriteImageAttr (val, contentCatalog, assemblyModel, ctx, assets) {
|
|
112
|
-
const match = val.startsWith('image:') && /^image::?(.+?)\[(.*?)\]$/.exec(val)
|
|
107
|
+
const match = val.startsWith('image:') && /^image::?(.+?)\[(.*?)\](@|)$/.exec(val)
|
|
113
108
|
if (!match) return
|
|
114
109
|
const newTarget = rewriteImageRef(match[1], contentCatalog, assemblyModel, ctx, assets)
|
|
115
|
-
return newTarget ? `image:${newTarget}[${match[2]}]` : val
|
|
110
|
+
return newTarget ? `image:${newTarget}[${match[2]}]${match[3]}` : val
|
|
116
111
|
}
|
|
117
112
|
|
|
118
|
-
function rewriteInlineImages (line, contentCatalog, assemblyModel, ctx, assets, escapeForInline) {
|
|
119
|
-
return line.replace(/(?<![\\+])image:([^:\s[](?:[^[]*[^\s[])?)\[([
|
|
120
|
-
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)
|
|
121
116
|
return newTarget ? `image:${newTarget}[${attrlist}]` : m
|
|
122
117
|
})
|
|
123
118
|
}
|
|
124
119
|
|
|
125
|
-
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' }))
|
|
126
122
|
const image = isResourceRef(target) && contentCatalog.resolveResource(target, ctx, 'image', ['image'])
|
|
127
123
|
if (!image?.out) return
|
|
128
124
|
let newTarget
|
|
129
125
|
const { filetype, embedReferenceStyle, linkReferenceStyle, outDirname, pubRoot, siteRoot } = assemblyModel
|
|
130
126
|
if (filetype !== 'html') {
|
|
131
|
-
newTarget = resolveEmbedTarget(image, outDirname, embedReferenceStyle, escapeForInline)
|
|
127
|
+
newTarget = resolveEmbedTarget(image, outDirname, embedReferenceStyle, escapeForInline ?? false)
|
|
132
128
|
assets.add(image)
|
|
133
129
|
} else if (siteRoot || linkReferenceStyle === 'relative') {
|
|
134
|
-
newTarget = resolveLinkTarget(image, siteRoot, pubRoot, linkReferenceStyle, escapeForInline, false)
|
|
130
|
+
newTarget = resolveLinkTarget(image, siteRoot, pubRoot, linkReferenceStyle, escapeForInline ?? false, false)
|
|
135
131
|
if (linkReferenceStyle === 'relative') assets.add(image)
|
|
136
132
|
}
|
|
137
133
|
if (!newTarget) return
|
|
@@ -160,6 +156,8 @@ function rewriteStyleAttribute (block, lines, idx, idLeader, replacementStyle =
|
|
|
160
156
|
if (cellSpec) {
|
|
161
157
|
prevLine = cellSpec[2]
|
|
162
158
|
cellSpec = cellSpec[1]
|
|
159
|
+
} else if (~prevLine.indexOf('id=')) {
|
|
160
|
+
prevLine = `[${prevLine.slice(1, -1).replace(NAMED_ID_ATTR_RX, '')}]`
|
|
163
161
|
}
|
|
164
162
|
let rawStyle
|
|
165
163
|
const commaIdx = prevLine.indexOf(',')
|
package/lib/util/rx.js
ADDED
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"
|
package/lib/util/generate-id.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const ReservedIdNames = 'content header footnotes footer footer-text premable toc toctitle'.split(' ')
|
|
4
|
-
|
|
5
|
-
function generateId (componentSrc, componentVersion, coordinateSep, scopeSep, prefix, asXrefTarget, fragment) {
|
|
6
|
-
let { component, module: mod, relative } = componentSrc
|
|
7
|
-
let id = relative.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
|
|
8
|
-
if (component !== componentVersion.name) {
|
|
9
|
-
id = [component, mod === 'ROOT' ? '' : mod, id].join(coordinateSep)
|
|
10
|
-
} else if (mod !== 'ROOT') {
|
|
11
|
-
if (asXrefTarget && coordinateSep === ':' && /(?:pass|stem)$/.test(mod) && /^[a-z]+(?:,[a-z-]+)*$/.test(id)) {
|
|
12
|
-
prefix = ''
|
|
13
|
-
mod = mod.replace(/(?:pass|stem)$/, '\\$&')
|
|
14
|
-
}
|
|
15
|
-
id = mod + coordinateSep + id
|
|
16
|
-
} else if (ReservedIdNames.includes(id)) {
|
|
17
|
-
id += scopeSep
|
|
18
|
-
scopeSep = ''
|
|
19
|
-
}
|
|
20
|
-
if (prefix && !/^[\p{Alpha}_:]/u.test(id)) id = prefix + id
|
|
21
|
-
const idLeader = id + scopeSep
|
|
22
|
-
return { idLeader, id: fragment ? idLeader + fragment : id }
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
module.exports = generateId
|