@antora/assembler 1.0.0-rc.2 → 1.0.0-rc.4
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
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
|
|
|
@@ -25,8 +25,8 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
25
25
|
mediaType: targetMediaType,
|
|
26
26
|
loggerName = PACKAGE_NAME,
|
|
27
27
|
} = converter ?? {}
|
|
28
|
-
const assemblerConfig = await loadConfig.call(this,
|
|
29
|
-
if (assemblerConfig.enabled === false) return []
|
|
28
|
+
const assemblerConfig = await loadConfig.call(this, configSource, playbook, '-' + targetBackend)
|
|
29
|
+
if (assemblerConfig.enabled === false || !contentCatalog.publishableFamilies?.has('export')) return []
|
|
30
30
|
const context = isBound(this)
|
|
31
31
|
? this
|
|
32
32
|
: {
|
|
@@ -40,7 +40,7 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
40
40
|
assemblyConfig.embedReferenceStyle = embedReferenceStyle
|
|
41
41
|
const profile = (assemblyConfig.profile ??= targetBackend)
|
|
42
42
|
const intrinsicAttributes = { 'loader-assembler': '' }
|
|
43
|
-
buildConfig.cwd ??= process.cwd()
|
|
43
|
+
const cwd = (buildConfig.cwd ??= process.cwd())
|
|
44
44
|
if (profile) {
|
|
45
45
|
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), `build/assembler/${profile}`)
|
|
46
46
|
intrinsicAttributes[`assembler-profile-${profile}`] = ''
|
|
@@ -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(cwd, playbook) : 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
|
|
@@ -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
|
@@ -10,7 +10,7 @@ const { LEGACY_ASSEMBLY_KEYS } = require('./constants')
|
|
|
10
10
|
const CAMEL_CASE_STOP_PATHS = ['asciidoc.attributes', 'assembly.attributes']
|
|
11
11
|
const PACKAGE_NAME = require('../package.json').name
|
|
12
12
|
|
|
13
|
-
function loadConfig (
|
|
13
|
+
function loadConfig (configSource, playbook, preferredQualifier = '') {
|
|
14
14
|
let resolvedConfigSource
|
|
15
15
|
return (
|
|
16
16
|
configSource?.constructor === Object
|
|
@@ -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)
|
|
@@ -102,7 +102,7 @@ function loadConfig (playbook, configSource, preferredQualifier = '') {
|
|
|
102
102
|
if (command) {
|
|
103
103
|
if (command.constructor !== String) {
|
|
104
104
|
build.command = String(command)
|
|
105
|
-
} else if (command.
|
|
105
|
+
} else if (~command.indexOf('$')) {
|
|
106
106
|
const vars = {
|
|
107
107
|
$NODE: process.execPath,
|
|
108
108
|
$NPM: ospath.join(process.execPath, '../npm'),
|
|
@@ -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,15 +172,15 @@ 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 { items = [], roles = [], unresolved, urlType, url } = outlineEntry
|
|
184
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
|
|
@@ -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'))) {
|
|
@@ -365,10 +365,10 @@ function mergeAsciiDoc (
|
|
|
365
365
|
}
|
|
366
366
|
}
|
|
367
367
|
}
|
|
368
|
-
if (htitleAsciiDoc
|
|
368
|
+
if (htitleAsciiDoc) {
|
|
369
369
|
if (atDocumentRoot) {
|
|
370
|
-
//
|
|
371
|
-
pageFragment =
|
|
370
|
+
// Q: should we use htitleAsciiDoc to generate ID if not {empty} instead of pageStyle
|
|
371
|
+
pageFragment = `#_${idSeparators.coordinate}${pageIdLeader}${pageStyle}`
|
|
372
372
|
buffer.unshift(`[#${pageId}]`)
|
|
373
373
|
} else {
|
|
374
374
|
pageFragment = `#${pageId}`
|
|
@@ -393,31 +393,30 @@ function mergeAsciiDoc (
|
|
|
393
393
|
} else if (atBookRoot) {
|
|
394
394
|
nextSectionLevel = undefined
|
|
395
395
|
}
|
|
396
|
-
let enclosed
|
|
397
396
|
if (heading) {
|
|
398
397
|
const rolesAttr = pageRoles.reduce((str, it) => str + '.' + it, '')
|
|
399
398
|
const notitleAttrs = heading.notitle ? '%notitle,toclevels=0,outlinelevels=0' : ''
|
|
400
399
|
buffer.push(`[${heading.style ?? pageStyle}${pageFragment}${rolesAttr}${notitleAttrs}]`)
|
|
401
400
|
buffer.push(`${'='.repeat(heading.level)} ${heading.title}`)
|
|
402
|
-
} else {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
|
|
401
|
+
} else if (atDocumentRoot) {
|
|
402
|
+
buffer.unshift(`[#${pageId}]`)
|
|
403
|
+
}
|
|
404
|
+
if (sectionMergeStrategy === 'enclose' && hasItems && hasSections && !(atDocumentRoot && heading)) {
|
|
405
|
+
// TODO: make overview section title configurable
|
|
406
|
+
//let overviewTitle = doc.getDocumentTitle()
|
|
407
|
+
//if (overviewTitle === navtitle) overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
408
|
+
const overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
409
|
+
const syntheticId = idSeparators.generateIdFromTitle(overviewTitle, idSeparators, pageIdLeader)
|
|
410
|
+
if (heading) buffer.push('')
|
|
411
|
+
let hlevel = level + (nextSectionLevel = 2)
|
|
412
|
+
if (hlevel > 6) {
|
|
413
|
+
const blockStyle = `discrete.h${hlevel}`
|
|
414
|
+
hlevel = 6
|
|
415
|
+
buffer.push(`[${blockStyle}#${syntheticId}]`)
|
|
416
|
+
} else {
|
|
417
|
+
buffer.push(`[#${syntheticId}]`)
|
|
420
418
|
}
|
|
419
|
+
buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
|
|
421
420
|
}
|
|
422
421
|
assembled.pages.set(page, pageFragment)
|
|
423
422
|
if (hasSections) fixSectionLevels(doc.getSections(), nextSectionLevel)
|
|
@@ -545,7 +544,7 @@ function mergeAsciiDoc (
|
|
|
545
544
|
if (block.getSectionName() === 'header') return
|
|
546
545
|
let blockStyle = sectionMergeStrategy === 'discrete' ? 'discrete' : undefined
|
|
547
546
|
lines[idx] = lines[idx].replace(/^=+ (.+)/, (_, rest) => {
|
|
548
|
-
let targetMarkerLength = block.level + 1 + level
|
|
547
|
+
let targetMarkerLength = block.level + 1 + level
|
|
549
548
|
if (targetMarkerLength > 6) {
|
|
550
549
|
blockStyle = `discrete.h${targetMarkerLength}`
|
|
551
550
|
targetMarkerLength = 6
|
|
@@ -806,9 +805,10 @@ function safePush (onto, entries) {
|
|
|
806
805
|
}
|
|
807
806
|
}
|
|
808
807
|
|
|
809
|
-
function generateIdFromTitle (titleAsciiDoc, idSeparators) {
|
|
808
|
+
function generateIdFromTitle (titleAsciiDoc, idSeparators, idLeader) {
|
|
810
809
|
const Section = this.$class().$const_get('::Asciidoctor::Section')
|
|
811
810
|
const baseId = Section.$generate_id(titleAsciiDoc, this)
|
|
811
|
+
if (idLeader) return `_${idSeparators.coordinate}${idLeader}${baseId}`
|
|
812
812
|
this.getCatalog().refs['$[]='](baseId, true)
|
|
813
813
|
return `_${idSeparators.coordinate}${baseId}`
|
|
814
814
|
}
|
|
@@ -6,6 +6,7 @@ function generateScopedId (componentSrc, componentVersion, separators, filetype,
|
|
|
6
6
|
let { component, module: mod, relative } = componentSrc
|
|
7
7
|
let id = relative.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
|
|
8
8
|
let { coordinate: coordinateSeparator, prefix: prefixSeparator } = separators
|
|
9
|
+
if (mod !== 'ROOT' && ~mod.indexOf('.')) mod = mod.replace(/[.]/g, '-')
|
|
9
10
|
if (component !== componentVersion.name) {
|
|
10
11
|
id = [component, mod === 'ROOT' ? '' : mod, id].join(coordinateSeparator)
|
|
11
12
|
} else if (mod !== 'ROOT') {
|
|
@@ -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.4",
|
|
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)",
|