@antora/assembler 1.0.0-beta.16 → 1.0.0-beta.17

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.
@@ -57,12 +57,12 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
57
57
  intrinsicAttributes[`assembler-filetype-${targetFiletype}`] = ''
58
58
  intrinsicAttributes['assembler-filetype'] = targetFiletype
59
59
  }
60
- Object.assign(assemblerConfig.asciidoc.attributes, intrinsicAttributes)
60
+ Object.assign(assemblerConfig.assembly.attributes, intrinsicAttributes)
61
61
  const assemblyFiles = produceAssemblyFiles(
62
62
  loadAsciiDoc,
63
63
  contentCatalog,
64
64
  assemblerConfig,
65
- createResolveAssemblyModel(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog)
65
+ generateSelectAssemblyProfile(context, contentCatalog, assemblyConfig, intrinsicAttributes, navigationCatalog)
66
66
  )
67
67
  if (!(assemblyFiles.length && typeof convert === 'function')) return assemblyFiles
68
68
  if (buildConfig.command == null && typeof getDefaultCommand === 'function') {
@@ -123,24 +123,33 @@ async function assembleContent (playbook, contentCatalog, converter, { configSou
123
123
  })
124
124
  }
125
125
 
126
- function createResolveAssemblyModel (context, contentCatalog, common, intrinsicAttributes, navigationCatalog) {
126
+ /**
127
+ * Generates a function that selects the active assembly profile, initializes an assembly model using
128
+ * the keys from the profile as well as any inherited shared keys, builds the navigation for the assembly, and
129
+ * returns the initialized assembly model. The assembly model is further populated after the call to this function.
130
+ */
131
+ function generateSelectAssemblyProfile (context, contentCatalog, base, intrinsicAttributes, navigationCatalog) {
127
132
  const logger = context.getLogger?.(PACKAGE_NAME)
128
133
  const { assemblerProfiles } = context.getVariables()
129
134
  if (!assemblerProfiles) {
130
135
  return (componentVersion) => {
131
136
  const navigation =
132
137
  navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version) ?? componentVersion.navigation
133
- return Object.assign({ logger }, common, { navigation })
138
+ return Object.assign({}, base, { attributes: Object.assign({}, base.attributes), navigation, logger })
134
139
  }
135
140
  }
136
141
  const boundSendToLog = sendToLog.bind(logger)
137
142
  const { buildNavigation = require('@antora/navigation-builder') } = context.getFunctions()
138
143
  return (componentVersion) => {
144
+ const attributes = Object.assign({}, base.attributes)
139
145
  const componentVersionProfiles = assemblerProfiles.get(componentVersion.version + '@' + componentVersion.name)
140
146
  const overrides =
141
147
  componentVersionProfiles?.get(intrinsicAttributes['assembler-profile']) ?? componentVersionProfiles?.get() ?? {}
142
148
  const { navFiles, messages } = overrides
143
- const model = Object.assign({ logger }, common, overrides)
149
+ Object.entries(overrides.attributes ?? {}).forEach(([name, val]) => {
150
+ attributes[name] = val
151
+ })
152
+ const model = Object.assign({}, base, overrides, { attributes, logger })
144
153
  delete model.navFiles
145
154
  delete model.messages
146
155
  const navigationOverride = navigationCatalog?.getNavigation(componentVersion.name, componentVersion.version)
@@ -14,6 +14,7 @@ const ASSEMBLY_KEYS = [
14
14
  'linkReferenceStyle',
15
15
  'dropExplicitXrefText',
16
16
  ]
17
+ const CAMEL_CASE_STOP_PATHS = ['asciidoc.attributes', 'assembly.attributes']
17
18
 
18
19
  function loadConfig (playbook, configSource) {
19
20
  let resolvedConfigSource
@@ -38,19 +39,11 @@ function loadConfig (playbook, configSource) {
38
39
  return fsp
39
40
  .readFile(resolvedConfigSource)
40
41
  .then((data) =>
41
- Object.assign(camelCaseKeys(yaml.load(data), ['asciidoc']), { file: resolvedConfigSource })
42
+ Object.assign(camelCaseKeys(yaml.load(data), CAMEL_CASE_STOP_PATHS), { file: resolvedConfigSource })
42
43
  )
43
44
  })
44
45
  ).then((config) => {
45
46
  if (config.enabled === false) return config
46
- let asciidocAttrs
47
- if (!config.asciidoc) {
48
- config.asciidoc = { attributes: (asciidocAttrs = {}) }
49
- } else if (!(asciidocAttrs = config.asciidoc.attributes)) {
50
- config.asciidoc.attributes = asciidocAttrs = {}
51
- }
52
- if (!('revdate' in asciidocAttrs)) asciidocAttrs.revdate = getLocalDate()
53
- asciidocAttrs['page-partial'] = null
54
47
  const remapComponentVersionsKey = !('componentVersionFilter' in config)
55
48
  const componentVersionFilter = (config.componentVersionFilter ??= {})
56
49
  if (remapComponentVersionsKey && 'componentVersions' in config) {
@@ -71,8 +64,24 @@ function loadConfig (playbook, configSource) {
71
64
  delete config[key]
72
65
  }
73
66
  }
74
- if (!('doctype' in assembly)) assembly.doctype = 'doctype' in asciidocAttrs ? asciidocAttrs.doctype : 'book'
75
- delete asciidocAttrs.doctype
67
+ let assemblyAttrs
68
+ if ('attributes' in assembly) {
69
+ assemblyAttrs = assembly.attributes ?? {}
70
+ if ('asciidoc' in config) delete config.asciidoc
71
+ } else if ('asciidoc' in config) {
72
+ assemblyAttrs = assembly.attributes = config.asciidoc?.attributes ?? {}
73
+ delete config.asciidoc
74
+ } else {
75
+ assemblyAttrs = assembly.attributes = {}
76
+ }
77
+ assemblyAttrs['page-partial'] = null
78
+ config.asciidoc = Object.defineProperty({}, 'attributes', {
79
+ get: function () {
80
+ return this.assembly.attributes
81
+ }.bind(config),
82
+ })
83
+ if (!('doctype' in assembly)) assembly.doctype = 'doctype' in assemblyAttrs ? assemblyAttrs.doctype : 'book'
84
+ delete assemblyAttrs.doctype
76
85
  if (!('rootLevel' in assembly)) assembly.rootLevel = 0
77
86
  if (!('insertStartPage' in assembly)) assembly.insertStartPage = true
78
87
  if (['discrete', 'fuse', 'enclose'].indexOf(assembly.sectionMergeStrategy) < 0) {
@@ -84,6 +93,7 @@ function loadConfig (playbook, configSource) {
84
93
  if (['always', 'if-redundant', 'never'].indexOf(assembly.dropExplicitXrefText) < 0) {
85
94
  assembly.dropExplicitXrefText = 'never'
86
95
  }
96
+ assembly.revdate = getLocalDate()
87
97
  const build = (config.build ??= {})
88
98
  if (build.dir === '$' + '{playbook.output.dir}') {
89
99
  throw new Error('Not implemented')
@@ -1,8 +1,10 @@
1
1
  'use strict'
2
2
 
3
3
  const createAsciiDocFile = require('./util/create-asciidoc-file')
4
- const parseResourceRef = require('./util/parse-resource-ref')
5
- const path = require('node:path/posix')
4
+ const createResourceKey = require('./util/create-resource-key')
5
+ const generateId = require('./util/generate-id')
6
+ const { resolveLinkTarget } = require('./util/resolver')
7
+ const { rewriteXrefs, rewriteImageRef, rewriteInlineImages, rewriteStyleAttribute } = require('./util/rewriter')
6
8
  const sanitize = require('./util/sanitize')
7
9
  const unconvertInlineAsciiDoc = require('./util/unconvert-inline-asciidoc')
8
10
 
@@ -10,7 +12,6 @@ const AttributeEntryRx = /^:([^:-][^:]*):(?: .*)?$/
10
12
  const BuiltInNamedEntities = { amp: '&', apos: "'", gt: '>', lt: '<', nbsp: ' ', quot: '"' }
11
13
  const CharRefRx = /&(?:([a-z][a-z]+\d{0,2})|#(?:(\d{2,6})|x([a-z\d]{2,5})));/g
12
14
  const DiscardAttributes = 'doctype leveloffset assembly-navtitle assembly-style underscore'.split(' ')
13
- const ReservedIdNames = 'content header footnotes footer footer-text premable toc toctitle'.split(' ')
14
15
 
15
16
  function produceAssemblyFile (
16
17
  loadAsciiDoc,
@@ -113,26 +114,19 @@ function mergeAsciiDoc (
113
114
  let navtitlePlain = sanitize(navtitle)
114
115
  let navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
115
116
  const { items = [], unresolved, urlType, url } = outlineEntry
116
- const {
117
- doctype,
118
- filetype,
119
- embedReferenceStyle: embedRefStyle,
120
- linkReferenceStyle: linkRefStyle,
121
- outDirname,
122
- siteRoot,
123
- xmlIds,
124
- logger,
125
- } = assemblyModel
117
+ const { doctype, linkReferenceStyle, pubRoot, siteRoot, xmlIds, logger } = assemblyModel
118
+ const assembled = pagesInOutline.assembled
126
119
  // FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
127
120
  const page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
128
121
  const atDocumentRoot = !buffer.inBody
129
122
  const atBookRoot = atDocumentRoot && !level && doctype === 'book' && (supportsParts = true)
130
123
  const hasItems = items.length > 0
131
- const pubRoot = outDirname ? '/' + outDirname : ''
124
+ const idPrefix =
125
+ asciidocConfig.attributes['assembler-idprefix'] ?? ('assembler-idprefix' in asciidocConfig.attributes ? '' : '_')
132
126
  const idSeparator = xmlIds ? '-' : ':'
133
127
  const idScopeSeparator = idSeparator.repeat(3)
134
128
  const idCoordinateSeparator = idSeparator === '-' ? '----' : idSeparator
135
- if (page && !pagesInOutline.assembled.pages.has(page)) {
129
+ if (page && !assembled.pages.has(page)) {
136
130
  const contents = page.src.contents
137
131
  if (contents == null) {
138
132
  buffer.inBody ??= false
@@ -143,16 +137,15 @@ function mergeAsciiDoc (
143
137
  Object.assign({}, page, { contents: trimAsciiDoc(contents), mediaType })
144
138
  )
145
139
  const doc = loadAsciiDoc(pageAsAsciiDoc, contentCatalog, asciidocConfig)
146
- let asciidoctorLogger = doc.getLogger()
147
- if (logger) {
148
- asciidoctorLogger = Object.assign(asciidoctorLogger.$dup(), {
149
- delegate: {
150
- warn () {
151
- return logger.warn.apply(logger, arguments)
140
+ doc.logger = logger
141
+ ? Object.assign(doc.getLogger().$dup(), {
142
+ delegate: {
143
+ warn () {
144
+ return logger.warn.apply(logger, arguments)
145
+ },
152
146
  },
153
- },
154
- })
155
- }
147
+ })
148
+ : doc.getLogger()
156
149
  if (doc.hasAttribute('assembly-navtitle')) {
157
150
  navtitleAsciiDoc = doc.getAttribute('assembly-navtitle')
158
151
  navtitlePlain = sanitize((navtitle = doc.$apply_reftext_subs(navtitleAsciiDoc)))
@@ -180,7 +173,13 @@ function mergeAsciiDoc (
180
173
  }
181
174
  // NOTE: in Antora, docname is relative src path from module without file extension
182
175
  const docname = doc.getAttribute('docname')
183
- const { idPrefix, id: idScope } = generateId(page.src, componentVersion, idCoordinateSeparator, idScopeSeparator)
176
+ const { idLeader, id: idScope } = generateId(
177
+ page.src,
178
+ componentVersion,
179
+ idCoordinateSeparator,
180
+ idScopeSeparator,
181
+ idPrefix
182
+ )
184
183
  let pageFragment = ''
185
184
  let pageRoles = ''
186
185
  let pageStyle = doc.getAttribute('assembly-style', '')
@@ -236,7 +235,7 @@ function mergeAsciiDoc (
236
235
  assemblyModel = Object.assign({}, assemblyModel, { sectionMergeStrategy: 'discrete' })
237
236
  } else {
238
237
  if (atDocumentRoot) {
239
- pageFragment = `#${idPrefix}${doc.getId() ?? pageStyle}`
238
+ pageFragment = `#${idLeader}${doc.getId() ?? pageStyle}`
240
239
  buffer.unshift(`[#${idScope}]`)
241
240
  } else {
242
241
  pageFragment = `#${idScope}`
@@ -290,7 +289,7 @@ function mergeAsciiDoc (
290
289
  buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
291
290
  if (toggleSectids) buffer.push(':sectids:')
292
291
  }
293
- pagesInOutline.assembled.pages.set(page, pageFragment)
292
+ assembled.pages.set(page, pageFragment)
294
293
  if (doc.hasSections()) {
295
294
  fixSectionLevels(doc.getSections(), atBookRoot && !pageStyle ? undefined : nextSectionLevel)
296
295
  }
@@ -318,10 +317,6 @@ function mergeAsciiDoc (
318
317
  }
319
318
  }
320
319
  const refs = doc.getCatalog().refs
321
- const scannedInlineAnchors = Object.values(refs.$$smap).reduce((accum, it) => {
322
- if (it.node_name === 'inline_anchor') accum[it.id] = it
323
- return accum
324
- }, {})
325
320
  allBlocks.forEach((block) => {
326
321
  const contentModel = block.content_model
327
322
  if (
@@ -380,112 +375,33 @@ function mergeAsciiDoc (
380
375
  if (!refs['$key?'](refid) && (~refid.indexOf(' ') || refid.toLowerCase() !== refid)) {
381
376
  if ((refid = doc.$resolve_id(refid))['$nil?']()) return m
382
377
  }
383
- const alphaPrefix = !(refid in scannedInlineAnchors) || /^[\p{Alpha}_:]/u.test(idPrefix) ? '' : 'iid-'
384
- return `<<${alphaPrefix}${idPrefix}${refid}${text ? ',' + text : ''}>>`
378
+ return `<<${idLeader}${refid}${text ? ',' + text : ''}>>`
385
379
  })
386
380
  }
387
381
  // NOTE: the next check takes care of inline and block anchors
388
382
  if (~line.indexOf('[[')) {
389
- line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, (m, refid, text) => {
390
- const alphaPrefix = !(refid in scannedInlineAnchors) || /^[\p{Alpha}_:]/u.test(idPrefix) ? '' : 'iid-'
391
- return `[[${alphaPrefix}${idPrefix}${refid}${text}]]`
383
+ line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, (_, refid, text) => {
384
+ return `[[${idLeader}${refid}${text}]]`
392
385
  })
393
386
  }
394
387
  if (~line.indexOf('xref:')) {
395
- // Q: should we allow : as first character of target?
396
- line = line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (_, target_, text) => {
397
- let target = target_
398
- if (~target.indexOf('{')) target = doc.$sub_attributes(target, Opal.hash({ attribute_missing: 'skip' }))
399
- let fragment, resource, resourceRef
400
- const hashIdx = target.indexOf('#')
401
- if (~hashIdx) {
402
- resourceRef = target.slice(0, hashIdx)
403
- fragment = target.slice(hashIdx + 1)
404
- } else if (target.endsWith('.adoc') || ~target.indexOf('$')) {
405
- resourceRef = target
406
- fragment = ''
407
- } else {
408
- fragment = target
409
- }
410
- // Q: should we validate the internal ID here?
411
- if (!resourceRef) return `xref:${idPrefix}${fragment}[${text}]`
412
- const resourceId = parseResourceRef(resourceRef, page.src, 'page', contentCatalog)
413
- if (resourceId.family !== 'page' || !(resource = pagesInOutline.get(createResourceKey(resourceId)))) {
414
- if ((resource = contentCatalog.getById(resourceId))?.pub) {
415
- text ||= resource.asciidoc?.xreftext || target_
416
- if (siteRoot || linkRefStyle === 'relative') {
417
- return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkRefStyle)}${fragment && '#' + fragment}[${text}]`
418
- }
419
- asciidoctorLogger.warn(
420
- doc.createLogMessage(
421
- `Cannot create external ${resourceId.family} reference in assembly because site URL is unknown: ${target_}`,
422
- { source_location: { file: relative, lineno: idx + 1 } }
423
- )
424
- )
425
- }
426
- const linkAttrlist = text
427
- ? (~text.indexOf(',') ? `"${text}"` : text) + ',role=unresolved'
428
- : (~target.indexOf(':') ? `${target},` : '') + 'role=unresolved'
429
- return `link:${target_}[${linkAttrlist}]`
430
- }
431
- if (fragment === resource.asciidoc.id) fragment = ''
432
- if (
433
- text &&
434
- (assemblyModel.dropExplicitXrefText === 'always' ||
435
- (assemblyModel.dropExplicitXrefText === 'if-redundant' && text === resource.title))
436
- ) {
437
- text = ''
438
- }
439
- const refid = generateId(
440
- resource.src,
441
- componentVersion,
442
- idCoordinateSeparator,
443
- idScopeSeparator,
444
- text.length > 0,
445
- fragment
446
- ).id
447
- return `xref:${refid}[${text}]`
448
- })
449
- }
450
- if (~line.indexOf('link:{attachmentsdir}/')) {
451
- line = line.replace(/(?<![\\+])link:\{attachmentsdir\}\/([^\s[]+)\[(|.*?[^\\])\]/g, (m, relative, text) => {
452
- const attachment =
453
- (siteRoot || linkRefStyle === 'relative') &&
454
- contentCatalog.getById({
455
- component: componentVersion.name,
456
- version: componentVersion.version,
457
- module: module_,
458
- family: 'attachment',
459
- relative,
460
- })
461
- // TODO: handle unresolved attachment page
462
- return attachment?.out ? `${resolveLinkTarget(attachment, siteRoot, pubRoot, linkRefStyle)}[${text}]` : m
463
- })
388
+ line = rewriteXrefs(
389
+ line,
390
+ contentCatalog,
391
+ assemblyModel,
392
+ page.src,
393
+ true,
394
+ idCoordinateSeparator,
395
+ idScopeSeparator,
396
+ idPrefix,
397
+ idLeader,
398
+ pagesInOutline,
399
+ doc,
400
+ { file: relative, lineno: idx + 1 }
401
+ )
464
402
  }
465
403
  if (~line.indexOf('image:') && !line.startsWith('image::')) {
466
- line = line.replace(/(?<![\\+])image:([^:\s[](?:[^[]*[^\s[])?)\[([^\]]*)\]/g, (m, target, attrlist) => {
467
- let image
468
- if (
469
- isResourceSpec(target) &&
470
- (image = contentCatalog.resolveResource(target, page.src, 'image', ['image']))?.out
471
- ) {
472
- if (filetype !== 'html') {
473
- pagesInOutline.assembled.assets.add(image)
474
- return `image:${resolveEmbedTarget(image, outDirname, embedRefStyle, true)}[${attrlist}]`
475
- }
476
- if (siteRoot || linkRefStyle === 'relative') {
477
- pagesInOutline.assembled.assets.add(image)
478
- return `image:${resolveLinkTarget(image, siteRoot, pubRoot, linkRefStyle)}[${attrlist}]`
479
- }
480
- asciidoctorLogger.warn(
481
- doc.createLogMessage(
482
- `Cannot create external image reference in assembly because site URL is unknown: ${target}`,
483
- { source_location: { file: relative, lineno: idx + 1 } }
484
- )
485
- )
486
- }
487
- return m
488
- })
404
+ line = rewriteInlineImages(line, contentCatalog, assemblyModel, page.src, assembled.assets, true)
489
405
  }
490
406
  lines[idx] = line
491
407
  }
@@ -511,7 +427,7 @@ function mergeAsciiDoc (
511
427
  return '='.repeat(targetMarkerLength) + ' ' + rest
512
428
  })
513
429
  // NOTE: ID will be undefined if sectids are turned off
514
- if (block.getId()) rewriteStyleAttribute(block, lines, idx, idPrefix, blockStyle)
430
+ if (block.getId()) rewriteStyleAttribute(block, lines, idx, idLeader, blockStyle)
515
431
  } else {
516
432
  if (context === 'image') {
517
433
  let line = lines[idx] || ''
@@ -530,27 +446,17 @@ function mergeAsciiDoc (
530
446
  imageMacroOffset = -1
531
447
  }
532
448
  }
533
- if (imageMacroOffset >= 0) {
449
+ if (~imageMacroOffset) {
534
450
  const target = block.getAttribute('target')
535
- if (isResourceSpec(target)) {
536
- const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
537
- // FIXME: handle (or report) case when image is not resolved
538
- if (image?.out && (filetype !== 'html' || siteRoot)) {
539
- const attrlist = line.slice(line.indexOf('[') + 1, -1)
540
- pagesInOutline.assembled.assets.add(image)
541
- lines[idx] =
542
- filetype === 'html'
543
- ? `${prefix}image::${resolveLinkTarget(image, siteRoot, pubRoot, linkRefStyle, false)}[${attrlist}]`
544
- : `${prefix}image::${resolveEmbedTarget(image, outDirname, embedRefStyle)}[${attrlist}]`
545
- }
546
- }
451
+ const newTarget = rewriteImageRef(target, contentCatalog, assemblyModel, page.src, assembled.assets)
452
+ if (newTarget) lines[idx] = `${prefix}image::${newTarget}${line.slice(line.indexOf('['))}`
547
453
  lastImageMacroAt = [idx, imageMacroOffset]
548
454
  }
549
455
  } else if (context === 'document' && block.hasHeader()) {
550
456
  // nested document
551
457
  idx = (block.getHeader().getLineNumber() || idx + 1) - 1
552
458
  }
553
- if (block.getId()) rewriteStyleAttribute(block, lines, idx, idPrefix)
459
+ if (block.getId()) rewriteStyleAttribute(block, lines, idx, idLeader)
554
460
  }
555
461
  })
556
462
  safePush(
@@ -606,10 +512,17 @@ function mergeAsciiDoc (
606
512
  const resource = files.find((it) => it.pub.url === url)
607
513
  if (resource) {
608
514
  if (resource.src.family === 'page' && pagesInOutline.has(resource.pub.url)) {
609
- const refid = generateId(resource.src, componentVersion, idCoordinateSeparator, idScopeSeparator, true).id
515
+ const refid = generateId(
516
+ resource.src,
517
+ componentVersion,
518
+ idCoordinateSeparator,
519
+ idScopeSeparator,
520
+ idPrefix,
521
+ true
522
+ ).id
610
523
  sectionTitle = `xref:${refid}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
611
524
  } else if (siteRoot) {
612
- sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot, linkRefStyle)}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
525
+ sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, true)}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
613
526
  }
614
527
  }
615
528
  }
@@ -732,60 +645,6 @@ function fixSectionLevels (sections, expectedLevel) {
732
645
  })
733
646
  }
734
647
 
735
- function rewriteStyleAttribute (block, lines, idx, idPrefix, replacementStyle = '') {
736
- let prevLine = lines[idx - 1]
737
- const char0 = prevLine?.charAt()
738
- if (char0) {
739
- if (
740
- (char0 === '.' && /^\.\.?[^ \t.]/.test(prevLine)) ||
741
- (char0 === '[' &&
742
- prevLine.charAt(1) === '[' &&
743
- /^\[\[(?:|[\p{Alpha}_:][\p{Alpha}0-9_\-:.]*(?:, *.+)?)\]\]$/u.test(prevLine))
744
- ) {
745
- return rewriteStyleAttribute(block, lines, idx - 1, idPrefix, replacementStyle)
746
- }
747
- }
748
- let cellSpec
749
- if (
750
- char0 &&
751
- (char0 === '[' || (block.getDocument().isNested() && (cellSpec = prevLine.match(/^([^[|]*)\| *(\[.+)/)))) &&
752
- prevLine.charAt(prevLine.length - 1) === ']'
753
- ) {
754
- if (cellSpec) {
755
- prevLine = cellSpec[2]
756
- cellSpec = cellSpec[1]
757
- }
758
- let rawStyle
759
- const commaIdx = prevLine.indexOf(',')
760
- if (~commaIdx) {
761
- rawStyle = prevLine.slice(1, commaIdx)
762
- if (~rawStyle.indexOf('=')) rawStyle = undefined
763
- } else if (!~prevLine.indexOf('=')) {
764
- rawStyle = prevLine.slice(1, prevLine.length - 1)
765
- }
766
- if (rawStyle) {
767
- if (~rawStyle.indexOf('#')) {
768
- prevLine = prevLine.replace(/#[^.%,\]]+/, `#${idPrefix}${block.getId()}`)
769
- if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
770
- } else {
771
- prevLine = `[${
772
- replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
773
- }#${idPrefix}${block.getId()}${prevLine.slice(rawStyle.length + 1)}`
774
- }
775
- } else {
776
- prevLine = `[${replacementStyle}#${idPrefix}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.slice(1)}`
777
- }
778
- if (cellSpec) prevLine = `${cellSpec}|${prevLine}`
779
- lines[idx - 1] = prevLine
780
- } else {
781
- lines.splice(idx, 0, `[${replacementStyle}#${idPrefix}${block.getId()}]`)
782
- }
783
- }
784
-
785
- function isResourceSpec (str) {
786
- return !(~str.indexOf(':') && (~str.indexOf('://') || (str.startsWith('data:') && ~str.indexOf(','))))
787
- }
788
-
789
648
  function getObjectId (obj) {
790
649
  return global.Opal.id(obj)
791
650
  }
@@ -812,58 +671,4 @@ function safePush (onto, entries) {
812
671
  }
813
672
  }
814
673
 
815
- function resolveEmbedTarget (resource, outDirname, referenceStyle, escapeForInline) {
816
- const target =
817
- referenceStyle === 'output-relative' ? resource.out.path : path.relative(outDirname + '/', resource.out.path)
818
- return escapeForInline ? target.replace(/_/g, '{underscore}') : target
819
- }
820
-
821
- function resolveLinkTarget (resource, siteRoot, pubRoot, referenceStyle, escapeForInline = true) {
822
- let target
823
- if (resource.site?.url) {
824
- target = ['', resource.pub.url]
825
- } else {
826
- switch (referenceStyle) {
827
- case 'absolute':
828
- target = ['', siteRoot.url + resource.pub.url]
829
- break
830
- case 'root-relative':
831
- target = ['link:', siteRoot.path + resource.pub.url]
832
- break
833
- default:
834
- target = ['link:', computeRelativeUrl(pubRoot + '/', resource.pub.url)]
835
- }
836
- }
837
- if (escapeForInline) target[1] = target[1].replace(/_/g, '{underscore}')
838
- return target.join('')
839
- }
840
-
841
- function computeRelativeUrl (from, to) {
842
- const rel = path.relative(from, to)
843
- return to.charAt(to.length - 1) === '/' ? rel + '/' : rel
844
- }
845
-
846
- function createResourceKey ({ component, version, module: mod, family, relative }) {
847
- return `${version}@${component}:${mod === 'ROOT' ? '' : mod}:${family === 'page' ? '' : family + '$'}${relative}`
848
- }
849
-
850
- function generateId (componentSrc, componentVersion, coordinateSep, scopeSep, asXrefTarget, fragment) {
851
- let { component, module: mod, relative } = componentSrc
852
- let id = relative.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
853
- if (component !== componentVersion.name) {
854
- id = [component, mod === 'ROOT' ? '' : mod, id].join(coordinateSep)
855
- } else if (mod !== 'ROOT') {
856
- if (asXrefTarget && coordinateSep === ':' && /(?:pass|stem)$/.test(mod) && /^[a-z]+(?:,[a-z-]+)*$/.test(id)) {
857
- mod = mod.replace(/(?:pass|stem)$/, '\\$&')
858
- }
859
- id = mod + coordinateSep + id
860
- } else if (ReservedIdNames.includes(id)) {
861
- id += scopeSep
862
- scopeSep = ''
863
- }
864
- const idPrefix = id + scopeSep
865
- if (fragment) id = idPrefix + fragment
866
- return { idPrefix, id }
867
- }
868
-
869
674
  module.exports = produceAssemblyFile
@@ -4,14 +4,15 @@ const computeOut = require('./util/compute-out')
4
4
  const createAsciiDocFile = require('./util/create-asciidoc-file')
5
5
  const filterComponentVersions = require('./filter-component-versions')
6
6
  const produceAssemblyFile = require('./produce-assembly-file')
7
+ const { rewriteImageAttr, rewriteInlineImages, rewriteXrefs } = require('./util/rewriter')
7
8
  const selectMutableAttributes = require('./select-mutable-attributes')
8
9
 
9
10
  const ATTR_REF_RX = /\\?\{(\w[\w-]*)\}/g
10
- const IMAGE_MACRO_RX = /^image::?(.+?)\[(.*?)\]$/
11
11
 
12
- function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, resolveAssemblyModel) {
13
- const { asciidoc: assemblerAsciiDocConfig, assembly: assemblyConfig } = assemblerConfig
14
- resolveAssemblyModel ??= (componentVersion) => ({
12
+ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, selectAssemblyProfile) {
13
+ const { assembly: assemblyConfig } = assemblerConfig
14
+ selectAssemblyProfile ??= (componentVersion) => ({
15
+ attributes: assemblyConfig.attributes,
15
16
  doctype: assemblyConfig.doctype,
16
17
  insertStartPage: assemblyConfig.insertStartPage,
17
18
  rootLevel: assemblyConfig.rootLevel,
@@ -21,36 +22,42 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
21
22
  embedReferenceStyle: assemblyConfig.embedReferenceStyle,
22
23
  linkReferenceStyle: assemblyConfig.linkReferenceStyle,
23
24
  dropExplicitXrefText: assemblyConfig.dropExplicitXrefText,
25
+ revdate: assemblyConfig.revdate,
24
26
  logger: assemblyConfig.logger, // for tests
25
27
  })
26
- const assemblerAsciiDocAttributes = Object.assign({}, assemblerAsciiDocConfig.attributes)
27
- const { revdate, 'source-highlighter': sourceHighlighter } = assemblerAsciiDocAttributes
28
- delete assemblerAsciiDocAttributes.revdate
29
- delete assemblerAsciiDocAttributes['source-highlighter']
28
+ const baseAssemblyAttributes = assemblyConfig.attributes
30
29
  const publishableFiles = contentCatalog.getFiles().filter((file) => file.out)
31
30
  let siteRoot
32
31
  const configMdc = assemblerConfig.file ? { file: { path: assemblerConfig.file } } : {}
33
32
  return filterComponentVersions(contentCatalog.getComponents(), assemblerConfig.componentVersionFilter.names).reduce(
34
33
  (accum, componentVersion) => {
35
- const assemblyModel = resolveAssemblyModel(componentVersion)
36
- if (!assemblyModel.navigation) return accum
34
+ const assemblyModel = selectAssemblyProfile(componentVersion)
35
+ const { attributes: assemblyAttributes, logger, navigation, rootLevel } = assemblyModel
36
+ if (!navigation) return accum
37
+ const contextualLogger = logger ? { warn: logger.warn.bind(logger, configMdc) } : undefined
37
38
  const { name: componentName, version, title } = componentVersion
38
39
  const componentVersionAsciiDocConfig = getAsciiDocConfigWithAsciidoctorReducerExtension(componentVersion)
40
+ let sourceHighlighter
41
+ if ('source-highlighter' in assemblyAttributes) {
42
+ sourceHighlighter = assemblyAttributes['source-highlighter']
43
+ delete assemblyAttributes['source-highlighter']
44
+ }
39
45
  const mergedAsciiDocAttributes = collateAsciiDocAttributes(
40
- Object.assign({ revdate }, componentVersionAsciiDocConfig.attributes),
41
- assemblerAsciiDocAttributes,
42
- { logger: assemblyModel.logger, mdc: configMdc }
46
+ Object.assign({ revdate: assemblyModel.revdate }, componentVersionAsciiDocConfig.attributes),
47
+ assemblyAttributes,
48
+ contextualLogger
43
49
  )
44
50
  const mergedAsciiDocConfig = Object.assign({}, componentVersionAsciiDocConfig, {
45
51
  attributes: mergedAsciiDocAttributes,
46
52
  })
47
- assemblyModel.outDirname = computeOut.call(contentCatalog, {
53
+ assemblyModel.filetype = baseAssemblyAttributes['assembler-filetype']
54
+ const outDirname = (assemblyModel.outDirname = computeOut.call(contentCatalog, {
48
55
  component: componentName,
49
56
  version,
50
57
  family: 'export',
51
58
  relative: '.index.adoc',
52
- }).dirname
53
- assemblyModel.filetype = assemblerAsciiDocAttributes['assembler-filetype']
59
+ }).dirname)
60
+ assemblyModel.pubRoot = outDirname ? '/' + outDirname : ''
54
61
  assemblyModel.siteRoot =
55
62
  siteRoot === undefined
56
63
  ? (siteRoot ??= ((val) => {
@@ -69,17 +76,22 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
69
76
  assemblyModel.linkReferenceStyle = 'absolute'
70
77
  }
71
78
  const auxiliaryImages = new Set()
79
+ const ctx = { component: componentName, version }
72
80
  Object.entries(mergedAsciiDocAttributes).forEach(([name, val]) => {
73
- const match = name.endsWith('-image') && val.startsWith('image:') && IMAGE_MACRO_RX.exec(val)
74
- if (!(match && isResourceRef(match[1]))) return
75
- // Q should we allow image to be resolved relative to component version?
76
- const image = contentCatalog.resolveResource(match[1], undefined, 'image', ['image'])
77
- if (!image?.out) return
78
- mergedAsciiDocAttributes[name] = `image:${image.out.path}[${match[2]}]`
79
- auxiliaryImages.add(image)
81
+ if (!(typeof val === 'string' && ~val.indexOf(':'))) return
82
+ if (name.endsWith('-image')) {
83
+ const newVal = rewriteImageAttr(val, contentCatalog, assemblyModel, ctx, auxiliaryImages)
84
+ if (newVal) {
85
+ mergedAsciiDocAttributes[name] = newVal
86
+ return
87
+ }
88
+ }
89
+ const oldVal = val
90
+ if (~val.indexOf('image:')) val = rewriteInlineImages(val, contentCatalog, assemblyModel, ctx, auxiliaryImages)
91
+ if (~val.indexOf('xref:')) val = rewriteXrefs(val, contentCatalog, assemblyModel, ctx)
92
+ if (val !== oldVal) mergedAsciiDocAttributes[name] = val
80
93
  })
81
94
  const rootEntry = { content: title }
82
- const { navigation, rootLevel } = assemblyModel
83
95
  let startPage =
84
96
  'startPage' in componentVersion
85
97
  ? componentVersion.startPage
@@ -123,9 +135,12 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
123
135
  accum.push(assemblyFile)
124
136
  return true
125
137
  }, false)
126
- sourceHighlighter
127
- ? (mergedAsciiDocAttributes['source-highlighter'] = sourceHighlighter)
128
- : delete mergedAsciiDocAttributes['source-highlighter']
138
+ // NOTE restore source highlighter for conversion if defined in Assembler config
139
+ if (sourceHighlighter !== undefined) {
140
+ mergedAsciiDocAttributes['source-highlighter'] = sourceHighlighter
141
+ } else if (assemblyModel.filetype !== 'html') {
142
+ delete mergedAsciiDocAttributes['source-highlighter']
143
+ }
129
144
  return accum
130
145
  },
131
146
  []
@@ -151,10 +166,6 @@ function includedInNav (items, url) {
151
166
  return items.find((it) => it.url === url || includedInNav(it.items || [], url))
152
167
  }
153
168
 
154
- function isResourceRef (target) {
155
- return ~target.indexOf(':') && !(~target.indexOf('://') || (target.startsWith('data:') && ~target.indexOf(',')))
156
- }
157
-
158
169
  function prepareOutlines (navigation, rootEntry, rootLevel) {
159
170
  const singleEntry = navigation.length === 1
160
171
  const items = navigation.reduce((accum, it) => {
@@ -178,7 +189,7 @@ function prepareOutlines (navigation, rootEntry, rootLevel) {
178
189
  return items
179
190
  }
180
191
 
181
- function collateAsciiDocAttributes (collated, additional, { logger, mdc }) {
192
+ function collateAsciiDocAttributes (collated, additional, logger) {
182
193
  Object.entries(additional).forEach(([name, val]) => {
183
194
  if (val && val.constructor === String) {
184
195
  let alias
@@ -189,9 +200,7 @@ function collateAsciiDocAttributes (collated, additional, { logger, mdc }) {
189
200
  if (refname in collated && ref === val) {
190
201
  alias = refval
191
202
  } else if (collated['attribute-missing'] === 'warn') {
192
- if (logger) {
193
- logger.warn(mdc, "Skipping reference to missing attribute '%s' in value of '%s' attribute", refname, name)
194
- }
203
+ logger?.warn("Skipping reference to missing attribute '%s' in value of '%s' attribute", refname, name)
195
204
  }
196
205
  return ref
197
206
  }
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ function createResourceKey ({ component, version, module: mod, family, relative }) {
4
+ return `${version}@${component}:${mod === 'ROOT' ? '' : mod}:${family === 'page' ? '' : family + '$'}${relative}`
5
+ }
6
+
7
+ module.exports = createResourceKey
@@ -0,0 +1,25 @@
1
+ 'use strict'
2
+
3
+ const ReservedIdNames = 'content header footnotes footer footer-text premable toc toctitle'.split(' ')
4
+
5
+ function generateId (componentSrc, componentVersion, coordinateSep, scopeSep, prefix, asXrefTarget, fragment) {
6
+ let { component, module: mod, relative } = componentSrc
7
+ let id = relative.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
8
+ if (component !== componentVersion.name) {
9
+ id = [component, mod === 'ROOT' ? '' : mod, id].join(coordinateSep)
10
+ } else if (mod !== 'ROOT') {
11
+ if (asXrefTarget && coordinateSep === ':' && /(?:pass|stem)$/.test(mod) && /^[a-z]+(?:,[a-z-]+)*$/.test(id)) {
12
+ prefix = ''
13
+ mod = mod.replace(/(?:pass|stem)$/, '\\$&')
14
+ }
15
+ id = mod + coordinateSep + id
16
+ } else if (ReservedIdNames.includes(id)) {
17
+ id += scopeSep
18
+ scopeSep = ''
19
+ }
20
+ if (prefix && !/^[\p{Alpha}_:]/u.test(id)) id = prefix + id
21
+ const idLeader = id + scopeSep
22
+ return { idLeader, id: fragment ? idLeader + fragment : id }
23
+ }
24
+
25
+ module.exports = generateId
@@ -0,0 +1,36 @@
1
+ 'use strict'
2
+
3
+ const path = require('node:path/posix')
4
+
5
+ function resolveEmbedTarget (resource, outDirname, referenceStyle, escapeForInline) {
6
+ const target =
7
+ referenceStyle === 'output-relative' ? resource.out.path : path.relative(outDirname + '/', resource.out.path)
8
+ return escapeForInline ? target.replace(/_/g, '{underscore}') : target
9
+ }
10
+
11
+ function resolveLinkTarget (resource, siteRoot, pubRoot, referenceStyle, escapeForInline, ensurePrefix = true) {
12
+ let target
13
+ if (resource.site?.url) {
14
+ target = ['', resource.pub.url]
15
+ } else {
16
+ switch (referenceStyle) {
17
+ case 'absolute':
18
+ target = ['', siteRoot.url + resource.pub.url]
19
+ break
20
+ case 'root-relative':
21
+ target = [ensurePrefix ? 'link:' : '', siteRoot.path + resource.pub.url]
22
+ break
23
+ default:
24
+ target = [ensurePrefix ? 'link:' : '', computeRelativeUrl(pubRoot + '/', resource.pub.url)]
25
+ }
26
+ }
27
+ if (escapeForInline) target[1] = target[1].replace(/_/g, '{underscore}')
28
+ return target.join('')
29
+ }
30
+
31
+ function computeRelativeUrl (from, to) {
32
+ const rel = path.relative(from, to)
33
+ return to.charAt(to.length - 1) === '/' ? rel + '/' : rel
34
+ }
35
+
36
+ module.exports = { resolveEmbedTarget, resolveLinkTarget }
@@ -0,0 +1,202 @@
1
+ 'use strict'
2
+
3
+ const createResourceKey = require('./create-resource-key')
4
+ const generateId = require('./generate-id')
5
+ const { resolveEmbedTarget, resolveLinkTarget } = require('./resolver')
6
+ const parseResourceRef = require('./parse-resource-ref')
7
+
8
+ function rewriteXrefs (
9
+ line,
10
+ contentCatalog,
11
+ assemblyModel,
12
+ ctx,
13
+ escapeForInline,
14
+ idCoordinateSep,
15
+ idScopeSep,
16
+ idPrefix,
17
+ idLeader,
18
+ pagesInOutline,
19
+ doc,
20
+ sourceLocation
21
+ ) {
22
+ return line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (_, target, text) =>
23
+ rewriteXref(
24
+ target,
25
+ text,
26
+ contentCatalog,
27
+ assemblyModel,
28
+ ctx,
29
+ escapeForInline,
30
+ idCoordinateSep,
31
+ idScopeSep,
32
+ idPrefix,
33
+ idLeader,
34
+ pagesInOutline,
35
+ doc,
36
+ sourceLocation
37
+ )
38
+ )
39
+ }
40
+
41
+ function rewriteXref (
42
+ target,
43
+ text,
44
+ contentCatalog,
45
+ assemblyModel,
46
+ ctx,
47
+ escapeForInline,
48
+ idCoordinateSep,
49
+ idScopeSep,
50
+ idPrefix,
51
+ idLeader,
52
+ pagesInOutline,
53
+ doc,
54
+ sourceLocation
55
+ ) {
56
+ const rawTarget = target
57
+ if (doc && ~target.indexOf('{')) target = doc.$sub_attributes(target, global.Opal.hash({ attribute_missing: 'skip' }))
58
+ let fragment, resourceRef
59
+ const hashIdx = target.indexOf('#')
60
+ if (~hashIdx) {
61
+ resourceRef = target.slice(0, hashIdx)
62
+ fragment = target.slice(hashIdx + 1)
63
+ } else if (target.endsWith('.adoc') || ~target.indexOf('$')) {
64
+ resourceRef = target
65
+ fragment = ''
66
+ } else {
67
+ fragment = target
68
+ }
69
+ if (!resourceRef) return `xref:${idLeader || ''}${fragment}[${text}]`
70
+ const resourceId = parseResourceRef(resourceRef, ctx, 'page', contentCatalog)
71
+ const family = resourceId.family
72
+ let resource
73
+ if (family !== 'page' || !(resource = pagesInOutline?.get(createResourceKey(resourceId)))) {
74
+ if ((resource = contentCatalog.getById(resourceId))?.pub) {
75
+ const { linkReferenceStyle, pubRoot, siteRoot } = assemblyModel
76
+ text ||= resource.asciidoc?.xreftext || rawTarget
77
+ if (siteRoot || linkReferenceStyle === 'relative') {
78
+ return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, escapeForInline)}${fragment && '#' + fragment}[${text}]`
79
+ }
80
+ if (doc) {
81
+ const msg = `Cannot create external ${family} reference in assembly because site URL is unknown: ${rawTarget}`
82
+ doc.logger.warn(doc.createLogMessage(msg, { source_location: sourceLocation }))
83
+ }
84
+ }
85
+ const linkAttrlist = text
86
+ ? (~text.indexOf(',') ? `"${text}"` : text) + ',role=unresolved'
87
+ : (~target.indexOf(':') ? `${rawTarget},` : '') + 'role=unresolved'
88
+ return `link:${rawTarget}[${linkAttrlist}]`
89
+ }
90
+ if (fragment === resource.asciidoc.id) fragment = ''
91
+ if (
92
+ text &&
93
+ (assemblyModel.dropExplicitXrefText === 'always' ||
94
+ (assemblyModel.dropExplicitXrefText === 'if-redundant' && text === resource.title))
95
+ ) {
96
+ text = ''
97
+ }
98
+ const componentVersionCtx = { name: ctx.component, version: ctx.version }
99
+ const refid = generateId(
100
+ resource.src,
101
+ componentVersionCtx,
102
+ idCoordinateSep,
103
+ idScopeSep,
104
+ idPrefix,
105
+ text.length > 0,
106
+ fragment
107
+ ).id
108
+ return `xref:${refid}[${text}]`
109
+ }
110
+
111
+ function rewriteImageAttr (val, contentCatalog, assemblyModel, ctx, assets) {
112
+ const match = val.startsWith('image:') && /^image::?(.+?)\[(.*?)\]$/.exec(val)
113
+ if (!match) return
114
+ const newTarget = rewriteImageRef(match[1], contentCatalog, assemblyModel, ctx, assets)
115
+ return newTarget ? `image:${newTarget}[${match[2]}]` : val
116
+ }
117
+
118
+ function rewriteInlineImages (line, contentCatalog, assemblyModel, ctx, assets, escapeForInline) {
119
+ return line.replace(/(?<![\\+])image:([^:\s[](?:[^[]*[^\s[])?)\[([^\]]*)\]/g, (m, target, attrlist) => {
120
+ const newTarget = rewriteImageRef(target, contentCatalog, assemblyModel, ctx, assets, escapeForInline)
121
+ return newTarget ? `image:${newTarget}[${attrlist}]` : m
122
+ })
123
+ }
124
+
125
+ function rewriteImageRef (target, contentCatalog, assemblyModel, ctx, assets, escapeForInline = false) {
126
+ const image = isResourceRef(target) && contentCatalog.resolveResource(target, ctx, 'image', ['image'])
127
+ if (!image?.out) return
128
+ let newTarget
129
+ const { filetype, embedReferenceStyle, linkReferenceStyle, outDirname, pubRoot, siteRoot } = assemblyModel
130
+ if (filetype !== 'html') {
131
+ newTarget = resolveEmbedTarget(image, outDirname, embedReferenceStyle, escapeForInline)
132
+ assets.add(image)
133
+ } else if (siteRoot || linkReferenceStyle === 'relative') {
134
+ newTarget = resolveLinkTarget(image, siteRoot, pubRoot, linkReferenceStyle, escapeForInline, false)
135
+ if (linkReferenceStyle === 'relative') assets.add(image)
136
+ }
137
+ if (!newTarget) return
138
+ return newTarget
139
+ }
140
+
141
+ function rewriteStyleAttribute (block, lines, idx, idLeader, replacementStyle = '') {
142
+ let prevLine = lines[idx - 1]
143
+ const char0 = prevLine?.charAt()
144
+ if (char0) {
145
+ if (
146
+ (char0 === '.' && /^\.\.?[^ \t.]/.test(prevLine)) ||
147
+ (char0 === '[' &&
148
+ prevLine.charAt(1) === '[' &&
149
+ /^\[\[(?:|[\p{Alpha}_:][\p{Alpha}0-9_\-:.]*(?:, *.+)?)\]\]$/u.test(prevLine))
150
+ ) {
151
+ return rewriteStyleAttribute(block, lines, idx - 1, idLeader, replacementStyle)
152
+ }
153
+ }
154
+ let cellSpec
155
+ if (
156
+ char0 &&
157
+ (char0 === '[' || (block.getDocument().isNested() && (cellSpec = prevLine.match(/^([^[|]*)\| *(\[.+)/)))) &&
158
+ prevLine.charAt(prevLine.length - 1) === ']'
159
+ ) {
160
+ if (cellSpec) {
161
+ prevLine = cellSpec[2]
162
+ cellSpec = cellSpec[1]
163
+ }
164
+ let rawStyle
165
+ const commaIdx = prevLine.indexOf(',')
166
+ if (~commaIdx) {
167
+ rawStyle = prevLine.slice(1, commaIdx)
168
+ if (~rawStyle.indexOf('=')) rawStyle = undefined
169
+ } else if (!~prevLine.indexOf('=')) {
170
+ rawStyle = prevLine.slice(1, prevLine.length - 1)
171
+ }
172
+ if (rawStyle) {
173
+ if (~rawStyle.indexOf('#')) {
174
+ prevLine = prevLine.replace(/#[^.%,\]]+/, `#${idLeader}${block.getId()}`)
175
+ if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
176
+ } else {
177
+ prevLine = `[${
178
+ replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
179
+ }#${idLeader}${block.getId()}${prevLine.slice(rawStyle.length + 1)}`
180
+ }
181
+ } else {
182
+ prevLine = `[${replacementStyle}#${idLeader}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.slice(1)}`
183
+ }
184
+ if (cellSpec) prevLine = `${cellSpec}|${prevLine}`
185
+ lines[idx - 1] = prevLine
186
+ } else {
187
+ lines.splice(idx, 0, `[${replacementStyle}#${idLeader}${block.getId()}]`)
188
+ }
189
+ }
190
+
191
+ function isResourceRef (str) {
192
+ return !(~str.indexOf(':') && (~str.indexOf('://') || (str.startsWith('data:') && ~str.indexOf(','))))
193
+ }
194
+
195
+ module.exports = {
196
+ rewriteXref,
197
+ rewriteXrefs,
198
+ rewriteImageAttr,
199
+ rewriteImageRef,
200
+ rewriteInlineImages,
201
+ rewriteStyleAttribute,
202
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/assembler",
3
- "version": "1.0.0-beta.16",
3
+ "version": "1.0.0-beta.17",
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)",
@@ -30,7 +30,8 @@
30
30
  "./parse-resource-ref": "./lib/util/parse-resource-ref.js",
31
31
  "./produce-assembly-file": "./lib/produce-assembly-file.js",
32
32
  "./produce-assembly-files": "./lib/produce-assembly-files.js",
33
- "./select-mutable-attributes": "./lib/select-mutable-attributes.js"
33
+ "./select-mutable-attributes": "./lib/select-mutable-attributes.js",
34
+ "./package.json": "./package.json"
34
35
  },
35
36
  "imports": {
36
37
  "#asciidoctor-log-adapter": "./adapters/asciidoctor/jsonl-logger.rb",