@antora/assembler 1.0.0-beta.2 → 1.0.0-beta.20
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/adapters/asciidoctor/jsonl-logger.rb +25 -0
- package/lib/assemble-content.js +154 -53
- package/lib/configure.js +32 -14
- package/lib/filter-component-versions.js +2 -1
- package/lib/load-config.js +83 -24
- package/lib/log-command.js +18 -0
- package/lib/produce-assembly-file.js +341 -305
- package/lib/produce-assembly-files.js +94 -38
- package/lib/select-mutable-attributes.js +1 -0
- package/lib/util/compute-out.js +3 -3
- package/lib/util/create-asciidoc-file.js +0 -1
- package/lib/util/create-resource-key.js +7 -0
- package/lib/util/generate-scoped-id.js +25 -0
- package/lib/util/parse-resource-ref.js +36 -0
- package/lib/util/resolver.js +36 -0
- package/lib/util/rewriter.js +200 -0
- package/lib/util/rx.js +5 -0
- package/package.json +8 -3
|
@@ -1,49 +1,80 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const computeOut = require('./util/compute-out')
|
|
3
4
|
const createAsciiDocFile = require('./util/create-asciidoc-file')
|
|
4
5
|
const filterComponentVersions = require('./filter-component-versions')
|
|
5
6
|
const produceAssemblyFile = require('./produce-assembly-file')
|
|
6
7
|
const selectMutableAttributes = require('./select-mutable-attributes')
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
+
const ATTR_REF_RX = /\\?\{(\w[\w-]*)\}/g
|
|
9
10
|
|
|
10
|
-
function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig,
|
|
11
|
-
const {
|
|
12
|
-
|
|
11
|
+
function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, selectAssemblyProfile) {
|
|
12
|
+
const { assembly: assemblyConfig } = assemblerConfig
|
|
13
|
+
selectAssemblyProfile ??= (componentVersion) => ({
|
|
14
|
+
attributes: assemblyConfig.attributes,
|
|
13
15
|
doctype: assemblyConfig.doctype,
|
|
14
16
|
insertStartPage: assemblyConfig.insertStartPage,
|
|
15
17
|
rootLevel: assemblyConfig.rootLevel,
|
|
16
18
|
sectionMergeStrategy: assemblyConfig.sectionMergeStrategy,
|
|
17
19
|
navigation: componentVersion.navigation,
|
|
18
20
|
xmlIds: assemblyConfig.xmlIds,
|
|
21
|
+
embedReferenceStyle: assemblyConfig.embedReferenceStyle,
|
|
22
|
+
linkReferenceStyle: assemblyConfig.linkReferenceStyle,
|
|
23
|
+
dropExplicitXrefText: assemblyConfig.dropExplicitXrefText,
|
|
24
|
+
revdate: assemblyConfig.revdate,
|
|
25
|
+
logger: assemblyConfig.logger, // for tests
|
|
19
26
|
})
|
|
20
|
-
const
|
|
21
|
-
const { revdate, 'source-highlighter': sourceHighlighter } = assemblerAsciiDocAttributes
|
|
22
|
-
delete assemblerAsciiDocAttributes.revdate
|
|
23
|
-
delete assemblerAsciiDocAttributes['source-highlighter']
|
|
27
|
+
const baseAssemblyAttributes = assemblyConfig.attributes
|
|
24
28
|
const publishableFiles = contentCatalog.getFiles().filter((file) => file.out)
|
|
25
|
-
|
|
29
|
+
let siteRoot
|
|
30
|
+
const configMdc = assemblerConfig.file ? { file: { path: assemblerConfig.file } } : {}
|
|
31
|
+
return filterComponentVersions(contentCatalog.getComponents(), assemblerConfig.componentVersionFilter).reduce(
|
|
26
32
|
(accum, componentVersion) => {
|
|
27
|
-
const assemblyModel =
|
|
28
|
-
|
|
33
|
+
const assemblyModel = selectAssemblyProfile(componentVersion)
|
|
34
|
+
const { attributes: assemblyAttributes, logger, navigation, rootLevel } = assemblyModel
|
|
35
|
+
if (!navigation) return accum
|
|
36
|
+
const contextualLogger = logger ? { warn: logger.warn.bind(logger, configMdc) } : undefined
|
|
29
37
|
const { name: componentName, version, title } = componentVersion
|
|
30
38
|
const componentVersionAsciiDocConfig = getAsciiDocConfigWithAsciidoctorReducerExtension(componentVersion)
|
|
39
|
+
let sourceHighlighter
|
|
40
|
+
if ('source-highlighter' in assemblyAttributes) {
|
|
41
|
+
sourceHighlighter = assemblyAttributes['source-highlighter']
|
|
42
|
+
delete assemblyAttributes['source-highlighter']
|
|
43
|
+
}
|
|
44
|
+
const mergedAsciiDocAttributes = collateAsciiDocAttributes(
|
|
45
|
+
Object.assign({}, componentVersionAsciiDocConfig.attributes),
|
|
46
|
+
assemblyAttributes,
|
|
47
|
+
contextualLogger
|
|
48
|
+
)
|
|
31
49
|
const mergedAsciiDocConfig = Object.assign({}, componentVersionAsciiDocConfig, {
|
|
32
|
-
attributes:
|
|
33
|
-
})
|
|
34
|
-
const mergedAsciiDocAttributes = mergedAsciiDocConfig.attributes
|
|
35
|
-
const auxiliaryImages = new Set()
|
|
36
|
-
Object.entries(mergedAsciiDocAttributes).forEach(([name, val]) => {
|
|
37
|
-
const match = name.endsWith('-image') && val.startsWith('image:') && IMAGE_MACRO_RX.exec(val)
|
|
38
|
-
if (!(match && isResourceRef(match[1]))) return
|
|
39
|
-
// Q should we allow image to be resolved relative to component version?
|
|
40
|
-
const image = contentCatalog.resolveResource(match[1], undefined, 'image', ['image'])
|
|
41
|
-
if (!image?.out) return
|
|
42
|
-
mergedAsciiDocAttributes[name] = `image:${image.out.path}[${match[2]}]`
|
|
43
|
-
auxiliaryImages.add(image)
|
|
50
|
+
attributes: mergedAsciiDocAttributes,
|
|
44
51
|
})
|
|
52
|
+
assemblyModel.filetype = baseAssemblyAttributes['assembler-filetype']
|
|
53
|
+
const outDirname = (assemblyModel.outDirname = computeOut.call(contentCatalog, {
|
|
54
|
+
component: componentName,
|
|
55
|
+
version,
|
|
56
|
+
family: 'export',
|
|
57
|
+
relative: '.index.adoc',
|
|
58
|
+
}).dirname)
|
|
59
|
+
assemblyModel.pubRoot = outDirname ? '/' + outDirname : ''
|
|
60
|
+
assemblyModel.siteRoot =
|
|
61
|
+
siteRoot === undefined
|
|
62
|
+
? (siteRoot ??= ((val) => {
|
|
63
|
+
if (!val) return null
|
|
64
|
+
if (val.charAt(val.length - 1) === '/') val = val.slice(0, val.length - 1)
|
|
65
|
+
if (!val || val.charAt() === '/') return { path: val }
|
|
66
|
+
return { url: val, path: extractUrlPath(val) }
|
|
67
|
+
})(mergedAsciiDocAttributes['site-url'] || mergedAsciiDocAttributes['primary-site-url']))
|
|
68
|
+
: siteRoot
|
|
69
|
+
if (assemblyModel.filetype === 'html') {
|
|
70
|
+
let linkRefStyle = assemblyModel.linkReferenceStyle
|
|
71
|
+
if (linkRefStyle === 'absolute' && siteRoot?.url == null) linkRefStyle = 'root-relative'
|
|
72
|
+
if (linkRefStyle === 'root-relative' && siteRoot?.path == null) linkRefStyle = 'relative'
|
|
73
|
+
assemblyModel.linkReferenceStyle = linkRefStyle
|
|
74
|
+
} else if (!(assemblyModel.filetype === 'pdf' && assemblyModel.linkReferenceStyle === 'relative')) {
|
|
75
|
+
assemblyModel.linkReferenceStyle = 'absolute'
|
|
76
|
+
}
|
|
45
77
|
const rootEntry = { content: title }
|
|
46
|
-
const { navigation, rootLevel } = assemblyModel
|
|
47
78
|
let startPage =
|
|
48
79
|
'startPage' in componentVersion
|
|
49
80
|
? componentVersion.startPage
|
|
@@ -67,7 +98,6 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
|
|
|
67
98
|
})
|
|
68
99
|
}
|
|
69
100
|
const mutableAttributes = selectMutableAttributes(loadAsciiDoc, contentCatalog, startPage, mergedAsciiDocConfig)
|
|
70
|
-
delete mutableAttributes.doctype // Q: should this be in selectMutableAttributes?
|
|
71
101
|
prepareOutlines(navigation, rootEntry, rootLevel).reduce((any, outline) => {
|
|
72
102
|
const assemblyFile = produceAssemblyFile(
|
|
73
103
|
loadAsciiDoc,
|
|
@@ -80,17 +110,14 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
|
|
|
80
110
|
assemblyModel
|
|
81
111
|
)
|
|
82
112
|
if (!assemblyFile) return any
|
|
83
|
-
if
|
|
84
|
-
|
|
85
|
-
|
|
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']
|
|
86
118
|
}
|
|
87
|
-
accum.push(assemblyFile)
|
|
88
|
-
return true
|
|
119
|
+
return !!accum.push(assemblyFile)
|
|
89
120
|
}, false)
|
|
90
|
-
mergedAsciiDocAttributes.doctype = assemblyModel.doctype
|
|
91
|
-
sourceHighlighter
|
|
92
|
-
? (mergedAsciiDocAttributes['source-highlighter'] = sourceHighlighter)
|
|
93
|
-
: delete mergedAsciiDocAttributes['source-highlighter']
|
|
94
121
|
return accum
|
|
95
122
|
},
|
|
96
123
|
[]
|
|
@@ -116,10 +143,6 @@ function includedInNav (items, url) {
|
|
|
116
143
|
return items.find((it) => it.url === url || includedInNav(it.items || [], url))
|
|
117
144
|
}
|
|
118
145
|
|
|
119
|
-
function isResourceRef (target) {
|
|
120
|
-
return ~target.indexOf(':') && !(~target.indexOf('://') || (target.startsWith('data:') && ~target.indexOf(',')))
|
|
121
|
-
}
|
|
122
|
-
|
|
123
146
|
function prepareOutlines (navigation, rootEntry, rootLevel) {
|
|
124
147
|
const singleEntry = navigation.length === 1
|
|
125
148
|
const items = navigation.reduce((accum, it) => {
|
|
@@ -143,4 +166,37 @@ function prepareOutlines (navigation, rootEntry, rootLevel) {
|
|
|
143
166
|
return items
|
|
144
167
|
}
|
|
145
168
|
|
|
169
|
+
function collateAsciiDocAttributes (collated, additional, logger) {
|
|
170
|
+
Object.entries(additional).forEach(([name, val]) => {
|
|
171
|
+
if (val && val.constructor === String) {
|
|
172
|
+
let alias
|
|
173
|
+
val = val.replace(ATTR_REF_RX, (ref, refname) => {
|
|
174
|
+
if (ref.charAt() === '\\') return ref.substr(1)
|
|
175
|
+
const refval = collated[refname]
|
|
176
|
+
if (refval == null || refval === false) {
|
|
177
|
+
if (refname in collated && ref === val) {
|
|
178
|
+
alias = refval
|
|
179
|
+
} else if (collated['attribute-missing'] === 'warn') {
|
|
180
|
+
logger?.warn("Skipping reference to missing attribute '%s' in value of '%s' attribute", refname, name)
|
|
181
|
+
}
|
|
182
|
+
return ref
|
|
183
|
+
}
|
|
184
|
+
if (refval.constructor === String) return refval
|
|
185
|
+
if (ref !== val) return refval.toString()
|
|
186
|
+
alias = refval
|
|
187
|
+
return ref
|
|
188
|
+
})
|
|
189
|
+
if (alias !== undefined) val = alias
|
|
190
|
+
}
|
|
191
|
+
collated[name] = val
|
|
192
|
+
})
|
|
193
|
+
return collated
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function extractUrlPath (url) {
|
|
197
|
+
if (!url) return ''
|
|
198
|
+
const urlPath = new URL(url).pathname
|
|
199
|
+
return urlPath === '/' ? '' : urlPath
|
|
200
|
+
}
|
|
201
|
+
|
|
146
202
|
module.exports = produceAssemblyFiles
|
|
@@ -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
|
package/lib/util/compute-out.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
const { posix: path } = require('node:path')
|
|
4
4
|
|
|
5
5
|
function computeOut (src) {
|
|
6
|
-
const { component, version, module: module_, family, relative } = src
|
|
6
|
+
const { component, version, module: module_ = 'ROOT', family, relative } = src
|
|
7
7
|
const outRelative = family === 'page' ? relative.replace(/\.adoc$/, '.html') : relative
|
|
8
|
-
const { dir: dirname, base: basename
|
|
8
|
+
const { dir: dirname, base: basename } = path.parse(outRelative)
|
|
9
9
|
const componentVersion = this.getComponentVersion(component, version)
|
|
10
10
|
const versionSegment =
|
|
11
11
|
'activeVersionSegment' in componentVersion
|
|
@@ -48,7 +48,7 @@ function resolveActiveVersionSegment (component, version) {
|
|
|
48
48
|
this.removeFile((startPage = this.addFile({ src: startPageSrc })))
|
|
49
49
|
}
|
|
50
50
|
const outPathSegments = startPage.out.path.split('/')
|
|
51
|
-
for (const
|
|
51
|
+
for (const _ of startPage.out.moduleRootPath.split('/')) outPathSegments.pop()
|
|
52
52
|
if (startPageSrc.module !== 'ROOT') outPathSegments.pop()
|
|
53
53
|
if (startPageSrc.component !== 'ROOT') outPathSegments.shift()
|
|
54
54
|
return outPathSegments.length ? outPathSegments[0] : ''
|
|
@@ -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
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function parseResourceRef (ref, ctx = {}, family = undefined, contentCatalog = undefined) {
|
|
4
|
+
const atIdx = ref.indexOf('@')
|
|
5
|
+
let firstColonIdx = ref.indexOf(':')
|
|
6
|
+
let component, version, module_
|
|
7
|
+
if (~atIdx && (~firstColonIdx ? atIdx < firstColonIdx : true)) {
|
|
8
|
+
if ((version = ref.slice(0, atIdx)) === '_') version = ''
|
|
9
|
+
ref = ref.slice(atIdx + 1)
|
|
10
|
+
if (~firstColonIdx) firstColonIdx -= atIdx + 1
|
|
11
|
+
}
|
|
12
|
+
const addColons = ~firstColonIdx ? (~ref.indexOf(':', firstColonIdx + 1) ? '' : ':') : '::'
|
|
13
|
+
const segments = (addColons + ref).split(':')
|
|
14
|
+
if ((component = segments[0])) {
|
|
15
|
+
module_ = segments[1] || 'ROOT'
|
|
16
|
+
version ??= contentCatalog?.getComponent(component)?.latest.version
|
|
17
|
+
} else {
|
|
18
|
+
component = ctx.component
|
|
19
|
+
version ??= ctx.version
|
|
20
|
+
module_ = segments[1] || ctx.module || 'ROOT'
|
|
21
|
+
}
|
|
22
|
+
let relative = segments.length > 3 ? segments.slice(2).join(':') : segments[2]
|
|
23
|
+
const dollarIdx = relative.indexOf('$')
|
|
24
|
+
if (~dollarIdx) {
|
|
25
|
+
family = relative.slice(0, dollarIdx) || family
|
|
26
|
+
relative = relative.slice(dollarIdx + 1)
|
|
27
|
+
}
|
|
28
|
+
if (relative.charAt() === '.' && relative.charAt(1) === '/') {
|
|
29
|
+
const ctxRelative = ctx.relative
|
|
30
|
+
const topic = ctxRelative ? ctxRelative.slice(0, (ctxRelative.lastIndexOf('/') + 1 || 1) - 1) : undefined
|
|
31
|
+
relative = (topic ? topic + '/' : '') + relative.slice(2)
|
|
32
|
+
}
|
|
33
|
+
return { component, version, module: module_, family, relative }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = parseResourceRef
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const path = require('node:path/posix')
|
|
4
|
+
|
|
5
|
+
function resolveEmbedTarget (resource, outDirname, referenceStyle, escapeForInline) {
|
|
6
|
+
const target =
|
|
7
|
+
referenceStyle === 'output-relative' ? resource.out.path : path.relative(outDirname + '/', resource.out.path)
|
|
8
|
+
return escapeForInline ? target.replace(/_/g, '{underscore}') : target
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function resolveLinkTarget (resource, siteRoot, pubRoot, referenceStyle, escapeForInline, ensurePrefix = true) {
|
|
12
|
+
let target
|
|
13
|
+
if (resource.site?.url) {
|
|
14
|
+
target = ['', resource.pub.url]
|
|
15
|
+
} else {
|
|
16
|
+
switch (referenceStyle) {
|
|
17
|
+
case 'absolute':
|
|
18
|
+
target = ['', siteRoot.url + resource.pub.url]
|
|
19
|
+
break
|
|
20
|
+
case 'root-relative':
|
|
21
|
+
target = [ensurePrefix ? 'link:' : '', siteRoot.path + resource.pub.url]
|
|
22
|
+
break
|
|
23
|
+
default:
|
|
24
|
+
target = [ensurePrefix ? 'link:' : '', computeRelativeUrl(pubRoot + '/', resource.pub.url)]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (escapeForInline) target[1] = target[1].replace(/_/g, '{underscore}')
|
|
28
|
+
return target.join('')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function computeRelativeUrl (from, to) {
|
|
32
|
+
const rel = path.relative(from, to)
|
|
33
|
+
return to.charAt(to.length - 1) === '/' ? rel + '/' : rel
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = { resolveEmbedTarget, resolveLinkTarget }
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const createResourceKey = require('./create-resource-key')
|
|
4
|
+
const generateScopedId = require('./generate-scoped-id')
|
|
5
|
+
const parseResourceRef = require('./parse-resource-ref')
|
|
6
|
+
const { resolveEmbedTarget, resolveLinkTarget } = require('./resolver')
|
|
7
|
+
|
|
8
|
+
const { NAMED_ID_ATTR_RX } = require('./rx')
|
|
9
|
+
|
|
10
|
+
function rewriteXrefs (
|
|
11
|
+
line,
|
|
12
|
+
contentCatalog,
|
|
13
|
+
assemblyModel,
|
|
14
|
+
ctx,
|
|
15
|
+
escapeForInline,
|
|
16
|
+
pagesInOutline,
|
|
17
|
+
idSeparators,
|
|
18
|
+
idLeader,
|
|
19
|
+
doc,
|
|
20
|
+
sourceLocation
|
|
21
|
+
) {
|
|
22
|
+
return line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|[\s\S]*?[^\\])\]/gu, (_, target, text) =>
|
|
23
|
+
rewriteXref(
|
|
24
|
+
target,
|
|
25
|
+
text,
|
|
26
|
+
contentCatalog,
|
|
27
|
+
assemblyModel,
|
|
28
|
+
ctx,
|
|
29
|
+
escapeForInline,
|
|
30
|
+
pagesInOutline,
|
|
31
|
+
idSeparators,
|
|
32
|
+
idLeader,
|
|
33
|
+
doc,
|
|
34
|
+
sourceLocation
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function rewriteXref (
|
|
40
|
+
target,
|
|
41
|
+
text,
|
|
42
|
+
contentCatalog,
|
|
43
|
+
assemblyModel,
|
|
44
|
+
ctx,
|
|
45
|
+
escapeForInline,
|
|
46
|
+
pagesInOutline,
|
|
47
|
+
idSeparators,
|
|
48
|
+
idLeader,
|
|
49
|
+
doc,
|
|
50
|
+
sourceLocation
|
|
51
|
+
) {
|
|
52
|
+
const rawTarget = target
|
|
53
|
+
if (doc && ~target.indexOf('{')) target = doc.$sub_attributes(target, global.Opal.hash({ attribute_missing: 'skip' }))
|
|
54
|
+
let fragment, resourceRef
|
|
55
|
+
const hashIdx = target.indexOf('#')
|
|
56
|
+
if (~hashIdx) {
|
|
57
|
+
resourceRef = target.slice(0, hashIdx)
|
|
58
|
+
fragment = target.slice(hashIdx + 1)
|
|
59
|
+
} else if (target.endsWith('.adoc') || ~target.indexOf('$')) {
|
|
60
|
+
resourceRef = target
|
|
61
|
+
fragment = ''
|
|
62
|
+
} else {
|
|
63
|
+
fragment = target
|
|
64
|
+
}
|
|
65
|
+
if (!resourceRef) return `xref:${idLeader == null ? rawTarget : idLeader + fragment}[${text}]`
|
|
66
|
+
const resourceId = parseResourceRef(resourceRef, ctx, 'page', contentCatalog)
|
|
67
|
+
const family = resourceId.family
|
|
68
|
+
let resource
|
|
69
|
+
if (family !== 'page' || !(resource = pagesInOutline?.get(createResourceKey(resourceId)))) {
|
|
70
|
+
if ((resource = contentCatalog.getById(resourceId))?.pub) {
|
|
71
|
+
const { linkReferenceStyle, pubRoot, siteRoot } = assemblyModel
|
|
72
|
+
text ||= resource.asciidoc?.xreftext || rawTarget
|
|
73
|
+
if (siteRoot || linkReferenceStyle === 'relative') {
|
|
74
|
+
const hash = fragment && !(family === 'page' && fragment === resource.asciidoc.id) ? '#' + fragment : ''
|
|
75
|
+
return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, escapeForInline)}${hash}[${text}]`
|
|
76
|
+
}
|
|
77
|
+
if (doc) {
|
|
78
|
+
const msg = `Cannot create external ${family} reference in assembly because site URL is unknown: ${rawTarget}`
|
|
79
|
+
doc.logger.warn(doc.createLogMessage(msg, { source_location: sourceLocation }))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const linkAttrlist = text
|
|
83
|
+
? (~text.indexOf(',') ? `"${text}"` : text) + ',role=unresolved'
|
|
84
|
+
: (~target.indexOf(':') ? `${rawTarget},` : '') + 'role=unresolved'
|
|
85
|
+
return `link:${rawTarget}[${linkAttrlist}]`
|
|
86
|
+
}
|
|
87
|
+
if (fragment === resource.asciidoc.id) fragment = ''
|
|
88
|
+
if (
|
|
89
|
+
text &&
|
|
90
|
+
(assemblyModel.dropExplicitXrefText === 'always' ||
|
|
91
|
+
(assemblyModel.dropExplicitXrefText === 'if-redundant' && text === resource.title))
|
|
92
|
+
) {
|
|
93
|
+
text = ''
|
|
94
|
+
}
|
|
95
|
+
const componentVersionCtx = { name: ctx.component, version: ctx.version }
|
|
96
|
+
const refid = generateScopedId(
|
|
97
|
+
resource.src,
|
|
98
|
+
componentVersionCtx,
|
|
99
|
+
idSeparators,
|
|
100
|
+
assemblyModel.filetype,
|
|
101
|
+
text.length > 0
|
|
102
|
+
)
|
|
103
|
+
return `xref:${fragment ? refid + idSeparators.scope + fragment : refid}[${text}]`
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function rewriteImageAttr (val, contentCatalog, assemblyModel, ctx, assets) {
|
|
107
|
+
const match = val.startsWith('image:') && /^image::?(.+?)\[(.*?)\](@|)$/.exec(val)
|
|
108
|
+
if (!match) return
|
|
109
|
+
const newTarget = rewriteImageRef(match[1], contentCatalog, assemblyModel, ctx, assets)
|
|
110
|
+
return newTarget ? `image:${newTarget}[${match[2]}]${match[3]}` : val
|
|
111
|
+
}
|
|
112
|
+
|
|
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)
|
|
116
|
+
return newTarget ? `image:${newTarget}[${attrlist}]` : m
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
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' }))
|
|
122
|
+
const image = isResourceRef(target) && contentCatalog.resolveResource(target, ctx, 'image', ['image'])
|
|
123
|
+
if (!image?.out) return
|
|
124
|
+
let newTarget
|
|
125
|
+
const { filetype, embedReferenceStyle, linkReferenceStyle, outDirname, pubRoot, siteRoot } = assemblyModel
|
|
126
|
+
if (filetype !== 'html') {
|
|
127
|
+
newTarget = resolveEmbedTarget(image, outDirname, embedReferenceStyle, escapeForInline ?? false)
|
|
128
|
+
assets.add(image)
|
|
129
|
+
} else if (siteRoot || linkReferenceStyle === 'relative') {
|
|
130
|
+
newTarget = resolveLinkTarget(image, siteRoot, pubRoot, linkReferenceStyle, escapeForInline ?? false, false)
|
|
131
|
+
if (linkReferenceStyle === 'relative') assets.add(image)
|
|
132
|
+
}
|
|
133
|
+
if (!newTarget) return
|
|
134
|
+
return newTarget
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function rewriteStyleAttribute (block, lines, idx, idLeader, replacementStyle = '') {
|
|
138
|
+
let prevLine = lines[idx - 1]
|
|
139
|
+
const char0 = prevLine?.charAt()
|
|
140
|
+
if (char0) {
|
|
141
|
+
if (
|
|
142
|
+
(char0 === '.' && /^\.\.?[^ \t.]/.test(prevLine)) ||
|
|
143
|
+
(char0 === '[' &&
|
|
144
|
+
prevLine.charAt(1) === '[' &&
|
|
145
|
+
/^\[\[(?:|[\p{Alpha}_:][\p{Alpha}0-9_\-:.]*(?:, *.+)?)\]\]$/u.test(prevLine))
|
|
146
|
+
) {
|
|
147
|
+
return rewriteStyleAttribute(block, lines, idx - 1, idLeader, replacementStyle)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
let cellSpec
|
|
151
|
+
if (
|
|
152
|
+
char0 &&
|
|
153
|
+
(char0 === '[' || (block.getDocument().isNested() && (cellSpec = prevLine.match(/^([^[|]*)\| *(\[.+)/)))) &&
|
|
154
|
+
prevLine.charAt(prevLine.length - 1) === ']'
|
|
155
|
+
) {
|
|
156
|
+
if (cellSpec) {
|
|
157
|
+
prevLine = cellSpec[2]
|
|
158
|
+
cellSpec = cellSpec[1]
|
|
159
|
+
} else if (~prevLine.indexOf('id=')) {
|
|
160
|
+
prevLine = `[${prevLine.slice(1, -1).replace(NAMED_ID_ATTR_RX, '')}]`
|
|
161
|
+
}
|
|
162
|
+
let rawStyle
|
|
163
|
+
const commaIdx = prevLine.indexOf(',')
|
|
164
|
+
if (~commaIdx) {
|
|
165
|
+
rawStyle = prevLine.slice(1, commaIdx)
|
|
166
|
+
if (~rawStyle.indexOf('=')) rawStyle = undefined
|
|
167
|
+
} else if (!~prevLine.indexOf('=')) {
|
|
168
|
+
rawStyle = prevLine.slice(1, prevLine.length - 1)
|
|
169
|
+
}
|
|
170
|
+
if (rawStyle) {
|
|
171
|
+
if (~rawStyle.indexOf('#')) {
|
|
172
|
+
prevLine = prevLine.replace(/#[^.%,\]]+/, `#${idLeader}${block.getId()}`)
|
|
173
|
+
if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
|
|
174
|
+
} else {
|
|
175
|
+
prevLine = `[${
|
|
176
|
+
replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
|
|
177
|
+
}#${idLeader}${block.getId()}${prevLine.slice(rawStyle.length + 1)}`
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
prevLine = `[${replacementStyle}#${idLeader}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.slice(1)}`
|
|
181
|
+
}
|
|
182
|
+
if (cellSpec) prevLine = `${cellSpec}|${prevLine}`
|
|
183
|
+
lines[idx - 1] = prevLine
|
|
184
|
+
} else {
|
|
185
|
+
lines.splice(idx, 0, `[${replacementStyle}#${idLeader}${block.getId()}]`)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function isResourceRef (str) {
|
|
190
|
+
return !(~str.indexOf(':') && (~str.indexOf('://') || (str.startsWith('data:') && ~str.indexOf(','))))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
rewriteXref,
|
|
195
|
+
rewriteXrefs,
|
|
196
|
+
rewriteImageAttr,
|
|
197
|
+
rewriteImageRef,
|
|
198
|
+
rewriteInlineImages,
|
|
199
|
+
rewriteStyleAttribute,
|
|
200
|
+
}
|
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.20",
|
|
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,17 +27,21 @@
|
|
|
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",
|
|
31
|
+
"./parse-resource-ref": "./lib/util/parse-resource-ref.js",
|
|
30
32
|
"./produce-assembly-file": "./lib/produce-assembly-file.js",
|
|
31
33
|
"./produce-assembly-files": "./lib/produce-assembly-files.js",
|
|
32
|
-
"./select-mutable-attributes": "./lib/select-mutable-attributes.js"
|
|
34
|
+
"./select-mutable-attributes": "./lib/select-mutable-attributes.js",
|
|
35
|
+
"./package.json": "./package.json"
|
|
33
36
|
},
|
|
34
37
|
"imports": {
|
|
35
|
-
"#
|
|
38
|
+
"#asciidoctor-log-adapter": "./adapters/asciidoctor/jsonl-logger.rb",
|
|
36
39
|
"#unconvert-inline-asciidoc": "./lib/util/unconvert-inline-asciidoc.js"
|
|
37
40
|
},
|
|
38
41
|
"dependencies": {
|
|
39
42
|
"@asciidoctor/reducer": "~1.1",
|
|
40
43
|
"@antora/expand-path-helper": "~3.0",
|
|
44
|
+
"@antora/run-command-helper": "~1.0",
|
|
41
45
|
"braces": "~3.0",
|
|
42
46
|
"picomatch": "~3.0",
|
|
43
47
|
"js-yaml": "~4.1"
|
|
@@ -51,6 +55,7 @@
|
|
|
51
55
|
"node": ">=16.0.0"
|
|
52
56
|
},
|
|
53
57
|
"files": [
|
|
58
|
+
"adapters/",
|
|
54
59
|
"lib/"
|
|
55
60
|
],
|
|
56
61
|
"keywords": [
|