@antora/assembler 1.0.0-beta.2 → 1.0.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,49 +1,80 @@
1
1
  'use strict'
2
2
 
3
+ const computeOut = require('./util/compute-out')
3
4
  const createAsciiDocFile = require('./util/create-asciidoc-file')
4
5
  const filterComponentVersions = require('./filter-component-versions')
5
6
  const produceAssemblyFile = require('./produce-assembly-file')
6
7
  const selectMutableAttributes = require('./select-mutable-attributes')
7
8
 
8
- const IMAGE_MACRO_RX = /^image::?(.+?)\[(.*?)\]$/
9
+ const ATTR_REF_RX = /\\?\{(\w[\w-]*)\}/g
9
10
 
10
- function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, resolveAssemblyModel) {
11
- const { asciidoc: assemblerAsciiDocConfig, assembly: assemblyConfig } = assemblerConfig
12
- resolveAssemblyModel ??= (componentVersion) => ({
11
+ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, selectAssemblyProfile) {
12
+ const { assembly: assemblyConfig } = assemblerConfig
13
+ selectAssemblyProfile ??= (componentVersion) => ({
14
+ attributes: assemblyConfig.attributes,
13
15
  doctype: assemblyConfig.doctype,
14
16
  insertStartPage: assemblyConfig.insertStartPage,
15
17
  rootLevel: assemblyConfig.rootLevel,
16
18
  sectionMergeStrategy: assemblyConfig.sectionMergeStrategy,
17
19
  navigation: componentVersion.navigation,
18
20
  xmlIds: assemblyConfig.xmlIds,
21
+ embedReferenceStyle: assemblyConfig.embedReferenceStyle,
22
+ linkReferenceStyle: assemblyConfig.linkReferenceStyle,
23
+ dropExplicitXrefText: assemblyConfig.dropExplicitXrefText,
24
+ revdate: assemblyConfig.revdate,
25
+ logger: assemblyConfig.logger, // for tests
19
26
  })
20
- const assemblerAsciiDocAttributes = Object.assign({}, assemblerAsciiDocConfig.attributes)
21
- const { revdate, 'source-highlighter': sourceHighlighter } = assemblerAsciiDocAttributes
22
- delete assemblerAsciiDocAttributes.revdate
23
- delete assemblerAsciiDocAttributes['source-highlighter']
27
+ const baseAssemblyAttributes = assemblyConfig.attributes
24
28
  const publishableFiles = contentCatalog.getFiles().filter((file) => file.out)
25
- return filterComponentVersions(contentCatalog.getComponents(), assemblerConfig.componentVersionFilter.names).reduce(
29
+ let siteRoot
30
+ const configMdc = assemblerConfig.file ? { file: { path: assemblerConfig.file } } : {}
31
+ return filterComponentVersions(contentCatalog.getComponents(), assemblerConfig.componentVersionFilter).reduce(
26
32
  (accum, componentVersion) => {
27
- const assemblyModel = resolveAssemblyModel(componentVersion)
28
- if (!assemblyModel.navigation) return accum
33
+ const assemblyModel = selectAssemblyProfile(componentVersion)
34
+ const { attributes: assemblyAttributes, logger, navigation, rootLevel } = assemblyModel
35
+ if (!navigation) return accum
36
+ const contextualLogger = logger ? { warn: logger.warn.bind(logger, configMdc) } : undefined
29
37
  const { name: componentName, version, title } = componentVersion
30
38
  const componentVersionAsciiDocConfig = getAsciiDocConfigWithAsciidoctorReducerExtension(componentVersion)
39
+ let sourceHighlighter
40
+ if ('source-highlighter' in assemblyAttributes) {
41
+ sourceHighlighter = assemblyAttributes['source-highlighter']
42
+ delete assemblyAttributes['source-highlighter']
43
+ }
44
+ const mergedAsciiDocAttributes = collateAsciiDocAttributes(
45
+ Object.assign({}, componentVersionAsciiDocConfig.attributes),
46
+ assemblyAttributes,
47
+ contextualLogger
48
+ )
31
49
  const mergedAsciiDocConfig = Object.assign({}, componentVersionAsciiDocConfig, {
32
- attributes: Object.assign({ revdate }, componentVersionAsciiDocConfig.attributes, assemblerAsciiDocAttributes),
33
- })
34
- const mergedAsciiDocAttributes = mergedAsciiDocConfig.attributes
35
- const auxiliaryImages = new Set()
36
- Object.entries(mergedAsciiDocAttributes).forEach(([name, val]) => {
37
- const match = name.endsWith('-image') && val.startsWith('image:') && IMAGE_MACRO_RX.exec(val)
38
- if (!(match && isResourceRef(match[1]))) return
39
- // Q should we allow image to be resolved relative to component version?
40
- const image = contentCatalog.resolveResource(match[1], undefined, 'image', ['image'])
41
- if (!image?.out) return
42
- mergedAsciiDocAttributes[name] = `image:${image.out.path}[${match[2]}]`
43
- auxiliaryImages.add(image)
50
+ attributes: mergedAsciiDocAttributes,
44
51
  })
52
+ assemblyModel.filetype = baseAssemblyAttributes['assembler-filetype']
53
+ const outDirname = (assemblyModel.outDirname = computeOut.call(contentCatalog, {
54
+ component: componentName,
55
+ version,
56
+ family: 'export',
57
+ relative: '.index.adoc',
58
+ }).dirname)
59
+ assemblyModel.pubRoot = outDirname ? '/' + outDirname : ''
60
+ assemblyModel.siteRoot =
61
+ siteRoot === undefined
62
+ ? (siteRoot ??= ((val) => {
63
+ if (!val) return null
64
+ if (val.charAt(val.length - 1) === '/') val = val.slice(0, val.length - 1)
65
+ if (!val || val.charAt() === '/') return { path: val }
66
+ return { url: val, path: extractUrlPath(val) }
67
+ })(mergedAsciiDocAttributes['site-url'] || mergedAsciiDocAttributes['primary-site-url']))
68
+ : siteRoot
69
+ if (assemblyModel.filetype === 'html') {
70
+ let linkRefStyle = assemblyModel.linkReferenceStyle
71
+ if (linkRefStyle === 'absolute' && siteRoot?.url == null) linkRefStyle = 'root-relative'
72
+ if (linkRefStyle === 'root-relative' && siteRoot?.path == null) linkRefStyle = 'relative'
73
+ assemblyModel.linkReferenceStyle = linkRefStyle
74
+ } else if (!(assemblyModel.filetype === 'pdf' && assemblyModel.linkReferenceStyle === 'relative')) {
75
+ assemblyModel.linkReferenceStyle = 'absolute'
76
+ }
45
77
  const rootEntry = { content: title }
46
- const { navigation, rootLevel } = assemblyModel
47
78
  let startPage =
48
79
  'startPage' in componentVersion
49
80
  ? componentVersion.startPage
@@ -67,7 +98,6 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
67
98
  })
68
99
  }
69
100
  const mutableAttributes = selectMutableAttributes(loadAsciiDoc, contentCatalog, startPage, mergedAsciiDocConfig)
70
- delete mutableAttributes.doctype // Q: should this be in selectMutableAttributes?
71
101
  prepareOutlines(navigation, rootEntry, rootLevel).reduce((any, outline) => {
72
102
  const assemblyFile = produceAssemblyFile(
73
103
  loadAsciiDoc,
@@ -80,17 +110,14 @@ function produceAssemblyFiles (loadAsciiDoc, contentCatalog, assemblerConfig, re
80
110
  assemblyModel
81
111
  )
82
112
  if (!assemblyFile) return any
83
- if (!any && auxiliaryImages.size) {
84
- const assembledAssets = assemblyFile.assembler.assembled.assets
85
- auxiliaryImages.forEach((asset) => assembledAssets.add(asset))
113
+ // NOTE restore source highlighter for conversion if defined in Assembler config
114
+ if (sourceHighlighter !== undefined) {
115
+ assemblyFile.asciidoc.attributes['source-highlighter'] = sourceHighlighter
116
+ } else if (assemblyModel.filetype !== 'html') {
117
+ delete assemblyFile.asciidoc.attributes['source-highlighter']
86
118
  }
87
- accum.push(assemblyFile)
88
- return true
119
+ return !!accum.push(assemblyFile)
89
120
  }, false)
90
- mergedAsciiDocAttributes.doctype = assemblyModel.doctype
91
- sourceHighlighter
92
- ? (mergedAsciiDocAttributes['source-highlighter'] = sourceHighlighter)
93
- : delete mergedAsciiDocAttributes['source-highlighter']
94
121
  return accum
95
122
  },
96
123
  []
@@ -116,10 +143,6 @@ function includedInNav (items, url) {
116
143
  return items.find((it) => it.url === url || includedInNav(it.items || [], url))
117
144
  }
118
145
 
119
- function isResourceRef (target) {
120
- return ~target.indexOf(':') && !(~target.indexOf('://') || (target.startsWith('data:') && ~target.indexOf(',')))
121
- }
122
-
123
146
  function prepareOutlines (navigation, rootEntry, rootLevel) {
124
147
  const singleEntry = navigation.length === 1
125
148
  const items = navigation.reduce((accum, it) => {
@@ -143,4 +166,37 @@ function prepareOutlines (navigation, rootEntry, rootLevel) {
143
166
  return items
144
167
  }
145
168
 
169
+ function collateAsciiDocAttributes (collated, additional, logger) {
170
+ Object.entries(additional).forEach(([name, val]) => {
171
+ if (val && val.constructor === String) {
172
+ let alias
173
+ val = val.replace(ATTR_REF_RX, (ref, refname) => {
174
+ if (ref.charAt() === '\\') return ref.substr(1)
175
+ const refval = collated[refname]
176
+ if (refval == null || refval === false) {
177
+ if (refname in collated && ref === val) {
178
+ alias = refval
179
+ } else if (collated['attribute-missing'] === 'warn') {
180
+ logger?.warn("Skipping reference to missing attribute '%s' in value of '%s' attribute", refname, name)
181
+ }
182
+ return ref
183
+ }
184
+ if (refval.constructor === String) return refval
185
+ if (ref !== val) return refval.toString()
186
+ alias = refval
187
+ return ref
188
+ })
189
+ if (alias !== undefined) val = alias
190
+ }
191
+ collated[name] = val
192
+ })
193
+ return collated
194
+ }
195
+
196
+ function extractUrlPath (url) {
197
+ if (!url) return ''
198
+ const urlPath = new URL(url).pathname
199
+ return urlPath === '/' ? '' : urlPath
200
+ }
201
+
146
202
  module.exports = produceAssemblyFiles
@@ -17,6 +17,7 @@ function selectMutableAttributes (loadAsciiDoc, contentCatalog, referencePage, a
17
17
  ]
18
18
  // we could consider using an Asciidoctor extension here to grab attributes passed via the API instead
19
19
  const immutableAttributeNames = doc.attribute_overrides.$keys()['$-'](additionalMutableNames)
20
+ immutableAttributeNames.push('doctype')
20
21
  return Object.entries(doc.getAttributes()).reduce((accum, [name, val]) => {
21
22
  if (!immutableAttributeNames.includes(name)) accum[name] = val
22
23
  return accum
@@ -3,9 +3,9 @@
3
3
  const { posix: path } = require('node:path')
4
4
 
5
5
  function computeOut (src) {
6
- const { component, version, module: module_, family, relative } = src
6
+ const { component, version, module: module_ = 'ROOT', family, relative } = src
7
7
  const outRelative = family === 'page' ? relative.replace(/\.adoc$/, '.html') : relative
8
- const { dir: dirname, base: basename, ext: extname, name: stem } = path.parse(outRelative)
8
+ const { dir: dirname, base: basename } = path.parse(outRelative)
9
9
  const componentVersion = this.getComponentVersion(component, version)
10
10
  const versionSegment =
11
11
  'activeVersionSegment' in componentVersion
@@ -48,7 +48,7 @@ function resolveActiveVersionSegment (component, version) {
48
48
  this.removeFile((startPage = this.addFile({ src: startPageSrc })))
49
49
  }
50
50
  const outPathSegments = startPage.out.path.split('/')
51
- for (const depth of startPage.out.moduleRootPath.split('/')) outPathSegments.pop()
51
+ for (const _ of startPage.out.moduleRootPath.split('/')) outPathSegments.pop()
52
52
  if (startPageSrc.module !== 'ROOT') outPathSegments.pop()
53
53
  if (startPageSrc.component !== 'ROOT') outPathSegments.shift()
54
54
  return outPathSegments.length ? outPathSegments[0] : ''
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const computeOut = require('./compute-out')
4
- const { posix: path } = require('node:path')
5
4
 
6
5
  function createAsciiDocFile (contentCatalog, file) {
7
6
  file.mediaType = 'text/asciidoc'
@@ -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 HTML_RESERVED_ID_NAMES = 'content header footnotes footer footer-text premable toc toctitle'.split(' ')
4
+
5
+ function generateScopedId (componentSrc, componentVersion, separators, filetype, asXrefTarget) {
6
+ let { component, module: mod, relative } = componentSrc
7
+ let id = relative.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
8
+ let { coordinate: coordinateSeparator, prefix: prefixSeparator } = separators
9
+ if (component !== componentVersion.name) {
10
+ id = [component, mod === 'ROOT' ? '' : mod, id].join(coordinateSeparator)
11
+ } else if (mod !== 'ROOT') {
12
+ if (asXrefTarget && coordinateSeparator === ':' && /(?:pass|stem)$/.test(mod) && /^[a-z]+(?:,[a-z-]+)*$/.test(id)) {
13
+ prefixSeparator = ''
14
+ mod = mod.replace(/(?:pass|stem)$/, '\\$&')
15
+ }
16
+ id = mod + coordinateSeparator + id
17
+ } else if (filetype === 'html' && HTML_RESERVED_ID_NAMES.includes(id)) {
18
+ id = prefixSeparator + id
19
+ prefixSeparator = ''
20
+ }
21
+ if (prefixSeparator && !/^[\p{Alpha}_:]/u.test(id)) id = prefixSeparator + id
22
+ return id
23
+ }
24
+
25
+ module.exports = generateScopedId
@@ -0,0 +1,36 @@
1
+ 'use strict'
2
+
3
+ function parseResourceRef (ref, ctx = {}, family = undefined, contentCatalog = undefined) {
4
+ const atIdx = ref.indexOf('@')
5
+ let firstColonIdx = ref.indexOf(':')
6
+ let component, version, module_
7
+ if (~atIdx && (~firstColonIdx ? atIdx < firstColonIdx : true)) {
8
+ if ((version = ref.slice(0, atIdx)) === '_') version = ''
9
+ ref = ref.slice(atIdx + 1)
10
+ if (~firstColonIdx) firstColonIdx -= atIdx + 1
11
+ }
12
+ const addColons = ~firstColonIdx ? (~ref.indexOf(':', firstColonIdx + 1) ? '' : ':') : '::'
13
+ const segments = (addColons + ref).split(':')
14
+ if ((component = segments[0])) {
15
+ module_ = segments[1] || 'ROOT'
16
+ version ??= contentCatalog?.getComponent(component)?.latest.version
17
+ } else {
18
+ component = ctx.component
19
+ version ??= ctx.version
20
+ module_ = segments[1] || ctx.module || 'ROOT'
21
+ }
22
+ let relative = segments.length > 3 ? segments.slice(2).join(':') : segments[2]
23
+ const dollarIdx = relative.indexOf('$')
24
+ if (~dollarIdx) {
25
+ family = relative.slice(0, dollarIdx) || family
26
+ relative = relative.slice(dollarIdx + 1)
27
+ }
28
+ if (relative.charAt() === '.' && relative.charAt(1) === '/') {
29
+ const ctxRelative = ctx.relative
30
+ const topic = ctxRelative ? ctxRelative.slice(0, (ctxRelative.lastIndexOf('/') + 1 || 1) - 1) : undefined
31
+ relative = (topic ? topic + '/' : '') + relative.slice(2)
32
+ }
33
+ return { component, version, module: module_, family, relative }
34
+ }
35
+
36
+ module.exports = parseResourceRef
@@ -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,200 @@
1
+ 'use strict'
2
+
3
+ const createResourceKey = require('./create-resource-key')
4
+ const generateScopedId = require('./generate-scoped-id')
5
+ const parseResourceRef = require('./parse-resource-ref')
6
+ const { resolveEmbedTarget, resolveLinkTarget } = require('./resolver')
7
+
8
+ const { NAMED_ID_ATTR_RX } = require('./rx')
9
+
10
+ function rewriteXrefs (
11
+ line,
12
+ contentCatalog,
13
+ assemblyModel,
14
+ ctx,
15
+ escapeForInline,
16
+ pagesInOutline,
17
+ idSeparators,
18
+ idLeader,
19
+ doc,
20
+ sourceLocation
21
+ ) {
22
+ return line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|[\s\S]*?[^\\])\]/gu, (_, target, text) =>
23
+ rewriteXref(
24
+ target,
25
+ text,
26
+ contentCatalog,
27
+ assemblyModel,
28
+ ctx,
29
+ escapeForInline,
30
+ pagesInOutline,
31
+ idSeparators,
32
+ idLeader,
33
+ doc,
34
+ sourceLocation
35
+ )
36
+ )
37
+ }
38
+
39
+ function rewriteXref (
40
+ target,
41
+ text,
42
+ contentCatalog,
43
+ assemblyModel,
44
+ ctx,
45
+ escapeForInline,
46
+ pagesInOutline,
47
+ idSeparators,
48
+ idLeader,
49
+ doc,
50
+ sourceLocation
51
+ ) {
52
+ const rawTarget = target
53
+ if (doc && ~target.indexOf('{')) target = doc.$sub_attributes(target, global.Opal.hash({ attribute_missing: 'skip' }))
54
+ let fragment, resourceRef
55
+ const hashIdx = target.indexOf('#')
56
+ if (~hashIdx) {
57
+ resourceRef = target.slice(0, hashIdx)
58
+ fragment = target.slice(hashIdx + 1)
59
+ } else if (target.endsWith('.adoc') || ~target.indexOf('$')) {
60
+ resourceRef = target
61
+ fragment = ''
62
+ } else {
63
+ fragment = target
64
+ }
65
+ if (!resourceRef) return `xref:${idLeader == null ? rawTarget : idLeader + fragment}[${text}]`
66
+ const resourceId = parseResourceRef(resourceRef, ctx, 'page', contentCatalog)
67
+ const family = resourceId.family
68
+ let resource
69
+ if (family !== 'page' || !(resource = pagesInOutline?.get(createResourceKey(resourceId)))) {
70
+ if ((resource = contentCatalog.getById(resourceId))?.pub) {
71
+ const { linkReferenceStyle, pubRoot, siteRoot } = assemblyModel
72
+ text ||= resource.asciidoc?.xreftext || rawTarget
73
+ if (siteRoot || linkReferenceStyle === 'relative') {
74
+ const hash = fragment && !(family === 'page' && fragment === resource.asciidoc.id) ? '#' + fragment : ''
75
+ return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, escapeForInline)}${hash}[${text}]`
76
+ }
77
+ if (doc) {
78
+ const msg = `Cannot create external ${family} reference in assembly because site URL is unknown: ${rawTarget}`
79
+ doc.logger.warn(doc.createLogMessage(msg, { source_location: sourceLocation }))
80
+ }
81
+ }
82
+ const linkAttrlist = text
83
+ ? (~text.indexOf(',') ? `"${text}"` : text) + ',role=unresolved'
84
+ : (~target.indexOf(':') ? `${rawTarget},` : '') + 'role=unresolved'
85
+ return `link:${rawTarget}[${linkAttrlist}]`
86
+ }
87
+ if (fragment === resource.asciidoc.id) fragment = ''
88
+ if (
89
+ text &&
90
+ (assemblyModel.dropExplicitXrefText === 'always' ||
91
+ (assemblyModel.dropExplicitXrefText === 'if-redundant' && text === resource.title))
92
+ ) {
93
+ text = ''
94
+ }
95
+ const componentVersionCtx = { name: ctx.component, version: ctx.version }
96
+ const refid = generateScopedId(
97
+ resource.src,
98
+ componentVersionCtx,
99
+ idSeparators,
100
+ assemblyModel.filetype,
101
+ text.length > 0
102
+ )
103
+ return `xref:${fragment ? refid + idSeparators.scope + fragment : refid}[${text}]`
104
+ }
105
+
106
+ function rewriteImageAttr (val, contentCatalog, assemblyModel, ctx, assets) {
107
+ const match = val.startsWith('image:') && /^image::?(.+?)\[(.*?)\](@|)$/.exec(val)
108
+ if (!match) return
109
+ const newTarget = rewriteImageRef(match[1], contentCatalog, assemblyModel, ctx, assets)
110
+ return newTarget ? `image:${newTarget}[${match[2]}]${match[3]}` : val
111
+ }
112
+
113
+ function rewriteInlineImages (line, contentCatalog, assemblyModel, ctx, assets, escapeForInline, doc) {
114
+ return line.replace(/(?<![\\+])image:([^:\s[](?:[^[]*[^\s[])?)\[(|[\s\S]*?[^\\])\]/g, (m, target, attrlist) => {
115
+ const newTarget = rewriteImageRef(target, contentCatalog, assemblyModel, ctx, assets, escapeForInline, doc)
116
+ return newTarget ? `image:${newTarget}[${attrlist}]` : m
117
+ })
118
+ }
119
+
120
+ function rewriteImageRef (target, contentCatalog, assemblyModel, ctx, assets, escapeForInline, doc) {
121
+ if (doc && ~target.indexOf('{')) target = doc.$sub_attributes(target, global.Opal.hash({ attribute_missing: 'skip' }))
122
+ const image = isResourceRef(target) && contentCatalog.resolveResource(target, ctx, 'image', ['image'])
123
+ if (!image?.out) return
124
+ let newTarget
125
+ const { filetype, embedReferenceStyle, linkReferenceStyle, outDirname, pubRoot, siteRoot } = assemblyModel
126
+ if (filetype !== 'html') {
127
+ newTarget = resolveEmbedTarget(image, outDirname, embedReferenceStyle, escapeForInline ?? false)
128
+ assets.add(image)
129
+ } else if (siteRoot || linkReferenceStyle === 'relative') {
130
+ newTarget = resolveLinkTarget(image, siteRoot, pubRoot, linkReferenceStyle, escapeForInline ?? false, false)
131
+ if (linkReferenceStyle === 'relative') assets.add(image)
132
+ }
133
+ if (!newTarget) return
134
+ return newTarget
135
+ }
136
+
137
+ function rewriteStyleAttribute (block, lines, idx, idLeader, replacementStyle = '') {
138
+ let prevLine = lines[idx - 1]
139
+ const char0 = prevLine?.charAt()
140
+ if (char0) {
141
+ if (
142
+ (char0 === '.' && /^\.\.?[^ \t.]/.test(prevLine)) ||
143
+ (char0 === '[' &&
144
+ prevLine.charAt(1) === '[' &&
145
+ /^\[\[(?:|[\p{Alpha}_:][\p{Alpha}0-9_\-:.]*(?:, *.+)?)\]\]$/u.test(prevLine))
146
+ ) {
147
+ return rewriteStyleAttribute(block, lines, idx - 1, idLeader, replacementStyle)
148
+ }
149
+ }
150
+ let cellSpec
151
+ if (
152
+ char0 &&
153
+ (char0 === '[' || (block.getDocument().isNested() && (cellSpec = prevLine.match(/^([^[|]*)\| *(\[.+)/)))) &&
154
+ prevLine.charAt(prevLine.length - 1) === ']'
155
+ ) {
156
+ if (cellSpec) {
157
+ prevLine = cellSpec[2]
158
+ cellSpec = cellSpec[1]
159
+ } else if (~prevLine.indexOf('id=')) {
160
+ prevLine = `[${prevLine.slice(1, -1).replace(NAMED_ID_ATTR_RX, '')}]`
161
+ }
162
+ let rawStyle
163
+ const commaIdx = prevLine.indexOf(',')
164
+ if (~commaIdx) {
165
+ rawStyle = prevLine.slice(1, commaIdx)
166
+ if (~rawStyle.indexOf('=')) rawStyle = undefined
167
+ } else if (!~prevLine.indexOf('=')) {
168
+ rawStyle = prevLine.slice(1, prevLine.length - 1)
169
+ }
170
+ if (rawStyle) {
171
+ if (~rawStyle.indexOf('#')) {
172
+ prevLine = prevLine.replace(/#[^.%,\]]+/, `#${idLeader}${block.getId()}`)
173
+ if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
174
+ } else {
175
+ prevLine = `[${
176
+ replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
177
+ }#${idLeader}${block.getId()}${prevLine.slice(rawStyle.length + 1)}`
178
+ }
179
+ } else {
180
+ prevLine = `[${replacementStyle}#${idLeader}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.slice(1)}`
181
+ }
182
+ if (cellSpec) prevLine = `${cellSpec}|${prevLine}`
183
+ lines[idx - 1] = prevLine
184
+ } else {
185
+ lines.splice(idx, 0, `[${replacementStyle}#${idLeader}${block.getId()}]`)
186
+ }
187
+ }
188
+
189
+ function isResourceRef (str) {
190
+ return !(~str.indexOf(':') && (~str.indexOf('://') || (str.startsWith('data:') && ~str.indexOf(','))))
191
+ }
192
+
193
+ module.exports = {
194
+ rewriteXref,
195
+ rewriteXrefs,
196
+ rewriteImageAttr,
197
+ rewriteImageRef,
198
+ rewriteInlineImages,
199
+ rewriteStyleAttribute,
200
+ }
package/lib/util/rx.js ADDED
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ const NAMED_ID_ATTR_RX = /(?:^|, *)id=(?:("[^"]*"|'[^']*'|[^,]*))/
4
+
5
+ module.exports = { NAMED_ID_ATTR_RX }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antora/assembler",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.20",
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)",
@@ -27,17 +27,21 @@
27
27
  ".": "./lib/index.js",
28
28
  "./filter-component-versions": "./lib/filter-component-versions.js",
29
29
  "./load-config": "./lib/load-config.js",
30
+ "./log-command": "./lib/log-command.js",
31
+ "./parse-resource-ref": "./lib/util/parse-resource-ref.js",
30
32
  "./produce-assembly-file": "./lib/produce-assembly-file.js",
31
33
  "./produce-assembly-files": "./lib/produce-assembly-files.js",
32
- "./select-mutable-attributes": "./lib/select-mutable-attributes.js"
34
+ "./select-mutable-attributes": "./lib/select-mutable-attributes.js",
35
+ "./package.json": "./package.json"
33
36
  },
34
37
  "imports": {
35
- "#run-command": "@antora/run-command-helper",
38
+ "#asciidoctor-log-adapter": "./adapters/asciidoctor/jsonl-logger.rb",
36
39
  "#unconvert-inline-asciidoc": "./lib/util/unconvert-inline-asciidoc.js"
37
40
  },
38
41
  "dependencies": {
39
42
  "@asciidoctor/reducer": "~1.1",
40
43
  "@antora/expand-path-helper": "~3.0",
44
+ "@antora/run-command-helper": "~1.0",
41
45
  "braces": "~3.0",
42
46
  "picomatch": "~3.0",
43
47
  "js-yaml": "~4.1"
@@ -51,6 +55,7 @@
51
55
  "node": ">=16.0.0"
52
56
  },
53
57
  "files": [
58
+ "adapters/",
54
59
  "lib/"
55
60
  ],
56
61
  "keywords": [