@antora/assembler 1.0.0-alpha.1 → 1.0.0-alpha.10
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 +6 -5
- package/lib/filter-component-versions.js +2 -2
- package/lib/index.js +1 -2
- package/lib/load-config.js +35 -23
- package/lib/produce-aggregate-document.js +233 -148
- package/lib/produce-aggregate-documents.js +92 -32
- package/lib/util/sanitize.js +13 -0
- package/lib/util/unconvert-inline-asciidoc.js +95 -0
- package/package.json +13 -9
- package/lib/asciidoctor/reducer-extension.js +0 -230
- package/lib/util/lazy-readable.js +0 -19
- package/lib/util/run-command.js +0 -56
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const path = require('node:path/posix')
|
|
4
|
+
const sanitize = require('./util/sanitize')
|
|
5
|
+
const unconvertInlineAsciiDoc = require('./util/unconvert-inline-asciidoc')
|
|
5
6
|
|
|
6
7
|
function produceAggregateDocument (
|
|
7
8
|
loadAsciiDoc,
|
|
8
9
|
contentCatalog,
|
|
9
10
|
componentVersion,
|
|
10
11
|
outline,
|
|
12
|
+
doctype,
|
|
11
13
|
pages,
|
|
12
14
|
asciidocConfig,
|
|
13
15
|
mutableAttributes,
|
|
@@ -15,8 +17,19 @@ function produceAggregateDocument (
|
|
|
15
17
|
) {
|
|
16
18
|
const pagesInOutline = selectPagesInOutline(outline, pages)
|
|
17
19
|
const navtitle = outline.content
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
+
const templateFile = contentCatalog.addFile({
|
|
21
|
+
src: {
|
|
22
|
+
component: componentVersion.name,
|
|
23
|
+
version: componentVersion.version,
|
|
24
|
+
module: 'ROOT',
|
|
25
|
+
family: 'page',
|
|
26
|
+
relative: generateSlug(navtitle),
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
const { dir: outDir, name: outName } = path.parse(templateFile.out.path)
|
|
30
|
+
const path_ = outName === templateFile.src.relative ? path.join(outDir, outName + '.adoc') : outDir + '.adoc'
|
|
31
|
+
contentCatalog.removeFile(templateFile)
|
|
32
|
+
const header = buildAsciiDocHeader(componentVersion, navtitle, doctype)
|
|
20
33
|
const body = aggregateAsciiDoc(
|
|
21
34
|
loadAsciiDoc,
|
|
22
35
|
contentCatalog,
|
|
@@ -28,29 +41,33 @@ function produceAggregateDocument (
|
|
|
28
41
|
mutableAttributes,
|
|
29
42
|
sectionMergeStrategy
|
|
30
43
|
)
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
return new templateFile.constructor({
|
|
45
|
+
aggregate: true,
|
|
33
46
|
asciidoc: asciidocConfig,
|
|
34
47
|
contents: Buffer.from([...header, ...body].join('\n') + '\n'),
|
|
35
48
|
mediaType: 'text/asciidoc',
|
|
36
|
-
path:
|
|
49
|
+
path: path_,
|
|
37
50
|
src: {
|
|
38
51
|
component: componentVersion.name,
|
|
39
52
|
version: componentVersion.version,
|
|
40
|
-
basename: path.basename(
|
|
41
|
-
stem,
|
|
53
|
+
basename: path.basename(path_),
|
|
54
|
+
stem: path.basename(path_, '.adoc'),
|
|
42
55
|
extname: '.adoc',
|
|
43
56
|
},
|
|
44
57
|
})
|
|
45
58
|
}
|
|
46
59
|
|
|
47
|
-
function buildAsciiDocHeader (componentVersion, navtitle) {
|
|
48
|
-
const
|
|
49
|
-
const
|
|
60
|
+
function buildAsciiDocHeader (componentVersion, navtitle, doctype = 'book') {
|
|
61
|
+
const navtitlePlain = sanitize(navtitle)
|
|
62
|
+
const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
63
|
+
let doctitle = navtitleAsciiDoc
|
|
64
|
+
if (navtitlePlain !== componentVersion.title) doctitle = `${componentVersion.title}: ${doctitle}`
|
|
65
|
+
const version = componentVersion.version === 'master' ? '' : componentVersion.version
|
|
50
66
|
return [
|
|
51
67
|
`= ${doctitle}`,
|
|
52
|
-
...(version ? [
|
|
53
|
-
|
|
68
|
+
...(version ? [`:revnumber: ${version}`] : []),
|
|
69
|
+
...(doctype === 'article' ? [] : [`:doctype: ${doctype}`]),
|
|
70
|
+
':underscore: _',
|
|
54
71
|
// Q: should we pass these via the CLI so they cannot be modified?
|
|
55
72
|
`:page-component-name: ${componentVersion.name}`,
|
|
56
73
|
`:page-component-version:${version ? ' ' + version : ''}`,
|
|
@@ -63,7 +80,7 @@ function buildAsciiDocHeader (componentVersion, navtitle) {
|
|
|
63
80
|
function selectPagesInOutline (outlineEntry, pages) {
|
|
64
81
|
const page = outlineEntry.urlType === 'internal' ? pages.find((it) => it.pub.url === outlineEntry.url) : undefined
|
|
65
82
|
return (outlineEntry.items || []).reduce(
|
|
66
|
-
(accum, item) =>
|
|
83
|
+
(accum, item) => selectPagesInOutline(item, pages).forEach((v, k) => accum.set(k, v)) ?? accum,
|
|
67
84
|
new Map(
|
|
68
85
|
page && [
|
|
69
86
|
[`${page.src.module === 'ROOT' ? '' : page.src.module + ':'}${page.src.relative}`, page],
|
|
@@ -83,16 +100,26 @@ function aggregateAsciiDoc (
|
|
|
83
100
|
asciidocConfig,
|
|
84
101
|
mutableAttributes,
|
|
85
102
|
sectionMergeStrategy,
|
|
103
|
+
lastComponentVersion = componentVersion,
|
|
86
104
|
level = 0
|
|
87
105
|
) {
|
|
88
106
|
const buffer = []
|
|
89
107
|
// TODO: we could try to be smart about it and make sure the page with fragment is included at least once
|
|
90
108
|
if (outlineEntry.hash) return buffer
|
|
91
|
-
const { content: navtitle, items, unresolved, urlType, url } = outlineEntry
|
|
109
|
+
const { content: navtitle, items = [], unresolved, urlType, url } = outlineEntry
|
|
110
|
+
const hasItems = items.length > 0
|
|
111
|
+
const navtitlePlain = sanitize(navtitle)
|
|
112
|
+
const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
113
|
+
const siteUrl = ((val) => {
|
|
114
|
+
if (!val || val === '/') return ''
|
|
115
|
+
return val.charAt(val.length - 1) === '/' ? val.slice(0, val.length - 1) : val
|
|
116
|
+
})(asciidocConfig.attributes['site-url'])
|
|
92
117
|
// FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
|
|
93
118
|
let page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
|
|
119
|
+
if (page && pagesInOutline.aggregated?.includes(page)) page = undefined
|
|
94
120
|
if (page) {
|
|
95
121
|
let contents = page.src.contents
|
|
122
|
+
if (contents == null) return buffer
|
|
96
123
|
// NOTE: blank lines at top and bottom of document create mismatch when using line numbers to navigate source lines
|
|
97
124
|
// IMPORTANT: this must not leave behind lines the parser will drop!
|
|
98
125
|
// IDEA: another option is to capture initial lineno of reader and use as offset (but preseves those blank lines)
|
|
@@ -102,17 +129,37 @@ function aggregateAsciiDoc (
|
|
|
102
129
|
.replace(/^(?:[ \t]*\r\n?|[ \t]*\n)+/, '')
|
|
103
130
|
.trimRight()
|
|
104
131
|
)
|
|
132
|
+
;(pagesInOutline.aggregated ??= []).push(page)
|
|
105
133
|
page = new page.constructor(Object.assign({}, page, { contents, mediaType: 'text/asciidoc' }))
|
|
106
|
-
const { module: module_, relative, origin } = page.src
|
|
134
|
+
const { component, version, module: module_, relative, origin } = page.src
|
|
135
|
+
const topicPrefix = ~relative.indexOf('/') ? path.dirname(relative) + '/' : ''
|
|
107
136
|
const doc = loadAsciiDoc(page, contentCatalog, asciidocConfig)
|
|
108
|
-
const
|
|
137
|
+
const refs = doc.getCatalog().refs
|
|
109
138
|
// NOTE: in Antora, docname is relative src path from module without file extension
|
|
110
139
|
const docname = doc.getAttribute('docname')
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
140
|
+
const docnameForId = docname.replace(/[/]/g, '::').replace(/[.]/g, '-')
|
|
141
|
+
const scopeId = component !== componentVersion.name
|
|
142
|
+
const idprefix =
|
|
143
|
+
(scopeId ? component + ':' : '') +
|
|
144
|
+
(module_ === 'ROOT' ? (scopeId ? ':' : '') : module_ + ':') +
|
|
145
|
+
docnameForId +
|
|
146
|
+
':::'
|
|
114
147
|
buffer.push('')
|
|
115
148
|
buffer.push(`:docname: ${docname}`)
|
|
149
|
+
if (component !== lastComponentVersion.name) {
|
|
150
|
+
const thisComponentVersion =
|
|
151
|
+
component === componentVersion.name && version === componentVersion.version
|
|
152
|
+
? componentVersion
|
|
153
|
+
: contentCatalog.getComponentVersion(component, version)
|
|
154
|
+
if (thisComponentVersion) {
|
|
155
|
+
buffer.push(`:page-component-name: ${thisComponentVersion.name}`)
|
|
156
|
+
buffer.push(`:page-component-version:${thisComponentVersion.version ? ' ' + thisComponentVersion.version : ''}`)
|
|
157
|
+
buffer.push(':page-version: {page-component-version}')
|
|
158
|
+
buffer.push(`:page-component-display-version: ${thisComponentVersion.displayVersion}`)
|
|
159
|
+
buffer.push(`:page-component-title: ${thisComponentVersion.title}`)
|
|
160
|
+
lastComponentVersion = thisComponentVersion
|
|
161
|
+
}
|
|
162
|
+
}
|
|
116
163
|
buffer.push(`:page-module: ${module_}`)
|
|
117
164
|
buffer.push(`:page-relative-src-path: ${relative}`)
|
|
118
165
|
//buffer.push(`:page-origin-type: ${origin.type}`)
|
|
@@ -124,7 +171,7 @@ function aggregateAsciiDoc (
|
|
|
124
171
|
let enclosed
|
|
125
172
|
// NOTE: if level is 0, doctitle has already been added and we're in the document header
|
|
126
173
|
if (level) {
|
|
127
|
-
if (level === 1 &&
|
|
174
|
+
if (level === 1 && navtitlePlain === componentVersion.title) {
|
|
128
175
|
level--
|
|
129
176
|
} else {
|
|
130
177
|
let hlevel = level + 1
|
|
@@ -134,12 +181,12 @@ function aggregateAsciiDoc (
|
|
|
134
181
|
} else {
|
|
135
182
|
buffer.push(`[#${idprefix}]`)
|
|
136
183
|
}
|
|
137
|
-
buffer.push(`${'='.repeat(hlevel)} ${
|
|
184
|
+
buffer.push(`${'='.repeat(hlevel)} ${navtitleAsciiDoc}`)
|
|
138
185
|
}
|
|
139
186
|
} else {
|
|
140
187
|
header.unshift(`[#${idprefix}]`)
|
|
141
188
|
}
|
|
142
|
-
if (sectionMergeStrategy === 'enclose' &&
|
|
189
|
+
if (sectionMergeStrategy === 'enclose' && hasItems && doc.hasSections()) {
|
|
143
190
|
enclosed = true
|
|
144
191
|
// TODO: make overview section title configurable
|
|
145
192
|
//let overviewTitle = doc.getDocumentTitle()
|
|
@@ -166,27 +213,15 @@ function aggregateAsciiDoc (
|
|
|
166
213
|
buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
|
|
167
214
|
if (toggleSectids) buffer.push(':sectids:')
|
|
168
215
|
}
|
|
169
|
-
const siteUrl = doc.getAttribute('site-url')
|
|
170
216
|
const lines = doc.getSourceLines()
|
|
171
217
|
const ignoreLines = []
|
|
172
218
|
// TODO: think more about when multipart is allowed; perhaps configurable
|
|
173
219
|
if (doc.hasSections()) fixSectionLevels(doc.getSections(), level === 0)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (block.getContext() === 'table') {
|
|
180
|
-
const rows = block.rows
|
|
181
|
-
;[...rows.body, ...rows.foot].forEach((row) => {
|
|
182
|
-
row.forEach((cell) => {
|
|
183
|
-
if (cell.style !== 'asciidoc') return
|
|
184
|
-
accum.push(...cell.inner_document.findBy((it) => it.getContext() !== 'document'))
|
|
185
|
-
})
|
|
186
|
-
})
|
|
187
|
-
}
|
|
188
|
-
return accum
|
|
189
|
-
}, [])
|
|
220
|
+
const allBlocks = doc.findBy({ traverse_documents: true }, (it) =>
|
|
221
|
+
it.getContext() === 'document'
|
|
222
|
+
? it.getDocument().isNested()
|
|
223
|
+
: !(it.getContext() === 'table_cell' && it.getStyle() === 'asciidoc')
|
|
224
|
+
)
|
|
190
225
|
allBlocks.forEach((block) => {
|
|
191
226
|
const contentModel = block.content_model
|
|
192
227
|
if (
|
|
@@ -213,16 +248,33 @@ function aggregateAsciiDoc (
|
|
|
213
248
|
for (let i = idx; i < block.lines.length + (delimited ? idx + 2 : idx); i++) ignoreLines.push(i)
|
|
214
249
|
}
|
|
215
250
|
})
|
|
251
|
+
let skipping
|
|
216
252
|
for (let idx = 0, len = lines.length; idx < len; idx++) {
|
|
217
253
|
if (~ignoreLines.indexOf(idx)) continue
|
|
218
254
|
let line = lines[idx]
|
|
255
|
+
if (line.startsWith('//')) {
|
|
256
|
+
if (line[2] !== '/') continue
|
|
257
|
+
if (line.length > 3 && line === '/'.repeat(line.length)) {
|
|
258
|
+
if (skipping) {
|
|
259
|
+
if (line === skipping) skipping = undefined
|
|
260
|
+
} else {
|
|
261
|
+
skipping = line
|
|
262
|
+
}
|
|
263
|
+
continue
|
|
264
|
+
}
|
|
265
|
+
} else if (skipping) {
|
|
266
|
+
continue
|
|
267
|
+
}
|
|
268
|
+
if (line.charAt() === ':' && /^:(?:leveloffset: .*|!leveloffset:|leveloffset!:)$/.test(line)) {
|
|
269
|
+
if (lines[idx - 1] === '') lines[idx - 1] = undefined
|
|
270
|
+
lines[idx] = undefined
|
|
271
|
+
continue
|
|
272
|
+
}
|
|
219
273
|
if (~line.indexOf('<<')) {
|
|
220
274
|
line = line.replace(/(?<![\\+])<<#?([\p{Alpha}0-9_/.:{][^>,]*?)(?:|, *([^>]+?))?>>/gu, (m, refid, text) => {
|
|
221
|
-
// support natural xref
|
|
222
|
-
if (!
|
|
223
|
-
if (
|
|
224
|
-
return m
|
|
225
|
-
}
|
|
275
|
+
// support natural xref
|
|
276
|
+
if (!refs['$key?'](refid) && (~refid.indexOf(' ') || refid.toLowerCase() !== refid)) {
|
|
277
|
+
if ((refid = doc.$resolve_id(refid))['$nil?']()) return m
|
|
226
278
|
}
|
|
227
279
|
return `<<${idprefix}${refid}${text ? ',' + text : ''}>>`
|
|
228
280
|
})
|
|
@@ -233,38 +285,56 @@ function aggregateAsciiDoc (
|
|
|
233
285
|
}
|
|
234
286
|
if (~line.indexOf('xref:')) {
|
|
235
287
|
// Q: should we allow : as first character of target?
|
|
236
|
-
line = line.replace(/xref:([\p{Alpha}
|
|
288
|
+
line = line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (m, target, text) => {
|
|
237
289
|
let pagePart, fragment, targetPage
|
|
238
290
|
const hashIdx = target.indexOf('#')
|
|
239
291
|
if (~hashIdx) {
|
|
240
|
-
pagePart = target.
|
|
241
|
-
fragment = target.
|
|
292
|
+
pagePart = target.slice(0, hashIdx)
|
|
293
|
+
fragment = target.slice(hashIdx + 1)
|
|
242
294
|
// TODO: for now, assume .adoc; in the future, consider other file extensions
|
|
243
|
-
if (
|
|
295
|
+
if (pagePart && !pagePart.endsWith('.adoc')) pagePart += '.adoc'
|
|
244
296
|
} else if (target.endsWith('.adoc')) {
|
|
245
297
|
pagePart = target
|
|
246
298
|
fragment = ''
|
|
247
299
|
} else {
|
|
248
300
|
fragment = target
|
|
249
301
|
}
|
|
250
|
-
if (!pagePart)
|
|
302
|
+
if (!pagePart) {
|
|
303
|
+
// Q: should we validate the internal ID here?
|
|
304
|
+
return text && ~text.indexOf('=')
|
|
305
|
+
? `xref:${idprefix}${fragment}[${text}]`
|
|
306
|
+
: `<<${idprefix}${fragment}${text ? ',' + text.replace(/\\]/g, ']') : ''}>>`
|
|
307
|
+
}
|
|
251
308
|
if (~pagePart.indexOf('@') || /:.*:/.test(pagePart)) {
|
|
309
|
+
if (siteUrl && (targetPage = contentCatalog.resolvePage(pagePart, page.src)) && targetPage.out) {
|
|
310
|
+
text ||= targetPage.asciidoc?.xreftext || target
|
|
311
|
+
return `${siteUrl}${targetPage.pub.url}${fragment && '#' + fragment}[${text}]`
|
|
312
|
+
}
|
|
252
313
|
// TODO: handle unresolved page better
|
|
253
|
-
return
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
pagePart = pagePart.
|
|
314
|
+
return m
|
|
315
|
+
}
|
|
316
|
+
let targetModule
|
|
317
|
+
const colonIdx = pagePart.indexOf(':')
|
|
318
|
+
if (~colonIdx) {
|
|
319
|
+
targetModule = pagePart.slice(0, colonIdx)
|
|
320
|
+
pagePart = pagePart.slice(colonIdx + 1)
|
|
321
|
+
} else {
|
|
322
|
+
targetModule = module_
|
|
260
323
|
}
|
|
324
|
+
if (pagePart.startsWith('./')) pagePart = topicPrefix + pagePart.slice(2)
|
|
325
|
+
if (targetModule !== 'ROOT') pagePart = `${targetModule}:${pagePart}`
|
|
261
326
|
if (!(targetPage = pagesInOutline.get(pagePart))) {
|
|
327
|
+
if (siteUrl && (targetPage = contentCatalog.resolvePage(pagePart, page.src)) && targetPage.out) {
|
|
328
|
+
text ||= targetPage.asciidoc?.xreftext || target
|
|
329
|
+
return `${siteUrl}${targetPage.pub.url}${fragment && '#' + fragment}[${text}]`
|
|
330
|
+
}
|
|
262
331
|
// TODO: handle unresolved page better
|
|
263
|
-
return
|
|
264
|
-
? `${siteUrl}${targetPage.pub.url}${fragment && '#' + fragment}[${text}]`
|
|
265
|
-
: m
|
|
332
|
+
return m
|
|
266
333
|
}
|
|
267
|
-
pagePart = pagePart
|
|
334
|
+
pagePart = pagePart
|
|
335
|
+
.replace(/[/]/g, '::')
|
|
336
|
+
.replace(/\.adoc$/, '')
|
|
337
|
+
.replace(/[.]/g, '-')
|
|
268
338
|
const refid = `${pagePart}:::${fragment}`
|
|
269
339
|
return `<<${refid}${text && text !== targetPage.title ? ',' + text.replace(/\\]/g, ']') : ''}>>`
|
|
270
340
|
})
|
|
@@ -280,7 +350,8 @@ function aggregateAsciiDoc (
|
|
|
280
350
|
family: 'attachment',
|
|
281
351
|
relative,
|
|
282
352
|
})
|
|
283
|
-
|
|
353
|
+
// TODO: handle unresolved attachment page
|
|
354
|
+
return attachment?.out ? `${siteUrl}${attachment.pub.url.replace(/_/g, '{underscore}')}[${text}]` : m
|
|
284
355
|
})
|
|
285
356
|
}
|
|
286
357
|
if (~line.indexOf('image:') && !line.startsWith('image::')) {
|
|
@@ -288,9 +359,9 @@ function aggregateAsciiDoc (
|
|
|
288
359
|
if (isResourceSpec(target)) {
|
|
289
360
|
const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
|
|
290
361
|
// TODO: handle (or report) unresolved image better
|
|
291
|
-
if (image) {
|
|
362
|
+
if (image?.out) {
|
|
292
363
|
image.out.assembled = true
|
|
293
|
-
return `image:${image.
|
|
364
|
+
return `image:${image.out.path.replace(/_/g, '{underscore}')}[${attrlist}]`
|
|
294
365
|
}
|
|
295
366
|
}
|
|
296
367
|
return m
|
|
@@ -301,66 +372,69 @@ function aggregateAsciiDoc (
|
|
|
301
372
|
// NOTE: need to do this last since it modifies the line numbers
|
|
302
373
|
// we could remap the line numbers to make them resilient
|
|
303
374
|
// or we could mark which lines to remove and filter them after
|
|
375
|
+
let lastImageMacroAt
|
|
304
376
|
;[...allBlocks].reverse().forEach((block) => {
|
|
305
377
|
const lineno = block.getLineNumber()
|
|
306
378
|
// NOTE: lineno is not defined for preamble
|
|
307
379
|
if (typeof lineno !== 'number') return
|
|
308
380
|
const context = block.getContext()
|
|
309
|
-
|
|
310
|
-
if (context === 'section') {
|
|
381
|
+
let idx = lineno - 1
|
|
382
|
+
if (context === 'section' && !block.getDocument().isNested()) {
|
|
311
383
|
if (block.getSectionName() === 'header') {
|
|
312
|
-
lines
|
|
384
|
+
lines[idx] = undefined
|
|
313
385
|
return
|
|
314
386
|
}
|
|
315
387
|
let blockStyle = sectionMergeStrategy === 'discrete' ? 'discrete' : undefined
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
lines[idx] = lines[idx].replace(/^=+( .+)/, (_, rest) => {
|
|
319
|
-
let targetMarkerLength = block.level + (1 - leveloffset) + level + (enclosed ? 1 : 0)
|
|
388
|
+
lines[idx] = lines[idx].replace(/^=+ (.+)/, (_, rest) => {
|
|
389
|
+
let targetMarkerLength = block.level + 1 + level + (enclosed ? 1 : 0)
|
|
320
390
|
if (targetMarkerLength > 6) {
|
|
321
391
|
targetMarkerLength = 6
|
|
322
392
|
blockStyle = 'discrete'
|
|
323
393
|
}
|
|
324
|
-
return '='.repeat(targetMarkerLength) + rest
|
|
394
|
+
return '='.repeat(targetMarkerLength) + ' ' + rest
|
|
325
395
|
})
|
|
326
396
|
// NOTE: ID will be undefined if sectids are turned off
|
|
327
397
|
if (block.getId()) rewriteStyleAttribute(block, lines, idx, idprefix, blockStyle)
|
|
328
398
|
} else {
|
|
329
399
|
if (context === 'image') {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
//
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
400
|
+
let line = lines[idx] || ''
|
|
401
|
+
let prefix = ''
|
|
402
|
+
// Q: can we use startsWith('image::') in certain cases?
|
|
403
|
+
let imageMacroOffset = (
|
|
404
|
+
lastImageMacroAt?.[0] === idx ? line.slice(0, lastImageMacroAt[1]) : line
|
|
405
|
+
).lastIndexOf('image::')
|
|
406
|
+
if (imageMacroOffset > 0) {
|
|
407
|
+
if (
|
|
408
|
+
block.getDocument().isNested() &&
|
|
409
|
+
(prefix = line.slice(0, imageMacroOffset)).trimRight().endsWith('|')
|
|
410
|
+
) {
|
|
411
|
+
line = line.slice(prefix.length)
|
|
412
|
+
} else {
|
|
413
|
+
imageMacroOffset = -1
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (imageMacroOffset >= 0) {
|
|
417
|
+
const target = block.getAttribute('target')
|
|
418
|
+
if (isResourceSpec(target)) {
|
|
419
|
+
const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
|
|
420
|
+
// FIXME: handle (or report) case when image is not resolved
|
|
421
|
+
if (image?.out) {
|
|
422
|
+
const boxedAttrlist = line.slice(line.indexOf('['))
|
|
423
|
+
image.out.assembled = true
|
|
424
|
+
lines[idx] = `${prefix}image::${image.out.path}${boxedAttrlist}`
|
|
425
|
+
}
|
|
356
426
|
}
|
|
427
|
+
lastImageMacroAt = [idx, imageMacroOffset]
|
|
357
428
|
}
|
|
429
|
+
} else if (context === 'document' && block.hasHeader()) {
|
|
430
|
+
// nested document
|
|
431
|
+
idx = (block.getHeader().getLineNumber() || idx + 1) - 1
|
|
358
432
|
}
|
|
359
433
|
if (block.getId()) rewriteStyleAttribute(block, lines, idx, idprefix)
|
|
360
434
|
}
|
|
361
435
|
})
|
|
362
|
-
buffer.push(...lines)
|
|
363
|
-
const attributeEntries = Object.entries(doc.
|
|
436
|
+
buffer.push(...lines.filter((it) => it !== undefined))
|
|
437
|
+
const attributeEntries = Object.entries(doc.source_header_attributes?.$$smap || {})
|
|
364
438
|
if (attributeEntries.length) {
|
|
365
439
|
const resolvedAttributeEntries = attributeEntries.reduce(
|
|
366
440
|
(accum, [name, val]) => {
|
|
@@ -372,7 +446,15 @@ function aggregateAsciiDoc (
|
|
|
372
446
|
} else if (val !== initialVal) {
|
|
373
447
|
accum.push(`:${name}:${initialVal ? ' ' + initialVal : ''}`)
|
|
374
448
|
}
|
|
375
|
-
} else if (
|
|
449
|
+
} else if (
|
|
450
|
+
!(
|
|
451
|
+
val == null ||
|
|
452
|
+
doc.isAttributeLocked(name) ||
|
|
453
|
+
name === 'doctype' ||
|
|
454
|
+
name === 'leveloffset' ||
|
|
455
|
+
name === 'underscore'
|
|
456
|
+
)
|
|
457
|
+
) {
|
|
376
458
|
accum.push(`:!${name}:`)
|
|
377
459
|
}
|
|
378
460
|
return accum
|
|
@@ -381,43 +463,51 @@ function aggregateAsciiDoc (
|
|
|
381
463
|
)
|
|
382
464
|
if (resolvedAttributeEntries.length > 1) buffer.push(...resolvedAttributeEntries)
|
|
383
465
|
}
|
|
384
|
-
} else {
|
|
385
|
-
if (level) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
466
|
+
} else if (level) {
|
|
467
|
+
if (level === 1 && navtitlePlain === componentVersion.title) {
|
|
468
|
+
level--
|
|
469
|
+
} else {
|
|
470
|
+
buffer.push('')
|
|
471
|
+
// NOTE: try to toggle sectids; otherwise, fallback to globally unique synthetic ID
|
|
472
|
+
// Q: should we unset docname, page-module, etc?
|
|
473
|
+
let toggleSectids, syntheticId
|
|
474
|
+
if (!('sectids' in asciidocConfig.attributes)) {
|
|
475
|
+
buffer.push(':!sectids:')
|
|
476
|
+
toggleSectids = true
|
|
477
|
+
} else if (typeof asciidocConfig.attributes.sectids === 'string') {
|
|
478
|
+
if ('sectids' in mutableAttributes) {
|
|
393
479
|
buffer.push(':!sectids:')
|
|
394
480
|
toggleSectids = true
|
|
395
|
-
} else
|
|
396
|
-
|
|
397
|
-
buffer.push(':!sectids:')
|
|
398
|
-
toggleSectids = true
|
|
399
|
-
} else {
|
|
400
|
-
syntheticId = `__object-id-${global.Opal.hash(outlineEntry).$object_id()}`
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
// Q: should we unset docname, page-module, etc?
|
|
404
|
-
const sectionTitle = urlType === 'external' ? `${url}[${navtitle.replace(/\]/, '\\]')}]` : navtitle
|
|
405
|
-
let hlevel = level + 1
|
|
406
|
-
if (hlevel > 6) {
|
|
407
|
-
hlevel = 6
|
|
408
|
-
buffer.push(syntheticId ? `[discrete#${syntheticId}]` : '[discrete]')
|
|
409
|
-
} else if (syntheticId) {
|
|
410
|
-
buffer.push(`[#${syntheticId}]`)
|
|
481
|
+
} else {
|
|
482
|
+
syntheticId = `__object-id-${global.Opal.hash(outlineEntry).$object_id()}`
|
|
411
483
|
}
|
|
412
|
-
buffer.push(`${'='.repeat(hlevel)} ${sectionTitle}`)
|
|
413
|
-
if (toggleSectids) buffer.push(':sectids:')
|
|
414
484
|
}
|
|
485
|
+
let sectionTitle = navtitleAsciiDoc
|
|
486
|
+
if (urlType === 'external') {
|
|
487
|
+
sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
488
|
+
} else if (urlType === 'internal' && !unresolved && siteUrl) {
|
|
489
|
+
const resource = contentCatalog.getFiles().find((it) => it.out && it.pub.url === url)
|
|
490
|
+
if (resource) sectionTitle = `${siteUrl}${resource.pub.url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
491
|
+
}
|
|
492
|
+
let hlevel = level + 1
|
|
493
|
+
if (hlevel > 6) {
|
|
494
|
+
hlevel = 6
|
|
495
|
+
buffer.push(syntheticId ? `[discrete#${syntheticId}]` : '[discrete]')
|
|
496
|
+
} else if (syntheticId) {
|
|
497
|
+
buffer.push(`[#${syntheticId}]`)
|
|
498
|
+
}
|
|
499
|
+
buffer.push(`${'='.repeat(hlevel)} ${sectionTitle}`)
|
|
500
|
+
if (toggleSectids) buffer.push(':sectids:')
|
|
415
501
|
}
|
|
416
502
|
}
|
|
417
503
|
|
|
418
504
|
const nextLevel = level + 1
|
|
419
|
-
if (
|
|
420
|
-
|
|
505
|
+
if (hasItems) {
|
|
506
|
+
// NOTE: drop first child if same as parent; should we keep if content is different?
|
|
507
|
+
;(urlType === 'internal' && urlType === items[0].urlType && url === items[0].url && !items[0].items
|
|
508
|
+
? items.slice(1)
|
|
509
|
+
: items
|
|
510
|
+
).forEach((item) => {
|
|
421
511
|
buffer.push(
|
|
422
512
|
...aggregateAsciiDoc(
|
|
423
513
|
loadAsciiDoc,
|
|
@@ -429,6 +519,7 @@ function aggregateAsciiDoc (
|
|
|
429
519
|
asciidocConfig,
|
|
430
520
|
mutableAttributes,
|
|
431
521
|
sectionMergeStrategy,
|
|
522
|
+
lastComponentVersion,
|
|
432
523
|
nextLevel
|
|
433
524
|
)
|
|
434
525
|
)
|
|
@@ -437,18 +528,12 @@ function aggregateAsciiDoc (
|
|
|
437
528
|
return buffer
|
|
438
529
|
}
|
|
439
530
|
|
|
440
|
-
function
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
.toLowerCase()
|
|
447
|
-
.replace(/&.+?;|[^ \p{Alpha}0-9_\-.]/gu, '')
|
|
448
|
-
.replace(/[ _.]/g, '-')
|
|
449
|
-
.replace(/--+/g, '-')
|
|
450
|
-
)
|
|
451
|
-
return path.join(...segments)
|
|
531
|
+
function generateSlug (title) {
|
|
532
|
+
return title
|
|
533
|
+
.toLowerCase()
|
|
534
|
+
.replace(/&.+?;|[^ \p{Alpha}0-9_\-.]/gu, '')
|
|
535
|
+
.replace(/[ _.]/g, '-')
|
|
536
|
+
.replace(/--+/g, '-')
|
|
452
537
|
}
|
|
453
538
|
|
|
454
539
|
function fixSectionLevels (sections, multipart) {
|
|
@@ -485,10 +570,10 @@ function rewriteStyleAttribute (block, lines, idx, idprefix, replacementStyle =
|
|
|
485
570
|
let rawStyle
|
|
486
571
|
const commaIdx = prevLine.indexOf(',')
|
|
487
572
|
if (~commaIdx) {
|
|
488
|
-
rawStyle = prevLine.
|
|
573
|
+
rawStyle = prevLine.slice(1, commaIdx)
|
|
489
574
|
if (~rawStyle.indexOf('=')) rawStyle = undefined
|
|
490
575
|
} else if (!~prevLine.indexOf('=')) {
|
|
491
|
-
rawStyle = prevLine.
|
|
576
|
+
rawStyle = prevLine.slice(1, prevLine.length - 1)
|
|
492
577
|
}
|
|
493
578
|
if (rawStyle) {
|
|
494
579
|
if (~rawStyle.indexOf('#')) {
|
|
@@ -496,11 +581,11 @@ function rewriteStyleAttribute (block, lines, idx, idprefix, replacementStyle =
|
|
|
496
581
|
if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
|
|
497
582
|
} else {
|
|
498
583
|
prevLine = `[${
|
|
499
|
-
replacementStyle ? rawStyle.replace(/^[^.%]
|
|
500
|
-
}#${idprefix}${block.getId()}${prevLine.
|
|
584
|
+
replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
|
|
585
|
+
}#${idprefix}${block.getId()}${prevLine.slice(rawStyle.length + 1)}`
|
|
501
586
|
}
|
|
502
587
|
} else {
|
|
503
|
-
prevLine = `[${replacementStyle}#${idprefix}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.
|
|
588
|
+
prevLine = `[${replacementStyle}#${idprefix}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.slice(1)}`
|
|
504
589
|
}
|
|
505
590
|
if (cellSpec) prevLine = `${cellSpec}|${prevLine}`
|
|
506
591
|
lines[idx - 1] = prevLine
|