@antora/assembler 1.0.0-rc.1 → 1.0.0-rc.3
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 +20 -18
- package/lib/load-config.js +13 -8
- package/lib/produce-assembly-file.js +18 -22
- package/lib/util/lazy-readable.js +7 -1
- package/lib/util/matcher.js +1 -1
- package/package.json +2 -2
package/lib/assemble-content.js
CHANGED
|
@@ -11,7 +11,7 @@ const produceAssemblyFiles = require('./produce-assembly-files')
|
|
|
11
11
|
const PromiseQueue = require('./util/promise-queue')
|
|
12
12
|
const runCommand = require('@antora/run-command-helper')
|
|
13
13
|
|
|
14
|
-
const invariably = { new: () => ({}), void: () => undefined }
|
|
14
|
+
const invariably = { true: () => true, false: () => false, new: () => ({}), void: () => undefined }
|
|
15
15
|
const PACKAGE_NAME = require('../package.json').name
|
|
16
16
|
const NEWLINE_RX = /(?:\r?\n)+/
|
|
17
17
|
|
|
@@ -65,35 +65,36 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
65
65
|
generateSelectAssemblyProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog)
|
|
66
66
|
)
|
|
67
67
|
if (!(assemblyFiles.length && typeof convert === 'function')) return assemblyFiles
|
|
68
|
-
|
|
69
|
-
buildConfig.command = await getDefaultCommand(buildConfig.cwd)
|
|
70
|
-
}
|
|
68
|
+
buildConfig.command ??= typeof getDefaultCommand === 'function' ? await getDefaultCommand(buildConfig.cwd) : null
|
|
71
69
|
const { publishSite: publishFiles = require('@antora/site-publisher') } = generatorFunctions
|
|
72
70
|
await prepareWorkspace(publishFiles, assemblyFiles, buildConfig)
|
|
73
71
|
const helpers = { logCommand: logCommand.bind(context, loggerName), runCommand }
|
|
74
72
|
const boundConvert = convert.bind(context)
|
|
73
|
+
const boundResolveFileOrContents = resolveFileOrContents.bind(context)
|
|
75
74
|
return new PromiseQueue({ concurrency: buildConfig.processLimit })
|
|
76
75
|
.add(
|
|
77
76
|
assemblyFiles.map((doc) => async () => {
|
|
78
77
|
const relativeToOutput = embedReferenceStyle === 'output-relative'
|
|
79
78
|
const convertAttributes = prepareConvertAttributes(doc, targetExtname, relativeToOutput, assemblerConfig)
|
|
80
79
|
if (buildConfig.mkdirs) await fsp.mkdir(convertAttributes.outdir, { recursive: true, force: true })
|
|
81
|
-
return boundConvert(doc, convertAttributes, buildConfig, helpers).then((result) =>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
return boundConvert(doc, convertAttributes, buildConfig, helpers).then((result) =>
|
|
81
|
+
boundResolveFileOrContents(result, convertAttributes, buildConfig, loggerName).then((fileOrContents) =>
|
|
82
|
+
coerceToExportFormat(doc, targetBackend, targetExtname, targetMediaType, fileOrContents)
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
85
|
})
|
|
86
86
|
)
|
|
87
87
|
.toPromise()
|
|
88
88
|
.then((files) => {
|
|
89
|
-
|
|
89
|
+
const { publish, qualifyExports } = buildConfig
|
|
90
|
+
if (!publish) {
|
|
90
91
|
for (const file of files) delete file.assembler.assembled
|
|
91
92
|
return files
|
|
92
93
|
}
|
|
93
|
-
const qualifyExports = buildConfig.qualifyExports
|
|
94
94
|
return files.map((file) => {
|
|
95
|
-
const pages = file.assembler.assembled.pages
|
|
95
|
+
const pages = file.isNull() ? undefined : file.assembler.assembled.pages
|
|
96
96
|
delete file.assembler.assembled
|
|
97
|
+
if (!pages) return file
|
|
97
98
|
file = contentCatalog.addFile(file)
|
|
98
99
|
const extname = file.extname
|
|
99
100
|
const download = file.assembler.downloadStem + extname
|
|
@@ -148,7 +149,7 @@ function generateSelectAssemblyProfile (context, contentCatalog, baseModel, intr
|
|
|
148
149
|
const navigation = navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version)
|
|
149
150
|
const attributes = Object.assign({}, baseModel.attributes)
|
|
150
151
|
const model = Object.assign({}, baseModel, overrides, { attributes, logger })
|
|
151
|
-
if (!overrides) return
|
|
152
|
+
if (!overrides) return Object.assign(model, { navigation: navigation || componentVersion.navigation })
|
|
152
153
|
delete model.navFiles
|
|
153
154
|
delete model.messages
|
|
154
155
|
if (overrides.attributes) Object.entries(overrides.attributes).forEach(([name, val]) => (attributes[name] = val))
|
|
@@ -208,10 +209,10 @@ function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assembl
|
|
|
208
209
|
return Object.defineProperty(attributes, 'toArgs', { enumerable: false })
|
|
209
210
|
}
|
|
210
211
|
|
|
211
|
-
function coerceToExportFormat (
|
|
212
|
+
function coerceToExportFormat (assemblyFile, targetBackend, targetExtname, targetMediaType, fileOrContents) {
|
|
212
213
|
const file =
|
|
213
214
|
fileOrContents == null || Buffer.isBuffer(fileOrContents) || typeof fileOrContents.pipe === 'function'
|
|
214
|
-
? Object.assign(
|
|
215
|
+
? Object.assign(assemblyFile, { contents: fileOrContents })
|
|
215
216
|
: fileOrContents
|
|
216
217
|
;(file.assembler ??= {}).backend = targetBackend
|
|
217
218
|
if (file.extname === targetExtname) return file
|
|
@@ -253,7 +254,7 @@ function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
|
|
|
253
254
|
})
|
|
254
255
|
}
|
|
255
256
|
|
|
256
|
-
function resolveFileOrContents (convertResult, convertAttributes, buildConfig, loggerName) {
|
|
257
|
+
async function resolveFileOrContents (convertResult, convertAttributes, buildConfig, loggerName) {
|
|
257
258
|
let fileOrContents
|
|
258
259
|
if (convertResult?.status != null) {
|
|
259
260
|
if ('file' in convertResult) {
|
|
@@ -292,9 +293,10 @@ function resolveFileOrContents (convertResult, convertAttributes, buildConfig, l
|
|
|
292
293
|
} else if (convertResult !== undefined) {
|
|
293
294
|
return convertResult
|
|
294
295
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
296
|
+
if (fileOrContents !== undefined) return fileOrContents
|
|
297
|
+
const outfile = convertAttributes.outfile
|
|
298
|
+
const outfileExists = await fsp.access(outfile).then(invariably.true, invariably.false)
|
|
299
|
+
return outfileExists ? new LazyReadable(() => fs.createReadStream(outfile)) : null
|
|
298
300
|
}
|
|
299
301
|
|
|
300
302
|
function isBound (obj) {
|
package/lib/load-config.js
CHANGED
|
@@ -52,7 +52,7 @@ function loadConfig (playbook, configSource, preferredQualifier = '') {
|
|
|
52
52
|
}
|
|
53
53
|
if (componentVersionFilter.names == null) {
|
|
54
54
|
componentVersionFilter.names = ['*']
|
|
55
|
-
} else if (
|
|
55
|
+
} else if (componentVersionFilter.names.constructor === String) {
|
|
56
56
|
componentVersionFilter.names = componentVersionFilter.names.split(', ')
|
|
57
57
|
}
|
|
58
58
|
const remapAssemblyKeys = !('assembly' in config)
|
|
@@ -98,14 +98,19 @@ function loadConfig (playbook, configSource, preferredQualifier = '') {
|
|
|
98
98
|
build.dir &&= expandPath(build.dir, { dot: playbook.dir })
|
|
99
99
|
// used as cwd of command (and any scripts it requires)
|
|
100
100
|
build.cwd = build.cwd == null ? playbook.dir : expandPath(build.cwd, { dot: playbook.dir })
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
const command = build.command
|
|
102
|
+
if (command) {
|
|
103
|
+
if (command.constructor !== String) {
|
|
104
|
+
build.command = String(command)
|
|
105
|
+
} else if (~command.indexOf('$')) {
|
|
106
|
+
const vars = {
|
|
107
|
+
$NODE: process.execPath,
|
|
108
|
+
$NPM: ospath.join(process.execPath, '../npm'),
|
|
109
|
+
$NPX: ospath.join(process.execPath, '../npx'),
|
|
110
|
+
$PWD: build.cwd,
|
|
111
|
+
}
|
|
112
|
+
build.command = build.command.replace(/^\$(?:NODE|NP[MX])(?= )|\$PWD\b/g, (ref) => vars[ref])
|
|
107
113
|
}
|
|
108
|
-
build.command = build.command.replace(/^\$(?:NODE|NP[MX])(?= )|\$PWD\b/g, (ref) => vars[ref])
|
|
109
114
|
}
|
|
110
115
|
if (!('clean' in build) && 'output' in playbook) build.clean = playbook.output.clean
|
|
111
116
|
if (!('publish' in build)) build.publish = true
|
|
@@ -101,7 +101,7 @@ function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocCon
|
|
|
101
101
|
if (configShared == null) {
|
|
102
102
|
const assets = pagesInOutline.assembled.assets
|
|
103
103
|
for (const [name, val] of Object.entries(sharedAttributes)) {
|
|
104
|
-
if (!(
|
|
104
|
+
if (!(val?.constructor === String && ~val.indexOf(':'))) continue
|
|
105
105
|
let newVal
|
|
106
106
|
if (!name.endsWith('-image') || !(newVal = rewriteImageAttr(val, contentCatalog, assemblyModel, ctx, assets))) {
|
|
107
107
|
if (~(newVal = val).indexOf('image:')) {
|
|
@@ -112,7 +112,7 @@ function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocCon
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
for (const [name, val] of Object.entries(sharedAttributes)) {
|
|
115
|
-
if (!(
|
|
115
|
+
if (!(val?.constructor === String && ~val.indexOf(':'))) continue
|
|
116
116
|
if (~val.indexOf('xref:')) {
|
|
117
117
|
const newVal = rewriteXrefs(val, contentCatalog, assemblyModel, ctx, false, pagesInOutline, idSeparators)
|
|
118
118
|
if (newVal !== val) (attributesModified ??= {})[name] = newVal
|
|
@@ -133,7 +133,7 @@ function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
|
|
|
133
133
|
const buffer = [
|
|
134
134
|
`= ${doctitle}`,
|
|
135
135
|
...(version ? [`:revnumber: ${displayVersion}`] : []),
|
|
136
|
-
...(doctype === 'article' ? [] : [`:doctype: ${doctype
|
|
136
|
+
...(doctype === 'article' ? [] : [`:doctype: ${doctype}`]),
|
|
137
137
|
':underscore: _',
|
|
138
138
|
// Q: should we pass these via the CLI so they cannot be modified?
|
|
139
139
|
`:page-component-name: ${componentVersion.name}`,
|
|
@@ -172,16 +172,16 @@ function mergeAsciiDoc (
|
|
|
172
172
|
level = 0,
|
|
173
173
|
supportsParts = false
|
|
174
174
|
) {
|
|
175
|
+
const { items = [], roles = [], unresolved, urlType, url, hash } = outlineEntry
|
|
175
176
|
// TODO: we could try to be smart about it and make sure the page with fragment is included at least once
|
|
176
|
-
if (
|
|
177
|
+
if (hash || roles.includes('site-only')) {
|
|
177
178
|
buffer.inBody ??= false
|
|
178
179
|
return buffer
|
|
179
180
|
}
|
|
180
181
|
let navtitle = outlineEntry.navtitle ?? outlineEntry.content
|
|
181
182
|
let navtitlePlain = sanitize(navtitle)
|
|
182
183
|
let navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
183
|
-
const {
|
|
184
|
-
const { filetype, linkReferenceStyle, pubRoot, siteRoot, logger, rootLevel } = assemblyModel
|
|
184
|
+
const { filetype, linkReferenceStyle, pubRoot, sectionMergeStrategy, siteRoot, logger, rootLevel } = assemblyModel
|
|
185
185
|
const assembled = pagesInOutline.assembled
|
|
186
186
|
// FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
|
|
187
187
|
const page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
|
|
@@ -194,10 +194,8 @@ function mergeAsciiDoc (
|
|
|
194
194
|
buffer.inBody ??= false
|
|
195
195
|
return buffer
|
|
196
196
|
}
|
|
197
|
-
const isRootPage = atDocumentRoot && !level
|
|
198
|
-
const { component, version, module: module_, relative, origin, mediaType } = page.src
|
|
199
197
|
const pageAsAsciiDoc = new page.constructor(
|
|
200
|
-
Object.assign({}, page, { contents: trimAsciiDoc(contents), mediaType })
|
|
198
|
+
Object.assign({}, page, { contents: trimAsciiDoc(contents), mediaType: page.src.mediaType })
|
|
201
199
|
)
|
|
202
200
|
const doc = loadAsciiDoc(pageAsAsciiDoc, contentCatalog, asciidocConfig)
|
|
203
201
|
doc.logger = logger
|
|
@@ -210,6 +208,8 @@ function mergeAsciiDoc (
|
|
|
210
208
|
})
|
|
211
209
|
: doc.getLogger()
|
|
212
210
|
doc.source_header_attributes ??= doc.parent.$to_h()
|
|
211
|
+
const isRootPage = atDocumentRoot && !level
|
|
212
|
+
const { component, version, module: module_, relative, origin } = page.src
|
|
213
213
|
if (isRootPage && doc.hasAttribute('assembly-slug')) buffer.slug = doc.getAttribute('assembly-slug')
|
|
214
214
|
let hasAssemblyNavtitleAttr
|
|
215
215
|
if ((hasAssemblyNavtitleAttr = !!doc.getAttribute('assembly-navtitle'))) {
|
|
@@ -231,13 +231,7 @@ function mergeAsciiDoc (
|
|
|
231
231
|
let pageFragment = ''
|
|
232
232
|
let pageRoles = []
|
|
233
233
|
let pageStyle = doc.getAttribute('assembly-style', '')
|
|
234
|
-
if (
|
|
235
|
-
!pageStyle &&
|
|
236
|
-
atBookRoot &&
|
|
237
|
-
rootLevel === 0 &&
|
|
238
|
-
assemblyModel.sectionMergeStrategy === 'fuse' &&
|
|
239
|
-
!doc.source_header_attributes['$key?']('assembly-style')
|
|
240
|
-
) {
|
|
234
|
+
if (!pageStyle && atBookRoot && rootLevel === 0 && !doc.source_header_attributes['$key?']('assembly-style')) {
|
|
241
235
|
pageStyle = 'preface'
|
|
242
236
|
}
|
|
243
237
|
let part
|
|
@@ -248,6 +242,7 @@ function mergeAsciiDoc (
|
|
|
248
242
|
pageStyle = pageStyle.substring(0, pageStyle.length - 5)
|
|
249
243
|
if (!supportsParts) part = undefined
|
|
250
244
|
}
|
|
245
|
+
const hasSections = doc.hasSections()
|
|
251
246
|
let nextSectionLevel = 1
|
|
252
247
|
const lines = doc.getSourceLines()
|
|
253
248
|
const ignoreLines = []
|
|
@@ -353,15 +348,16 @@ function mergeAsciiDoc (
|
|
|
353
348
|
? ''
|
|
354
349
|
: undefined
|
|
355
350
|
if (htitleOverride != null) {
|
|
351
|
+
const keepSections = hasSections && sectionMergeStrategy !== 'discrete'
|
|
356
352
|
if (htitleOverride === '') {
|
|
357
|
-
if (headerHasBlockAttrs ||
|
|
353
|
+
if (headerHasBlockAttrs || keepSections) {
|
|
358
354
|
htitleAsciiDoc = '{empty}'
|
|
359
355
|
notitle = true
|
|
360
356
|
} else {
|
|
361
357
|
if (hasPrefaceTitleAttr) buffer.splice(++buffer.endHeaderIdx, 0, ':preface-title:')
|
|
362
358
|
htitleAsciiDoc = undefined
|
|
363
359
|
}
|
|
364
|
-
} else if (hasPrefaceTitleAttr && !
|
|
360
|
+
} else if (hasPrefaceTitleAttr && !headerHasBlockAttrs && !keepSections) {
|
|
365
361
|
buffer.splice(++buffer.endHeaderIdx, 0, `:preface-title: ${unconvertInlineAsciiDoc(htitleOverride)}`)
|
|
366
362
|
htitleAsciiDoc = undefined
|
|
367
363
|
} else {
|
|
@@ -405,7 +401,7 @@ function mergeAsciiDoc (
|
|
|
405
401
|
buffer.push(`${'='.repeat(heading.level)} ${heading.title}`)
|
|
406
402
|
} else {
|
|
407
403
|
if (atDocumentRoot) buffer.unshift(`[#${pageId}]`)
|
|
408
|
-
if (
|
|
404
|
+
if (sectionMergeStrategy === 'enclose' && hasItems && hasSections) {
|
|
409
405
|
enclosed = true
|
|
410
406
|
// TODO: make overview section title configurable
|
|
411
407
|
//let overviewTitle = doc.getDocumentTitle()
|
|
@@ -424,14 +420,14 @@ function mergeAsciiDoc (
|
|
|
424
420
|
}
|
|
425
421
|
}
|
|
426
422
|
assembled.pages.set(page, pageFragment)
|
|
427
|
-
if (
|
|
423
|
+
if (hasSections) fixSectionLevels(doc.getSections(), nextSectionLevel)
|
|
428
424
|
const allBlocks = doc.findBy({ traverse_documents: true }, (it) =>
|
|
429
425
|
it.getContext() === 'document'
|
|
430
426
|
? it.getDocument().isNested()
|
|
431
427
|
: !(it.getContext() === 'table_cell' && it.getStyle() === 'asciidoc')
|
|
432
428
|
)
|
|
433
429
|
if (doc.getDoctype() === 'manpage') {
|
|
434
|
-
const firstSectionIdx =
|
|
430
|
+
const firstSectionIdx = hasSections ? doc.getSections()[0].getLineNumber() - 1 : lines.length
|
|
435
431
|
for (let idx = 0; idx < firstSectionIdx; idx++) {
|
|
436
432
|
if (~ignoreLines.indexOf(idx)) continue
|
|
437
433
|
const line = lines[idx]
|
|
@@ -547,7 +543,7 @@ function mergeAsciiDoc (
|
|
|
547
543
|
let idx = lineno - 1
|
|
548
544
|
if (context === 'section' && !block.getDocument().isNested()) {
|
|
549
545
|
if (block.getSectionName() === 'header') return
|
|
550
|
-
let blockStyle =
|
|
546
|
+
let blockStyle = sectionMergeStrategy === 'discrete' ? 'discrete' : undefined
|
|
551
547
|
lines[idx] = lines[idx].replace(/^=+ (.+)/, (_, rest) => {
|
|
552
548
|
let targetMarkerLength = block.level + 1 + level + (enclosed ? 1 : 0)
|
|
553
549
|
if (targetMarkerLength > 6) {
|
|
@@ -8,7 +8,13 @@ class LazyReadable extends PassThrough {
|
|
|
8
8
|
super(options)
|
|
9
9
|
this._read = function () {
|
|
10
10
|
delete this._read // restores original method
|
|
11
|
-
fn.call(this, options)
|
|
11
|
+
fn.call(this, options)
|
|
12
|
+
.on('error', (err) => {
|
|
13
|
+
let msg = 'Failed to create stream'
|
|
14
|
+
if (err.syscall === 'open') msg += ` for path '${err.path}'`
|
|
15
|
+
this.emit('error', new Error(msg, { cause: err }))
|
|
16
|
+
})
|
|
17
|
+
.pipe(this)
|
|
12
18
|
return this._read.apply(this, arguments)
|
|
13
19
|
}
|
|
14
20
|
this.emit('readable')
|
package/lib/util/matcher.js
CHANGED
|
@@ -27,7 +27,7 @@ function filterCollection (candidates, patterns, opts = {}) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function compilePattern (str) {
|
|
30
|
-
const negated = str.charAt() === '!' ?
|
|
30
|
+
const negated = str.charAt() === '!' ? (str = str.substring(1)).constructor === String : false
|
|
31
31
|
if (str === '**') return Object.assign(new RegExp(), { globstar: true, negated })
|
|
32
32
|
if (str === '*') return Object.assign(new RegExp(), { star: true, negated })
|
|
33
33
|
const buffer = { result: '', brace: undefined, group: undefined }
|
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.3",
|
|
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)",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@asciidoctor/reducer": "~1.1",
|
|
42
42
|
"@antora/expand-path-helper": "~3.0",
|
|
43
43
|
"@antora/run-command-helper": "~1.1",
|
|
44
|
-
"js-yaml": "~4.
|
|
44
|
+
"js-yaml": "~4.3"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@antora/asciidoc-loader": "3.2.0-rc.2",
|