@antora/assembler 1.0.0-beta.15 → 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.
- package/lib/assemble-content.js +14 -5
- package/lib/load-config.js +21 -11
- package/lib/produce-assembly-file.js +59 -244
- package/lib/produce-assembly-files.js +44 -35
- package/lib/util/create-resource-key.js +7 -0
- package/lib/util/generate-id.js +25 -0
- package/lib/util/resolver.js +36 -0
- package/lib/util/rewriter.js +202 -0
- package/package.json +3 -2
package/lib/assemble-content.js
CHANGED
|
@@ -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.
|
|
60
|
+
Object.assign(assemblerConfig.assembly.attributes, intrinsicAttributes)
|
|
61
61
|
const assemblyFiles = produceAssemblyFiles(
|
|
62
62
|
loadAsciiDoc,
|
|
63
63
|
contentCatalog,
|
|
64
64
|
assemblerConfig,
|
|
65
|
-
|
|
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
|
-
|
|
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({
|
|
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
|
-
|
|
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)
|
package/lib/load-config.js
CHANGED
|
@@ -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),
|
|
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
|
-
|
|
75
|
-
|
|
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
|
|
5
|
-
const
|
|
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
|
-
|
|
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
|
|
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 && !
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 {
|
|
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 = `#${
|
|
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
|
-
|
|
292
|
+
assembled.pages.set(page, pageFragment)
|
|
294
293
|
if (doc.hasSections()) {
|
|
295
294
|
fixSectionLevels(doc.getSections(), atBookRoot && !pageStyle ? undefined : nextSectionLevel)
|
|
296
295
|
}
|
|
@@ -376,106 +375,33 @@ function mergeAsciiDoc (
|
|
|
376
375
|
if (!refs['$key?'](refid) && (~refid.indexOf(' ') || refid.toLowerCase() !== refid)) {
|
|
377
376
|
if ((refid = doc.$resolve_id(refid))['$nil?']()) return m
|
|
378
377
|
}
|
|
379
|
-
return `<<${
|
|
378
|
+
return `<<${idLeader}${refid}${text ? ',' + text : ''}>>`
|
|
380
379
|
})
|
|
381
380
|
}
|
|
382
381
|
// NOTE: the next check takes care of inline and block anchors
|
|
383
382
|
if (~line.indexOf('[[')) {
|
|
384
|
-
line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu,
|
|
385
|
-
|
|
386
|
-
if (~line.indexOf('xref:')) {
|
|
387
|
-
// Q: should we allow : as first character of target?
|
|
388
|
-
line = line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (_, target, text) => {
|
|
389
|
-
let fragment, resource, resourceRef
|
|
390
|
-
const hashIdx = target.indexOf('#')
|
|
391
|
-
if (~hashIdx) {
|
|
392
|
-
resourceRef = target.slice(0, hashIdx)
|
|
393
|
-
fragment = target.slice(hashIdx + 1)
|
|
394
|
-
} else if (target.endsWith('.adoc') || ~target.indexOf('$')) {
|
|
395
|
-
resourceRef = target
|
|
396
|
-
fragment = ''
|
|
397
|
-
} else {
|
|
398
|
-
fragment = target
|
|
399
|
-
}
|
|
400
|
-
// Q: should we validate the internal ID here?
|
|
401
|
-
if (!resourceRef) return `xref:${idPrefix}${fragment}[${text}]`
|
|
402
|
-
const resourceId = parseResourceRef(resourceRef, page.src, 'page', contentCatalog)
|
|
403
|
-
if (resourceId.family !== 'page' || !(resource = pagesInOutline.get(createResourceKey(resourceId)))) {
|
|
404
|
-
if ((resource = contentCatalog.getById(resourceId))?.pub) {
|
|
405
|
-
text ||= resource.asciidoc?.xreftext || target
|
|
406
|
-
if (siteRoot || linkRefStyle === 'relative') {
|
|
407
|
-
return `${resolveLinkTarget(resource, siteRoot, pubRoot, linkRefStyle)}${fragment && '#' + fragment}[${text}]`
|
|
408
|
-
}
|
|
409
|
-
asciidoctorLogger.warn(
|
|
410
|
-
doc.createLogMessage(
|
|
411
|
-
`Cannot create external ${resourceId.family} reference in assembly because site URL is unknown: ${target}`,
|
|
412
|
-
{ source_location: { file: relative, lineno: idx + 1 } }
|
|
413
|
-
)
|
|
414
|
-
)
|
|
415
|
-
}
|
|
416
|
-
const linkAttrlist = text
|
|
417
|
-
? (~text.indexOf(',') ? `"${text}"` : text) + ',role=unresolved'
|
|
418
|
-
: 'role=unresolved'
|
|
419
|
-
return `link:${target}[${linkAttrlist}]`
|
|
420
|
-
}
|
|
421
|
-
if (fragment === resource.asciidoc.id) fragment = ''
|
|
422
|
-
if (
|
|
423
|
-
text &&
|
|
424
|
-
(assemblyModel.dropExplicitXrefText === 'always' ||
|
|
425
|
-
(assemblyModel.dropExplicitXrefText === 'if-redundant' && text === resource.title))
|
|
426
|
-
) {
|
|
427
|
-
text = ''
|
|
428
|
-
}
|
|
429
|
-
const refid = generateId(
|
|
430
|
-
resource.src,
|
|
431
|
-
componentVersion,
|
|
432
|
-
idCoordinateSeparator,
|
|
433
|
-
idScopeSeparator,
|
|
434
|
-
text.length > 0,
|
|
435
|
-
fragment
|
|
436
|
-
).id
|
|
437
|
-
return `xref:${refid}[${text}]`
|
|
383
|
+
line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, (_, refid, text) => {
|
|
384
|
+
return `[[${idLeader}${refid}${text}]]`
|
|
438
385
|
})
|
|
439
386
|
}
|
|
440
|
-
if (~line.indexOf('
|
|
441
|
-
line =
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
387
|
+
if (~line.indexOf('xref:')) {
|
|
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
|
+
)
|
|
454
402
|
}
|
|
455
403
|
if (~line.indexOf('image:') && !line.startsWith('image::')) {
|
|
456
|
-
line = line
|
|
457
|
-
let image
|
|
458
|
-
if (
|
|
459
|
-
isResourceSpec(target) &&
|
|
460
|
-
(image = contentCatalog.resolveResource(target, page.src, 'image', ['image']))?.out
|
|
461
|
-
) {
|
|
462
|
-
if (filetype !== 'html') {
|
|
463
|
-
pagesInOutline.assembled.assets.add(image)
|
|
464
|
-
return `image:${resolveEmbedTarget(image, outDirname, embedRefStyle, true)}[${attrlist}]`
|
|
465
|
-
}
|
|
466
|
-
if (siteRoot || linkRefStyle === 'relative') {
|
|
467
|
-
pagesInOutline.assembled.assets.add(image)
|
|
468
|
-
return `image:${resolveLinkTarget(image, siteRoot, pubRoot, linkRefStyle)}[${attrlist}]`
|
|
469
|
-
}
|
|
470
|
-
asciidoctorLogger.warn(
|
|
471
|
-
doc.createLogMessage(
|
|
472
|
-
`Cannot create external image reference in assembly because site URL is unknown: ${target}`,
|
|
473
|
-
{ source_location: { file: relative, lineno: idx + 1 } }
|
|
474
|
-
)
|
|
475
|
-
)
|
|
476
|
-
}
|
|
477
|
-
return m
|
|
478
|
-
})
|
|
404
|
+
line = rewriteInlineImages(line, contentCatalog, assemblyModel, page.src, assembled.assets, true)
|
|
479
405
|
}
|
|
480
406
|
lines[idx] = line
|
|
481
407
|
}
|
|
@@ -501,7 +427,7 @@ function mergeAsciiDoc (
|
|
|
501
427
|
return '='.repeat(targetMarkerLength) + ' ' + rest
|
|
502
428
|
})
|
|
503
429
|
// NOTE: ID will be undefined if sectids are turned off
|
|
504
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
430
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, idLeader, blockStyle)
|
|
505
431
|
} else {
|
|
506
432
|
if (context === 'image') {
|
|
507
433
|
let line = lines[idx] || ''
|
|
@@ -520,27 +446,17 @@ function mergeAsciiDoc (
|
|
|
520
446
|
imageMacroOffset = -1
|
|
521
447
|
}
|
|
522
448
|
}
|
|
523
|
-
if (imageMacroOffset
|
|
449
|
+
if (~imageMacroOffset) {
|
|
524
450
|
const target = block.getAttribute('target')
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
// FIXME: handle (or report) case when image is not resolved
|
|
528
|
-
if (image?.out && (filetype !== 'html' || siteRoot)) {
|
|
529
|
-
const attrlist = line.slice(line.indexOf('[') + 1, -1)
|
|
530
|
-
pagesInOutline.assembled.assets.add(image)
|
|
531
|
-
lines[idx] =
|
|
532
|
-
filetype === 'html'
|
|
533
|
-
? `${prefix}image::${resolveLinkTarget(image, siteRoot, pubRoot, linkRefStyle, false)}[${attrlist}]`
|
|
534
|
-
: `${prefix}image::${resolveEmbedTarget(image, outDirname, embedRefStyle)}[${attrlist}]`
|
|
535
|
-
}
|
|
536
|
-
}
|
|
451
|
+
const newTarget = rewriteImageRef(target, contentCatalog, assemblyModel, page.src, assembled.assets)
|
|
452
|
+
if (newTarget) lines[idx] = `${prefix}image::${newTarget}${line.slice(line.indexOf('['))}`
|
|
537
453
|
lastImageMacroAt = [idx, imageMacroOffset]
|
|
538
454
|
}
|
|
539
455
|
} else if (context === 'document' && block.hasHeader()) {
|
|
540
456
|
// nested document
|
|
541
457
|
idx = (block.getHeader().getLineNumber() || idx + 1) - 1
|
|
542
458
|
}
|
|
543
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
459
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, idLeader)
|
|
544
460
|
}
|
|
545
461
|
})
|
|
546
462
|
safePush(
|
|
@@ -596,10 +512,17 @@ function mergeAsciiDoc (
|
|
|
596
512
|
const resource = files.find((it) => it.pub.url === url)
|
|
597
513
|
if (resource) {
|
|
598
514
|
if (resource.src.family === 'page' && pagesInOutline.has(resource.pub.url)) {
|
|
599
|
-
const refid = generateId(
|
|
515
|
+
const refid = generateId(
|
|
516
|
+
resource.src,
|
|
517
|
+
componentVersion,
|
|
518
|
+
idCoordinateSeparator,
|
|
519
|
+
idScopeSeparator,
|
|
520
|
+
idPrefix,
|
|
521
|
+
true
|
|
522
|
+
).id
|
|
600
523
|
sectionTitle = `xref:${refid}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
601
524
|
} else if (siteRoot) {
|
|
602
|
-
sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot,
|
|
525
|
+
sectionTitle = `${resolveLinkTarget(resource, siteRoot, pubRoot, linkReferenceStyle, true)}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
603
526
|
}
|
|
604
527
|
}
|
|
605
528
|
}
|
|
@@ -722,60 +645,6 @@ function fixSectionLevels (sections, expectedLevel) {
|
|
|
722
645
|
})
|
|
723
646
|
}
|
|
724
647
|
|
|
725
|
-
function rewriteStyleAttribute (block, lines, idx, idPrefix, replacementStyle = '') {
|
|
726
|
-
let prevLine = lines[idx - 1]
|
|
727
|
-
const char0 = prevLine?.charAt()
|
|
728
|
-
if (char0) {
|
|
729
|
-
if (
|
|
730
|
-
(char0 === '.' && /^\.\.?[^ \t.]/.test(prevLine)) ||
|
|
731
|
-
(char0 === '[' &&
|
|
732
|
-
prevLine.charAt(1) === '[' &&
|
|
733
|
-
/^\[\[(?:|[\p{Alpha}_:][\p{Alpha}0-9_\-:.]*(?:, *.+)?)\]\]$/u.test(prevLine))
|
|
734
|
-
) {
|
|
735
|
-
return rewriteStyleAttribute(block, lines, idx - 1, idPrefix, replacementStyle)
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
let cellSpec
|
|
739
|
-
if (
|
|
740
|
-
char0 &&
|
|
741
|
-
(char0 === '[' || (block.getDocument().isNested() && (cellSpec = prevLine.match(/^([^[|]*)\| *(\[.+)/)))) &&
|
|
742
|
-
prevLine.charAt(prevLine.length - 1) === ']'
|
|
743
|
-
) {
|
|
744
|
-
if (cellSpec) {
|
|
745
|
-
prevLine = cellSpec[2]
|
|
746
|
-
cellSpec = cellSpec[1]
|
|
747
|
-
}
|
|
748
|
-
let rawStyle
|
|
749
|
-
const commaIdx = prevLine.indexOf(',')
|
|
750
|
-
if (~commaIdx) {
|
|
751
|
-
rawStyle = prevLine.slice(1, commaIdx)
|
|
752
|
-
if (~rawStyle.indexOf('=')) rawStyle = undefined
|
|
753
|
-
} else if (!~prevLine.indexOf('=')) {
|
|
754
|
-
rawStyle = prevLine.slice(1, prevLine.length - 1)
|
|
755
|
-
}
|
|
756
|
-
if (rawStyle) {
|
|
757
|
-
if (~rawStyle.indexOf('#')) {
|
|
758
|
-
prevLine = prevLine.replace(/#[^.%,\]]+/, `#${idPrefix}${block.getId()}`)
|
|
759
|
-
if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
|
|
760
|
-
} else {
|
|
761
|
-
prevLine = `[${
|
|
762
|
-
replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
|
|
763
|
-
}#${idPrefix}${block.getId()}${prevLine.slice(rawStyle.length + 1)}`
|
|
764
|
-
}
|
|
765
|
-
} else {
|
|
766
|
-
prevLine = `[${replacementStyle}#${idPrefix}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.slice(1)}`
|
|
767
|
-
}
|
|
768
|
-
if (cellSpec) prevLine = `${cellSpec}|${prevLine}`
|
|
769
|
-
lines[idx - 1] = prevLine
|
|
770
|
-
} else {
|
|
771
|
-
lines.splice(idx, 0, `[${replacementStyle}#${idPrefix}${block.getId()}]`)
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
function isResourceSpec (str) {
|
|
776
|
-
return !(~str.indexOf(':') && (~str.indexOf('://') || (str.startsWith('data:') && ~str.indexOf(','))))
|
|
777
|
-
}
|
|
778
|
-
|
|
779
648
|
function getObjectId (obj) {
|
|
780
649
|
return global.Opal.id(obj)
|
|
781
650
|
}
|
|
@@ -802,58 +671,4 @@ function safePush (onto, entries) {
|
|
|
802
671
|
}
|
|
803
672
|
}
|
|
804
673
|
|
|
805
|
-
function resolveEmbedTarget (resource, outDirname, referenceStyle, escapeForInline) {
|
|
806
|
-
const target =
|
|
807
|
-
referenceStyle === 'output-relative' ? resource.out.path : path.relative(outDirname + '/', resource.out.path)
|
|
808
|
-
return escapeForInline ? target.replace(/_/g, '{underscore}') : target
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
function resolveLinkTarget (resource, siteRoot, pubRoot, referenceStyle, escapeForInline = true) {
|
|
812
|
-
let target
|
|
813
|
-
if (resource.site?.url) {
|
|
814
|
-
target = ['', resource.pub.url]
|
|
815
|
-
} else {
|
|
816
|
-
switch (referenceStyle) {
|
|
817
|
-
case 'absolute':
|
|
818
|
-
target = ['', siteRoot.url + resource.pub.url]
|
|
819
|
-
break
|
|
820
|
-
case 'root-relative':
|
|
821
|
-
target = ['link:', siteRoot.path + resource.pub.url]
|
|
822
|
-
break
|
|
823
|
-
default:
|
|
824
|
-
target = ['link:', computeRelativeUrl(pubRoot + '/', resource.pub.url)]
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
if (escapeForInline) target[1] = target[1].replace(/_/g, '{underscore}')
|
|
828
|
-
return target.join('')
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
function computeRelativeUrl (from, to) {
|
|
832
|
-
const rel = path.relative(from, to)
|
|
833
|
-
return to.charAt(to.length - 1) === '/' ? rel + '/' : rel
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
function createResourceKey ({ component, version, module: mod, family, relative }) {
|
|
837
|
-
return `${version}@${component}:${mod === 'ROOT' ? '' : mod}:${family === 'page' ? '' : family + '$'}${relative}`
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
function generateId (componentSrc, componentVersion, coordinateSep, scopeSep, asXrefTarget, fragment) {
|
|
841
|
-
let { component, module: mod, relative } = componentSrc
|
|
842
|
-
let id = relative.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
|
|
843
|
-
if (component !== componentVersion.name) {
|
|
844
|
-
id = [component, mod === 'ROOT' ? '' : mod, id].join(coordinateSep)
|
|
845
|
-
} else if (mod !== 'ROOT') {
|
|
846
|
-
if (asXrefTarget && coordinateSep === ':' && /(?:pass|stem)$/.test(mod) && /^[a-z]+(?:,[a-z-]+)*$/.test(id)) {
|
|
847
|
-
mod = mod.replace(/(?:pass|stem)$/, '\\$&')
|
|
848
|
-
}
|
|
849
|
-
id = mod + coordinateSep + id
|
|
850
|
-
} else if (ReservedIdNames.includes(id)) {
|
|
851
|
-
id += scopeSep
|
|
852
|
-
scopeSep = ''
|
|
853
|
-
}
|
|
854
|
-
const idPrefix = id + scopeSep
|
|
855
|
-
if (fragment) id = idPrefix + fragment
|
|
856
|
-
return { idPrefix, id }
|
|
857
|
-
}
|
|
858
|
-
|
|
859
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,
|
|
13
|
-
const {
|
|
14
|
-
|
|
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
|
|
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 =
|
|
36
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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,
|
|
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
|
-
|
|
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,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.
|
|
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",
|