@antora/assembler 1.0.0-rc.5 → 1.0.0-rc.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/assemble-content.js +36 -83
- package/lib/compile-conversion-attributes.js +73 -0
- package/lib/configure.js +18 -15
- package/lib/constants.js +1 -0
- package/lib/filter-component-versions.js +1 -1
- package/lib/index.js +1 -1
- package/lib/load-config.js +6 -6
- package/lib/log-command.js +3 -3
- package/lib/produce-assembly-file.js +164 -126
- package/lib/produce-assembly-files.js +126 -144
- package/lib/util/collate-asciidoc-attributes.js +32 -0
- package/lib/util/deep-clone.js +18 -0
- package/lib/util/identify-mutable-attributes.js +25 -0
- package/lib/util/lazy-readable.js +12 -9
- package/lib/util/rewriter.js +36 -11
- package/lib/util/to-hash.js +7 -0
- package/package.json +4 -6
- package/lib/select-mutable-attributes.js +0 -27
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const collateAsciiDocAttributes = require('./util/collate-asciidoc-attributes')
|
|
3
4
|
const filterComponentVersions = require('./filter-component-versions')
|
|
4
5
|
const produceAssemblyFile = require('./produce-assembly-file')
|
|
5
|
-
const
|
|
6
|
+
const identifyMutableAttributes = require('./util/identify-mutable-attributes')
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
function produceAssemblyFiles (
|
|
9
|
+
loadAsciiDoc,
|
|
10
|
+
contentCatalog,
|
|
11
|
+
assemblerConfig,
|
|
12
|
+
selectAssemblerProfile,
|
|
13
|
+
selectComponentVersions
|
|
14
|
+
) {
|
|
10
15
|
const { assembly: assemblyConfig } = assemblerConfig
|
|
11
|
-
|
|
16
|
+
selectAssemblerProfile ??= (componentVersion) => ({
|
|
12
17
|
attributes: assemblyConfig.attributes,
|
|
13
18
|
doctype: assemblyConfig.doctype,
|
|
14
19
|
insertStartPage: assemblyConfig.insertStartPage,
|
|
@@ -24,129 +29,134 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, se
|
|
|
24
29
|
})
|
|
25
30
|
const baseAssemblyAttributes = assemblyConfig.attributes
|
|
26
31
|
const publishableFiles = contentCatalog.getFiles().filter((file) => file.out && file.pub)
|
|
32
|
+
const reducerExtension = require('@asciidoctor/reducer') // require lazily to wait for Opal to be defined
|
|
27
33
|
let siteRoot
|
|
28
34
|
const configMdc = assemblerConfig.file ? { file: { path: assemblerConfig.file } } : {}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
selectComponentVersions ??= filterComponentVersions.bind(null, assemblerConfig.componentVersionFilter)
|
|
36
|
+
return selectComponentVersions(contentCatalog.getComponents()).reduce((accum, componentVersion) => {
|
|
37
|
+
const assemblyModel = selectAssemblerProfile(componentVersion)
|
|
38
|
+
const { attributes: assemblyAttributes, logger, navigation, rootLevel } = assemblyModel
|
|
39
|
+
if (!navigation) return accum
|
|
40
|
+
const contextualLogger = logger ? { warn: logger.warn.bind(logger, configMdc) } : undefined
|
|
41
|
+
const { name: componentName, version, title } = componentVersion
|
|
42
|
+
const asciidocConfig = getAsciiDocConfigWithAsciidoctorReducerExtension(componentVersion, reducerExtension)
|
|
43
|
+
let sourceHighlighter
|
|
44
|
+
if ('source-highlighter' in assemblyAttributes) {
|
|
45
|
+
sourceHighlighter = assemblyAttributes['source-highlighter']
|
|
46
|
+
delete assemblyAttributes['source-highlighter']
|
|
47
|
+
}
|
|
48
|
+
const mergedAsciiDocAttributes = collateAsciiDocAttributes(
|
|
49
|
+
Object.assign({}, asciidocConfig.attributes),
|
|
50
|
+
assemblyAttributes,
|
|
51
|
+
contextualLogger
|
|
52
|
+
)
|
|
53
|
+
const mergedAsciiDocConfig = Object.assign({}, asciidocConfig, {
|
|
54
|
+
attributes: mergedAsciiDocAttributes,
|
|
55
|
+
})
|
|
56
|
+
assemblyModel.filetype = baseAssemblyAttributes['assembler-filetype']
|
|
57
|
+
const outDirname = (assemblyModel.outDirname = contentCatalog.createFile({
|
|
58
|
+
src: {
|
|
59
|
+
component: componentName,
|
|
60
|
+
version,
|
|
61
|
+
componentVersion,
|
|
62
|
+
module: 'ROOT',
|
|
63
|
+
family: 'export',
|
|
64
|
+
relative: 'index.adoc',
|
|
65
|
+
},
|
|
66
|
+
}).out.dirname)
|
|
67
|
+
assemblyModel.pubRoot = outDirname ? '/' + outDirname : ''
|
|
68
|
+
assemblyModel.siteRoot =
|
|
69
|
+
siteRoot === undefined
|
|
70
|
+
? (siteRoot ??= ((val) => {
|
|
71
|
+
if (!val) return null
|
|
72
|
+
if (val.charAt(val.length - 1) === '/') val = val.substring(0, val.length - 1)
|
|
73
|
+
if (!val || val.charAt() === '/') return { path: val }
|
|
74
|
+
return { url: val, path: extractUrlPath(val) }
|
|
75
|
+
})(mergedAsciiDocAttributes['site-url'] || mergedAsciiDocAttributes['primary-site-url']))
|
|
76
|
+
: siteRoot
|
|
77
|
+
if (assemblyModel.filetype === 'html') {
|
|
78
|
+
let linkRefStyle = assemblyModel.linkReferenceStyle
|
|
79
|
+
if (linkRefStyle === 'absolute' && siteRoot?.url == null) linkRefStyle = 'root-relative'
|
|
80
|
+
if (linkRefStyle === 'root-relative' && siteRoot?.path == null) linkRefStyle = 'relative'
|
|
81
|
+
assemblyModel.linkReferenceStyle = linkRefStyle
|
|
82
|
+
} else if (!(assemblyModel.filetype === 'pdf' && assemblyModel.linkReferenceStyle === 'relative')) {
|
|
83
|
+
assemblyModel.linkReferenceStyle = 'absolute'
|
|
84
|
+
}
|
|
85
|
+
const outlineRoot = { content: title }
|
|
86
|
+
const startPage = resolveStartPage(contentCatalog, componentVersion, assemblyModel.insertStartPage, outlineRoot)
|
|
87
|
+
const mutableAttributes = identifyMutableAttributes(loadAsciiDoc, contentCatalog, startPage, mergedAsciiDocConfig)
|
|
88
|
+
prepareOutlines(navigation, outlineRoot, rootLevel).reduce((any, outline) => {
|
|
89
|
+
const assemblyFile = produceAssemblyFile(
|
|
90
|
+
loadAsciiDoc,
|
|
91
|
+
contentCatalog,
|
|
92
|
+
componentVersion,
|
|
93
|
+
outline,
|
|
94
|
+
publishableFiles,
|
|
95
|
+
mergedAsciiDocConfig,
|
|
96
|
+
mutableAttributes,
|
|
97
|
+
assemblyModel
|
|
46
98
|
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
component: componentName,
|
|
54
|
-
version,
|
|
55
|
-
componentVersion,
|
|
56
|
-
module: 'ROOT',
|
|
57
|
-
family: 'export',
|
|
58
|
-
relative: 'index.adoc',
|
|
59
|
-
},
|
|
60
|
-
}).out.dirname)
|
|
61
|
-
assemblyModel.pubRoot = outDirname ? '/' + outDirname : ''
|
|
62
|
-
assemblyModel.siteRoot =
|
|
63
|
-
siteRoot === undefined
|
|
64
|
-
? (siteRoot ??= ((val) => {
|
|
65
|
-
if (!val) return null
|
|
66
|
-
if (val.charAt(val.length - 1) === '/') val = val.substring(0, val.length - 1)
|
|
67
|
-
if (!val || val.charAt() === '/') return { path: val }
|
|
68
|
-
return { url: val, path: extractUrlPath(val) }
|
|
69
|
-
})(mergedAsciiDocAttributes['site-url'] || mergedAsciiDocAttributes['primary-site-url']))
|
|
70
|
-
: siteRoot
|
|
71
|
-
if (assemblyModel.filetype === 'html') {
|
|
72
|
-
let linkRefStyle = assemblyModel.linkReferenceStyle
|
|
73
|
-
if (linkRefStyle === 'absolute' && siteRoot?.url == null) linkRefStyle = 'root-relative'
|
|
74
|
-
if (linkRefStyle === 'root-relative' && siteRoot?.path == null) linkRefStyle = 'relative'
|
|
75
|
-
assemblyModel.linkReferenceStyle = linkRefStyle
|
|
76
|
-
} else if (!(assemblyModel.filetype === 'pdf' && assemblyModel.linkReferenceStyle === 'relative')) {
|
|
77
|
-
assemblyModel.linkReferenceStyle = 'absolute'
|
|
99
|
+
if (!assemblyFile) return any
|
|
100
|
+
// NOTE restore source highlighter for conversion if defined in Assembler config
|
|
101
|
+
if (sourceHighlighter !== undefined) {
|
|
102
|
+
assemblyFile.asciidoc.attributes['source-highlighter'] = sourceHighlighter
|
|
103
|
+
} else if (assemblyModel.filetype !== 'html') {
|
|
104
|
+
delete assemblyFile.asciidoc.attributes['source-highlighter']
|
|
78
105
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
: contentCatalog.resolvePage('index.adoc', { component: componentName, version })
|
|
84
|
-
if (startPage && startPage.src.component === componentName && startPage.src.version === version) {
|
|
85
|
-
if (assemblyModel.insertStartPage) {
|
|
86
|
-
const navtitle = startPage.asciidoc?.navtitle || rootEntry.content
|
|
87
|
-
Object.assign(rootEntry, { navtitle, url: startPage.pub.url, urlType: 'internal', roles: ['page'] })
|
|
88
|
-
}
|
|
89
|
-
} else {
|
|
90
|
-
// Q: should we always use a reference page as startPage for computing mutableAttributes?
|
|
91
|
-
startPage = contentCatalog.createFile({
|
|
92
|
-
src: {
|
|
93
|
-
component: componentVersion.name,
|
|
94
|
-
version: componentVersion.version,
|
|
95
|
-
componentVersion,
|
|
96
|
-
module: 'ROOT',
|
|
97
|
-
family: 'page',
|
|
98
|
-
relative: 'index.adoc',
|
|
99
|
-
origin: (componentVersion.origins || [])[0],
|
|
100
|
-
},
|
|
101
|
-
})
|
|
102
|
-
startPage.path = ['modules', startPage.src.module, startPage.src.family + 's', startPage.src.relative].join('/')
|
|
103
|
-
}
|
|
104
|
-
const mutableAttributes = selectMutableAttributes(loadAsciiDoc, contentCatalog, startPage, mergedAsciiDocConfig)
|
|
105
|
-
prepareOutlines(navigation, rootEntry, rootLevel).reduce((any, outline) => {
|
|
106
|
-
const assemblyFile = produceAssemblyFile(
|
|
107
|
-
loadAsciiDoc,
|
|
108
|
-
contentCatalog,
|
|
109
|
-
componentVersion,
|
|
110
|
-
outline,
|
|
111
|
-
publishableFiles,
|
|
112
|
-
mergedAsciiDocConfig,
|
|
113
|
-
mutableAttributes,
|
|
114
|
-
assemblyModel
|
|
115
|
-
)
|
|
116
|
-
if (!assemblyFile) return any
|
|
117
|
-
// NOTE restore source highlighter for conversion if defined in Assembler config
|
|
118
|
-
if (sourceHighlighter !== undefined) {
|
|
119
|
-
assemblyFile.asciidoc.attributes['source-highlighter'] = sourceHighlighter
|
|
120
|
-
} else if (assemblyModel.filetype !== 'html') {
|
|
121
|
-
delete assemblyFile.asciidoc.attributes['source-highlighter']
|
|
122
|
-
}
|
|
123
|
-
return !!accum.push(assemblyFile)
|
|
124
|
-
}, false)
|
|
125
|
-
return accum
|
|
126
|
-
},
|
|
127
|
-
[]
|
|
128
|
-
)
|
|
106
|
+
return !!accum.push(assemblyFile)
|
|
107
|
+
}, false)
|
|
108
|
+
return accum
|
|
109
|
+
}, [])
|
|
129
110
|
}
|
|
130
111
|
|
|
131
|
-
function getAsciiDocConfigWithAsciidoctorReducerExtension (componentVersion) {
|
|
132
|
-
const asciidoctorReducerExtension = require('@asciidoctor/reducer') // NOTE: must be required lazily
|
|
112
|
+
function getAsciiDocConfigWithAsciidoctorReducerExtension (componentVersion, reducerExtension) {
|
|
133
113
|
const asciidocConfig = componentVersion.asciidoc
|
|
134
|
-
const extensions = [
|
|
114
|
+
const extensions = [reducerExtension]
|
|
135
115
|
const configuredExtensions = asciidocConfig.extensions || []
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
116
|
+
return configuredExtensions.length
|
|
117
|
+
? Object.assign({}, asciidocConfig, {
|
|
118
|
+
extensions: configuredExtensions.reduce((accum, candidate) => {
|
|
119
|
+
if (candidate !== reducerExtension) accum.push(candidate)
|
|
120
|
+
return accum
|
|
121
|
+
}, extensions),
|
|
122
|
+
sourcemap: true,
|
|
123
|
+
})
|
|
124
|
+
: Object.assign({}, asciidocConfig, { extensions, sourcemap: true })
|
|
144
125
|
}
|
|
145
126
|
|
|
146
127
|
function includedInNav (items, url) {
|
|
147
128
|
return items.find((it) => it.url === url || includedInNav(it.items || [], url))
|
|
148
129
|
}
|
|
149
130
|
|
|
131
|
+
function resolveStartPage (contentCatalog, componentVersion, insertStartPage, outlineRoot) {
|
|
132
|
+
const { name: componentName, version } = componentVersion
|
|
133
|
+
let startPage =
|
|
134
|
+
'startPage' in componentVersion
|
|
135
|
+
? componentVersion.startPage
|
|
136
|
+
: contentCatalog.resolvePage('index.adoc', { component: componentName, version })
|
|
137
|
+
if (startPage && startPage.src.component === componentName && startPage.src.version === version) {
|
|
138
|
+
if (insertStartPage) {
|
|
139
|
+
const navtitle = startPage.asciidoc?.navtitle || outlineRoot.content
|
|
140
|
+
Object.assign(outlineRoot, { navtitle, url: startPage.pub.url, urlType: 'internal', roles: ['page'] })
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
startPage = contentCatalog.createFile({
|
|
144
|
+
src: {
|
|
145
|
+
component: componentName,
|
|
146
|
+
version,
|
|
147
|
+
componentVersion,
|
|
148
|
+
module: 'ROOT',
|
|
149
|
+
family: 'page',
|
|
150
|
+
relative: 'index.adoc',
|
|
151
|
+
origin: componentVersion.origins?.values().next().value,
|
|
152
|
+
},
|
|
153
|
+
})
|
|
154
|
+
// FIXME remove once upgrading to Antora 3.2.0
|
|
155
|
+
startPage.path = ['modules', startPage.src.module, startPage.src.family + 's', startPage.src.relative].join('/')
|
|
156
|
+
}
|
|
157
|
+
return startPage
|
|
158
|
+
}
|
|
159
|
+
|
|
150
160
|
function prepareOutlines (navigation, rootEntry, rootLevel) {
|
|
151
161
|
const singleEntry = navigation.length === 1
|
|
152
162
|
const items = navigation.reduce((accum, it) => {
|
|
@@ -170,37 +180,9 @@ function prepareOutlines (navigation, rootEntry, rootLevel) {
|
|
|
170
180
|
return items
|
|
171
181
|
}
|
|
172
182
|
|
|
173
|
-
function collateAsciiDocAttributes (collated, additional, logger) {
|
|
174
|
-
Object.entries(additional).forEach(([name, val]) => {
|
|
175
|
-
if (val && val.constructor === String) {
|
|
176
|
-
let alias
|
|
177
|
-
val = val.replace(ATTR_REF_RX, (ref, refname) => {
|
|
178
|
-
if (ref.charAt() === '\\') return ref.substring(1)
|
|
179
|
-
const refval = collated[refname]
|
|
180
|
-
if (refval == null || refval === false) {
|
|
181
|
-
if (refname in collated && ref === val) {
|
|
182
|
-
alias = refval
|
|
183
|
-
} else if (collated['attribute-missing'] === 'warn') {
|
|
184
|
-
logger?.warn("Skipping reference to missing attribute '%s' in value of '%s' attribute", refname, name)
|
|
185
|
-
}
|
|
186
|
-
return ref
|
|
187
|
-
}
|
|
188
|
-
if (refval.constructor === String) return refval
|
|
189
|
-
if (ref !== val) return refval.toString()
|
|
190
|
-
alias = refval
|
|
191
|
-
return ref
|
|
192
|
-
})
|
|
193
|
-
if (alias !== undefined) val = alias
|
|
194
|
-
}
|
|
195
|
-
collated[name] = val
|
|
196
|
-
})
|
|
197
|
-
return collated
|
|
198
|
-
}
|
|
199
|
-
|
|
200
183
|
function extractUrlPath (url) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return urlPath === '/' ? '' : urlPath
|
|
184
|
+
const parsedUrl = url ? URL.parse(url) : null
|
|
185
|
+
return parsedUrl == null || parsedUrl.pathname === '/' ? '' : parsedUrl.pathname
|
|
204
186
|
}
|
|
205
187
|
|
|
206
188
|
module.exports = produceAssemblyFiles
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const ATTR_REF_RX = /\\?\{(\w[\w-]*)\}/g
|
|
4
|
+
|
|
5
|
+
function collateAsciiDocAttributes (collated, additional = {}, logger = undefined) {
|
|
6
|
+
Object.entries(additional).forEach(([name, val]) => {
|
|
7
|
+
if (val && val.constructor === String) {
|
|
8
|
+
let alias
|
|
9
|
+
val = val.replace(ATTR_REF_RX, (ref, refname) => {
|
|
10
|
+
if (ref.charAt() === '\\') return ref.substring(1)
|
|
11
|
+
const refval = collated[refname]
|
|
12
|
+
if (refval == null || refval === false) {
|
|
13
|
+
if (refname in collated && ref === val) {
|
|
14
|
+
alias = refval
|
|
15
|
+
} else if (collated['attribute-missing'] === 'warn') {
|
|
16
|
+
logger?.warn("Skipping reference to missing attribute '%s' in value of '%s' attribute", refname, name)
|
|
17
|
+
}
|
|
18
|
+
return ref
|
|
19
|
+
}
|
|
20
|
+
if (refval.constructor === String) return refval
|
|
21
|
+
if (ref !== val) return refval.toString()
|
|
22
|
+
alias = refval
|
|
23
|
+
return ref
|
|
24
|
+
})
|
|
25
|
+
if (alias !== undefined) val = alias
|
|
26
|
+
}
|
|
27
|
+
collated[name] = val
|
|
28
|
+
})
|
|
29
|
+
return collated
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = collateAsciiDocAttributes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function deepClone (o) {
|
|
4
|
+
switch (o.constructor) {
|
|
5
|
+
case Object:
|
|
6
|
+
return Object.keys(o).reduce((accum, k) => {
|
|
7
|
+
const v = o[k]
|
|
8
|
+
accum[k] = !v || typeof v !== 'object' ? v : deepClone(v)
|
|
9
|
+
return accum
|
|
10
|
+
}, {})
|
|
11
|
+
case Array:
|
|
12
|
+
return o.map((it) => (!it || typeof it !== 'object' ? it : deepClone(it)))
|
|
13
|
+
default:
|
|
14
|
+
return o
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = deepClone
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Identifies AsciiDoc attributes that can be modified in the body of the page.
|
|
5
|
+
*
|
|
6
|
+
* This determination is made by loading a reference page with empty contents, retrieving the list of document attribute
|
|
7
|
+
* names, then removing any attributes that were passed via the API with some exceptions. The reason for this function
|
|
8
|
+
* is to be more prudent with which attributes to manage at the boundaries of an assembled page.
|
|
9
|
+
*/
|
|
10
|
+
function identifyMutableAttributes (loadAsciiDoc, contentCatalog, referencePage, asciidocConfig) {
|
|
11
|
+
const doc = loadAsciiDoc(
|
|
12
|
+
new referencePage.constructor(
|
|
13
|
+
Object.assign({}, referencePage, { contents: Buffer.alloc(0), mediaType: 'text/asciidoc' })
|
|
14
|
+
),
|
|
15
|
+
contentCatalog,
|
|
16
|
+
asciidocConfig
|
|
17
|
+
)
|
|
18
|
+
const immutableAttributeNames = doc.attribute_overrides.$keys()['$+'](['doctype'])
|
|
19
|
+
return Object.entries(doc.getAttributes()).reduce((accum, [name, val]) => {
|
|
20
|
+
if (!immutableAttributeNames.includes(name)) accum[name] = val
|
|
21
|
+
return accum
|
|
22
|
+
}, {})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = identifyMutableAttributes
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const fs = require('node:fs')
|
|
3
4
|
const { PassThrough } = require('node:stream')
|
|
4
5
|
|
|
5
6
|
// adapted from https://github.com/jpommerening/node-lazystream/blob/master/lib/lazystream.js | license: MIT
|
|
6
7
|
class LazyReadable extends PassThrough {
|
|
7
|
-
constructor (
|
|
8
|
-
super(
|
|
8
|
+
constructor (fnOrPath) {
|
|
9
|
+
super()
|
|
10
|
+
const fn = typeof fnOrPath === 'function' ? fnOrPath : fs.createReadStream.bind(null, fnOrPath)
|
|
9
11
|
this._read = function () {
|
|
10
12
|
delete this._read // restores original method
|
|
11
|
-
fn.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const readable = fn().on('error', (err) => {
|
|
14
|
+
let msg = 'Failed to create stream'
|
|
15
|
+
if (err.syscall === 'open') msg += ` for path '${err.path}'`
|
|
16
|
+
this.emit('error', new Error(msg, { cause: err }))
|
|
17
|
+
})
|
|
18
|
+
readable.pipe(this)
|
|
19
|
+
this.fd = readable.fd
|
|
20
|
+
this.path = readable.path
|
|
18
21
|
return this._read.apply(this, arguments)
|
|
19
22
|
}
|
|
20
23
|
this.emit('readable')
|
package/lib/util/rewriter.js
CHANGED
|
@@ -4,6 +4,7 @@ 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
|
+
const toHash = require('./to-hash')
|
|
7
8
|
|
|
8
9
|
const { NAMED_ID_ATTR_RX } = require('./rx')
|
|
9
10
|
|
|
@@ -19,7 +20,7 @@ function rewriteXrefs (
|
|
|
19
20
|
doc,
|
|
20
21
|
sourceLocation
|
|
21
22
|
) {
|
|
22
|
-
return line.replace(/(?<![\\+])xref:((
|
|
23
|
+
return line.replace(/(?<![\\+])xref:((?:\.\/|:)?[\p{Alpha}0-9_/.{#].*?)\[(|[\s\S]*?[^\\])\]/gu, (_, target, text) =>
|
|
23
24
|
rewriteXref(
|
|
24
25
|
target,
|
|
25
26
|
text,
|
|
@@ -50,7 +51,9 @@ function rewriteXref (
|
|
|
50
51
|
sourceLocation
|
|
51
52
|
) {
|
|
52
53
|
const rawTarget = target
|
|
53
|
-
if (
|
|
54
|
+
if (sourceLocation && ~target.indexOf('{')) {
|
|
55
|
+
target = doc.$sub_attributes(target, toHash({ attribute_missing: 'skip' }))
|
|
56
|
+
}
|
|
54
57
|
let fragment, resourceRef
|
|
55
58
|
const hashIdx = target.indexOf('#')
|
|
56
59
|
if (~hashIdx) {
|
|
@@ -72,22 +75,24 @@ function rewriteXref (
|
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
77
|
if (family !== 'page' || !(resource = pagesInOutline?.get(createResourceKey(resourceId)))) {
|
|
78
|
+
let defaultText
|
|
75
79
|
if ((resource = contentCatalog.getById(resourceId))?.pub) {
|
|
76
80
|
const { linkReferenceStyle, pubRoot, siteRoot } = assemblyModel
|
|
77
|
-
|
|
81
|
+
defaultText = resource.asciidoc?.xreftext || rawTarget
|
|
78
82
|
if (siteRoot || linkReferenceStyle === 'relative') {
|
|
79
83
|
const hash = fragment && !(family === 'page' && fragment === resource.asciidoc.id) ? '#' + fragment : ''
|
|
80
|
-
|
|
84
|
+
const linkTarget = resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, escapeForInline)
|
|
85
|
+
return `${linkTarget}${hash}[${updateAttrlist(doc, text, defaultText)}]`
|
|
81
86
|
}
|
|
82
|
-
if (
|
|
87
|
+
if (sourceLocation) {
|
|
83
88
|
const msg = `Cannot create external ${family} reference in assembly because site URL is unknown: ${rawTarget}`
|
|
84
89
|
doc.logger.warn(doc.createLogMessage(msg, { source_location: sourceLocation }))
|
|
85
90
|
}
|
|
91
|
+
} else if (~target.indexOf(':')) {
|
|
92
|
+
defaultText = rawTarget // use explicit text so AsciiDoc processor displays it correctly
|
|
86
93
|
}
|
|
87
|
-
const linkAttrlist = text
|
|
88
|
-
|
|
89
|
-
: (~target.indexOf(':') ? `${rawTarget},` : '') + 'role=unresolved'
|
|
90
|
-
return `link:${rawTarget}[${linkAttrlist}]`
|
|
94
|
+
const linkAttrlist = updateAttrlist(doc, text, defaultText, 'unresolved')
|
|
95
|
+
return `link:${rawTarget.charAt() === ':' ? resourceId.module + rawTarget : rawTarget}[${linkAttrlist}]`
|
|
91
96
|
}
|
|
92
97
|
if (fragment === resource.asciidoc.id) fragment = ''
|
|
93
98
|
if (
|
|
@@ -123,7 +128,7 @@ function rewriteInlineImages (line, contentCatalog, assemblyModel, ctx, assets,
|
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
function rewriteImageRef (target, contentCatalog, assemblyModel, ctx, assets, escapeForInline, doc) {
|
|
126
|
-
if (doc && ~target.indexOf('{')) target = doc.$sub_attributes(target,
|
|
131
|
+
if (doc && ~target.indexOf('{')) target = doc.$sub_attributes(target, toHash({ attribute_missing: 'skip' }))
|
|
127
132
|
const image = isResourceRef(target) && contentCatalog.resolveResource(target, ctx, 'image', ['image'])
|
|
128
133
|
if (!image?.out) return
|
|
129
134
|
let newTarget
|
|
@@ -135,7 +140,6 @@ function rewriteImageRef (target, contentCatalog, assemblyModel, ctx, assets, es
|
|
|
135
140
|
newTarget = resolveLinkTarget(image, siteRoot, pubRoot, linkReferenceStyle, escapeForInline ?? false, false)
|
|
136
141
|
if (linkReferenceStyle === 'relative') assets.add(image)
|
|
137
142
|
}
|
|
138
|
-
if (!newTarget) return
|
|
139
143
|
return newTarget
|
|
140
144
|
}
|
|
141
145
|
|
|
@@ -196,6 +200,27 @@ function isResourceRef (str) {
|
|
|
196
200
|
return !(~str.indexOf(':') && (~str.indexOf('://') || (str.startsWith('data:') && ~str.indexOf(','))))
|
|
197
201
|
}
|
|
198
202
|
|
|
203
|
+
function updateAttrlist (doc, attrlist, defaultText, role) {
|
|
204
|
+
const attrs = { named: {}, pos: [] }
|
|
205
|
+
if (attrlist) {
|
|
206
|
+
const normalize = ~attrlist.indexOf('\n')
|
|
207
|
+
if (normalize) attrlist = attrlist.replace(/\n/g, '\x1e')
|
|
208
|
+
doc
|
|
209
|
+
.$parse_attributes(attrlist)
|
|
210
|
+
.$entries()
|
|
211
|
+
.forEach(([k, v]) => {
|
|
212
|
+
v = v['$nil?']() ? '' : normalize ? v.replace(/\x1e/g, '\n') : v
|
|
213
|
+
typeof k === 'number' ? attrs.pos.push(v) : (attrs.named[k] = v)
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
if (defaultText) attrs.pos[0] ||= defaultText
|
|
217
|
+
if (role) attrs.named.role = (attrs.named.role ? attrs.named.role + ' ' : '') + role
|
|
218
|
+
const entries = []
|
|
219
|
+
for (const v of attrs.pos) entries.push(~v.indexOf(',') || ~v.indexOf('=') ? `"${v}"` : v)
|
|
220
|
+
for (const [k, v] of Object.entries(attrs.named)) entries.push(`${k}=` + (~v.indexOf(',') ? `"${v}"` : v))
|
|
221
|
+
return entries.join(',')
|
|
222
|
+
}
|
|
223
|
+
|
|
199
224
|
module.exports = {
|
|
200
225
|
rewriteXref,
|
|
201
226
|
rewriteXrefs,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antora/assembler",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.7",
|
|
4
4
|
"description": "A JavaScript library that merges AsciiDoc content from multiple pages in an Antora site into assembly files and delegates to an exporter to convert those files to another format, such as PDF.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"author": "OpenDevise Inc. (https://opendevise.com)",
|
|
@@ -27,20 +27,18 @@
|
|
|
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
|
-
"./produce-assembly-file": "./lib/produce-assembly-file.js",
|
|
32
|
-
"./produce-assembly-files": "./lib/produce-assembly-files.js",
|
|
33
|
-
"./select-mutable-attributes": "./lib/select-mutable-attributes.js",
|
|
34
30
|
"./package.json": "./package.json"
|
|
35
31
|
},
|
|
36
32
|
"imports": {
|
|
37
33
|
"#asciidoctor-log-adapter": "./adapters/asciidoctor/jsonl-logger.rb",
|
|
34
|
+
"#identify-mutable-attributes": "./lib/util/identify-mutable-attributes.js",
|
|
35
|
+
"#produce-assembly-files": "./lib/produce-assembly-files.js",
|
|
38
36
|
"#unconvert-inline-asciidoc": "./lib/util/unconvert-inline-asciidoc.js"
|
|
39
37
|
},
|
|
40
38
|
"dependencies": {
|
|
41
|
-
"@asciidoctor/reducer": "~1.1",
|
|
42
39
|
"@antora/expand-path-helper": "~3.0",
|
|
43
40
|
"@antora/run-command-helper": "~1.1",
|
|
41
|
+
"@asciidoctor/reducer": "~1.1",
|
|
44
42
|
"js-yaml": "~5.2"
|
|
45
43
|
},
|
|
46
44
|
"devDependencies": {
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
function selectMutableAttributes (loadAsciiDoc, contentCatalog, referencePage, asciidocConfig) {
|
|
4
|
-
const doc = loadAsciiDoc(
|
|
5
|
-
new referencePage.constructor(
|
|
6
|
-
Object.assign({}, referencePage, { contents: Buffer.alloc(0), mediaType: 'text/asciidoc' })
|
|
7
|
-
),
|
|
8
|
-
contentCatalog,
|
|
9
|
-
asciidocConfig
|
|
10
|
-
)
|
|
11
|
-
const additionalMutableNames = [
|
|
12
|
-
'page-component-name',
|
|
13
|
-
'page-component-version',
|
|
14
|
-
'page-version',
|
|
15
|
-
'page-component-display-version',
|
|
16
|
-
'page-component-title',
|
|
17
|
-
]
|
|
18
|
-
// we could consider using an Asciidoctor extension here to grab attributes passed via the API instead
|
|
19
|
-
const immutableAttributeNames = doc.attribute_overrides.$keys()['$-'](additionalMutableNames)
|
|
20
|
-
immutableAttributeNames.push('doctype')
|
|
21
|
-
return Object.entries(doc.getAttributes()).reduce((accum, [name, val]) => {
|
|
22
|
-
if (!immutableAttributeNames.includes(name)) accum[name] = val
|
|
23
|
-
return accum
|
|
24
|
-
}, {})
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
module.exports = selectMutableAttributes
|