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

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.
@@ -11,11 +11,12 @@ 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 = { true: () => true, false: () => false, 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,
@@ -64,7 +65,8 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
64
65
  loadAsciiDoc,
65
66
  contentCatalog,
66
67
  assemblerConfig,
67
- generateSelectAssemblyProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog)
68
+ generateSelectAssemblyProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog),
69
+ typeof componentVersionFilter === 'function' ? componentVersionFilter : undefined
68
70
  )
69
71
  if (!(assemblyFiles.length && typeof convert === 'function')) return assemblyFiles
70
72
  buildConfig.command ??= typeof getDefaultCommand === 'function' ? await getDefaultCommand(cwd, playbook) : null
@@ -77,8 +79,7 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
77
79
  .add(
78
80
  assemblyFiles.map((doc) => async () => {
79
81
  const relativeToOutput = embedReferenceStyle === 'output-relative'
80
- const convertAttributes = prepareConvertAttributes(doc, targetExtname, relativeToOutput, assemblerConfig)
81
- if (buildConfig.mkdirs) await fsp.mkdir(convertAttributes.outdir, { recursive: true, force: true })
82
+ const convertAttributes = await prepareConvertAttributes(doc, targetExtname, relativeToOutput, assemblerConfig)
82
83
  return boundConvert(doc, convertAttributes, buildConfig, helpers).then((result) =>
83
84
  boundResolveFileOrContents(result, convertAttributes, buildConfig, loggerName).then((fileOrContents) =>
84
85
  coerceToExportFormat(doc, targetBackend, targetExtname, targetMediaType, fileOrContents)
@@ -172,14 +173,14 @@ function generateSelectAssemblyProfile (context, contentCatalog, baseModel, intr
172
173
  }
173
174
  }
174
175
 
175
- function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assemblerConfig) {
176
+ async function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assemblerConfig) {
176
177
  const {
177
178
  asciidoc: { attributes: docAttributes } = { attributes: {} },
178
179
  extname: docfilesuffix,
179
180
  path: reldocfile,
180
181
  src: { family, relative },
181
182
  } = doc
182
- const { cwd = process.cwd(), dir = cwd } = assemblerConfig.build
183
+ const { cwd = process.cwd(), dir = cwd, mkdirs } = assemblerConfig.build
183
184
  const docname = family + '$' + relative.substring(0, relative.length - docfilesuffix.length)
184
185
  const docfile = ospath.join(dir, reldocfile)
185
186
  const outdir = ospath.dirname(docfile)
@@ -194,22 +195,23 @@ function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assembl
194
195
  outdir,
195
196
  outfile,
196
197
  outfilesuffix: targetExtname,
197
- toArgs (optionFlag) {
198
+ toArgs (attributeOptionFlag, outputOptionFlag) {
198
199
  const args = []
199
- for (let [name, val] of Object.entries(this)) {
200
- if (val) {
201
- val = name + '=' + val
202
- } else if (val === '') {
203
- if (name === 'asciidoctor-log-integration') {
204
- args.push('-r', require.resolve('#asciidoctor-log-adapter'))
205
- continue
206
- }
207
- val = name
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
208
  } else {
209
- val = `${name}!${val === false ? '@' : ''}`
209
+ optionValue = val === '' ? name : name + '=' + val
210
210
  }
211
- args.push(optionFlag, val)
211
+ args.push(attributeOptionFlag, optionValue)
212
212
  }
213
+ if (requireAsciidoctorLogAdapter) args.push('-r', requireAsciidoctorLogAdapter)
214
+ if (outputOptionFlag) args.push(outputOptionFlag, this.outfile)
213
215
  return args
214
216
  },
215
217
  })
@@ -224,7 +226,18 @@ function prepareConvertAttributes (doc, targetExtname, relativeToOutput, assembl
224
226
  this.outfile += value
225
227
  },
226
228
  })
227
- return Object.defineProperty(attributes, 'toArgs', { enumerable: false })
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
228
241
  }
229
242
 
230
243
  function coerceToExportFormat (assemblyFile, targetBackend, targetExtname, targetMediaType, fileOrContents) {
@@ -237,7 +250,8 @@ function coerceToExportFormat (assemblyFile, targetBackend, targetExtname, targe
237
250
  const { path: sourcePath, extname: sourceExtname } = file
238
251
  const relativeWithoutExtname = file.src.relative.substring(0, file.src.relative.length - sourceExtname.length)
239
252
  const newPath = sourcePath.substring(0, sourcePath.length - sourceExtname.length) + targetExtname
240
- Object.assign(file, { mediaType: (file.src.mediaType = targetMediaType), path: newPath })
253
+ Object.assign(file, { mediaType: (file.src.mediaType = targetMediaType), path: (file.src.path = newPath) })
254
+ delete file.src.abspath
241
255
  file.src.basename = path.basename((file.src.relative = relativeWithoutExtname + (file.src.extname = targetExtname)))
242
256
  return file
243
257
  }
@@ -260,8 +274,9 @@ function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
260
274
  }
261
275
  if (keepSource) {
262
276
  for (const file of assemblyFiles) {
263
- file.src.contents = file.contents
264
277
  file.out = { path: file.path }
278
+ file.src.contents = file.contents
279
+ file.src.abspath = ospath.join(dir, file.path)
265
280
  files.push(file)
266
281
  }
267
282
  }
@@ -272,7 +287,7 @@ function prepareWorkspace (publishFiles, assemblyFiles, buildConfig) {
272
287
  })
273
288
  }
274
289
 
275
- async function resolveFileOrContents (convertResult, convertAttributes, buildConfig, loggerName) {
290
+ function resolveFileOrContents (convertResult, convertAttributes, buildConfig, loggerName) {
276
291
  let fileOrContents
277
292
  if (convertResult?.status != null) {
278
293
  if ('file' in convertResult) {
@@ -312,12 +327,11 @@ async function resolveFileOrContents (convertResult, convertAttributes, buildCon
312
327
  if (additionalLines.length > 1) logger.info({ command, file }, additionalLines.join('\n'))
313
328
  }
314
329
  } else if (convertResult !== undefined) {
315
- return convertResult
330
+ return Promise.resolve(convertResult)
316
331
  }
317
- if (fileOrContents !== undefined) return fileOrContents
332
+ if (fileOrContents !== undefined) return Promise.resolve(fileOrContents)
318
333
  const outfile = convertAttributes.outfile
319
- const outfileExists = await fsp.access(outfile).then(invariably.true, invariably.false)
320
- return outfileExists ? new LazyReadable(() => fs.createReadStream(outfile)) : null
334
+ return fsp.access(outfile).then(() => new LazyReadable(() => fs.createReadStream(outfile)), invariably.null)
321
335
  }
322
336
 
323
337
  function isBound (obj) {
package/lib/configure.js CHANGED
@@ -7,25 +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
+ const { assembleContent = require('./assemble-content'), ...assembleContentProviders } = providers
20
21
  if (config.configSource?.constructor === Object) {
21
- assembleContentConfig.configSource = config.configSource
22
- await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentConfig)
22
+ assembleContentProviders.configSource = config.configSource
23
+ await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentProviders)
23
24
  } else {
24
25
  const singleConfig = !('configFiles' in config)
25
26
  const configFiles = singleConfig ? config.configFile : config.configFiles
26
27
  for (const configSource of Array.isArray(configFiles) ? configFiles : [configFiles]) {
27
- const assembleContentConfigWithConfigSource = Object.assign({}, assembleContentConfig, { configSource })
28
- await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentConfigWithConfigSource)
28
+ const assembleContentProvidersWithConfigSource = Object.assign({}, assembleContentProviders, { configSource })
29
+ await assembleContent.call(this, playbook, contentCatalog, converter, assembleContentProvidersWithConfigSource)
29
30
  if (singleConfig) break
30
31
  }
31
32
  }
@@ -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) {
@@ -134,7 +134,7 @@ function loadConfig (configSource, playbook, preferredQualifier = '') {
134
134
  })
135
135
  }
136
136
 
137
- function camelCaseKeys (o, stopPaths = [], p = undefined) {
137
+ function camelCaseKeys (o, stopPaths, p = undefined) {
138
138
  if (Array.isArray(o)) return o.map((it) => camelCaseKeys(it, stopPaths, p))
139
139
  if (o == null || o.constructor !== Object) return o
140
140
  const pathPrefix = p ? p + '.' : ''
@@ -20,6 +20,7 @@ const DISCARD_ATTRIBUTE_NAMES = [
20
20
  'doctype',
21
21
  'leveloffset',
22
22
  'preface-title',
23
+ 'assembly-doctype',
23
24
  'assembly-header-attributes',
24
25
  'assembly-navtitle',
25
26
  'assembly-slug',
@@ -39,8 +40,8 @@ function produceAssemblyFile (
39
40
  assemblyModel
40
41
  ) {
41
42
  const pagesByUrl = files.reduce((map, it) => (it.src.family === 'page' ? map.set(it.pub.url, it) : map), new Map())
42
- if (outline.urlType === 'internal' && !pagesByUrl.get(outline.url) && !(outline.items || []).length) return
43
43
  const pagesInOutline = selectPagesInOutline(outline, pagesByUrl)
44
+ if (outline.urlType === 'internal' && !pagesByUrl.has(outline.pathname) && !outline.items?.length) return
44
45
  const { rootLevel, xmlIds } = assemblyModel
45
46
  const idSeparators = {
46
47
  prefix:
@@ -77,6 +78,7 @@ function produceAssemblyFile (
77
78
  mutableAttributes,
78
79
  assemblyModel
79
80
  )
81
+ if (buffer[buffer.doctypeIdx] === ':doctype: article') buffer.splice(buffer.doctypeIdx, 1)
80
82
  const stem =
81
83
  (buffer.slug ? sanitizeSlug(buffer.slug) : generateSlug(rootLevel === 0 ? 'index' : buffer.navtitle)) ||
82
84
  'export-' + (assemblyModel.stemSeq = (assemblyModel.exportSeq ?? 0) + 1)
@@ -88,7 +90,7 @@ function produceAssemblyFile (
88
90
  src: { component, version, componentVersion, module: 'ROOT', family: 'export', relative: stem + '.adoc' },
89
91
  pub: false,
90
92
  })
91
- file.path = file.out.path // use out path as file path so assets can easily be published to same hierarchy
93
+ file.path = file.src.path = file.out.path // use out path as path so assets can be published to same hierarchy
92
94
  delete file.out
93
95
  return file
94
96
  }
@@ -124,7 +126,6 @@ function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocCon
124
126
  }
125
127
 
126
128
  function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
127
- const doctype = assemblyModel.doctype ?? 'book'
128
129
  const navtitlePlain = sanitize(navtitle)
129
130
  const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
130
131
  const doctitle = (componentVersion.title === navtitlePlain ? '' : componentVersion.title + ': ') + navtitleAsciiDoc
@@ -133,7 +134,7 @@ function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
133
134
  const buffer = [
134
135
  `= ${doctitle}`,
135
136
  ...(version ? [`:revnumber: ${displayVersion}`] : []),
136
- ...(doctype === 'article' ? [] : [`:doctype: ${doctype}`]),
137
+ `:doctype: ${assemblyModel.doctype ?? 'book'}`,
137
138
  ':underscore: _',
138
139
  // Q: should we pass these via the CLI so they cannot be modified?
139
140
  `:page-component-name: ${componentVersion.name}`,
@@ -142,17 +143,22 @@ function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
142
143
  `:page-component-display-version: ${displayVersion}`,
143
144
  `:page-component-title: ${componentVersion.title}`,
144
145
  ]
146
+ buffer.doctypeIdx = version ? 2 : 1
145
147
  return Object.assign(buffer, { navtitle })
146
148
  }
147
149
 
148
150
  function selectPagesInOutline (outlineEntry, pagesByUrl, accum) {
149
151
  accum ??= Object.assign(new Map(), { assembled: { pages: new Map(), assets: new Set() } })
150
- const page = outlineEntry.urlType === 'internal' ? pagesByUrl.get(outlineEntry.url) : undefined
151
- if (page) {
152
- accum.set(createResourceKey(page.src), page)
153
- accum.set(outlineEntry.url, page)
152
+ const { urlType, url, hash, unresolved, items = [] } = outlineEntry
153
+ if (urlType === 'internal' && !unresolved) {
154
+ const pathname = (outlineEntry.pathname ??= hash ? url.substring(0, url.length - hash.length) : url)
155
+ let page
156
+ if (!accum.has(pathname) && (page = pagesByUrl.get(pathname))) {
157
+ accum.set(createResourceKey(page.src), page)
158
+ accum.set(pathname, page)
159
+ }
154
160
  }
155
- for (const item of outlineEntry.items || []) selectPagesInOutline(item, pagesByUrl, accum)
161
+ for (const item of items) selectPagesInOutline(item, pagesByUrl, accum)
156
162
  return accum
157
163
  }
158
164
 
@@ -172,21 +178,22 @@ function mergeAsciiDoc (
172
178
  level = 0,
173
179
  supportsParts = false
174
180
  ) {
175
- const { items = [], roles = [], unresolved, urlType, url, hash } = outlineEntry
181
+ const { items = [], roles = [], unresolved, urlType, url, pathname, hash } = outlineEntry
176
182
  // TODO: we could try to be smart about it and make sure the page with fragment is included at least once
177
- if (hash || roles.includes('site-only')) {
183
+ if (roles.includes('site-only')) {
178
184
  buffer.inBody ??= false
179
185
  return buffer
180
186
  }
181
- let navtitle = outlineEntry.navtitle ?? outlineEntry.content
187
+ const assembled = pagesInOutline.assembled
188
+ // FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
189
+ const page = urlType === 'internal' && !unresolved ? pagesInOutline.get(pathname) : undefined
190
+ let navtitle = outlineEntry.navtitle ?? (page && hash ? page.asciidoc.navtitle : outlineEntry.content)
182
191
  let navtitlePlain = sanitize(navtitle)
183
192
  let navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
184
193
  const { filetype, linkReferenceStyle, pubRoot, sectionMergeStrategy, siteRoot, logger, rootLevel } = assemblyModel
185
- const assembled = pagesInOutline.assembled
186
- // FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
187
- const page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
194
+ const doctype = assemblyModel.doctype ?? 'book'
188
195
  const atDocumentRoot = !buffer.inBody
189
- const atBookRoot = atDocumentRoot && !level && assemblyModel.doctype === 'book' && (supportsParts = true)
196
+ let atBookRoot = atDocumentRoot && !level && doctype === 'book' && (supportsParts = true)
190
197
  const hasItems = items.length > 0
191
198
  if (page && !assembled.pages.has(page)) {
192
199
  const contents = page.src.contents
@@ -211,6 +218,11 @@ function mergeAsciiDoc (
211
218
  doc.source_header_attributes ??= doc.parent.$to_h()
212
219
  const isRootPage = atDocumentRoot && !level
213
220
  const { component, version, module: module_, relative, origin } = page.src
221
+ const doctypeOverride = doc.getAttribute('assembly-doctype')
222
+ if (doctypeOverride) {
223
+ if (doctypeOverride !== doctype) buffer[buffer.doctypeIdx] = `:doctype: ${doctypeOverride}`
224
+ if (doctypeOverride !== 'book') atBookRoot = supportsParts = false
225
+ }
214
226
  if (isRootPage && doc.hasAttribute('assembly-slug')) buffer.slug = doc.getAttribute('assembly-slug')
215
227
  let hasAssemblyNavtitleAttr
216
228
  if ((hasAssemblyNavtitleAttr = !!doc.getAttribute('assembly-navtitle'))) {
@@ -279,6 +291,7 @@ function mergeAsciiDoc (
279
291
  for (const entry of processDocumentHeader(doc, lines, ignoreLines, isRootPage)) {
280
292
  if (entry.type === 'author_line') {
281
293
  buffer.splice(1, 0, entry.lines[0])
294
+ buffer.doctypeIdx += 1
282
295
  buffer.endHeaderIdx += 1
283
296
  } else if (entry.type === 'attribute_entry') {
284
297
  const name = entry.name
@@ -371,6 +384,7 @@ function mergeAsciiDoc (
371
384
  // Q: should we use htitleAsciiDoc to generate ID if not {empty} instead of pageStyle
372
385
  pageFragment = `#_${idSeparators.coordinate}${pageIdLeader}${pageStyle}`
373
386
  buffer.unshift(`[#${pageId}]`)
387
+ buffer.doctypeIdx += 1
374
388
  } else {
375
389
  pageFragment = `#${pageId}`
376
390
  }
@@ -401,6 +415,7 @@ function mergeAsciiDoc (
401
415
  buffer.push(`${'='.repeat(heading.level)} ${heading.title}`)
402
416
  } else if (atDocumentRoot) {
403
417
  buffer.unshift(`[#${pageId}]`)
418
+ buffer.doctypeIdx += 1
404
419
  }
405
420
  if (sectionMergeStrategy === 'enclose' && hasItems && hasSections && !(atDocumentRoot && heading)) {
406
421
  // TODO: make overview section title configurable
@@ -610,7 +625,7 @@ function mergeAsciiDoc (
610
625
  )
611
626
  if (resolvedAttributeEntries.length > 1) safePush(buffer, resolvedAttributeEntries)
612
627
  }
613
- } else if (level) {
628
+ } else if (level && !hash) {
614
629
  if (atDocumentRoot && rootLevel === 0 && navtitlePlain === componentVersion.title) {
615
630
  buffer.inBody ??= false
616
631
  level--
@@ -622,7 +637,7 @@ function mergeAsciiDoc (
622
637
  if (urlType === 'external') {
623
638
  sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
624
639
  } else if (urlType === 'internal' && !unresolved) {
625
- const resource = files.find((it) => it.pub.url === url)
640
+ const resource = files.find((it) => it.pub.url === pathname)
626
641
  if (resource) {
627
642
  if (resource.src.family === 'page' && pagesInOutline.has(resource.pub.url)) {
628
643
  const refid = generateScopedId(resource.src, componentVersion, idSeparators, filetype, true)
@@ -641,7 +656,7 @@ function mergeAsciiDoc (
641
656
  if (hasItems) {
642
657
  const nextLevel = level + 1
643
658
  // NOTE: drop first child if same as parent; should we keep if content is different?
644
- ;(urlType === 'internal' && urlType === items[0].urlType && url === items[0].url && !items[0].items
659
+ ;(urlType === 'internal' && items[0].urlType === 'internal' && pathname === items[0].url && !items[0].items?.length
645
660
  ? items.slice(1)
646
661
  : items
647
662
  ).forEach((item) => {
@@ -6,7 +6,13 @@ const selectMutableAttributes = require('./select-mutable-attributes')
6
6
 
7
7
  const ATTR_REF_RX = /\\?\{(\w[\w-]*)\}/g
8
8
 
9
- function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, selectAssemblyProfile) {
9
+ function produceAssemblyFiles (
10
+ loadAsciiDoc,
11
+ contentCatalog,
12
+ assemblerConfig,
13
+ selectAssemblyProfile,
14
+ selectComponentVersions
15
+ ) {
10
16
  const { assembly: assemblyConfig } = assemblerConfig
11
17
  selectAssemblyProfile ??= (componentVersion) => ({
12
18
  attributes: assemblyConfig.attributes,
@@ -26,106 +32,104 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, se
26
32
  const publishableFiles = contentCatalog.getFiles().filter((file) => file.out && file.pub)
27
33
  let siteRoot
28
34
  const configMdc = assemblerConfig.file ? { file: { path: assemblerConfig.file } } : {}
29
- return filterComponentVersions(contentCatalog.getComponents(), assemblerConfig.componentVersionFilter).reduce(
30
- (accum, componentVersion) => {
31
- const assemblyModel = selectAssemblyProfile(componentVersion)
32
- const { attributes: assemblyAttributes, logger, navigation, rootLevel } = assemblyModel
33
- if (!navigation) return accum
34
- const contextualLogger = logger ? { warn: logger.warn.bind(logger, configMdc) } : undefined
35
- const { name: componentName, version, title } = componentVersion
36
- const componentVersionAsciiDocConfig = getAsciiDocConfigWithAsciidoctorReducerExtension(componentVersion)
37
- let sourceHighlighter
38
- if ('source-highlighter' in assemblyAttributes) {
39
- sourceHighlighter = assemblyAttributes['source-highlighter']
40
- delete assemblyAttributes['source-highlighter']
35
+ selectComponentVersions ??= filterComponentVersions.bind(null, assemblerConfig.componentVersionFilter)
36
+ return selectComponentVersions(contentCatalog.getComponents()).reduce((accum, componentVersion) => {
37
+ const assemblyModel = selectAssemblyProfile(componentVersion)
38
+ const { attributes: assemblyAttributes, logger, navigation, rootLevel } = assemblyModel
39
+ if (!navigation) return accum
40
+ const contextualLogger = logger ? { warn: logger.warn.bind(logger, configMdc) } : undefined
41
+ const { name: componentName, version, title } = componentVersion
42
+ const componentVersionAsciiDocConfig = getAsciiDocConfigWithAsciidoctorReducerExtension(componentVersion)
43
+ let sourceHighlighter
44
+ if ('source-highlighter' in assemblyAttributes) {
45
+ sourceHighlighter = assemblyAttributes['source-highlighter']
46
+ delete assemblyAttributes['source-highlighter']
47
+ }
48
+ const mergedAsciiDocAttributes = collateAsciiDocAttributes(
49
+ Object.assign({}, componentVersionAsciiDocConfig.attributes),
50
+ assemblyAttributes,
51
+ contextualLogger
52
+ )
53
+ const mergedAsciiDocConfig = Object.assign({}, componentVersionAsciiDocConfig, {
54
+ attributes: mergedAsciiDocAttributes,
55
+ })
56
+ assemblyModel.filetype = baseAssemblyAttributes['assembler-filetype']
57
+ const outDirname = (assemblyModel.outDirname = contentCatalog.createFile({
58
+ src: {
59
+ component: componentName,
60
+ version,
61
+ componentVersion,
62
+ module: 'ROOT',
63
+ family: 'export',
64
+ relative: 'index.adoc',
65
+ },
66
+ }).out.dirname)
67
+ assemblyModel.pubRoot = outDirname ? '/' + outDirname : ''
68
+ assemblyModel.siteRoot =
69
+ siteRoot === undefined
70
+ ? (siteRoot ??= ((val) => {
71
+ if (!val) return null
72
+ if (val.charAt(val.length - 1) === '/') val = val.substring(0, val.length - 1)
73
+ if (!val || val.charAt() === '/') return { path: val }
74
+ return { url: val, path: extractUrlPath(val) }
75
+ })(mergedAsciiDocAttributes['site-url'] || mergedAsciiDocAttributes['primary-site-url']))
76
+ : siteRoot
77
+ if (assemblyModel.filetype === 'html') {
78
+ let linkRefStyle = assemblyModel.linkReferenceStyle
79
+ if (linkRefStyle === 'absolute' && siteRoot?.url == null) linkRefStyle = 'root-relative'
80
+ if (linkRefStyle === 'root-relative' && siteRoot?.path == null) linkRefStyle = 'relative'
81
+ assemblyModel.linkReferenceStyle = linkRefStyle
82
+ } else if (!(assemblyModel.filetype === 'pdf' && assemblyModel.linkReferenceStyle === 'relative')) {
83
+ assemblyModel.linkReferenceStyle = 'absolute'
84
+ }
85
+ const rootEntry = { content: title }
86
+ let startPage =
87
+ 'startPage' in componentVersion
88
+ ? componentVersion.startPage
89
+ : contentCatalog.resolvePage('index.adoc', { component: componentName, version })
90
+ if (startPage && startPage.src.component === componentName && startPage.src.version === version) {
91
+ if (assemblyModel.insertStartPage) {
92
+ const navtitle = startPage.asciidoc?.navtitle || rootEntry.content
93
+ Object.assign(rootEntry, { navtitle, url: startPage.pub.url, urlType: 'internal', roles: ['page'] })
41
94
  }
42
- const mergedAsciiDocAttributes = collateAsciiDocAttributes(
43
- Object.assign({}, componentVersionAsciiDocConfig.attributes),
44
- assemblyAttributes,
45
- contextualLogger
46
- )
47
- const mergedAsciiDocConfig = Object.assign({}, componentVersionAsciiDocConfig, {
48
- attributes: mergedAsciiDocAttributes,
49
- })
50
- assemblyModel.filetype = baseAssemblyAttributes['assembler-filetype']
51
- const outDirname = (assemblyModel.outDirname = contentCatalog.createFile({
95
+ } else {
96
+ // Q: should we always use a reference page as startPage for computing mutableAttributes?
97
+ startPage = contentCatalog.createFile({
52
98
  src: {
53
- component: componentName,
54
- version,
99
+ component: componentVersion.name,
100
+ version: componentVersion.version,
55
101
  componentVersion,
56
102
  module: 'ROOT',
57
- family: 'export',
103
+ family: 'page',
58
104
  relative: 'index.adoc',
105
+ origin: (componentVersion.origins || [])[0],
59
106
  },
60
- }).out.dirname)
61
- assemblyModel.pubRoot = outDirname ? '/' + outDirname : ''
62
- assemblyModel.siteRoot =
63
- siteRoot === undefined
64
- ? (siteRoot ??= ((val) => {
65
- if (!val) return null
66
- if (val.charAt(val.length - 1) === '/') val = val.substring(0, val.length - 1)
67
- if (!val || val.charAt() === '/') return { path: val }
68
- return { url: val, path: extractUrlPath(val) }
69
- })(mergedAsciiDocAttributes['site-url'] || mergedAsciiDocAttributes['primary-site-url']))
70
- : siteRoot
71
- if (assemblyModel.filetype === 'html') {
72
- let linkRefStyle = assemblyModel.linkReferenceStyle
73
- if (linkRefStyle === 'absolute' && siteRoot?.url == null) linkRefStyle = 'root-relative'
74
- if (linkRefStyle === 'root-relative' && siteRoot?.path == null) linkRefStyle = 'relative'
75
- assemblyModel.linkReferenceStyle = linkRefStyle
76
- } else if (!(assemblyModel.filetype === 'pdf' && assemblyModel.linkReferenceStyle === 'relative')) {
77
- assemblyModel.linkReferenceStyle = 'absolute'
78
- }
79
- const rootEntry = { content: title }
80
- let startPage =
81
- 'startPage' in componentVersion
82
- ? componentVersion.startPage
83
- : contentCatalog.resolvePage('index.adoc', { component: componentName, version })
84
- if (startPage && startPage.src.component === componentName && startPage.src.version === version) {
85
- if (assemblyModel.insertStartPage) {
86
- const navtitle = startPage.asciidoc?.navtitle || rootEntry.content
87
- Object.assign(rootEntry, { navtitle, url: startPage.pub.url, urlType: 'internal', roles: ['page'] })
88
- }
89
- } else {
90
- // Q: should we always use a reference page as startPage for computing mutableAttributes?
91
- startPage = contentCatalog.createFile({
92
- src: {
93
- component: componentVersion.name,
94
- version: componentVersion.version,
95
- componentVersion,
96
- module: 'ROOT',
97
- family: 'page',
98
- relative: 'index.adoc',
99
- origin: (componentVersion.origins || [])[0],
100
- },
101
- })
102
- startPage.path = ['modules', startPage.src.module, startPage.src.family + 's', startPage.src.relative].join('/')
107
+ })
108
+ startPage.path = ['modules', startPage.src.module, startPage.src.family + 's', startPage.src.relative].join('/')
109
+ }
110
+ const mutableAttributes = selectMutableAttributes(loadAsciiDoc, contentCatalog, startPage, mergedAsciiDocConfig)
111
+ prepareOutlines(navigation, rootEntry, rootLevel).reduce((any, outline) => {
112
+ const assemblyFile = produceAssemblyFile(
113
+ loadAsciiDoc,
114
+ contentCatalog,
115
+ componentVersion,
116
+ outline,
117
+ publishableFiles,
118
+ mergedAsciiDocConfig,
119
+ mutableAttributes,
120
+ assemblyModel
121
+ )
122
+ if (!assemblyFile) return any
123
+ // NOTE restore source highlighter for conversion if defined in Assembler config
124
+ if (sourceHighlighter !== undefined) {
125
+ assemblyFile.asciidoc.attributes['source-highlighter'] = sourceHighlighter
126
+ } else if (assemblyModel.filetype !== 'html') {
127
+ delete assemblyFile.asciidoc.attributes['source-highlighter']
103
128
  }
104
- const mutableAttributes = selectMutableAttributes(loadAsciiDoc, contentCatalog, startPage, mergedAsciiDocConfig)
105
- prepareOutlines(navigation, rootEntry, rootLevel).reduce((any, outline) => {
106
- const assemblyFile = produceAssemblyFile(
107
- loadAsciiDoc,
108
- contentCatalog,
109
- componentVersion,
110
- outline,
111
- publishableFiles,
112
- mergedAsciiDocConfig,
113
- mutableAttributes,
114
- assemblyModel
115
- )
116
- if (!assemblyFile) return any
117
- // NOTE restore source highlighter for conversion if defined in Assembler config
118
- if (sourceHighlighter !== undefined) {
119
- assemblyFile.asciidoc.attributes['source-highlighter'] = sourceHighlighter
120
- } else if (assemblyModel.filetype !== 'html') {
121
- delete assemblyFile.asciidoc.attributes['source-highlighter']
122
- }
123
- return !!accum.push(assemblyFile)
124
- }, false)
125
- return accum
126
- },
127
- []
128
- )
129
+ return !!accum.push(assemblyFile)
130
+ }, false)
131
+ return accum
132
+ }, [])
129
133
  }
130
134
 
131
135
  function getAsciiDocConfigWithAsciidoctorReducerExtension (componentVersion) {
@@ -19,7 +19,7 @@ function rewriteXrefs (
19
19
  doc,
20
20
  sourceLocation
21
21
  ) {
22
- return line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|[\s\S]*?[^\\])\]/gu, (_, target, text) =>
22
+ return line.replace(/(?<![\\+])xref:((?:\.\/|:)?[\p{Alpha}0-9_/.{#].*?)\[(|[\s\S]*?[^\\])\]/gu, (_, target, text) =>
23
23
  rewriteXref(
24
24
  target,
25
25
  text,
@@ -87,7 +87,7 @@ function rewriteXref (
87
87
  const linkAttrlist = text
88
88
  ? (~text.indexOf(',') ? `"${text}"` : text) + ',role=unresolved'
89
89
  : (~target.indexOf(':') ? `${rawTarget},` : '') + 'role=unresolved'
90
- return `link:${rawTarget}[${linkAttrlist}]`
90
+ return `link:${rawTarget.charAt() === ':' ? resourceId.module + rawTarget : rawTarget}[${linkAttrlist}]`
91
91
  }
92
92
  if (fragment === resource.asciidoc.id) fragment = ''
93
93
  if (
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/assembler",
3
- "version": "1.0.0-rc.5",
3
+ "version": "1.0.0-rc.6",
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)",
@@ -38,9 +38,9 @@
38
38
  "#unconvert-inline-asciidoc": "./lib/util/unconvert-inline-asciidoc.js"
39
39
  },
40
40
  "dependencies": {
41
- "@asciidoctor/reducer": "~1.1",
42
41
  "@antora/expand-path-helper": "~3.0",
43
42
  "@antora/run-command-helper": "~1.1",
43
+ "@asciidoctor/reducer": "~1.1",
44
44
  "js-yaml": "~5.2"
45
45
  },
46
46
  "devDependencies": {