@antora/assembler 1.0.0-rc.6 → 1.0.0-rc.8

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')
@@ -22,12 +22,13 @@ async function assembleContent (playbook, contentCatalog, converter, providers)
22
22
  getDefaultCommand,
23
23
  extname: targetExtname = '',
24
24
  backend: targetBackend = targetExtname.substring(1),
25
+ format: targetFormat = targetBackend,
25
26
  embedReferenceStyle = 'relative',
26
27
  mediaType: targetMediaType,
27
28
  loggerName = `${PACKAGE_NAME} [${targetBackend}-exporter]`,
28
29
  xmlCompliant,
29
30
  } = converter ?? {}
30
- const assemblerConfig = await loadConfig.call(this, configSource, playbook, '-' + targetBackend)
31
+ const assemblerConfig = await loadConfig.call(this, configSource, playbook, targetBackend ? '-' + targetBackend : '')
31
32
  if (assemblerConfig.enabled === false || !contentCatalog.publishableFamilies?.has('export')) return []
32
33
  const context = isBound(this)
33
34
  ? this
@@ -35,9 +36,10 @@ async function assembleContent (playbook, contentCatalog, converter, providers)
35
36
  getFunctions: invariably.new,
36
37
  getLogger: invariably.void,
37
38
  getVariables: invariably.new,
39
+ require,
38
40
  }
39
41
  const generatorFunctions = context.getFunctions()
40
- const { loadAsciiDoc = require('@antora/asciidoc-loader') } = generatorFunctions
42
+ const { loadAsciiDoc = context.require('@antora/asciidoc-loader') } = generatorFunctions
41
43
  const { assembly: assemblyConfig, build: buildConfig } = assemblerConfig
42
44
  assemblyConfig.embedReferenceStyle = embedReferenceStyle
43
45
  if (xmlCompliant) assemblyConfig.xmlIds ??= true
@@ -48,13 +50,17 @@ async function assembleContent (playbook, contentCatalog, converter, providers)
48
50
  buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), `build/assembler/${profile}`)
49
51
  intrinsicAttributes[`assembler-profile-${profile}`] = ''
50
52
  intrinsicAttributes['assembler-profile'] = profile
51
- if (targetBackend) {
52
- intrinsicAttributes[`assembler-backend-${targetBackend}`] = ''
53
- intrinsicAttributes['assembler-backend'] = targetBackend
54
- }
55
53
  } else {
56
54
  buildConfig.dir ??= ospath.join(playbook.dir ?? process.cwd(), 'build/assembler/_')
57
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
+ }
58
64
  if (targetExtname) {
59
65
  const targetFiletype = targetExtname.substring(1)
60
66
  intrinsicAttributes[`assembler-filetype-${targetFiletype}`] = ''
@@ -65,23 +71,29 @@ async function assembleContent (playbook, contentCatalog, converter, providers)
65
71
  loadAsciiDoc,
66
72
  contentCatalog,
67
73
  assemblerConfig,
68
- generateSelectAssemblyProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog),
74
+ generateSelectAssemblerProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog),
69
75
  typeof componentVersionFilter === 'function' ? componentVersionFilter : undefined
70
76
  )
71
77
  if (!(assemblyFiles.length && typeof convert === 'function')) return assemblyFiles
72
78
  buildConfig.command ??= typeof getDefaultCommand === 'function' ? await getDefaultCommand(cwd, playbook) : null
73
- const { publishSite: publishFiles = require('@antora/site-publisher') } = generatorFunctions
79
+ const { publishSite: publishFiles = context.require('@antora/site-publisher') } = generatorFunctions
74
80
  await prepareWorkspace(publishFiles, assemblyFiles, buildConfig)
75
- 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
+ })
76
88
  const boundConvert = convert.bind(context)
77
89
  const boundResolveFileOrContents = resolveFileOrContents.bind(context)
78
90
  return new PromiseQueue({ concurrency: buildConfig.processLimit })
79
91
  .add(
80
92
  assemblyFiles.map((doc) => async () => {
81
93
  const relativeToOutput = embedReferenceStyle === 'output-relative'
82
- const convertAttributes = await prepareConvertAttributes(doc, targetExtname, relativeToOutput, assemblerConfig)
83
- return boundConvert(doc, convertAttributes, buildConfig, helpers).then((result) =>
84
- boundResolveFileOrContents(result, convertAttributes, buildConfig, loggerName).then((fileOrContents) =>
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) =>
85
97
  coerceToExportFormat(doc, targetBackend, targetExtname, targetMediaType, fileOrContents)
86
98
  )
87
99
  )
@@ -110,6 +122,7 @@ async function assembleContent (playbook, contentCatalog, converter, providers)
110
122
  const assemblerMeta = (page.assembler ??= {})
111
123
  const exports = (assemblerMeta.exports ??= [])
112
124
  const exportEntry = { fragment, file }
125
+ if (fragment === pages.rootPageFragment) exportEntry.root = true
113
126
  const insertIdx = exports.findIndex(
114
127
  ({ file: candidate }) =>
115
128
  !(candidate.src.component === page.src.component && candidate.src.version === page.src.version)
@@ -129,11 +142,11 @@ async function assembleContent (playbook, contentCatalog, converter, providers)
129
142
  }
130
143
 
131
144
  /**
132
- * 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
133
146
  * the keys from the profile as well as any inherited shared keys, builds the navigation for the assembly, and
134
147
  * returns the initialized assembly model. The assembly model is further populated after the call to this function.
135
148
  */
136
- function generateSelectAssemblyProfile (context, contentCatalog, baseModel, intrinsicAttributes, navigationCatalog) {
149
+ function generateSelectAssemblerProfile (context, contentCatalog, baseModel, intrinsicAttributes, navigationCatalog) {
137
150
  const logger = context.getLogger?.(PACKAGE_NAME)
138
151
  const { assemblerProfiles } = context.getVariables()
139
152
  if (!assemblerProfiles) {
@@ -144,7 +157,7 @@ function generateSelectAssemblyProfile (context, contentCatalog, baseModel, intr
144
157
  }
145
158
  }
146
159
  const boundSendToLog = sendToLog.bind(logger)
147
- const { buildAlternateNavigation = require('@antora/navigation-builder').buildAlternateNavigation } =
160
+ const { buildAlternateNavigation = context.require('@antora/navigation-builder').buildAlternateNavigation } =
148
161
  context.getFunctions()
149
162
  return (componentVersion) => {
150
163
  const componentVersionProfiles = assemblerProfiles.get(componentVersion.version + '@' + componentVersion.name)
@@ -173,73 +186,6 @@ function generateSelectAssemblyProfile (context, contentCatalog, baseModel, intr
173
186
  }
174
187
  }
175
188
 
176
- async function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assemblerConfig) {
177
- const {
178
- asciidoc: { attributes: docAttributes } = { attributes: {} },
179
- extname: docfilesuffix,
180
- path: reldocfile,
181
- src: { family, relative },
182
- } = doc
183
- const { cwd = process.cwd(), dir = cwd, mkdirs } = assemblerConfig.build
184
- const docname = family + '$' + relative.substring(0, relative.length - docfilesuffix.length)
185
- const docfile = ospath.join(dir, reldocfile)
186
- const outdir = ospath.dirname(docfile)
187
- const docdir = relativeToOutput ? dir : outdir
188
- const outfile = docfile.substring(0, docfile.length - docfilesuffix.length) + targetExtname
189
- const attributes = Object.assign({ revdate: `${assemblerConfig.assembly.revdate}@` }, docAttributes, {
190
- docdir,
191
- docfile,
192
- docfilesuffix,
193
- docname: `${docname}@`,
194
- imagesdir: '',
195
- outdir,
196
- outfile,
197
- outfilesuffix: targetExtname,
198
- toArgs (attributeOptionFlag, outputOptionFlag) {
199
- const args = []
200
- let requireAsciidoctorLogAdapter
201
- for (const [name, val] of Object.entries(this)) {
202
- let optionValue
203
- if (name === 'asciidoctor-log-integration') {
204
- if ((val ?? false) !== false) requireAsciidoctorLogAdapter = val
205
- continue
206
- } else if ((val ?? false) === false) {
207
- optionValue = `${name}!${val === false ? '=@' : ''}`
208
- } else {
209
- optionValue = val === '' ? name : name + '=' + val
210
- }
211
- args.push(attributeOptionFlag, optionValue)
212
- }
213
- if (requireAsciidoctorLogAdapter) args.push('-r', requireAsciidoctorLogAdapter)
214
- if (outputOptionFlag) args.push(outputOptionFlag, this.outfile)
215
- return args
216
- },
217
- })
218
- Object.defineProperty(attributes, 'outfilesuffix', {
219
- get () {
220
- return ospath.extname(this.outfile)
221
- },
222
- set (value) {
223
- const outfile = this.outfile
224
- const extname = ospath.extname(outfile)
225
- if (extname && outfile.endsWith(extname)) this.outfile = outfile.substring(0, outfile.length - extname.length)
226
- this.outfile += value
227
- },
228
- })
229
- Object.defineProperty(attributes, 'toArgs', { enumerable: false })
230
- if (mkdirs) await fsp.mkdir(outdir, { recursive: true, force: true })
231
- if (attributes['asciidoctor-log-integration'] != null) {
232
- const scriptSourcePath = require.resolve('#asciidoctor-log-adapter')
233
- let scriptTargetPath = scriptSourcePath
234
- if (attributes['asciidoctor-log-integration'] === 'copy-to-build-dir') {
235
- scriptTargetPath = ospath.join(dir, 'asciidoctor-log-adapter.rb')
236
- await fsp.cp(scriptSourcePath, scriptTargetPath, { force: true, recursive: true })
237
- }
238
- attributes['asciidoctor-log-integration'] = scriptTargetPath
239
- }
240
- return attributes
241
- }
242
-
243
189
  function coerceToExportFormat (assemblyFile, targetBackend, targetExtname, targetMediaType, fileOrContents) {
244
190
  const file =
245
191
  fileOrContents == null || Buffer.isBuffer(fileOrContents) || typeof fileOrContents.pipe === 'function'
@@ -287,7 +233,7 @@ function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
287
233
  })
288
234
  }
289
235
 
290
- function resolveFileOrContents (convertResult, convertAttributes, buildConfig, loggerName) {
236
+ function resolveFileOrContents (convertResult, attributes, buildConfig, loggerName) {
291
237
  let fileOrContents
292
238
  if (convertResult?.status != null) {
293
239
  if ('file' in convertResult) {
@@ -299,7 +245,7 @@ function resolveFileOrContents (convertResult, convertAttributes, buildConfig, l
299
245
  }
300
246
  let logger, match
301
247
  if (buildConfig.stderrSink === 'log' && convertResult.stderr?.length && (logger = this.getLogger(loggerName))) {
302
- const docfile = convertAttributes.docfile
248
+ const docfile = attributes.docfile
303
249
  const command = buildConfig.command
304
250
  const file = { path: docfile }
305
251
  const stderr = convertResult.stderr.toString().trimEnd()
@@ -330,8 +276,8 @@ function resolveFileOrContents (convertResult, convertAttributes, buildConfig, l
330
276
  return Promise.resolve(convertResult)
331
277
  }
332
278
  if (fileOrContents !== undefined) return Promise.resolve(fileOrContents)
333
- const outfile = convertAttributes.outfile
334
- return fsp.access(outfile).then(() => new LazyReadable(() => fs.createReadStream(outfile)), invariably.null)
279
+ const outfile = attributes.outfile
280
+ return fsp.access(outfile).then(() => new LazyReadable(outfile), invariably.null)
335
281
  }
336
282
 
337
283
  function isBound (obj) {
@@ -0,0 +1,75 @@
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')
20
+ const attributes = Object.assign({ revdate: `${assemblerConfig.assembly.revdate}@` }, docAttributes, {
21
+ docdir,
22
+ docfile,
23
+ docfilesuffix,
24
+ docname: `${docname}@`,
25
+ imagesdir: '',
26
+ imagesoutdir,
27
+ outdir,
28
+ outfile,
29
+ outfilesuffix: targetExtname,
30
+ toArgs (attributeOptionFlag, outputOptionFlag) {
31
+ const args = []
32
+ let requireAsciidoctorLogAdapter
33
+ for (const [name, val] of Object.entries(this)) {
34
+ let optionValue
35
+ if (name === 'asciidoctor-log-integration') {
36
+ if ((val ?? false) !== false) requireAsciidoctorLogAdapter = val
37
+ continue
38
+ } else if ((val ?? false) === false) {
39
+ optionValue = `${name}!${val === false ? '=@' : ''}`
40
+ } else {
41
+ optionValue = val === '' ? name : name + '=' + val
42
+ }
43
+ args.push(attributeOptionFlag, optionValue)
44
+ }
45
+ if (requireAsciidoctorLogAdapter) args.push('-r', requireAsciidoctorLogAdapter)
46
+ if (outputOptionFlag) args.push(outputOptionFlag, this.outfile)
47
+ return args
48
+ },
49
+ })
50
+ Object.defineProperty(attributes, 'outfilesuffix', {
51
+ get () {
52
+ return ospath.extname(this.outfile)
53
+ },
54
+ set (value) {
55
+ const outfile = this.outfile
56
+ const extname = ospath.extname(outfile)
57
+ if (extname && outfile.endsWith(extname)) this.outfile = outfile.substring(0, outfile.length - extname.length)
58
+ this.outfile += value
59
+ },
60
+ })
61
+ Object.defineProperty(attributes, 'toArgs', { enumerable: false })
62
+ if (mkdirs) await fsp.mkdir(outdir, { recursive: true, force: true })
63
+ if (attributes['asciidoctor-log-integration'] != null) {
64
+ const scriptSourcePath = require.resolve('#asciidoctor-log-adapter')
65
+ let scriptTargetPath = scriptSourcePath
66
+ if (attributes['asciidoctor-log-integration'] === 'copy-to-build-dir') {
67
+ scriptTargetPath = ospath.join(dir, 'asciidoctor-log-adapter.rb')
68
+ await fsp.cp(scriptSourcePath, scriptTargetPath, { force: true, recursive: true })
69
+ }
70
+ attributes['asciidoctor-log-integration'] = scriptTargetPath
71
+ }
72
+ return attributes
73
+ }
74
+
75
+ module.exports = compileConversionAttributes
package/lib/configure.js CHANGED
@@ -53,8 +53,9 @@ function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
53
53
  [...(componentVersion.origins ?? [])].find((it) => it.descriptor?.ext?.assembler)
54
54
  const assemblerConfig = getAssemblerConfigFromDescriptor(source?.descriptor)
55
55
  if (!assemblerConfig) return
56
- const componentVersionRef = `${componentVersion.version}@${componentVersion.name}`
57
- 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())
58
59
  const componentVersionProfiles = assemblerConfig.reduce((accum, entry) => {
59
60
  const data = {}
60
61
  const profile = entry.profile ?? undefined
@@ -69,8 +70,9 @@ function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
69
70
  data.navFiles = (nav.length ? [...new Set(nav)] : nav).reduce((navFiles, path_) => {
70
71
  const navFile = filesByPath.get(path_)
71
72
  if (navFile) {
72
- navFiles.push(initNavFile(navFile, component.name, componentVersion.version, navFiles.length))
73
+ navFiles.push(initNavFile(navFile, componentName, version, navFiles.length))
73
74
  } else {
75
+ const componentVersionRef = `${version === 'master' ? '' : version}@${componentName}`
74
76
  ;(data.messages ??= []).push([
75
77
  'warn',
76
78
  { source },
@@ -84,7 +86,7 @@ function getAssemblerProfiles (contentCatalog, assemblerProfiles = new Map()) {
84
86
  }
85
87
  return accum.set(profile, data)
86
88
  }, new Map())
87
- assemblerProfiles.set(componentVersionRef, componentVersionProfiles)
89
+ assemblerProfiles.set(componentVersionKey, componentVersionProfiles)
88
90
  })
89
91
  })
90
92
  return assemblerProfiles
package/lib/constants.js CHANGED
@@ -19,4 +19,5 @@ module.exports = Object.freeze({
19
19
  'rootLevel',
20
20
  'sectionMergeStrategy',
21
21
  ],
22
+ CAMEL_CASE_STOP_KEYS: ['asciidoc.attributes', 'assembly.attributes'],
22
23
  })
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,13 +1,13 @@
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
12
  const YAML_SCHEMA = yaml.CORE_SCHEMA.withTags(yaml.mergeTag)
13
13
 
@@ -15,7 +15,7 @@ function loadConfig (configSource, playbook, preferredQualifier = '') {
15
15
  let resolvedConfigSource
16
16
  return (
17
17
  configSource?.constructor === Object
18
- ? Promise.resolve(configSource)
18
+ ? Promise.resolve(deepClone(configSource))
19
19
  : fileExists(
20
20
  (resolvedConfigSource = expandPath(configSource ?? `./antora-assembler${preferredQualifier}.yml`, {
21
21
  dot: playbook.dir,
@@ -38,7 +38,7 @@ function loadConfig (configSource, playbook, preferredQualifier = '') {
38
38
  return {}
39
39
  }
40
40
  return fsp.readFile(resolvedConfigSource).then((data) =>
41
- Object.assign(camelCaseKeys(yaml.load(data, { schema: YAML_SCHEMA }), CAMEL_CASE_STOP_PATHS), {
41
+ Object.assign(camelCaseKeys(yaml.load(data, { schema: YAML_SCHEMA }), CAMEL_CASE_STOP_KEYS), {
42
42
  file: resolvedConfigSource,
43
43
  })
44
44
  )
@@ -141,7 +141,7 @@ function camelCaseKeys (o, stopPaths, p = undefined) {
141
141
  const accum = {}
142
142
  for (const [k, v] of Object.entries(o)) {
143
143
  const camelKey = k.charAt() + k.substring(1).replace(/_([a-z])/g, (_, l) => l.toUpperCase())
144
- 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)
145
145
  }
146
146
  return accum
147
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