@antora/assembler 1.0.0-rc.1 → 1.0.0-rc.10

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.
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
- const fs = require('node:fs')
4
- const { promises: fsp } = fs
3
+ const compileConversionAttributes = require('./compile-conversion-attributes')
4
+ const fsp = require('node:fs/promises')
5
5
  const LazyReadable = require('./util/lazy-readable')
6
6
  const loadConfig = require('./load-config')
7
7
  const logCommand = require('./log-command')
@@ -11,47 +11,56 @@ 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 = { new: () => ({}), null: () => null, void: () => undefined }
15
15
  const PACKAGE_NAME = require('../package.json').name
16
16
  const NEWLINE_RX = /(?:\r?\n)+/
17
17
 
18
- async function assembleContent (playbook, contentCatalog, converter, { configSource, navigationCatalog }) {
18
+ async function assembleContent (playbook, contentCatalog, converter, providers) {
19
+ const { configSource, navigationCatalog, componentVersionFilter } = providers
19
20
  const {
20
21
  convert = converter,
21
22
  getDefaultCommand,
22
23
  extname: targetExtname = '',
23
24
  backend: targetBackend = targetExtname.substring(1),
25
+ format: targetFormat = targetBackend,
24
26
  embedReferenceStyle = 'relative',
25
27
  mediaType: targetMediaType,
26
- loggerName = PACKAGE_NAME,
28
+ loggerName = `${PACKAGE_NAME} [${targetBackend}-exporter]`,
29
+ xmlCompliant,
27
30
  } = converter ?? {}
28
- const assemblerConfig = await loadConfig.call(this, playbook, configSource, '-' + targetBackend)
29
- if (assemblerConfig.enabled === false) return []
31
+ const assemblerConfig = await loadConfig.call(this, configSource, playbook, targetBackend ? '-' + targetBackend : '')
32
+ if (assemblerConfig.enabled === false || !contentCatalog.publishableFamilies?.has('export')) return []
30
33
  const context = isBound(this)
31
34
  ? this
32
35
  : {
33
36
  getFunctions: invariably.new,
34
37
  getLogger: invariably.void,
35
38
  getVariables: invariably.new,
39
+ require,
36
40
  }
37
41
  const generatorFunctions = context.getFunctions()
38
- const { loadAsciiDoc = require('@antora/asciidoc-loader') } = generatorFunctions
42
+ const { loadAsciiDoc = context.require('@antora/asciidoc-loader') } = generatorFunctions
39
43
  const { assembly: assemblyConfig, build: buildConfig } = assemblerConfig
40
44
  assemblyConfig.embedReferenceStyle = embedReferenceStyle
45
+ if (xmlCompliant) assemblyConfig.xmlIds ??= true
41
46
  const profile = (assemblyConfig.profile ??= targetBackend)
42
- const intrinsicAttributes = { 'loader-assembler': '' }
43
- buildConfig.cwd ??= process.cwd()
47
+ const intrinsicAttributes = { 'loader-assembler': '', 'assembler-root-level': assemblyConfig.rootLevel }
48
+ const cwd = (buildConfig.cwd ??= process.cwd())
44
49
  if (profile) {
45
50
  buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), `build/assembler/${profile}`)
46
51
  intrinsicAttributes[`assembler-profile-${profile}`] = ''
47
52
  intrinsicAttributes['assembler-profile'] = profile
48
- if (targetBackend) {
49
- intrinsicAttributes[`assembler-backend-${targetBackend}`] = ''
50
- intrinsicAttributes['assembler-backend'] = targetBackend
51
- }
52
53
  } else {
53
54
  buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), 'build/assembler/_')
54
55
  }
56
+ if (targetBackend) {
57
+ intrinsicAttributes[`assembler-backend-${targetBackend}`] = ''
58
+ intrinsicAttributes['assembler-backend'] = targetBackend
59
+ }
60
+ if (targetFormat) {
61
+ intrinsicAttributes[`assembler-format-${targetFormat}`] = ''
62
+ intrinsicAttributes['assembler-format'] = targetFormat
63
+ }
55
64
  if (targetExtname) {
56
65
  const targetFiletype = targetExtname.substring(1)
57
66
  intrinsicAttributes[`assembler-filetype-${targetFiletype}`] = ''
@@ -62,38 +71,45 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
62
71
  loadAsciiDoc,
63
72
  contentCatalog,
64
73
  assemblerConfig,
65
- generateSelectAssemblyProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog)
74
+ generateSelectAssemblerProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog),
75
+ typeof componentVersionFilter === 'function' ? componentVersionFilter : undefined
66
76
  )
67
77
  if (!(assemblyFiles.length && typeof convert === 'function')) return assemblyFiles
68
- if (buildConfig.command == null && typeof getDefaultCommand === 'function') {
69
- buildConfig.command = await getDefaultCommand(buildConfig.cwd)
70
- }
71
- const { publishSite: publishFiles = require('@antora/site-publisher') } = generatorFunctions
78
+ buildConfig.command ??= typeof getDefaultCommand === 'function' ? await getDefaultCommand(cwd, playbook) : null
79
+ const { publishSite: publishFiles = context.require('@antora/site-publisher') } = generatorFunctions
72
80
  await prepareWorkspace(publishFiles, assemblyFiles, buildConfig)
73
- const helpers = { logCommand: logCommand.bind(context, loggerName), runCommand }
81
+ const helpers = { runCommand }
82
+ Object.defineProperty(helpers, 'logger', {
83
+ get: () => context?.getLogger(loggerName),
84
+ })
85
+ Object.defineProperty(helpers, 'logCommand', {
86
+ get: () => logCommand.bind(null, helpers.logger),
87
+ })
74
88
  const boundConvert = convert.bind(context)
89
+ const boundResolveFileOrContents = resolveFileOrContents.bind(context)
75
90
  return new PromiseQueue({ concurrency: buildConfig.processLimit })
76
91
  .add(
77
92
  assemblyFiles.map((doc) => async () => {
78
93
  const relativeToOutput = embedReferenceStyle === 'output-relative'
79
- const convertAttributes = prepareConvertAttributes(doc, targetExtname, relativeToOutput, assemblerConfig)
80
- if (buildConfig.mkdirs) await fsp.mkdir(convertAttributes.outdir, { recursive: true, force: true })
81
- return boundConvert(doc, convertAttributes, buildConfig, helpers).then((result) => {
82
- const fileOrContents = resolveFileOrContents.call(context, result, convertAttributes, buildConfig, loggerName)
83
- return coerceToExportFormat(doc, targetBackend, targetExtname, targetMediaType, fileOrContents)
84
- })
94
+ const attributes = await compileConversionAttributes(doc, targetExtname, assemblerConfig, relativeToOutput)
95
+ return boundConvert(doc, attributes, buildConfig, helpers).then((result) =>
96
+ boundResolveFileOrContents(result, attributes, buildConfig, loggerName).then((fileOrContents) =>
97
+ coerceToExportFormat(doc, targetBackend, targetExtname, targetMediaType, fileOrContents)
98
+ )
99
+ )
85
100
  })
86
101
  )
87
102
  .toPromise()
88
103
  .then((files) => {
89
- if (!buildConfig.publish) {
104
+ const { publish, qualifyExports } = buildConfig
105
+ if (!publish) {
90
106
  for (const file of files) delete file.assembler.assembled
91
107
  return files
92
108
  }
93
- const qualifyExports = buildConfig.qualifyExports
94
109
  return files.map((file) => {
95
- const pages = file.assembler.assembled.pages
110
+ const pages = file.isNull() ? undefined : file.assembler.assembled.pages
96
111
  delete file.assembler.assembled
112
+ if (!pages) return file
97
113
  file = contentCatalog.addFile(file)
98
114
  const extname = file.extname
99
115
  const download = file.assembler.downloadStem + extname
@@ -106,6 +122,7 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
106
122
  const assemblerMeta = (page.assembler ??= {})
107
123
  const exports = (assemblerMeta.exports ??= [])
108
124
  const exportEntry = { fragment, file }
125
+ if (fragment === pages.rootPageFragment) exportEntry.root = true
109
126
  const insertIdx = exports.findIndex(
110
127
  ({ file: candidate }) =>
111
128
  !(candidate.src.component === page.src.component && candidate.src.version === page.src.version)
@@ -125,11 +142,11 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
125
142
  }
126
143
 
127
144
  /**
128
- * Generates a function that selects the active assembly profile, initializes an assembly model using
145
+ * Generates a function that selects the active assembler profile, initializes an assembly model using
129
146
  * the keys from the profile as well as any inherited shared keys, builds the navigation for the assembly, and
130
147
  * returns the initialized assembly model. The assembly model is further populated after the call to this function.
131
148
  */
132
- function generateSelectAssemblyProfile (context, contentCatalog, baseModel, intrinsicAttributes, navigationCatalog) {
149
+ function generateSelectAssemblerProfile (context, contentCatalog, baseModel, intrinsicAttributes, navigationCatalog) {
133
150
  const logger = context.getLogger?.(PACKAGE_NAME)
134
151
  const { assemblerProfiles } = context.getVariables()
135
152
  if (!assemblerProfiles) {
@@ -140,7 +157,7 @@ function generateSelectAssemblyProfile (context, contentCatalog, baseModel, intr
140
157
  }
141
158
  }
142
159
  const boundSendToLog = sendToLog.bind(logger)
143
- const { buildAlternateNavigation = require('@antora/navigation-builder').buildAlternateNavigation } =
160
+ const { buildAlternateNavigation = context.require('@antora/navigation-builder').buildAlternateNavigation } =
144
161
  context.getFunctions()
145
162
  return (componentVersion) => {
146
163
  const componentVersionProfiles = assemblerProfiles.get(componentVersion.version + '@' + componentVersion.name)
@@ -148,77 +165,39 @@ function generateSelectAssemblyProfile (context, contentCatalog, baseModel, intr
148
165
  const navigation = navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version)
149
166
  const attributes = Object.assign({}, baseModel.attributes)
150
167
  const model = Object.assign({}, baseModel, overrides, { attributes, logger })
151
- if (!overrides) return navigation ? Object.assign(model, { navigation }) : model
168
+ if (!overrides) return Object.assign(model, { navigation: navigation || componentVersion.navigation })
169
+ if ('rootLevel' in overrides) attributes['assembler-root-level'] = overrides.rootLevel
152
170
  delete model.navFiles
153
171
  delete model.messages
154
- if (overrides.attributes) Object.entries(overrides.attributes).forEach(([name, val]) => (attributes[name] = val))
172
+ if (overrides.attributes) {
173
+ for (const [name, val] of Object.entries(overrides.attributes)) {
174
+ if (!(name in intrinsicAttributes)) attributes[name] = val
175
+ }
176
+ }
155
177
  if (navigation) return Object.assign(model, { navigation })
156
178
  overrides.messages?.forEach(boundSendToLog)
157
179
  const navFiles = overrides.navFiles
158
180
  if (!navFiles) return Object.assign(model, { navigation: componentVersion.navigation })
159
181
  if (!navFiles.length) return Object.assign(model, { navigation: [] })
160
182
  model.navigation = buildAlternateNavigation(contentCatalog, componentVersion, navFiles, {
161
- attributes: intrinsicAttributes,
183
+ attributes: model.attributes,
162
184
  })
163
185
  return model
164
186
  }
165
187
  }
166
188
 
167
- function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assemblerConfig) {
168
- const {
169
- asciidoc: { attributes: docAttributes } = { attributes: {} },
170
- extname: docfilesuffix,
171
- path: reldocfile,
172
- src: { family, relative },
173
- } = doc
174
- const { cwd = process.cwd(), dir = cwd } = assemblerConfig.build
175
- const docname = family + '$' + relative.substring(0, relative.length - docfilesuffix.length)
176
- const docfile = ospath.join(dir, reldocfile)
177
- const outdir = ospath.dirname(docfile)
178
- const docdir = relativeToOutput ? dir : outdir
179
- const outfile = docfile.substring(0, docfile.length - docfilesuffix.length) + targetExtname
180
- const attributes = Object.assign({ revdate: `${assemblerConfig.assembly.revdate}@` }, docAttributes, {
181
- docdir,
182
- docfile,
183
- docfilesuffix,
184
- 'docname@': docname,
185
- imagesdir: '',
186
- outdir,
187
- outfile,
188
- outfilesuffix: targetExtname,
189
- toArgs (optionFlag) {
190
- const args = []
191
- for (let [name, val] of Object.entries(this)) {
192
- if (val) {
193
- val = name + '=' + val
194
- } else if (val === '') {
195
- if (name === 'asciidoctor-log-integration') {
196
- args.push('-r', require.resolve('#asciidoctor-log-adapter'))
197
- continue
198
- }
199
- val = name
200
- } else {
201
- val = `${name}!${val === false ? '@' : ''}`
202
- }
203
- args.push(optionFlag, val)
204
- }
205
- return args
206
- },
207
- })
208
- return Object.defineProperty(attributes, 'toArgs', { enumerable: false })
209
- }
210
-
211
- function coerceToExportFormat (originalFile, targetBackend, targetExtname, targetMediaType, fileOrContents) {
189
+ function coerceToExportFormat (assemblyFile, targetBackend, targetExtname, targetMediaType, fileOrContents) {
212
190
  const file =
213
191
  fileOrContents == null || Buffer.isBuffer(fileOrContents) || typeof fileOrContents.pipe === 'function'
214
- ? Object.assign(originalFile, { contents: fileOrContents })
192
+ ? Object.assign(assemblyFile, { contents: fileOrContents })
215
193
  : fileOrContents
216
194
  ;(file.assembler ??= {}).backend = targetBackend
217
195
  if (file.extname === targetExtname) return file
218
196
  const { path: sourcePath, extname: sourceExtname } = file
219
197
  const relativeWithoutExtname = file.src.relative.substring(0, file.src.relative.length - sourceExtname.length)
220
198
  const newPath = sourcePath.substring(0, sourcePath.length - sourceExtname.length) + targetExtname
221
- Object.assign(file, { mediaType: (file.src.mediaType = targetMediaType), path: newPath })
199
+ Object.assign(file, { mediaType: (file.src.mediaType = targetMediaType), path: (file.src.path = newPath) })
200
+ delete file.src.abspath
222
201
  file.src.basename = path.basename((file.src.relative = relativeWithoutExtname + (file.src.extname = targetExtname)))
223
202
  return file
224
203
  }
@@ -241,8 +220,9 @@ function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
241
220
  }
242
221
  if (keepSource) {
243
222
  for (const file of assemblyFiles) {
244
- file.src.contents = file.contents
245
223
  file.out = { path: file.path }
224
+ file.src.contents = file.contents
225
+ file.src.abspath = ospath.join(dir, file.path)
246
226
  files.push(file)
247
227
  }
248
228
  }
@@ -253,7 +233,7 @@ function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
253
233
  })
254
234
  }
255
235
 
256
- function resolveFileOrContents (convertResult, convertAttributes, buildConfig, loggerName) {
236
+ function resolveFileOrContents (convertResult, attributes, buildConfig, loggerName) {
257
237
  let fileOrContents
258
238
  if (convertResult?.status != null) {
259
239
  if ('file' in convertResult) {
@@ -264,12 +244,14 @@ function resolveFileOrContents (convertResult, convertAttributes, buildConfig, l
264
244
  fileOrContents = convertResult.stdout
265
245
  }
266
246
  let logger, match
267
- if (buildConfig.stderrSink === 'log' && convertResult.stderr.length && (logger = this.getLogger(loggerName))) {
268
- const docfile = convertAttributes.docfile
247
+ if (buildConfig.stderrSink === 'log' && convertResult.stderr?.length && (logger = this.getLogger(loggerName))) {
248
+ const docfile = attributes.docfile
269
249
  const command = buildConfig.command
250
+ const file = { path: docfile }
270
251
  const stderr = convertResult.stderr.toString().trimEnd()
252
+ const additionalLines = ['Additional stderr lines from command:']
271
253
  stderr.split(NEWLINE_RX).forEach((line) => {
272
- const ctx = { command, file: { path: docfile } }
254
+ const ctx = { command, file }
273
255
  if (line.charAt() === '{' && line.charAt(line.length - 1) === '}') {
274
256
  const entry = JSON.parse(line)
275
257
  if (entry.name) ctx.program = entry.name
@@ -285,16 +267,17 @@ function resolveFileOrContents (convertResult, convertAttributes, buildConfig, l
285
267
  if (lineno) ctx.line = parseInt(lineno, 10)
286
268
  logger[level === 'WARNING' ? 'warn' : level.toLowerCase()](ctx, msg)
287
269
  } else {
288
- logger.info(ctx, line)
270
+ additionalLines.push(line)
289
271
  }
290
272
  })
273
+ if (additionalLines.length > 1) logger.info({ command, file }, additionalLines.join('\n'))
291
274
  }
292
275
  } else if (convertResult !== undefined) {
293
- return convertResult
276
+ return Promise.resolve(convertResult)
294
277
  }
295
- return fileOrContents === undefined
296
- ? new LazyReadable(() => fs.createReadStream(convertAttributes.outfile))
297
- : fileOrContents
278
+ if (fileOrContents !== undefined) return Promise.resolve(fileOrContents)
279
+ const outfile = attributes.outfile
280
+ return fsp.access(outfile).then(() => new LazyReadable(outfile), invariably.null)
298
281
  }
299
282
 
300
283
  function isBound (obj) {
@@ -0,0 +1,76 @@
1
+ 'use strict'
2
+
3
+ const fsp = require('node:fs/promises')
4
+ const ospath = require('node:path')
5
+
6
+ async function compileConversionAttributes (doc, targetExtname, assemblerConfig, relativeToOutput = false) {
7
+ const {
8
+ asciidoc: { attributes: docAttributes } = { attributes: {} },
9
+ extname: docfilesuffix,
10
+ path: reldocfile,
11
+ src: { family, relative },
12
+ } = doc
13
+ const { cwd = process.cwd(), dir = cwd, mkdirs } = assemblerConfig.build
14
+ const docname = family + '$' + relative.substring(0, relative.length - docfilesuffix.length)
15
+ const docfile = (doc.src.path = ospath.join(dir, reldocfile))
16
+ const outdir = ospath.dirname(docfile)
17
+ const outfile = docfile.substring(0, docfile.length - docfilesuffix.length) + targetExtname
18
+ const docdir = relativeToOutput ? dir : outdir
19
+ //const imagesoutdir = ospath.join(outdir, '..', '_images/generated')
20
+ const imagesoutdir = outdir
21
+ const attributes = Object.assign({ revdate: `${assemblerConfig.assembly.revdate}@` }, docAttributes, {
22
+ docdir,
23
+ docfile,
24
+ docfilesuffix,
25
+ docname,
26
+ imagesdir: '',
27
+ imagesoutdir,
28
+ outdir,
29
+ outfile,
30
+ outfilesuffix: targetExtname,
31
+ toArgs (attributeOptionFlag, outputOptionFlag) {
32
+ const args = []
33
+ let requireAsciidoctorLogAdapter
34
+ for (const [name, val] of Object.entries(this)) {
35
+ let optionValue
36
+ if (name === 'asciidoctor-log-integration') {
37
+ if ((val ?? false) !== false) requireAsciidoctorLogAdapter = val
38
+ continue
39
+ } else if ((val ?? false) === false) {
40
+ optionValue = `${name}!${val === false ? '=@' : ''}`
41
+ } else {
42
+ optionValue = val === '' ? name : name + '=' + val
43
+ }
44
+ args.push(attributeOptionFlag, optionValue)
45
+ }
46
+ if (requireAsciidoctorLogAdapter) args.push('-r', requireAsciidoctorLogAdapter)
47
+ if (outputOptionFlag) args.push(outputOptionFlag, this.outfile)
48
+ return args
49
+ },
50
+ })
51
+ Object.defineProperty(attributes, 'outfilesuffix', {
52
+ get () {
53
+ return ospath.extname(this.outfile)
54
+ },
55
+ set (value) {
56
+ const outfile = this.outfile
57
+ const extname = ospath.extname(outfile)
58
+ if (extname && outfile.endsWith(extname)) this.outfile = outfile.substring(0, outfile.length - extname.length)
59
+ this.outfile += value
60
+ },
61
+ })
62
+ Object.defineProperty(attributes, 'toArgs', { enumerable: false })
63
+ if (mkdirs) await fsp.mkdir(outdir, { recursive: true, force: true })
64
+ if (attributes['asciidoctor-log-integration'] != null) {
65
+ const scriptSourcePath = require.resolve('#asciidoctor-log-adapter')
66
+ let scriptTargetPath = scriptSourcePath
67
+ if (attributes['asciidoctor-log-integration'] === 'copy-to-build-dir') {
68
+ scriptTargetPath = ospath.join(dir, 'asciidoctor-log-adapter.rb')
69
+ await fsp.cp(scriptSourcePath, scriptTargetPath, { force: true, recursive: true })
70
+ }
71
+ attributes['asciidoctor-log-integration'] = scriptTargetPath
72
+ }
73
+ return attributes
74
+ }
75
+
76
+ module.exports = compileConversionAttributes
package/lib/configure.js CHANGED
@@ -7,24 +7,26 @@ function configure (context, ...args) {
7
7
  }
8
8
 
9
9
  function internalConfigure (converter, config = {}, providers = {}) {
10
- this.once('componentsRegistered', ({ contentCatalog, assemblerProfiles }) => {
11
- contentCatalog.publishableFamilies.add('export')
12
- if (assemblerProfiles) return
13
- this.updateVariables({ assemblerProfiles: getAssemblerProfiles(contentCatalog) })
14
- })
10
+ if (!this.listeners('beforeProcess').includes(enableKeepSource)) {
11
+ this.once('componentsRegistered', ({ contentCatalog, assemblerProfiles }) => {
12
+ contentCatalog.publishableFamilies.add('export')
13
+ if (!assemblerProfiles) this.updateVariables({ assemblerProfiles: getAssemblerProfiles(contentCatalog) })
14
+ })
15
15
 
16
- this.once('beforeProcess', enableKeepSource)
16
+ this.once('beforeProcess', enableKeepSource)
17
+ }
17
18
 
18
19
  this.once('navigationBuilt', async ({ playbook, contentCatalog }) => {
19
- const { assembleContent = require('./assemble-content'), ...assembleContentConfig } = providers
20
- if (assembleContentConfig.configSource?.constructor === Object) {
21
- await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentConfig)
20
+ const { assembleContent = require('./assemble-content'), ...assembleContentProviders } = providers
21
+ if (config.configSource?.constructor === Object) {
22
+ assembleContentProviders.configSource = config.configSource
23
+ await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentProviders)
22
24
  } else {
23
25
  const singleConfig = !('configFiles' in config)
24
26
  const configFiles = singleConfig ? config.configFile : config.configFiles
25
27
  for (const configSource of Array.isArray(configFiles) ? configFiles : [configFiles]) {
26
- const assembleContentConfigWithConfigSource = Object.assign({}, assembleContentConfig, { configSource })
27
- await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentConfigWithConfigSource)
28
+ const assembleContentProvidersWithConfigSource = Object.assign({}, assembleContentProviders, { configSource })
29
+ await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentProvidersWithConfigSource)
28
30
  if (singleConfig) break
29
31
  }
30
32
  }
@@ -51,8 +53,9 @@ function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
51
53
  [...(componentVersion.origins ?? [])].find((it) => it.descriptor?.ext?.assembler)
52
54
  const assemblerConfig = getAssemblerConfigFromDescriptor(source?.descriptor)
53
55
  if (!assemblerConfig) return
54
- const componentVersionRef = `${componentVersion.version}@${componentVersion.name}`
55
- const filesByPath = componentVersion.files.reduce((accum, it) => accum.set(it.path, it), new Map())
56
+ const { name: componentName, version, files } = componentVersion
57
+ const componentVersionKey = `${version}@${componentName}`
58
+ const filesByPath = files.reduce((accum, it) => accum.set(it.path, it), new Map())
56
59
  const componentVersionProfiles = assemblerConfig.reduce((accum, entry) => {
57
60
  const data = {}
58
61
  const profile = entry.profile ?? undefined
@@ -67,8 +70,9 @@ function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
67
70
  data.navFiles = (nav.length ? [...new Set(nav)] : nav).reduce((navFiles, path_) => {
68
71
  const navFile = filesByPath.get(path_)
69
72
  if (navFile) {
70
- navFiles.push(initNavFile(navFile, component.name, componentVersion.version, navFiles.length))
73
+ navFiles.push(initNavFile(navFile, componentName, version, navFiles.length))
71
74
  } else {
75
+ const componentVersionRef = `${version === 'master' ? '' : version}@${componentName}`
72
76
  ;(data.messages ??= []).push([
73
77
  'warn',
74
78
  { source },
@@ -82,7 +86,7 @@ function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
82
86
  }
83
87
  return accum.set(profile, data)
84
88
  }, new Map())
85
- assemblerProfiles.set(componentVersionRef, componentVersionProfiles)
89
+ assemblerProfiles.set(componentVersionKey, componentVersionProfiles)
86
90
  })
87
91
  })
88
92
  return assemblerProfiles
package/lib/constants.js CHANGED
@@ -9,6 +9,7 @@ module.exports = Object.freeze({
9
9
  'insertStartPage',
10
10
  'profile',
11
11
  'rootLevel',
12
+ 'rootPageStyle',
12
13
  'sectionMergeStrategy',
13
14
  'xmlIds',
14
15
  ],
@@ -19,4 +20,5 @@ module.exports = Object.freeze({
19
20
  'rootLevel',
20
21
  'sectionMergeStrategy',
21
22
  ],
23
+ CAMEL_CASE_STOP_KEYS: ['asciidoc.attributes', 'assembly.attributes'],
22
24
  })
@@ -5,7 +5,7 @@ const { filterCollection, compilePattern } = require('./util/matcher')
5
5
  const VERSION_SEPARATOR_RX = /@(?!\()(?=(\w)?)/g
6
6
  const US = '\x1f'
7
7
 
8
- function filterComponentVersions (components, { names: patterns, prereleases = true }) {
8
+ function filterComponentVersions ({ names: patterns, prereleases = true }, components) {
9
9
  if (!patterns.length) return []
10
10
  const candidateMap = components.reduce((accum, { name: componentName, latest, versions }) => {
11
11
  for (const version of versions) {
package/lib/index.js CHANGED
@@ -3,4 +3,4 @@
3
3
  const assembleContent = require('./assemble-content')
4
4
  const configure = require('./configure')
5
5
 
6
- module.exports = { assembleContent, configure, configureAssembler: configure }
6
+ module.exports = { assembleContent, configure }
@@ -1,20 +1,21 @@
1
1
  'use strict'
2
2
 
3
+ const deepClone = require('./util/deep-clone')
3
4
  const expandPath = require('@antora/expand-path-helper')
4
5
  const fsp = require('node:fs/promises')
5
6
  const os = require('node:os')
6
7
  const ospath = require('node:path')
7
8
  const yaml = require('js-yaml')
8
9
 
9
- const { LEGACY_ASSEMBLY_KEYS } = require('./constants')
10
- const CAMEL_CASE_STOP_PATHS = ['asciidoc.attributes', 'assembly.attributes']
10
+ const { CAMEL_CASE_STOP_KEYS, LEGACY_ASSEMBLY_KEYS } = require('./constants')
11
11
  const PACKAGE_NAME = require('../package.json').name
12
+ const YAML_SCHEMA = yaml.CORE_SCHEMA.withTags(yaml.mergeTag)
12
13
 
13
- function loadConfig (playbook, configSource, preferredQualifier = '') {
14
+ function loadConfig (configSource, playbook, preferredQualifier = '') {
14
15
  let resolvedConfigSource
15
16
  return (
16
17
  configSource?.constructor === Object
17
- ? Promise.resolve(configSource)
18
+ ? Promise.resolve(deepClone(configSource))
18
19
  : fileExists(
19
20
  (resolvedConfigSource = expandPath(configSource ?? `./antora-assembler${preferredQualifier}.yml`, {
20
21
  dot: playbook.dir,
@@ -36,11 +37,11 @@ function loadConfig (playbook, configSource, preferredQualifier = '') {
36
37
  }
37
38
  return {}
38
39
  }
39
- return fsp
40
- .readFile(resolvedConfigSource)
41
- .then((data) =>
42
- Object.assign(camelCaseKeys(yaml.load(data), CAMEL_CASE_STOP_PATHS), { file: resolvedConfigSource })
43
- )
40
+ return fsp.readFile(resolvedConfigSource).then((data) =>
41
+ Object.assign(camelCaseKeys(yaml.load(data, { schema: YAML_SCHEMA }), CAMEL_CASE_STOP_KEYS), {
42
+ file: resolvedConfigSource,
43
+ })
44
+ )
44
45
  })
45
46
  ).then((config) => {
46
47
  if (config.enabled === false) return config
@@ -52,7 +53,7 @@ function loadConfig (playbook, configSource, preferredQualifier = '') {
52
53
  }
53
54
  if (componentVersionFilter.names == null) {
54
55
  componentVersionFilter.names = ['*']
55
- } else if (typeof componentVersionFilter.names === 'string') {
56
+ } else if (componentVersionFilter.names.constructor === String) {
56
57
  componentVersionFilter.names = componentVersionFilter.names.split(', ')
57
58
  }
58
59
  const remapAssemblyKeys = !('assembly' in config)
@@ -98,14 +99,19 @@ function loadConfig (playbook, configSource, preferredQualifier = '') {
98
99
  build.dir &&= expandPath(build.dir, { dot: playbook.dir })
99
100
  // used as cwd of command (and any scripts it requires)
100
101
  build.cwd = build.cwd == null ? playbook.dir : expandPath(build.cwd, { dot: playbook.dir })
101
- if (build.command?.includes('$')) {
102
- const vars = {
103
- $NODE: process.execPath,
104
- $NPM: ospath.join(process.execPath, '../npm'),
105
- $NPX: ospath.join(process.execPath, '../npx'),
106
- $PWD: build.cwd,
102
+ const command = build.command
103
+ if (command) {
104
+ if (command.constructor !== String) {
105
+ build.command = String(command)
106
+ } else if (~command.indexOf('$')) {
107
+ const vars = {
108
+ $NODE: process.execPath,
109
+ $NPM: ospath.join(process.execPath, '../npm'),
110
+ $NPX: ospath.join(process.execPath, '../npx'),
111
+ $PWD: build.cwd,
112
+ }
113
+ build.command = build.command.replace(/^\$(?:NODE|NP[MX])(?= )|\$PWD\b/g, (ref) => vars[ref])
107
114
  }
108
- build.command = build.command.replace(/^\$(?:NODE|NP[MX])(?= )|\$PWD\b/g, (ref) => vars[ref])
109
115
  }
110
116
  if (!('clean' in build) && 'output' in playbook) build.clean = playbook.output.clean
111
117
  if (!('publish' in build)) build.publish = true
@@ -128,14 +134,14 @@ function loadConfig (playbook, configSource, preferredQualifier = '') {
128
134
  })
129
135
  }
130
136
 
131
- function camelCaseKeys (o, stopPaths = [], p = undefined) {
137
+ function camelCaseKeys (o, stopPaths, p = undefined) {
132
138
  if (Array.isArray(o)) return o.map((it) => camelCaseKeys(it, stopPaths, p))
133
139
  if (o == null || o.constructor !== Object) return o
134
140
  const pathPrefix = p ? p + '.' : ''
135
141
  const accum = {}
136
142
  for (const [k, v] of Object.entries(o)) {
137
143
  const camelKey = k.charAt() + k.substring(1).replace(/_([a-z])/g, (_, l) => l.toUpperCase())
138
- accum[camelKey] = ~stopPaths.indexOf(pathPrefix + camelKey) ? v : camelCaseKeys(v, stopPaths, pathPrefix + camelKey)
144
+ accum[camelKey] = stopPaths.includes(pathPrefix + camelKey) ? v : camelCaseKeys(v, stopPaths, pathPrefix + camelKey)
139
145
  }
140
146
  return accum
141
147
  }
@@ -1,20 +1,18 @@
1
1
  'use strict'
2
2
 
3
- function logCommand (loggerName, command, extraArgsOrAttributeOptionFlag, file, convertAttributes) {
4
- const logger = this?.getLogger(loggerName)
3
+ function logCommand (logger, command, extraArgsOrAttributeOptionFlag, file, attributes) {
5
4
  if (!logger?.isLevelEnabled('debug')) return
6
- const { docfile, 'assembler-filetype': filetype } = convertAttributes
7
5
  const args = (
8
6
  Array.isArray(extraArgsOrAttributeOptionFlag)
9
7
  ? extraArgsOrAttributeOptionFlag
10
8
  : extraArgsOrAttributeOptionFlag
11
- ? convertAttributes.toArgs(extraArgsOrAttributeOptionFlag)
9
+ ? attributes.toArgs(extraArgsOrAttributeOptionFlag)
12
10
  : []
13
11
  ).map((it) => (~it.indexOf(' ') ? `'${it}'` : it))
14
- const ctx = { command: [command].concat(args).join(' '), file: { path: docfile } }
12
+ const ctx = { command: [command].concat(args).join(' '), file: file.src }
15
13
  const msg = `Running external command to export assembly in %s to %s: %s`
16
14
  const componentVersionStr = file.src.version ? `${file.src.version}@${file.src.component}` : file.src.component
17
- logger.debug(ctx, msg, componentVersionStr, filetype, file.src.relative)
15
+ logger.debug(ctx, msg, componentVersionStr, attributes['assembler-filetype'], file.src.relative)
18
16
  }
19
17
 
20
18
  module.exports = logCommand