@antora/assembler 1.0.0-alpha.9 → 1.0.0-beta.2
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/README.md +7 -4
- package/lib/assemble-content.js +214 -26
- package/lib/configure.js +96 -0
- package/lib/index.js +2 -2
- package/lib/load-config.js +41 -24
- package/lib/{produce-aggregate-document.js → produce-assembly-file.js} +309 -153
- package/lib/produce-assembly-files.js +146 -0
- package/lib/util/compute-out.js +57 -0
- package/lib/util/create-asciidoc-file.js +18 -0
- package/package.json +15 -13
- package/lib/asciidoctor/reducer-extension.js +0 -235
- package/lib/produce-aggregate-documents.js +0 -145
- package/lib/util/run-command.js +0 -60
|
@@ -1,64 +1,63 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const createAsciiDocFile = require('./util/create-asciidoc-file')
|
|
4
4
|
const path = require('node:path/posix')
|
|
5
5
|
const sanitize = require('./util/sanitize')
|
|
6
6
|
const unconvertInlineAsciiDoc = require('./util/unconvert-inline-asciidoc')
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const AttributeEntryRx = /^:([^:-][^:]*):(?: .*)?$/
|
|
9
|
+
const BuiltInNamedEntities = { amp: '&', apos: "'", gt: '>', lt: '<', nbsp: ' ', quot: '"' }
|
|
10
|
+
const CharRefRx = /&(?:([a-z][a-z]+\d{0,2})|#(?:(\d{2,6})|x([a-z\d]{2,5})));/g
|
|
11
|
+
const DiscardAttributes = 'doctype leveloffset assembly-style underscore'.split(' ')
|
|
12
|
+
const ReservedIdNames = 'content header footnotes footer footer-text premable toc toctitle'.split(' ')
|
|
13
|
+
|
|
14
|
+
function produceAssemblyFile (
|
|
9
15
|
loadAsciiDoc,
|
|
10
16
|
contentCatalog,
|
|
11
17
|
componentVersion,
|
|
12
18
|
outline,
|
|
13
|
-
|
|
14
|
-
pages,
|
|
19
|
+
files,
|
|
15
20
|
asciidocConfig,
|
|
16
21
|
mutableAttributes,
|
|
17
|
-
|
|
22
|
+
assemblyModel
|
|
18
23
|
) {
|
|
19
|
-
const
|
|
24
|
+
const pagesByUrl = files.reduce((map, it) => (it.src.family === 'page' ? map.set(it.pub.url, it) : map), new Map())
|
|
25
|
+
if (outline.urlType === 'internal' && !pagesByUrl.get(outline.url) && !(outline.items || []).length) return
|
|
26
|
+
const pagesInOutline = selectPagesInOutline(outline, pagesByUrl, componentVersion)
|
|
20
27
|
const navtitle = outline.content
|
|
21
|
-
const
|
|
22
|
-
src: {
|
|
23
|
-
component: componentVersion.name,
|
|
24
|
-
version: componentVersion.version,
|
|
25
|
-
module: 'ROOT',
|
|
26
|
-
family: 'page',
|
|
27
|
-
relative: generateSlug(navtitle),
|
|
28
|
-
},
|
|
29
|
-
})
|
|
30
|
-
const { dir: outDir, name: outName } = path.parse(templateFile.out.path)
|
|
31
|
-
const path_ = outName === templateFile.src.relative ? path.join(outDir, outName + '.adoc') : outDir + '.adoc'
|
|
32
|
-
contentCatalog.removeFile(templateFile)
|
|
33
|
-
const header = buildAsciiDocHeader(componentVersion, navtitle, doctype)
|
|
34
|
-
const body = aggregateAsciiDoc(
|
|
28
|
+
const buffer = mergeAsciiDoc(
|
|
35
29
|
loadAsciiDoc,
|
|
36
30
|
contentCatalog,
|
|
37
|
-
|
|
31
|
+
buildAsciiDocHeader(componentVersion, navtitle, assemblyModel),
|
|
38
32
|
componentVersion,
|
|
39
33
|
outline,
|
|
34
|
+
files,
|
|
40
35
|
pagesInOutline,
|
|
41
36
|
asciidocConfig,
|
|
42
37
|
mutableAttributes,
|
|
43
|
-
|
|
38
|
+
assemblyModel
|
|
44
39
|
)
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
const rootLevel = assemblyModel.rootLevel
|
|
41
|
+
const stem = rootLevel === 0 ? 'index' : generateSlug(navtitle)
|
|
42
|
+
const downloadStem = [componentVersion.name, componentVersion.version, rootLevel === 0 ? '' : stem]
|
|
43
|
+
.filter((it) => it)
|
|
44
|
+
.join('-')
|
|
45
|
+
return createAsciiDocFile(contentCatalog, {
|
|
47
46
|
asciidoc: asciidocConfig,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
path: path_,
|
|
47
|
+
assembler: { assembled: pagesInOutline.assembled, downloadStem, rootLevel },
|
|
48
|
+
contents: Buffer.from(buffer.join('\n') + '\n'),
|
|
51
49
|
src: {
|
|
52
50
|
component: componentVersion.name,
|
|
53
51
|
version: componentVersion.version,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
module: 'ROOT',
|
|
53
|
+
family: 'export',
|
|
54
|
+
relative: stem + '.adoc',
|
|
57
55
|
},
|
|
58
56
|
})
|
|
59
57
|
}
|
|
60
58
|
|
|
61
|
-
function buildAsciiDocHeader (componentVersion, navtitle,
|
|
59
|
+
function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
|
|
60
|
+
const doctype = assemblyModel.doctype ?? 'book'
|
|
62
61
|
const navtitlePlain = sanitize(navtitle)
|
|
63
62
|
const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
64
63
|
let doctitle = navtitleAsciiDoc
|
|
@@ -67,7 +66,7 @@ function buildAsciiDocHeader (componentVersion, navtitle, doctype = 'book') {
|
|
|
67
66
|
return [
|
|
68
67
|
`= ${doctitle}`,
|
|
69
68
|
...(version ? [`:revnumber: ${version}`] : []),
|
|
70
|
-
...(doctype === 'article' ? [] : [`:doctype: ${doctype}`]),
|
|
69
|
+
...(doctype === 'article' ? [] : [`:doctype: ${doctype ?? 'book'}`]),
|
|
71
70
|
':underscore: _',
|
|
72
71
|
// Q: should we pass these via the CLI so they cannot be modified?
|
|
73
72
|
`:page-component-name: ${componentVersion.name}`,
|
|
@@ -78,46 +77,52 @@ function buildAsciiDocHeader (componentVersion, navtitle, doctype = 'book') {
|
|
|
78
77
|
]
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
function selectPagesInOutline (outlineEntry,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
page
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
80
|
+
function selectPagesInOutline (outlineEntry, pagesByUrl, componentVersion, accum) {
|
|
81
|
+
accum ??= Object.assign(new Map(), { assembled: { pages: new Map(), assets: new Set() } })
|
|
82
|
+
const page = outlineEntry.urlType === 'internal' ? pagesByUrl.get(outlineEntry.url) : undefined
|
|
83
|
+
if (page) {
|
|
84
|
+
if (page.src.component === componentVersion.name && page.src.version === componentVersion.version) {
|
|
85
|
+
accum.set(`${page.src.module === 'ROOT' ? '' : page.src.module + ':'}${page.src.relative}`, page)
|
|
86
|
+
}
|
|
87
|
+
accum.set(page.pub.url, page)
|
|
88
|
+
}
|
|
89
|
+
for (const item of outlineEntry.items || []) selectPagesInOutline(item, pagesByUrl, componentVersion, accum)
|
|
90
|
+
return accum
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
function
|
|
93
|
+
function mergeAsciiDoc (
|
|
95
94
|
loadAsciiDoc,
|
|
96
95
|
contentCatalog,
|
|
97
|
-
|
|
96
|
+
buffer,
|
|
98
97
|
componentVersion,
|
|
99
98
|
outlineEntry,
|
|
99
|
+
files,
|
|
100
100
|
pagesInOutline,
|
|
101
101
|
asciidocConfig,
|
|
102
102
|
mutableAttributes,
|
|
103
|
-
|
|
103
|
+
assemblyModel,
|
|
104
104
|
lastComponentVersion = componentVersion,
|
|
105
|
-
level = 0
|
|
105
|
+
level = 0,
|
|
106
|
+
supportsParts = false
|
|
106
107
|
) {
|
|
107
|
-
const buffer = []
|
|
108
108
|
// TODO: we could try to be smart about it and make sure the page with fragment is included at least once
|
|
109
109
|
if (outlineEntry.hash) return buffer
|
|
110
110
|
const { content: navtitle, items = [], unresolved, urlType, url } = outlineEntry
|
|
111
|
+
const atDocumentRoot = !buffer.inBody
|
|
112
|
+
const atBookRoot = atDocumentRoot && !level && assemblyModel.doctype === 'book' && (supportsParts = true)
|
|
111
113
|
const hasItems = items.length > 0
|
|
112
114
|
const navtitlePlain = sanitize(navtitle)
|
|
113
115
|
const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
114
116
|
const siteUrl = ((val) => {
|
|
115
117
|
if (!val || val === '/') return ''
|
|
116
118
|
return val.charAt(val.length - 1) === '/' ? val.slice(0, val.length - 1) : val
|
|
117
|
-
})(asciidocConfig.attributes['site-url'])
|
|
119
|
+
})(asciidocConfig.attributes['primary-site-url'] || asciidocConfig.attributes['site-url'])
|
|
120
|
+
const idSeparator = assemblyModel.xmlIds ? '-' : ':'
|
|
121
|
+
const idScopeSeparator = idSeparator.repeat(3)
|
|
122
|
+
const idCoordinateSeparator = idSeparator === '-' ? '----' : idSeparator
|
|
118
123
|
// FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
|
|
119
124
|
let page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
|
|
120
|
-
if (page && pagesInOutline.
|
|
125
|
+
if (page && pagesInOutline.assembled.pages.has(page)) page = undefined
|
|
121
126
|
if (page) {
|
|
122
127
|
let contents = page.src.contents
|
|
123
128
|
if (contents == null) return buffer
|
|
@@ -130,20 +135,51 @@ function aggregateAsciiDoc (
|
|
|
130
135
|
.replace(/^(?:[ \t]*\r\n?|[ \t]*\n)+/, '')
|
|
131
136
|
.trimRight()
|
|
132
137
|
)
|
|
133
|
-
;(pagesInOutline.aggregated ??= []).push(page)
|
|
134
|
-
page = new page.constructor(Object.assign({}, page, { contents, mediaType: 'text/asciidoc' }))
|
|
135
138
|
const { component, version, module: module_, relative, origin } = page.src
|
|
136
|
-
const
|
|
137
|
-
const
|
|
139
|
+
const topicPrefix = ~relative.indexOf('/') ? path.dirname(relative) + '/' : ''
|
|
140
|
+
const pageAsAsciiDoc = new page.constructor(Object.assign({}, page, { contents, mediaType: page.src.mediaType }))
|
|
141
|
+
const doc = loadAsciiDoc(pageAsAsciiDoc, contentCatalog, asciidocConfig)
|
|
142
|
+
if (atDocumentRoot) {
|
|
143
|
+
const authors = doc.getAuthors()
|
|
144
|
+
if (authors.length) {
|
|
145
|
+
const authorLine = authors
|
|
146
|
+
.map((author) => {
|
|
147
|
+
const email = author.getEmail()
|
|
148
|
+
return email ? `${author.getName()} <${author.getEmail()}>` : author.getName()
|
|
149
|
+
})
|
|
150
|
+
.join('; ')
|
|
151
|
+
buffer.splice(1, 0, authorLine)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
138
154
|
// NOTE: in Antora, docname is relative src path from module without file extension
|
|
139
155
|
const docname = doc.getAttribute('docname')
|
|
140
|
-
const docnameForId = docname.replace(/[
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
156
|
+
const docnameForId = docname.replace(/[/.]/g, '-')
|
|
157
|
+
const qualifyId = component !== componentVersion.name
|
|
158
|
+
let idScope = docnameForId
|
|
159
|
+
let idPrefix
|
|
160
|
+
if (qualifyId) {
|
|
161
|
+
idScope = [component, module_ === 'ROOT' ? '' : module_, idScope].join(idCoordinateSeparator)
|
|
162
|
+
} else if (module_ !== 'ROOT') {
|
|
163
|
+
idScope = module_ + idCoordinateSeparator + idScope
|
|
164
|
+
} else if (ReservedIdNames.includes(docnameForId)) {
|
|
165
|
+
idScope = idPrefix = idScope + idScopeSeparator
|
|
166
|
+
}
|
|
167
|
+
idPrefix ??= idScope + idScopeSeparator
|
|
168
|
+
let pageFragment = ''
|
|
169
|
+
let pageRoles = ''
|
|
170
|
+
let pageStyle = doc.getAttribute('assembly-style', '')
|
|
171
|
+
let part
|
|
172
|
+
if ((part = pageStyle === 'part')) {
|
|
173
|
+
pageStyle = ''
|
|
174
|
+
if (!supportsParts) part = undefined
|
|
175
|
+
} else if ((part = pageStyle.endsWith('-part'))) {
|
|
176
|
+
pageStyle = pageStyle.slice(0, -5)
|
|
177
|
+
if (!supportsParts) part = undefined
|
|
178
|
+
}
|
|
179
|
+
let nextSectionLevel = 1
|
|
180
|
+
const lines = doc.getSourceLines()
|
|
181
|
+
const ignoreLines = []
|
|
182
|
+
buffer.inBody = true
|
|
147
183
|
buffer.push('')
|
|
148
184
|
buffer.push(`:docname: ${docname}`)
|
|
149
185
|
if (component !== lastComponentVersion.name) {
|
|
@@ -168,25 +204,50 @@ function aggregateAsciiDoc (
|
|
|
168
204
|
buffer.push(`:page-origin-refname: ${origin.branch || origin.tag}`)
|
|
169
205
|
buffer.push(`:page-origin-reftype: ${origin.branch ? 'branch' : 'tag'}`)
|
|
170
206
|
buffer.push(`:page-origin-refhash: ${origin.worktree ? '(worktree)' : origin.refhash}`)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (level) {
|
|
174
|
-
|
|
175
|
-
|
|
207
|
+
if (doc.hasHeader()) pageRoles = processDocumentHeader(doc, lines, buffer, ignoreLines)
|
|
208
|
+
let heading
|
|
209
|
+
if (pageStyle && (part && level === 1 ? (level = 0) : level) === 0) {
|
|
210
|
+
let htitleAsciiDoc = navtitleAsciiDoc
|
|
211
|
+
let htitlePlain = navtitlePlain
|
|
212
|
+
let htitleOverride
|
|
213
|
+
if (
|
|
214
|
+
(htitleOverride = pageStyle === 'preface' ? doc.getAttribute('preface-title') : undefined) ||
|
|
215
|
+
(atDocumentRoot && (htitleOverride = outlineEntry.navtitle))
|
|
216
|
+
) {
|
|
217
|
+
htitleAsciiDoc = unconvertInlineAsciiDoc(htitleOverride)
|
|
218
|
+
htitlePlain = sanitize(htitleOverride)
|
|
219
|
+
}
|
|
220
|
+
if (atDocumentRoot && pageStyle === 'preface' && htitlePlain === componentVersion.title) {
|
|
221
|
+
assemblyModel = Object.assign({}, assemblyModel, { sectionMergeStrategy: 'discrete' })
|
|
176
222
|
} else {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
buffer.push(`[discrete#${idprefix}]`)
|
|
223
|
+
if (atDocumentRoot) {
|
|
224
|
+
pageFragment = `#${idPrefix}${doc.getId() ?? pageStyle}`
|
|
225
|
+
buffer.unshift(`[#${idScope}]`)
|
|
181
226
|
} else {
|
|
182
|
-
|
|
227
|
+
pageFragment = `#${idScope}`
|
|
228
|
+
}
|
|
229
|
+
heading = { title: htitleAsciiDoc, level: part ? 1 : 2 }
|
|
230
|
+
nextSectionLevel++
|
|
231
|
+
}
|
|
232
|
+
} else if (level) {
|
|
233
|
+
if (atDocumentRoot && navtitlePlain === componentVersion.title) {
|
|
234
|
+
level--
|
|
235
|
+
} else {
|
|
236
|
+
pageFragment = `#${idScope}`
|
|
237
|
+
if (part && level === 1) level--
|
|
238
|
+
if ((heading = { title: navtitleAsciiDoc, level: level + 1 }).level > 6) {
|
|
239
|
+
Object.assign(heading, { level: 6, style: `discrete.h${heading.level}` })
|
|
183
240
|
}
|
|
184
|
-
buffer.push(`${'='.repeat(hlevel)} ${navtitleAsciiDoc}`)
|
|
185
241
|
}
|
|
186
|
-
} else {
|
|
187
|
-
header.unshift(`[#${idprefix}]`)
|
|
188
242
|
}
|
|
189
|
-
if (
|
|
243
|
+
if (heading) {
|
|
244
|
+
buffer.push(`[${heading.style ?? pageStyle}${pageFragment}${pageRoles}]`)
|
|
245
|
+
buffer.push(`${'='.repeat(heading.level)} ${heading.title}`)
|
|
246
|
+
} else if (atDocumentRoot) {
|
|
247
|
+
buffer.unshift(`[#${idScope}]`)
|
|
248
|
+
}
|
|
249
|
+
let enclosed
|
|
250
|
+
if (assemblyModel.sectionMergeStrategy === 'enclose' && hasItems && doc.hasSections()) {
|
|
190
251
|
enclosed = true
|
|
191
252
|
// TODO: make overview section title configurable
|
|
192
253
|
//let overviewTitle = doc.getDocumentTitle()
|
|
@@ -205,23 +266,43 @@ function aggregateAsciiDoc (
|
|
|
205
266
|
}
|
|
206
267
|
let hlevel = level + 2
|
|
207
268
|
if (hlevel > 6) {
|
|
269
|
+
const blockStyle = `discrete.h${hlevel}`
|
|
208
270
|
hlevel = 6
|
|
209
|
-
buffer.push(syntheticId ? `[
|
|
271
|
+
buffer.push(syntheticId ? `[${blockStyle}#${syntheticId}]` : `[${blockStyle}]`)
|
|
210
272
|
} else if (syntheticId) {
|
|
211
273
|
buffer.push(`[#${syntheticId}]`)
|
|
212
274
|
}
|
|
213
275
|
buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
|
|
214
276
|
if (toggleSectids) buffer.push(':sectids:')
|
|
215
277
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
278
|
+
pagesInOutline.assembled.pages.set(page, pageFragment)
|
|
279
|
+
if (doc.hasSections()) {
|
|
280
|
+
fixSectionLevels(doc.getSections(), atBookRoot && !pageStyle ? undefined : nextSectionLevel)
|
|
281
|
+
}
|
|
220
282
|
const allBlocks = doc.findBy({ traverse_documents: true }, (it) =>
|
|
221
283
|
it.getContext() === 'document'
|
|
222
284
|
? it.getDocument().isNested()
|
|
223
285
|
: !(it.getContext() === 'table_cell' && it.getStyle() === 'asciidoc')
|
|
224
286
|
)
|
|
287
|
+
if (doc.getDoctype() === 'manpage') {
|
|
288
|
+
const firstSectionIdx = doc.hasSections() ? doc.getSections()[0].getLineNumber() - 1 : lines.length
|
|
289
|
+
for (let idx = 0; idx < firstSectionIdx; idx++) {
|
|
290
|
+
if (~ignoreLines.indexOf(idx)) continue
|
|
291
|
+
const line = lines[idx]
|
|
292
|
+
if (line.startsWith('== ') && line.length > 3) {
|
|
293
|
+
allBlocks.unshift({
|
|
294
|
+
getContext: () => 'section',
|
|
295
|
+
getDocument: () => doc,
|
|
296
|
+
getId: () => doc.getAttribute('manname-id'),
|
|
297
|
+
getLineNumber: () => idx + 1,
|
|
298
|
+
getSectionName: () => undefined,
|
|
299
|
+
level: 1,
|
|
300
|
+
})
|
|
301
|
+
break
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const refs = doc.getCatalog().refs
|
|
225
306
|
allBlocks.forEach((block) => {
|
|
226
307
|
const contentModel = block.content_model
|
|
227
308
|
if (
|
|
@@ -249,7 +330,7 @@ function aggregateAsciiDoc (
|
|
|
249
330
|
}
|
|
250
331
|
})
|
|
251
332
|
let skipping
|
|
252
|
-
for (let idx = 0,
|
|
333
|
+
for (let idx = 0, lastIdx = lines.length - 1; idx <= lastIdx; idx++) {
|
|
253
334
|
if (~ignoreLines.indexOf(idx)) continue
|
|
254
335
|
let line = lines[idx]
|
|
255
336
|
if (line.startsWith('//')) {
|
|
@@ -265,7 +346,11 @@ function aggregateAsciiDoc (
|
|
|
265
346
|
} else if (skipping) {
|
|
266
347
|
continue
|
|
267
348
|
}
|
|
268
|
-
if (
|
|
349
|
+
if (
|
|
350
|
+
line.charAt() === ':' &&
|
|
351
|
+
~line.indexOf(':', 2) &&
|
|
352
|
+
(line.match(AttributeEntryRx) || ['', ''])[1].replace('!', '') === 'leveloffset'
|
|
353
|
+
) {
|
|
269
354
|
if (lines[idx - 1] === '') lines[idx - 1] = undefined
|
|
270
355
|
lines[idx] = undefined
|
|
271
356
|
continue
|
|
@@ -276,16 +361,16 @@ function aggregateAsciiDoc (
|
|
|
276
361
|
if (!refs['$key?'](refid) && (~refid.indexOf(' ') || refid.toLowerCase() !== refid)) {
|
|
277
362
|
if ((refid = doc.$resolve_id(refid))['$nil?']()) return m
|
|
278
363
|
}
|
|
279
|
-
return `<<${
|
|
364
|
+
return `<<${idPrefix}${refid}${text ? ',' + text : ''}>>`
|
|
280
365
|
})
|
|
281
366
|
}
|
|
282
367
|
// NOTE: the next check takes care of inline and block anchors
|
|
283
368
|
if (~line.indexOf('[[')) {
|
|
284
|
-
line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, `[[${
|
|
369
|
+
line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, `[[${idPrefix}$1$2]]`)
|
|
285
370
|
}
|
|
286
371
|
if (~line.indexOf('xref:')) {
|
|
287
372
|
// Q: should we allow : as first character of target?
|
|
288
|
-
line = line.replace(/(?<![\\+])xref:([\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (m, target, text) => {
|
|
373
|
+
line = line.replace(/(?<![\\+])xref:((?:\.\/)?[\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (m, target, text) => {
|
|
289
374
|
let pagePart, fragment, targetPage
|
|
290
375
|
const hashIdx = target.indexOf('#')
|
|
291
376
|
if (~hashIdx) {
|
|
@@ -302,8 +387,8 @@ function aggregateAsciiDoc (
|
|
|
302
387
|
if (!pagePart) {
|
|
303
388
|
// Q: should we validate the internal ID here?
|
|
304
389
|
return text && ~text.indexOf('=')
|
|
305
|
-
? `xref:${
|
|
306
|
-
: `<<${
|
|
390
|
+
? `xref:${idPrefix}${fragment}[${text}]`
|
|
391
|
+
: `<<${idPrefix}${fragment}${text ? ',' + text.replace(/\\]/g, ']') : ''}>>`
|
|
307
392
|
}
|
|
308
393
|
if (~pagePart.indexOf('@') || /:.*:/.test(pagePart)) {
|
|
309
394
|
if (siteUrl && (targetPage = contentCatalog.resolvePage(pagePart, page.src)) && targetPage.out) {
|
|
@@ -312,24 +397,30 @@ function aggregateAsciiDoc (
|
|
|
312
397
|
}
|
|
313
398
|
// TODO: handle unresolved page better
|
|
314
399
|
return m
|
|
315
|
-
} else if (pagePart.indexOf(':') < 0) {
|
|
316
|
-
if (module_ !== 'ROOT') pagePart = `${module_}:${pagePart}`
|
|
317
|
-
} else if (pagePart.startsWith('ROOT:')) {
|
|
318
|
-
pagePart = pagePart.slice(5)
|
|
319
400
|
}
|
|
320
|
-
|
|
321
|
-
|
|
401
|
+
let targetModule
|
|
402
|
+
const colonIdx = pagePart.indexOf(':')
|
|
403
|
+
if (~colonIdx) {
|
|
404
|
+
targetModule = pagePart.slice(0, colonIdx)
|
|
405
|
+
pagePart = pagePart.slice(colonIdx + 1)
|
|
406
|
+
} else {
|
|
407
|
+
targetModule = module_
|
|
408
|
+
}
|
|
409
|
+
if (pagePart.startsWith('./')) pagePart = topicPrefix + pagePart.slice(2)
|
|
410
|
+
const pageResourceRef = targetModule === 'ROOT' ? pagePart : `${targetModule}:${pagePart}`
|
|
411
|
+
if (!(targetPage = pagesInOutline.get(pageResourceRef))) {
|
|
412
|
+
if (siteUrl && (targetPage = contentCatalog.resolvePage(pageResourceRef, page.src)) && targetPage.out) {
|
|
322
413
|
text ||= targetPage.asciidoc?.xreftext || target
|
|
323
414
|
return `${siteUrl}${targetPage.pub.url}${fragment && '#' + fragment}[${text}]`
|
|
324
415
|
}
|
|
325
416
|
// TODO: handle unresolved page better
|
|
326
417
|
return m
|
|
327
418
|
}
|
|
328
|
-
pagePart = pagePart
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
419
|
+
if (targetModule !== 'ROOT') pagePart = `${targetModule}${idCoordinateSeparator}${pagePart}`
|
|
420
|
+
pagePart = pagePart.replace(/\.adoc$/, '').replace(/[/.]/g, '-')
|
|
421
|
+
const refid = fragment
|
|
422
|
+
? `${pagePart}${idScopeSeparator}${fragment}`
|
|
423
|
+
: pagePart + (ReservedIdNames.includes(pagePart) ? idScopeSeparator : '')
|
|
333
424
|
return `<<${refid}${text && text !== targetPage.title ? ',' + text.replace(/\\]/g, ']') : ''}>>`
|
|
334
425
|
})
|
|
335
426
|
}
|
|
@@ -354,7 +445,7 @@ function aggregateAsciiDoc (
|
|
|
354
445
|
const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
|
|
355
446
|
// TODO: handle (or report) unresolved image better
|
|
356
447
|
if (image?.out) {
|
|
357
|
-
|
|
448
|
+
pagesInOutline.assembled.assets.add(image)
|
|
358
449
|
return `image:${image.out.path.replace(/_/g, '{underscore}')}[${attrlist}]`
|
|
359
450
|
}
|
|
360
451
|
}
|
|
@@ -374,21 +465,18 @@ function aggregateAsciiDoc (
|
|
|
374
465
|
const context = block.getContext()
|
|
375
466
|
let idx = lineno - 1
|
|
376
467
|
if (context === 'section' && !block.getDocument().isNested()) {
|
|
377
|
-
if (block.getSectionName() === 'header')
|
|
378
|
-
|
|
379
|
-
return
|
|
380
|
-
}
|
|
381
|
-
let blockStyle = sectionMergeStrategy === 'discrete' ? 'discrete' : undefined
|
|
468
|
+
if (block.getSectionName() === 'header') return
|
|
469
|
+
let blockStyle = (assemblyModel.sectionMergeStrategy || 'discrete') === 'discrete' ? 'discrete' : undefined
|
|
382
470
|
lines[idx] = lines[idx].replace(/^=+ (.+)/, (_, rest) => {
|
|
383
471
|
let targetMarkerLength = block.level + 1 + level + (enclosed ? 1 : 0)
|
|
384
472
|
if (targetMarkerLength > 6) {
|
|
473
|
+
blockStyle = `discrete.h${targetMarkerLength}`
|
|
385
474
|
targetMarkerLength = 6
|
|
386
|
-
blockStyle = 'discrete'
|
|
387
475
|
}
|
|
388
476
|
return '='.repeat(targetMarkerLength) + ' ' + rest
|
|
389
477
|
})
|
|
390
478
|
// NOTE: ID will be undefined if sectids are turned off
|
|
391
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
479
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, idPrefix, blockStyle)
|
|
392
480
|
} else {
|
|
393
481
|
if (context === 'image') {
|
|
394
482
|
let line = lines[idx] || ''
|
|
@@ -414,7 +502,7 @@ function aggregateAsciiDoc (
|
|
|
414
502
|
// FIXME: handle (or report) case when image is not resolved
|
|
415
503
|
if (image?.out) {
|
|
416
504
|
const boxedAttrlist = line.slice(line.indexOf('['))
|
|
417
|
-
|
|
505
|
+
pagesInOutline.assembled.assets.add(image)
|
|
418
506
|
lines[idx] = `${prefix}image::${image.out.path}${boxedAttrlist}`
|
|
419
507
|
}
|
|
420
508
|
}
|
|
@@ -424,11 +512,14 @@ function aggregateAsciiDoc (
|
|
|
424
512
|
// nested document
|
|
425
513
|
idx = (block.getHeader().getLineNumber() || idx + 1) - 1
|
|
426
514
|
}
|
|
427
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
515
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, idPrefix)
|
|
428
516
|
}
|
|
429
517
|
})
|
|
430
|
-
|
|
431
|
-
|
|
518
|
+
safePush(
|
|
519
|
+
buffer,
|
|
520
|
+
lines.filter((it) => it !== undefined)
|
|
521
|
+
)
|
|
522
|
+
const attributeEntries = Object.entries(doc.source_header_attributes?.$$smap || {})
|
|
432
523
|
if (attributeEntries.length) {
|
|
433
524
|
const resolvedAttributeEntries = attributeEntries.reduce(
|
|
434
525
|
(accum, [name, val]) => {
|
|
@@ -440,27 +531,20 @@ function aggregateAsciiDoc (
|
|
|
440
531
|
} else if (val !== initialVal) {
|
|
441
532
|
accum.push(`:${name}:${initialVal ? ' ' + initialVal : ''}`)
|
|
442
533
|
}
|
|
443
|
-
} else if (
|
|
444
|
-
!(
|
|
445
|
-
val == null ||
|
|
446
|
-
doc.isAttributeLocked(name) ||
|
|
447
|
-
name === 'doctype' ||
|
|
448
|
-
name === 'leveloffset' ||
|
|
449
|
-
name === 'underscore'
|
|
450
|
-
)
|
|
451
|
-
) {
|
|
534
|
+
} else if (!(val == null || doc.isAttributeLocked(name) || DiscardAttributes.includes(name))) {
|
|
452
535
|
accum.push(`:!${name}:`)
|
|
453
536
|
}
|
|
454
537
|
return accum
|
|
455
538
|
},
|
|
456
539
|
['']
|
|
457
540
|
)
|
|
458
|
-
if (resolvedAttributeEntries.length > 1) buffer
|
|
541
|
+
if (resolvedAttributeEntries.length > 1) safePush(buffer, resolvedAttributeEntries)
|
|
459
542
|
}
|
|
460
543
|
} else if (level) {
|
|
461
|
-
if (
|
|
544
|
+
if (atDocumentRoot && navtitlePlain === componentVersion.title) {
|
|
462
545
|
level--
|
|
463
546
|
} else {
|
|
547
|
+
buffer.inBody = true
|
|
464
548
|
buffer.push('')
|
|
465
549
|
// NOTE: try to toggle sectids; otherwise, fallback to globally unique synthetic ID
|
|
466
550
|
// Q: should we unset docname, page-module, etc?
|
|
@@ -480,7 +564,7 @@ function aggregateAsciiDoc (
|
|
|
480
564
|
if (urlType === 'external') {
|
|
481
565
|
sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
482
566
|
} else if (urlType === 'internal' && !unresolved && siteUrl) {
|
|
483
|
-
const resource =
|
|
567
|
+
const resource = files.find((it) => it.pub.url === url)
|
|
484
568
|
if (resource) sectionTitle = `${siteUrl}${resource.pub.url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
485
569
|
}
|
|
486
570
|
let hlevel = level + 1
|
|
@@ -495,50 +579,112 @@ function aggregateAsciiDoc (
|
|
|
495
579
|
}
|
|
496
580
|
}
|
|
497
581
|
|
|
498
|
-
const nextLevel = level + 1
|
|
499
582
|
if (hasItems) {
|
|
583
|
+
const nextLevel = level + 1
|
|
500
584
|
// NOTE: drop first child if same as parent; should we keep if content is different?
|
|
501
585
|
;(urlType === 'internal' && urlType === items[0].urlType && url === items[0].url && !items[0].items
|
|
502
586
|
? items.slice(1)
|
|
503
587
|
: items
|
|
504
588
|
).forEach((item) => {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
589
|
+
mergeAsciiDoc(
|
|
590
|
+
loadAsciiDoc,
|
|
591
|
+
contentCatalog,
|
|
592
|
+
buffer,
|
|
593
|
+
componentVersion,
|
|
594
|
+
item,
|
|
595
|
+
files,
|
|
596
|
+
pagesInOutline,
|
|
597
|
+
asciidocConfig,
|
|
598
|
+
mutableAttributes,
|
|
599
|
+
assemblyModel,
|
|
600
|
+
lastComponentVersion,
|
|
601
|
+
nextLevel,
|
|
602
|
+
atBookRoot
|
|
519
603
|
)
|
|
520
604
|
})
|
|
521
605
|
}
|
|
522
606
|
return buffer
|
|
523
607
|
}
|
|
524
608
|
|
|
609
|
+
function processDocumentHeader (doc, lines, buffer, ignoreLines) {
|
|
610
|
+
const doctitleIdx = doc.getHeader().getLineNumber() - 1
|
|
611
|
+
const end = doc.getBlocks()[0]?.getLineNumber() ?? lines.length
|
|
612
|
+
let belowDoctitle
|
|
613
|
+
let open
|
|
614
|
+
const implicitLines = []
|
|
615
|
+
for (let idx = 0; idx < end; idx++) {
|
|
616
|
+
if (idx === doctitleIdx) {
|
|
617
|
+
lines[idx] = undefined
|
|
618
|
+
ignoreLines.push(idx)
|
|
619
|
+
belowDoctitle = true
|
|
620
|
+
continue
|
|
621
|
+
}
|
|
622
|
+
const line = lines[idx]
|
|
623
|
+
if (open === ':' || open === '-:') {
|
|
624
|
+
if (line) {
|
|
625
|
+
if (open === ':') buffer.push(line)
|
|
626
|
+
if (!line.endsWith(' \\')) open = undefined
|
|
627
|
+
} else {
|
|
628
|
+
open = undefined
|
|
629
|
+
}
|
|
630
|
+
} else if (line) {
|
|
631
|
+
const chr0 = line.charAt()
|
|
632
|
+
let attributeEntryMatch
|
|
633
|
+
if (chr0 === '/' && line.charAt(1) === '/') {
|
|
634
|
+
if (line.startsWith('////')) {
|
|
635
|
+
open = open ? (open === line ? undefined : open) : line
|
|
636
|
+
} else if (belowDoctitle && !open && line.charAt(2) === '/') {
|
|
637
|
+
break
|
|
638
|
+
}
|
|
639
|
+
buffer.push(line)
|
|
640
|
+
} else if (open) {
|
|
641
|
+
buffer.push(line)
|
|
642
|
+
} else if (chr0 === ':' && ~line.indexOf(':', 2) && (attributeEntryMatch = line.match(AttributeEntryRx))) {
|
|
643
|
+
const attributeName = attributeEntryMatch[1].replace('!', '')
|
|
644
|
+
if (DiscardAttributes.includes(attributeName)) {
|
|
645
|
+
if (line.endsWith(' \\')) open = '-:'
|
|
646
|
+
} else {
|
|
647
|
+
if (line.endsWith(' \\')) open = ':'
|
|
648
|
+
buffer.push(line)
|
|
649
|
+
}
|
|
650
|
+
} else if (belowDoctitle) {
|
|
651
|
+
if (implicitLines.length === 2 || !/[\p{Alpha}0-9]/u.test(chr0)) break
|
|
652
|
+
implicitLines.push(line)
|
|
653
|
+
}
|
|
654
|
+
} else if (belowDoctitle) {
|
|
655
|
+
break
|
|
656
|
+
}
|
|
657
|
+
lines[idx] = undefined
|
|
658
|
+
ignoreLines.push(idx)
|
|
659
|
+
}
|
|
660
|
+
return doc
|
|
661
|
+
.getRoles()
|
|
662
|
+
.map((role) => '.' + role)
|
|
663
|
+
.join('')
|
|
664
|
+
}
|
|
665
|
+
|
|
525
666
|
function generateSlug (title) {
|
|
526
667
|
return title
|
|
527
668
|
.toLowerCase()
|
|
528
|
-
.replace(
|
|
529
|
-
.replace(
|
|
530
|
-
|
|
669
|
+
.replace(/<[^>]+>/g, '')
|
|
670
|
+
.replace(CharRefRx, (_, name, dec, hex) => {
|
|
671
|
+
if (name) return BuiltInNamedEntities[name] ?? '?'
|
|
672
|
+
return String.fromCharCode(dec ? parseInt(dec, 10) : parseInt(hex, 16))
|
|
673
|
+
})
|
|
674
|
+
.replace(/[\x27\u2019]/g, '')
|
|
675
|
+
.replace(/[^\p{Alpha}0-9\-]/gu, '-')
|
|
676
|
+
.replace(/^-+|-+$|(-)-+/g, '$1')
|
|
531
677
|
}
|
|
532
678
|
|
|
533
|
-
function fixSectionLevels (sections,
|
|
679
|
+
function fixSectionLevels (sections, expectedLevel) {
|
|
534
680
|
sections.forEach((sect) => {
|
|
535
|
-
const
|
|
536
|
-
if (
|
|
537
|
-
if (sect.hasSections()) fixSectionLevels(sect.getSections(),
|
|
681
|
+
const forceLevel = expectedLevel ?? Math.min(1, sect.getLevel())
|
|
682
|
+
if (sect.getLevel() !== forceLevel) sect.level = forceLevel
|
|
683
|
+
if (sect.hasSections()) fixSectionLevels(sect.getSections(), forceLevel + 1)
|
|
538
684
|
})
|
|
539
685
|
}
|
|
540
686
|
|
|
541
|
-
function rewriteStyleAttribute (block, lines, idx,
|
|
687
|
+
function rewriteStyleAttribute (block, lines, idx, idPrefix, replacementStyle = '') {
|
|
542
688
|
let prevLine = lines[idx - 1]
|
|
543
689
|
const char0 = prevLine?.charAt()
|
|
544
690
|
if (char0) {
|
|
@@ -548,7 +694,7 @@ function rewriteStyleAttribute (block, lines, idx, idprefix, replacementStyle =
|
|
|
548
694
|
prevLine.charAt(1) === '[' &&
|
|
549
695
|
/^\[\[(?:|[\p{Alpha}_:][\p{Alpha}0-9_\-:.]*(?:, *.+)?)\]\]$/u.test(prevLine))
|
|
550
696
|
) {
|
|
551
|
-
return rewriteStyleAttribute(block, lines, idx - 1,
|
|
697
|
+
return rewriteStyleAttribute(block, lines, idx - 1, idPrefix, replacementStyle)
|
|
552
698
|
}
|
|
553
699
|
}
|
|
554
700
|
let cellSpec
|
|
@@ -564,27 +710,27 @@ function rewriteStyleAttribute (block, lines, idx, idprefix, replacementStyle =
|
|
|
564
710
|
let rawStyle
|
|
565
711
|
const commaIdx = prevLine.indexOf(',')
|
|
566
712
|
if (~commaIdx) {
|
|
567
|
-
rawStyle = prevLine.slice(1, commaIdx
|
|
713
|
+
rawStyle = prevLine.slice(1, commaIdx)
|
|
568
714
|
if (~rawStyle.indexOf('=')) rawStyle = undefined
|
|
569
715
|
} else if (!~prevLine.indexOf('=')) {
|
|
570
|
-
rawStyle = prevLine.slice(1, prevLine.length -
|
|
716
|
+
rawStyle = prevLine.slice(1, prevLine.length - 1)
|
|
571
717
|
}
|
|
572
718
|
if (rawStyle) {
|
|
573
719
|
if (~rawStyle.indexOf('#')) {
|
|
574
|
-
prevLine = prevLine.replace(/#[^.%,\]]+/, `#${
|
|
720
|
+
prevLine = prevLine.replace(/#[^.%,\]]+/, `#${idPrefix}${block.getId()}`)
|
|
575
721
|
if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
|
|
576
722
|
} else {
|
|
577
723
|
prevLine = `[${
|
|
578
724
|
replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
|
|
579
|
-
}#${
|
|
725
|
+
}#${idPrefix}${block.getId()}${prevLine.slice(rawStyle.length + 1)}`
|
|
580
726
|
}
|
|
581
727
|
} else {
|
|
582
|
-
prevLine = `[${replacementStyle}#${
|
|
728
|
+
prevLine = `[${replacementStyle}#${idPrefix}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.slice(1)}`
|
|
583
729
|
}
|
|
584
730
|
if (cellSpec) prevLine = `${cellSpec}|${prevLine}`
|
|
585
731
|
lines[idx - 1] = prevLine
|
|
586
732
|
} else {
|
|
587
|
-
lines.splice(idx, 0, `[${replacementStyle}#${
|
|
733
|
+
lines.splice(idx, 0, `[${replacementStyle}#${idPrefix}${block.getId()}]`)
|
|
588
734
|
}
|
|
589
735
|
}
|
|
590
736
|
|
|
@@ -596,4 +742,14 @@ function getObjectId (obj) {
|
|
|
596
742
|
return global.Opal.uid()
|
|
597
743
|
}
|
|
598
744
|
|
|
599
|
-
|
|
745
|
+
function safePush (onto, entries) {
|
|
746
|
+
try {
|
|
747
|
+
onto.push(...entries)
|
|
748
|
+
} catch (err) {
|
|
749
|
+
/* istanbul ignore if */
|
|
750
|
+
if (!(err instanceof RangeError)) throw err
|
|
751
|
+
for (const e of entries) onto.push(e)
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
module.exports = produceAssemblyFile
|