@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
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
autoload :JSON, 'json'
|
|
4
|
+
|
|
5
|
+
class JsonlFormatter < Asciidoctor::Logger::BasicFormatter
|
|
6
|
+
def call severity, time, progname, msg
|
|
7
|
+
entry = { 'time' => time.utc.to_i, 'level' => severity.downcase, 'name' => progname }
|
|
8
|
+
if ::String === msg
|
|
9
|
+
entry['msg'] = msg
|
|
10
|
+
else
|
|
11
|
+
loc = msg[:source_location]
|
|
12
|
+
entry['file'] = { 'path' => loc.file, 'line' => loc.lineno }
|
|
13
|
+
entry['msg'] = msg[:text]
|
|
14
|
+
end
|
|
15
|
+
(JSON.generate entry) + ?\n
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
proc do
|
|
20
|
+
logger = Asciidoctor::LoggerManager.logger
|
|
21
|
+
logger.formatter = JsonlFormatter.new
|
|
22
|
+
unless (progname = ::File.basename $PROGRAM_NAME).empty?
|
|
23
|
+
logger.progname = progname
|
|
24
|
+
end
|
|
25
|
+
end.call
|
package/lib/assemble-content.js
CHANGED
|
@@ -5,18 +5,30 @@ 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 }
|
|
15
17
|
const PACKAGE_NAME = require('../package.json').name
|
|
18
|
+
const NEWLINE_RX = /(?:\r?\n)+/
|
|
16
19
|
|
|
17
20
|
async function assembleContent (playbook, contentCatalog, converter, { configSource, navigationCatalog }) {
|
|
18
|
-
const
|
|
19
|
-
|
|
21
|
+
const {
|
|
22
|
+
convert = converter,
|
|
23
|
+
getDefaultCommand,
|
|
24
|
+
extname: targetExtname = '',
|
|
25
|
+
backend: targetBackend = targetExtname.slice(1),
|
|
26
|
+
embedReferenceStyle = 'relative',
|
|
27
|
+
mediaType: targetMediaType,
|
|
28
|
+
loggerName = PACKAGE_NAME,
|
|
29
|
+
} = converter ?? {}
|
|
30
|
+
const assemblerConfig = await loadConfig.call(this, playbook, configSource, '-' + targetBackend)
|
|
31
|
+
if (assemblerConfig.enabled === false) return []
|
|
20
32
|
const context = isBound(this)
|
|
21
33
|
? this
|
|
22
34
|
: {
|
|
@@ -26,12 +38,13 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
26
38
|
}
|
|
27
39
|
const generatorFunctions = context.getFunctions()
|
|
28
40
|
const { loadAsciiDoc = require('@antora/asciidoc-loader') } = generatorFunctions
|
|
29
|
-
const targetBackend = converter == null ? undefined : (converter.backend ?? converter.extname?.slice(1))
|
|
30
41
|
const { assembly: assemblyConfig, build: buildConfig } = assemblerConfig
|
|
31
|
-
|
|
42
|
+
assemblyConfig.embedReferenceStyle = embedReferenceStyle
|
|
43
|
+
const profile = (assemblyConfig.profile ??= targetBackend)
|
|
32
44
|
const intrinsicAttributes = { 'loader-assembler': '' }
|
|
45
|
+
buildConfig.cwd ??= process.cwd()
|
|
33
46
|
if (profile) {
|
|
34
|
-
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), `build/assembler
|
|
47
|
+
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), `build/assembler/${profile}`)
|
|
35
48
|
intrinsicAttributes[`assembler-profile-${profile}`] = ''
|
|
36
49
|
intrinsicAttributes['assembler-profile'] = profile
|
|
37
50
|
if (targetBackend) {
|
|
@@ -39,30 +52,38 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
39
52
|
intrinsicAttributes['assembler-backend'] = targetBackend
|
|
40
53
|
}
|
|
41
54
|
} else {
|
|
42
|
-
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), 'build/assembler')
|
|
55
|
+
buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), 'build/assembler/_')
|
|
56
|
+
}
|
|
57
|
+
if (targetExtname) {
|
|
58
|
+
const targetFiletype = targetExtname.slice(1)
|
|
59
|
+
intrinsicAttributes[`assembler-filetype-${targetFiletype}`] = ''
|
|
60
|
+
intrinsicAttributes['assembler-filetype'] = targetFiletype
|
|
43
61
|
}
|
|
44
|
-
Object.assign(
|
|
62
|
+
Object.assign(assemblyConfig.attributes, intrinsicAttributes)
|
|
45
63
|
const assemblyFiles = produceAssemblyFiles(
|
|
46
64
|
loadAsciiDoc,
|
|
47
65
|
contentCatalog,
|
|
48
66
|
assemblerConfig,
|
|
49
|
-
|
|
67
|
+
generateSelectAssemblyProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog)
|
|
50
68
|
)
|
|
51
|
-
|
|
52
|
-
if (
|
|
69
|
+
if (!(assemblyFiles.length && typeof convert === 'function')) return assemblyFiles
|
|
70
|
+
if (buildConfig.command == null && typeof getDefaultCommand === 'function') {
|
|
71
|
+
buildConfig.command = await getDefaultCommand(buildConfig.cwd)
|
|
72
|
+
}
|
|
53
73
|
const { publishSite: publishFiles = require('@antora/site-publisher') } = generatorFunctions
|
|
54
|
-
await prepareWorkspace(publishFiles, assemblyFiles,
|
|
74
|
+
await prepareWorkspace(publishFiles, assemblyFiles, buildConfig)
|
|
75
|
+
const helpers = { logCommand: logCommand.bind(context, loggerName), runCommand }
|
|
55
76
|
const boundConvert = convert.bind(context)
|
|
56
77
|
return new PromiseQueue({ concurrency: buildConfig.processLimit })
|
|
57
78
|
.add(
|
|
58
79
|
assemblyFiles.map((doc) => async () => {
|
|
59
|
-
const
|
|
80
|
+
const relativeToOutput = embedReferenceStyle === 'output-relative'
|
|
81
|
+
const convertAttributes = prepareConvertAttributes(doc, targetExtname, relativeToOutput, assemblerConfig)
|
|
60
82
|
if (buildConfig.mkdirs) await fsp.mkdir(convertAttributes.outdir, { recursive: true, force: true })
|
|
61
|
-
return boundConvert(doc, convertAttributes, buildConfig).then(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)
|
|
83
|
+
return boundConvert(doc, convertAttributes, buildConfig, helpers).then((result) => {
|
|
84
|
+
const fileOrContents = resolveFileOrContents.call(context, result, convertAttributes, buildConfig, loggerName)
|
|
85
|
+
return coerceToExportFormat(doc, targetBackend, targetExtname, targetMediaType, fileOrContents)
|
|
86
|
+
})
|
|
66
87
|
})
|
|
67
88
|
)
|
|
68
89
|
.toPromise()
|
|
@@ -85,7 +106,13 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
85
106
|
}
|
|
86
107
|
pages.forEach((fragment, page) => {
|
|
87
108
|
const assemblerMeta = (page.assembler ??= {})
|
|
88
|
-
|
|
109
|
+
const exports = (assemblerMeta.exports ??= [])
|
|
110
|
+
const exportEntry = { fragment, file }
|
|
111
|
+
const insertIdx = exports.findIndex(
|
|
112
|
+
({ file: candidate }) =>
|
|
113
|
+
!(candidate.src.component === page.src.component && candidate.src.version === page.src.version)
|
|
114
|
+
)
|
|
115
|
+
~insertIdx ? exports.splice(insertIdx, 0, exportEntry) : exports.push(exportEntry)
|
|
89
116
|
const extnameProp = extname.slice(1)
|
|
90
117
|
if (extnameProp in assemblerMeta) return
|
|
91
118
|
Object.defineProperty(assemblerMeta, extnameProp, {
|
|
@@ -99,71 +126,97 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
|
|
|
99
126
|
})
|
|
100
127
|
}
|
|
101
128
|
|
|
102
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Generates a function that selects the active assembly profile, initializes an assembly model using
|
|
131
|
+
* the keys from the profile as well as any inherited shared keys, builds the navigation for the assembly, and
|
|
132
|
+
* returns the initialized assembly model. The assembly model is further populated after the call to this function.
|
|
133
|
+
*/
|
|
134
|
+
function generateSelectAssemblyProfile (context, contentCatalog, baseModel, intrinsicAttributes, navigationCatalog) {
|
|
135
|
+
const logger = context.getLogger?.(PACKAGE_NAME)
|
|
103
136
|
const { assemblerProfiles } = context.getVariables()
|
|
104
137
|
if (!assemblerProfiles) {
|
|
105
138
|
return (componentVersion) => {
|
|
106
139
|
const navigation =
|
|
107
140
|
navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version) ?? componentVersion.navigation
|
|
108
|
-
return Object.assign({},
|
|
141
|
+
return Object.assign({}, baseModel, { attributes: Object.assign({}, baseModel.attributes), navigation, logger })
|
|
109
142
|
}
|
|
110
143
|
}
|
|
111
|
-
const boundSendToLog = sendToLog.bind(
|
|
112
|
-
const {
|
|
144
|
+
const boundSendToLog = sendToLog.bind(logger)
|
|
145
|
+
const {
|
|
146
|
+
buildNavigation = require('@antora/navigation-builder'),
|
|
147
|
+
buildAlternateNavigation = buildNavigation.buildAlternateNavigation ??
|
|
148
|
+
buildAlternateNavigationShim.bind(null, buildNavigation),
|
|
149
|
+
} = context.getFunctions()
|
|
113
150
|
return (componentVersion) => {
|
|
151
|
+
const attributes = Object.assign({}, baseModel.attributes)
|
|
114
152
|
const componentVersionProfiles = assemblerProfiles.get(componentVersion.version + '@' + componentVersion.name)
|
|
115
|
-
const overrides =
|
|
116
|
-
componentVersionProfiles?.get(intrinsicAttributes['assembler-profile']) ?? componentVersionProfiles?.get()
|
|
153
|
+
const overrides = componentVersionProfiles?.get(baseModel.profile) ?? componentVersionProfiles?.get() ?? {}
|
|
117
154
|
const { navFiles, messages } = overrides
|
|
118
|
-
|
|
155
|
+
Object.entries(overrides.attributes ?? {}).forEach(([name, val]) => (attributes[name] = val))
|
|
156
|
+
const model = Object.assign({}, baseModel, overrides, { attributes, logger })
|
|
119
157
|
delete model.navFiles
|
|
120
158
|
delete model.messages
|
|
121
159
|
const navigationOverride = navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version)
|
|
122
160
|
if (navigationOverride) return Object.assign(model, { navigation: navigationOverride })
|
|
123
161
|
messages?.forEach(boundSendToLog)
|
|
124
|
-
model
|
|
125
|
-
if (!navFiles) return model
|
|
162
|
+
if (!navFiles) return Object.assign(model, { navigation: componentVersion.navigation })
|
|
126
163
|
if (!navFiles.length) return Object.assign(model, { navigation: [] })
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
componentVersion.asciidoc = Object.assign({}, asciidoc_, {
|
|
130
|
-
attributes: Object.assign({}, asciidoc_.attributes, intrinsicAttributes),
|
|
164
|
+
model.navigation = buildAlternateNavigation(contentCatalog, componentVersion, navFiles, {
|
|
165
|
+
attributes: intrinsicAttributes,
|
|
131
166
|
})
|
|
132
|
-
buildNavigation(
|
|
133
|
-
new Proxy(contentCatalog, {
|
|
134
|
-
get (target, property) {
|
|
135
|
-
const method = target[property]
|
|
136
|
-
if (property !== 'findBy') return method
|
|
137
|
-
return (criteria) => (toJSON(criteria) === '{"family":"nav"}' ? navFiles : method.call(target, criteria))
|
|
138
|
-
},
|
|
139
|
-
})
|
|
140
|
-
)
|
|
141
|
-
model.navigation = componentVersion.navigation
|
|
142
|
-
Object.assign(componentVersion, { asciidoc: asciidoc_, navigation: navigation_ })
|
|
143
167
|
return model
|
|
144
168
|
}
|
|
145
169
|
}
|
|
146
170
|
|
|
147
|
-
function
|
|
171
|
+
function buildAlternateNavigationShim (
|
|
172
|
+
buildNavigation,
|
|
173
|
+
contentCatalog,
|
|
174
|
+
componentVersion,
|
|
175
|
+
navFiles,
|
|
176
|
+
asciidocConfigOverrides
|
|
177
|
+
) {
|
|
178
|
+
const { asciidoc: asciidoc_, navigation: navigation_ } = componentVersion
|
|
179
|
+
const asciidocConfig = (componentVersion.asciidoc = Object.assign({}, asciidoc_))
|
|
180
|
+
if (asciidocConfigOverrides) {
|
|
181
|
+
const attributesOverrides =
|
|
182
|
+
'attributes' in asciidocConfigOverrides
|
|
183
|
+
? { attributes: Object.assign({}, asciidocConfig.attributes, asciidocConfigOverrides.attributes) }
|
|
184
|
+
: undefined
|
|
185
|
+
Object.assign(asciidocConfig, asciidocConfigOverrides, attributesOverrides)
|
|
186
|
+
}
|
|
187
|
+
const contentCatalogProxy = new Proxy(contentCatalog, {
|
|
188
|
+
get (target, property) {
|
|
189
|
+
const method = target[property]
|
|
190
|
+
if (property !== 'findBy') return method
|
|
191
|
+
return (criteria) => (toJSON(criteria) === '{"family":"nav"}' ? navFiles : method.call(target, criteria))
|
|
192
|
+
},
|
|
193
|
+
})
|
|
194
|
+
buildNavigation(contentCatalogProxy)
|
|
195
|
+
const navigation = componentVersion.navigation
|
|
196
|
+
Object.assign(componentVersion, { asciidoc: asciidoc_, navigation: navigation_ })
|
|
197
|
+
return navigation
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assemblerConfig) {
|
|
148
201
|
const {
|
|
149
202
|
asciidoc: { attributes: docAttributes } = { attributes: {} },
|
|
150
203
|
extname: docfilesuffix,
|
|
151
204
|
path: reldocfile,
|
|
152
205
|
src: { family, relative },
|
|
153
206
|
} = doc
|
|
154
|
-
const { cwd = process.cwd(), dir = cwd } =
|
|
207
|
+
const { cwd = process.cwd(), dir = cwd } = assemblerConfig.build
|
|
155
208
|
const docname = family + '$' + relative.slice(0, relative.length - docfilesuffix.length)
|
|
156
209
|
const docfile = ospath.join(dir, reldocfile)
|
|
157
|
-
const
|
|
158
|
-
const
|
|
210
|
+
const outdir = ospath.dirname(docfile)
|
|
211
|
+
const docdir = relativeToOutput ? dir : outdir
|
|
159
212
|
const outfile = docfile.slice(0, docfile.length - docfilesuffix.length) + targetExtname
|
|
160
|
-
const attributes = Object.assign({}, docAttributes, {
|
|
213
|
+
const attributes = Object.assign({ revdate: `${assemblerConfig.assembly.revdate}@` }, docAttributes, {
|
|
161
214
|
docdir,
|
|
162
215
|
docfile,
|
|
163
216
|
docfilesuffix,
|
|
164
217
|
'docname@': docname,
|
|
165
|
-
imagesdir,
|
|
166
|
-
outdir
|
|
218
|
+
imagesdir: '',
|
|
219
|
+
outdir,
|
|
167
220
|
outfile,
|
|
168
221
|
outfilesuffix: targetExtname,
|
|
169
222
|
toArgs (optionFlag, command) {
|
|
@@ -173,11 +226,15 @@ function prepareConvertAttributes (doc, targetExtname, buildConfig) {
|
|
|
173
226
|
if (val) {
|
|
174
227
|
val = `${name}=${padCharRef && typeof val.charAt === 'function' && val.charAt() === '&' ? ' ' : ''}${val}`
|
|
175
228
|
} else if (val === '') {
|
|
229
|
+
if (name === 'asciidoctor-log-integration') {
|
|
230
|
+
args.push('-r', require.resolve('#asciidoctor-log-adapter'))
|
|
231
|
+
continue
|
|
232
|
+
}
|
|
176
233
|
val = name
|
|
177
234
|
} else {
|
|
178
235
|
val = `!${name}${val === false ? '@' : ''}`
|
|
179
236
|
}
|
|
180
|
-
args.push(
|
|
237
|
+
args.push(optionFlag, val)
|
|
181
238
|
}
|
|
182
239
|
return args
|
|
183
240
|
},
|
|
@@ -206,7 +263,7 @@ function findFirstExportWithExtname (extname) {
|
|
|
206
263
|
}
|
|
207
264
|
|
|
208
265
|
// TODO: if no workspace dir is defined, we shouldn't continue
|
|
209
|
-
function prepareWorkspace (publishFiles, assemblyFiles,
|
|
266
|
+
function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
|
|
210
267
|
const { dir, clean, keepSource } = buildConfig
|
|
211
268
|
const files = []
|
|
212
269
|
const outPaths = new Set()
|
|
@@ -217,13 +274,57 @@ function prepareWorkspace (publishFiles, assemblyFiles, contentCatalog, buildCon
|
|
|
217
274
|
outPaths.add(asset.out.path)
|
|
218
275
|
}
|
|
219
276
|
}
|
|
220
|
-
if (keepSource)
|
|
277
|
+
if (keepSource) {
|
|
278
|
+
for (const file of assemblyFiles) {
|
|
279
|
+
file.src.contents = file.contents
|
|
280
|
+
file.out = { path: file.path }
|
|
281
|
+
files.push(file)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
221
284
|
return publishFiles({ output: { clean, dir } }, { getFiles: () => files })
|
|
222
285
|
}
|
|
223
286
|
|
|
287
|
+
function resolveFileOrContents (convertResult, convertAttributes, buildConfig, loggerName) {
|
|
288
|
+
let fileOrContents
|
|
289
|
+
if (convertResult?.status != null) {
|
|
290
|
+
fileOrContents = 'file' in convertResult ? convertResult.file : convertResult.contents
|
|
291
|
+
let logger, match
|
|
292
|
+
if (buildConfig.stderrSink === 'log' && convertResult.stderr.length && (logger = this.getLogger(loggerName))) {
|
|
293
|
+
const docfile = convertAttributes.docfile
|
|
294
|
+
const command = buildConfig.command
|
|
295
|
+
const stderr = convertResult.stderr.toString().trimEnd()
|
|
296
|
+
stderr.split(NEWLINE_RX).forEach((line) => {
|
|
297
|
+
const ctx = { command, file: { path: docfile } }
|
|
298
|
+
if (line.charAt() === '{' && line.charAt(line.length - 1) === '}') {
|
|
299
|
+
const entry = JSON.parse(line)
|
|
300
|
+
if (entry.name) ctx.program = entry.name
|
|
301
|
+
if (entry.file?.line) ctx.line = entry.file.line
|
|
302
|
+
logger[entry.level](ctx, entry.msg)
|
|
303
|
+
} else if ((match = /^([^:]+):(\d+): warning: (.+)/.exec(line))) {
|
|
304
|
+
const [, scriptPath, lineno, msg] = match
|
|
305
|
+
ctx.stack = [{ file: { path: scriptPath }, line: parseInt(lineno, 10) }]
|
|
306
|
+
logger.warn(ctx, msg)
|
|
307
|
+
} else if ((match = /^asciidoctor: ([A-Z]+): (?:[^:]+: line (\d+): )?(.+)/.exec(line))) {
|
|
308
|
+
// NOTE we don't care about the filename in the message since it can only be docfile
|
|
309
|
+
const [, level, lineno, msg] = match
|
|
310
|
+
if (lineno) ctx.line = parseInt(lineno, 10)
|
|
311
|
+
logger[level === 'WARNING' ? 'warn' : level.toLowerCase()](ctx, msg)
|
|
312
|
+
} else {
|
|
313
|
+
logger.info(ctx, line)
|
|
314
|
+
}
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
} else if (convertResult !== undefined) {
|
|
318
|
+
return convertResult
|
|
319
|
+
}
|
|
320
|
+
return fileOrContents === undefined
|
|
321
|
+
? new LazyReadable(() => fs.createReadStream(convertAttributes.outfile))
|
|
322
|
+
: fileOrContents
|
|
323
|
+
}
|
|
324
|
+
|
|
224
325
|
function isBound (obj) {
|
|
225
326
|
if (obj == null) return false
|
|
226
|
-
for (const
|
|
327
|
+
for (const _ in obj) return true
|
|
227
328
|
return false
|
|
228
329
|
}
|
|
229
330
|
|
package/lib/configure.js
CHANGED
|
@@ -6,24 +6,46 @@ function configure (context, ...args) {
|
|
|
6
6
|
|
|
7
7
|
function internalConfigure (converter, config = {}, providers = {}) {
|
|
8
8
|
this.once('componentsRegistered', ({ contentCatalog, assemblerProfiles }) => {
|
|
9
|
-
|
|
9
|
+
if (assemblerProfiles) return
|
|
10
|
+
this.updateVariables({ assemblerProfiles: getAssemblerProfiles(contentCatalog) })
|
|
10
11
|
})
|
|
11
12
|
|
|
12
|
-
this.once('beforeProcess',
|
|
13
|
-
siteAsciiDocConfig.keepSource = true
|
|
14
|
-
})
|
|
13
|
+
this.once('beforeProcess', enableKeepSource)
|
|
15
14
|
|
|
16
15
|
this.once('navigationBuilt', async ({ playbook, contentCatalog }) => {
|
|
17
16
|
const { assembleContent = require('./assemble-content'), ...assembleContentConfig } = providers
|
|
18
|
-
assembleContentConfig.configSource
|
|
19
|
-
|
|
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
|
+
}
|
|
20
28
|
})
|
|
21
29
|
}
|
|
22
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
|
+
|
|
23
43
|
function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
|
|
24
44
|
contentCatalog.getComponents().forEach((component) => {
|
|
25
45
|
component.versions.forEach((componentVersion) => {
|
|
26
|
-
const source =
|
|
46
|
+
const source =
|
|
47
|
+
componentVersion.nav?.origin ??
|
|
48
|
+
[...(componentVersion.origins ?? [])].find((it) => it.descriptor?.ext?.assembler)
|
|
27
49
|
const assemblerConfig = getAssemblerConfigFromDescriptor(source?.descriptor)
|
|
28
50
|
if (!assemblerConfig) return
|
|
29
51
|
const componentVersionRef = `${componentVersion.version}@${componentVersion.name}`
|
|
@@ -64,14 +86,10 @@ function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
|
|
|
64
86
|
}
|
|
65
87
|
|
|
66
88
|
function getAssemblerConfigFromDescriptor (descriptor = {}) {
|
|
67
|
-
|
|
89
|
+
const assemblerConfig = descriptor.ext?.assembler
|
|
68
90
|
if (!assemblerConfig) return
|
|
69
|
-
if (Array.isArray(assemblerConfig))
|
|
70
|
-
|
|
71
|
-
} else {
|
|
72
|
-
assemblerConfig = [assemblerConfig]
|
|
73
|
-
}
|
|
74
|
-
return assemblerConfig
|
|
91
|
+
if (!Array.isArray(assemblerConfig)) return [assemblerConfig]
|
|
92
|
+
if (assemblerConfig.length) return assemblerConfig
|
|
75
93
|
}
|
|
76
94
|
|
|
77
95
|
function initNavFile (file, component, version, index) {
|
|
@@ -36,12 +36,13 @@ function compilePatterns (patterns) {
|
|
|
36
36
|
})
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
function filterComponentVersions (components, patterns) {
|
|
39
|
+
function filterComponentVersions (components, { names: patterns, prereleases = true }) {
|
|
40
40
|
if (!patterns.length) return []
|
|
41
41
|
const rxs = compilePatterns(patterns)
|
|
42
42
|
return components.reduce((accum, { latest, versions }) => {
|
|
43
43
|
accum.push(
|
|
44
44
|
...versions.filter((version) => {
|
|
45
|
+
if (!prereleases && version.prerelease && version !== latest) return false
|
|
45
46
|
let matched
|
|
46
47
|
for (const rx of rxs) {
|
|
47
48
|
let voteIfMatched
|
package/lib/load-config.js
CHANGED
|
@@ -5,29 +5,49 @@ const fsp = require('node:fs/promises')
|
|
|
5
5
|
const os = require('node:os')
|
|
6
6
|
const yaml = require('js-yaml')
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const ASSEMBLY_KEYS = [
|
|
9
|
+
'rootLevel',
|
|
10
|
+
'insertStartPage',
|
|
11
|
+
'sectionMergeStrategy',
|
|
12
|
+
'linkReferenceStyle',
|
|
13
|
+
'dropExplicitXrefText',
|
|
14
|
+
]
|
|
15
|
+
const CAMEL_CASE_STOP_PATHS = ['asciidoc.attributes', 'assembly.attributes']
|
|
16
|
+
const PACKAGE_NAME = require('../package.json').name
|
|
17
|
+
|
|
18
|
+
function loadConfig (playbook, configSource, preferredQualifier = '') {
|
|
19
|
+
let resolvedConfigSource
|
|
9
20
|
return (
|
|
10
|
-
configSource
|
|
21
|
+
configSource?.constructor === Object
|
|
11
22
|
? Promise.resolve(configSource)
|
|
12
|
-
:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
+
})
|
|
34
|
+
.then((exists) => {
|
|
35
|
+
if (!exists) {
|
|
36
|
+
let logger
|
|
37
|
+
if (configSource && (logger = this?.getLogger?.(PACKAGE_NAME))) {
|
|
38
|
+
const ctx = { file: { path: configSource } }
|
|
39
|
+
logger.warn(ctx, 'Could not resolve config file; reverting to default settings')
|
|
40
|
+
}
|
|
41
|
+
return {}
|
|
42
|
+
}
|
|
43
|
+
return fsp
|
|
44
|
+
.readFile(resolvedConfigSource)
|
|
45
|
+
.then((data) =>
|
|
46
|
+
Object.assign(camelCaseKeys(yaml.load(data), CAMEL_CASE_STOP_PATHS), { file: resolvedConfigSource })
|
|
47
|
+
)
|
|
48
|
+
})
|
|
21
49
|
).then((config) => {
|
|
22
|
-
if (config.enabled === false) return
|
|
23
|
-
let asciidocAttrs
|
|
24
|
-
if (!config.asciidoc) {
|
|
25
|
-
config.asciidoc = { attributes: (asciidocAttrs = {}) }
|
|
26
|
-
} else if (!(asciidocAttrs = config.asciidoc.attributes)) {
|
|
27
|
-
config.asciidoc.attributes = asciidocAttrs = {}
|
|
28
|
-
}
|
|
29
|
-
if (!('revdate' in asciidocAttrs)) asciidocAttrs.revdate = getLocalDate()
|
|
30
|
-
asciidocAttrs['page-partial'] = null
|
|
50
|
+
if (config.enabled === false) return config
|
|
31
51
|
const remapComponentVersionsKey = !('componentVersionFilter' in config)
|
|
32
52
|
const componentVersionFilter = (config.componentVersionFilter ??= {})
|
|
33
53
|
if (remapComponentVersionsKey && 'componentVersions' in config) {
|
|
@@ -42,25 +62,49 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
|
|
|
42
62
|
const remapAssemblyKeys = !('assembly' in config)
|
|
43
63
|
const assembly = (config.assembly ??= {})
|
|
44
64
|
if (remapAssemblyKeys) {
|
|
45
|
-
for (const key of
|
|
65
|
+
for (const key of ASSEMBLY_KEYS) {
|
|
46
66
|
if (!(key in config)) continue
|
|
47
67
|
assembly[key] = config[key]
|
|
48
68
|
delete config[key]
|
|
49
69
|
}
|
|
50
70
|
}
|
|
51
|
-
|
|
52
|
-
|
|
71
|
+
let assemblyAttrs
|
|
72
|
+
if ('attributes' in assembly) {
|
|
73
|
+
assemblyAttrs = assembly.attributes ?? {}
|
|
74
|
+
if ('asciidoc' in config) delete config.asciidoc
|
|
75
|
+
} else if ('asciidoc' in config) {
|
|
76
|
+
assemblyAttrs = assembly.attributes = config.asciidoc?.attributes ?? {}
|
|
77
|
+
delete config.asciidoc
|
|
78
|
+
} else {
|
|
79
|
+
assemblyAttrs = assembly.attributes = {}
|
|
80
|
+
}
|
|
81
|
+
assemblyAttrs['page-partial'] = null
|
|
82
|
+
config.asciidoc = Object.defineProperty({}, 'attributes', {
|
|
83
|
+
get: function () {
|
|
84
|
+
return this.assembly.attributes
|
|
85
|
+
}.bind(config),
|
|
86
|
+
})
|
|
87
|
+
if (!('doctype' in assembly)) assembly.doctype = 'doctype' in assemblyAttrs ? assemblyAttrs.doctype : 'book'
|
|
88
|
+
delete assemblyAttrs.doctype
|
|
53
89
|
if (!('rootLevel' in assembly)) assembly.rootLevel = 0
|
|
54
90
|
if (!('insertStartPage' in assembly)) assembly.insertStartPage = true
|
|
55
91
|
if (['discrete', 'fuse', 'enclose'].indexOf(assembly.sectionMergeStrategy) < 0) {
|
|
56
92
|
assembly.sectionMergeStrategy = 'discrete'
|
|
57
93
|
}
|
|
94
|
+
if (['relative', 'root-relative', 'absolute'].indexOf(assembly.linkReferenceStyle) < 0) {
|
|
95
|
+
assembly.linkReferenceStyle = 'absolute'
|
|
96
|
+
}
|
|
97
|
+
if (['always', 'if-redundant', 'never'].indexOf(assembly.dropExplicitXrefText) < 0) {
|
|
98
|
+
assembly.dropExplicitXrefText = 'never'
|
|
99
|
+
}
|
|
100
|
+
assembly.revdate = getLocalDate()
|
|
58
101
|
const build = (config.build ??= {})
|
|
59
102
|
if (build.dir === '$' + '{playbook.output.dir}') {
|
|
60
103
|
throw new Error('Not implemented')
|
|
61
104
|
}
|
|
62
105
|
build.dir &&= expandPath(build.dir, { dot: playbook.dir })
|
|
63
|
-
|
|
106
|
+
// used as cwd of command (and any scripts it requires)
|
|
107
|
+
build.cwd = build.cwd == null ? playbook.dir : expandPath(build.cwd, { dot: playbook.dir })
|
|
64
108
|
if (!('clean' in build) && 'output' in playbook) build.clean = playbook.output.clean
|
|
65
109
|
if (!('publish' in build)) build.publish = true
|
|
66
110
|
if ('keepAggregateSource' in build) {
|
|
@@ -70,6 +114,14 @@ function loadConfig (playbook, configSource = './antora-assembler.yml') {
|
|
|
70
114
|
if (!build.processLimit) {
|
|
71
115
|
build.processLimit = 'processLimit' in build ? Infinity : Math.round(os.cpus().length * 0.5)
|
|
72
116
|
}
|
|
117
|
+
if ('stderr' in build) {
|
|
118
|
+
if (build.stderr === 'log') {
|
|
119
|
+
build.stderr = 'buffer'
|
|
120
|
+
build.stderrSink = 'log'
|
|
121
|
+
} else if (!['ignore', 'print'].includes(build.stderr)) {
|
|
122
|
+
delete build.stderr
|
|
123
|
+
}
|
|
124
|
+
}
|
|
73
125
|
return config
|
|
74
126
|
})
|
|
75
127
|
}
|
|
@@ -86,6 +138,13 @@ function camelCaseKeys (o, stopPaths = [], p = undefined) {
|
|
|
86
138
|
return accum
|
|
87
139
|
}
|
|
88
140
|
|
|
141
|
+
function fileExists (path) {
|
|
142
|
+
return fsp.access(path).then(
|
|
143
|
+
() => true,
|
|
144
|
+
() => false
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
89
148
|
function getLocalDate (now = new Date()) {
|
|
90
149
|
return new Date(now - now.getTimezoneOffset() * 60000).toISOString().split('T')[0]
|
|
91
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
|