@antora/assembler 1.0.0-alpha.8 → 1.0.0-beta.1
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 +213 -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} +322 -166
- 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/lib/util/sanitize.js +3 -5
- package/lib/util/unconvert-inline-asciidoc.js +95 -0
- package/package.json +18 -13
- package/lib/asciidoctor/reducer-extension.js +0 -235
- package/lib/produce-aggregate-documents.js +0 -142
- package/lib/util/run-command.js +0 -60
|
@@ -1,70 +1,72 @@
|
|
|
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
|
+
const unconvertInlineAsciiDoc = require('./util/unconvert-inline-asciidoc')
|
|
6
7
|
|
|
7
|
-
|
|
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 (
|
|
8
15
|
loadAsciiDoc,
|
|
9
16
|
contentCatalog,
|
|
10
17
|
componentVersion,
|
|
11
18
|
outline,
|
|
12
|
-
|
|
13
|
-
pages,
|
|
19
|
+
files,
|
|
14
20
|
asciidocConfig,
|
|
15
21
|
mutableAttributes,
|
|
16
|
-
|
|
22
|
+
assemblyModel
|
|
17
23
|
) {
|
|
18
|
-
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)
|
|
19
27
|
const navtitle = outline.content
|
|
20
|
-
const
|
|
21
|
-
src: {
|
|
22
|
-
component: componentVersion.name,
|
|
23
|
-
version: componentVersion.version,
|
|
24
|
-
module: 'ROOT',
|
|
25
|
-
family: 'page',
|
|
26
|
-
relative: generateSlug(navtitle),
|
|
27
|
-
},
|
|
28
|
-
})
|
|
29
|
-
const path_ = `${templateFile.out.path}.adoc`
|
|
30
|
-
contentCatalog.removeFile(templateFile)
|
|
31
|
-
const header = buildAsciiDocHeader(componentVersion, navtitle, doctype)
|
|
32
|
-
const body = aggregateAsciiDoc(
|
|
28
|
+
const buffer = mergeAsciiDoc(
|
|
33
29
|
loadAsciiDoc,
|
|
34
30
|
contentCatalog,
|
|
35
|
-
|
|
31
|
+
buildAsciiDocHeader(componentVersion, navtitle, assemblyModel),
|
|
36
32
|
componentVersion,
|
|
37
33
|
outline,
|
|
34
|
+
files,
|
|
38
35
|
pagesInOutline,
|
|
39
36
|
asciidocConfig,
|
|
40
37
|
mutableAttributes,
|
|
41
|
-
|
|
38
|
+
assemblyModel
|
|
42
39
|
)
|
|
43
|
-
|
|
44
|
-
|
|
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, {
|
|
45
46
|
asciidoc: asciidocConfig,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
path: path_,
|
|
47
|
+
assembler: { assembled: pagesInOutline.assembled, downloadStem, rootLevel },
|
|
48
|
+
contents: Buffer.from(buffer.join('\n') + '\n'),
|
|
49
49
|
src: {
|
|
50
50
|
component: componentVersion.name,
|
|
51
51
|
version: componentVersion.version,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
module: 'ROOT',
|
|
53
|
+
family: 'export',
|
|
54
|
+
relative: stem + '.adoc',
|
|
55
55
|
},
|
|
56
56
|
})
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function buildAsciiDocHeader (componentVersion, navtitle,
|
|
60
|
-
const
|
|
59
|
+
function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
|
|
60
|
+
const doctype = assemblyModel.doctype ?? 'book'
|
|
61
|
+
const navtitlePlain = sanitize(navtitle)
|
|
62
|
+
const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
61
63
|
let doctitle = navtitleAsciiDoc
|
|
62
64
|
if (navtitlePlain !== componentVersion.title) doctitle = `${componentVersion.title}: ${doctitle}`
|
|
63
65
|
const version = componentVersion.version === 'master' ? '' : componentVersion.version
|
|
64
66
|
return [
|
|
65
67
|
`= ${doctitle}`,
|
|
66
68
|
...(version ? [`:revnumber: ${version}`] : []),
|
|
67
|
-
...(doctype === 'article' ? [] : [`:doctype: ${doctype}`]),
|
|
69
|
+
...(doctype === 'article' ? [] : [`:doctype: ${doctype ?? 'book'}`]),
|
|
68
70
|
':underscore: _',
|
|
69
71
|
// Q: should we pass these via the CLI so they cannot be modified?
|
|
70
72
|
`:page-component-name: ${componentVersion.name}`,
|
|
@@ -75,44 +77,52 @@ function buildAsciiDocHeader (componentVersion, navtitle, doctype = 'book') {
|
|
|
75
77
|
]
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
function selectPagesInOutline (outlineEntry,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
page
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
function
|
|
93
|
+
function mergeAsciiDoc (
|
|
92
94
|
loadAsciiDoc,
|
|
93
95
|
contentCatalog,
|
|
94
|
-
|
|
96
|
+
buffer,
|
|
95
97
|
componentVersion,
|
|
96
98
|
outlineEntry,
|
|
99
|
+
files,
|
|
97
100
|
pagesInOutline,
|
|
98
101
|
asciidocConfig,
|
|
99
102
|
mutableAttributes,
|
|
100
|
-
|
|
103
|
+
assemblyModel,
|
|
101
104
|
lastComponentVersion = componentVersion,
|
|
102
|
-
level = 0
|
|
105
|
+
level = 0,
|
|
106
|
+
supportsParts = false
|
|
103
107
|
) {
|
|
104
|
-
const buffer = []
|
|
105
108
|
// TODO: we could try to be smart about it and make sure the page with fragment is included at least once
|
|
106
109
|
if (outlineEntry.hash) return buffer
|
|
107
|
-
const { content: navtitle, items, unresolved, urlType, url } = outlineEntry
|
|
108
|
-
const
|
|
110
|
+
const { content: navtitle, items = [], unresolved, urlType, url } = outlineEntry
|
|
111
|
+
const atDocumentRoot = !buffer.inBody
|
|
112
|
+
const atBookRoot = atDocumentRoot && !level && assemblyModel.doctype === 'book' && (supportsParts = true)
|
|
113
|
+
const hasItems = items.length > 0
|
|
114
|
+
const navtitlePlain = sanitize(navtitle)
|
|
115
|
+
const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
109
116
|
const siteUrl = ((val) => {
|
|
110
117
|
if (!val || val === '/') return ''
|
|
111
118
|
return val.charAt(val.length - 1) === '/' ? val.slice(0, val.length - 1) : val
|
|
112
|
-
})(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
|
|
113
123
|
// FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
|
|
114
124
|
let page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
|
|
115
|
-
if (page && pagesInOutline.
|
|
125
|
+
if (page && pagesInOutline.assembled.pages.has(page)) page = undefined
|
|
116
126
|
if (page) {
|
|
117
127
|
let contents = page.src.contents
|
|
118
128
|
if (contents == null) return buffer
|
|
@@ -125,20 +135,51 @@ function aggregateAsciiDoc (
|
|
|
125
135
|
.replace(/^(?:[ \t]*\r\n?|[ \t]*\n)+/, '')
|
|
126
136
|
.trimRight()
|
|
127
137
|
)
|
|
128
|
-
;(pagesInOutline.aggregated ??= []).push(page)
|
|
129
|
-
page = new page.constructor(Object.assign({}, page, { contents, mediaType: 'text/asciidoc' }))
|
|
130
138
|
const { component, version, module: module_, relative, origin } = page.src
|
|
131
|
-
const
|
|
132
|
-
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
|
+
}
|
|
133
154
|
// NOTE: in Antora, docname is relative src path from module without file extension
|
|
134
155
|
const docname = doc.getAttribute('docname')
|
|
135
|
-
const docnameForId = docname.replace(/[
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
|
142
183
|
buffer.push('')
|
|
143
184
|
buffer.push(`:docname: ${docname}`)
|
|
144
185
|
if (component !== lastComponentVersion.name) {
|
|
@@ -163,25 +204,50 @@ function aggregateAsciiDoc (
|
|
|
163
204
|
buffer.push(`:page-origin-refname: ${origin.branch || origin.tag}`)
|
|
164
205
|
buffer.push(`:page-origin-reftype: ${origin.branch ? 'branch' : 'tag'}`)
|
|
165
206
|
buffer.push(`:page-origin-refhash: ${origin.worktree ? '(worktree)' : origin.refhash}`)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (level) {
|
|
169
|
-
|
|
170
|
-
|
|
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' })
|
|
171
222
|
} else {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
buffer.push(`[discrete#${idprefix}]`)
|
|
223
|
+
if (atDocumentRoot) {
|
|
224
|
+
pageFragment = `#${idPrefix}${doc.getId() ?? pageStyle}`
|
|
225
|
+
buffer.unshift(`[#${idScope}]`)
|
|
176
226
|
} else {
|
|
177
|
-
|
|
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}` })
|
|
178
240
|
}
|
|
179
|
-
buffer.push(`${'='.repeat(hlevel)} ${navtitleAsciiDoc}`)
|
|
180
241
|
}
|
|
181
|
-
} else {
|
|
182
|
-
header.unshift(`[#${idprefix}]`)
|
|
183
242
|
}
|
|
184
|
-
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()) {
|
|
185
251
|
enclosed = true
|
|
186
252
|
// TODO: make overview section title configurable
|
|
187
253
|
//let overviewTitle = doc.getDocumentTitle()
|
|
@@ -200,23 +266,43 @@ function aggregateAsciiDoc (
|
|
|
200
266
|
}
|
|
201
267
|
let hlevel = level + 2
|
|
202
268
|
if (hlevel > 6) {
|
|
269
|
+
const blockStyle = `discrete.h${hlevel}`
|
|
203
270
|
hlevel = 6
|
|
204
|
-
buffer.push(syntheticId ? `[
|
|
271
|
+
buffer.push(syntheticId ? `[${blockStyle}#${syntheticId}]` : `[${blockStyle}]`)
|
|
205
272
|
} else if (syntheticId) {
|
|
206
273
|
buffer.push(`[#${syntheticId}]`)
|
|
207
274
|
}
|
|
208
275
|
buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
|
|
209
276
|
if (toggleSectids) buffer.push(':sectids:')
|
|
210
277
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
278
|
+
pagesInOutline.assembled.pages.set(page, pageFragment)
|
|
279
|
+
if (doc.hasSections()) {
|
|
280
|
+
fixSectionLevels(doc.getSections(), atBookRoot && !pageStyle ? undefined : nextSectionLevel)
|
|
281
|
+
}
|
|
215
282
|
const allBlocks = doc.findBy({ traverse_documents: true }, (it) =>
|
|
216
283
|
it.getContext() === 'document'
|
|
217
284
|
? it.getDocument().isNested()
|
|
218
285
|
: !(it.getContext() === 'table_cell' && it.getStyle() === 'asciidoc')
|
|
219
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
|
|
220
306
|
allBlocks.forEach((block) => {
|
|
221
307
|
const contentModel = block.content_model
|
|
222
308
|
if (
|
|
@@ -244,7 +330,7 @@ function aggregateAsciiDoc (
|
|
|
244
330
|
}
|
|
245
331
|
})
|
|
246
332
|
let skipping
|
|
247
|
-
for (let idx = 0,
|
|
333
|
+
for (let idx = 0, lastIdx = lines.length - 1; idx <= lastIdx; idx++) {
|
|
248
334
|
if (~ignoreLines.indexOf(idx)) continue
|
|
249
335
|
let line = lines[idx]
|
|
250
336
|
if (line.startsWith('//')) {
|
|
@@ -260,7 +346,11 @@ function aggregateAsciiDoc (
|
|
|
260
346
|
} else if (skipping) {
|
|
261
347
|
continue
|
|
262
348
|
}
|
|
263
|
-
if (
|
|
349
|
+
if (
|
|
350
|
+
line.charAt() === ':' &&
|
|
351
|
+
~line.indexOf(':', 2) &&
|
|
352
|
+
(line.match(AttributeEntryRx) || ['', ''])[1].replace('!', '') === 'leveloffset'
|
|
353
|
+
) {
|
|
264
354
|
if (lines[idx - 1] === '') lines[idx - 1] = undefined
|
|
265
355
|
lines[idx] = undefined
|
|
266
356
|
continue
|
|
@@ -271,16 +361,16 @@ function aggregateAsciiDoc (
|
|
|
271
361
|
if (!refs['$key?'](refid) && (~refid.indexOf(' ') || refid.toLowerCase() !== refid)) {
|
|
272
362
|
if ((refid = doc.$resolve_id(refid))['$nil?']()) return m
|
|
273
363
|
}
|
|
274
|
-
return `<<${
|
|
364
|
+
return `<<${idPrefix}${refid}${text ? ',' + text : ''}>>`
|
|
275
365
|
})
|
|
276
366
|
}
|
|
277
367
|
// NOTE: the next check takes care of inline and block anchors
|
|
278
368
|
if (~line.indexOf('[[')) {
|
|
279
|
-
line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, `[[${
|
|
369
|
+
line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, `[[${idPrefix}$1$2]]`)
|
|
280
370
|
}
|
|
281
371
|
if (~line.indexOf('xref:')) {
|
|
282
372
|
// Q: should we allow : as first character of target?
|
|
283
|
-
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) => {
|
|
284
374
|
let pagePart, fragment, targetPage
|
|
285
375
|
const hashIdx = target.indexOf('#')
|
|
286
376
|
if (~hashIdx) {
|
|
@@ -297,8 +387,8 @@ function aggregateAsciiDoc (
|
|
|
297
387
|
if (!pagePart) {
|
|
298
388
|
// Q: should we validate the internal ID here?
|
|
299
389
|
return text && ~text.indexOf('=')
|
|
300
|
-
? `xref:${
|
|
301
|
-
: `<<${
|
|
390
|
+
? `xref:${idPrefix}${fragment}[${text}]`
|
|
391
|
+
: `<<${idPrefix}${fragment}${text ? ',' + text.replace(/\\]/g, ']') : ''}>>`
|
|
302
392
|
}
|
|
303
393
|
if (~pagePart.indexOf('@') || /:.*:/.test(pagePart)) {
|
|
304
394
|
if (siteUrl && (targetPage = contentCatalog.resolvePage(pagePart, page.src)) && targetPage.out) {
|
|
@@ -307,24 +397,30 @@ function aggregateAsciiDoc (
|
|
|
307
397
|
}
|
|
308
398
|
// TODO: handle unresolved page better
|
|
309
399
|
return m
|
|
310
|
-
} else if (pagePart.indexOf(':') < 0) {
|
|
311
|
-
if (module_ !== 'ROOT') pagePart = `${module_}:${pagePart}`
|
|
312
|
-
} else if (pagePart.startsWith('ROOT:')) {
|
|
313
|
-
pagePart = pagePart.slice(5)
|
|
314
400
|
}
|
|
315
|
-
|
|
316
|
-
|
|
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) {
|
|
317
413
|
text ||= targetPage.asciidoc?.xreftext || target
|
|
318
414
|
return `${siteUrl}${targetPage.pub.url}${fragment && '#' + fragment}[${text}]`
|
|
319
415
|
}
|
|
320
416
|
// TODO: handle unresolved page better
|
|
321
417
|
return m
|
|
322
418
|
}
|
|
323
|
-
pagePart = pagePart
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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 : '')
|
|
328
424
|
return `<<${refid}${text && text !== targetPage.title ? ',' + text.replace(/\\]/g, ']') : ''}>>`
|
|
329
425
|
})
|
|
330
426
|
}
|
|
@@ -340,9 +436,7 @@ function aggregateAsciiDoc (
|
|
|
340
436
|
relative,
|
|
341
437
|
})
|
|
342
438
|
// TODO: handle unresolved attachment page
|
|
343
|
-
return attachment
|
|
344
|
-
? `${siteUrl}${attachment.pub.url.replace(/_/g, '{underscore}')}[${text}]`
|
|
345
|
-
: m
|
|
439
|
+
return attachment?.out ? `${siteUrl}${attachment.pub.url.replace(/_/g, '{underscore}')}[${text}]` : m
|
|
346
440
|
})
|
|
347
441
|
}
|
|
348
442
|
if (~line.indexOf('image:') && !line.startsWith('image::')) {
|
|
@@ -350,8 +444,8 @@ function aggregateAsciiDoc (
|
|
|
350
444
|
if (isResourceSpec(target)) {
|
|
351
445
|
const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
|
|
352
446
|
// TODO: handle (or report) unresolved image better
|
|
353
|
-
if (image
|
|
354
|
-
|
|
447
|
+
if (image?.out) {
|
|
448
|
+
pagesInOutline.assembled.assets.add(image)
|
|
355
449
|
return `image:${image.out.path.replace(/_/g, '{underscore}')}[${attrlist}]`
|
|
356
450
|
}
|
|
357
451
|
}
|
|
@@ -371,21 +465,18 @@ function aggregateAsciiDoc (
|
|
|
371
465
|
const context = block.getContext()
|
|
372
466
|
let idx = lineno - 1
|
|
373
467
|
if (context === 'section' && !block.getDocument().isNested()) {
|
|
374
|
-
if (block.getSectionName() === 'header')
|
|
375
|
-
|
|
376
|
-
return
|
|
377
|
-
}
|
|
378
|
-
let blockStyle = sectionMergeStrategy === 'discrete' ? 'discrete' : undefined
|
|
468
|
+
if (block.getSectionName() === 'header') return
|
|
469
|
+
let blockStyle = (assemblyModel.sectionMergeStrategy || 'discrete') === 'discrete' ? 'discrete' : undefined
|
|
379
470
|
lines[idx] = lines[idx].replace(/^=+ (.+)/, (_, rest) => {
|
|
380
471
|
let targetMarkerLength = block.level + 1 + level + (enclosed ? 1 : 0)
|
|
381
472
|
if (targetMarkerLength > 6) {
|
|
473
|
+
blockStyle = `discrete.h${targetMarkerLength}`
|
|
382
474
|
targetMarkerLength = 6
|
|
383
|
-
blockStyle = 'discrete'
|
|
384
475
|
}
|
|
385
476
|
return '='.repeat(targetMarkerLength) + ' ' + rest
|
|
386
477
|
})
|
|
387
478
|
// NOTE: ID will be undefined if sectids are turned off
|
|
388
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
479
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, idPrefix, blockStyle)
|
|
389
480
|
} else {
|
|
390
481
|
if (context === 'image') {
|
|
391
482
|
let line = lines[idx] || ''
|
|
@@ -409,9 +500,9 @@ function aggregateAsciiDoc (
|
|
|
409
500
|
if (isResourceSpec(target)) {
|
|
410
501
|
const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
|
|
411
502
|
// FIXME: handle (or report) case when image is not resolved
|
|
412
|
-
if (image
|
|
503
|
+
if (image?.out) {
|
|
413
504
|
const boxedAttrlist = line.slice(line.indexOf('['))
|
|
414
|
-
|
|
505
|
+
pagesInOutline.assembled.assets.add(image)
|
|
415
506
|
lines[idx] = `${prefix}image::${image.out.path}${boxedAttrlist}`
|
|
416
507
|
}
|
|
417
508
|
}
|
|
@@ -421,11 +512,14 @@ function aggregateAsciiDoc (
|
|
|
421
512
|
// nested document
|
|
422
513
|
idx = (block.getHeader().getLineNumber() || idx + 1) - 1
|
|
423
514
|
}
|
|
424
|
-
if (block.getId()) rewriteStyleAttribute(block, lines, idx,
|
|
515
|
+
if (block.getId()) rewriteStyleAttribute(block, lines, idx, idPrefix)
|
|
425
516
|
}
|
|
426
517
|
})
|
|
427
|
-
|
|
428
|
-
|
|
518
|
+
safePush(
|
|
519
|
+
buffer,
|
|
520
|
+
lines.filter((it) => it !== undefined)
|
|
521
|
+
)
|
|
522
|
+
const attributeEntries = Object.entries(doc.source_header_attributes?.$$smap || {})
|
|
429
523
|
if (attributeEntries.length) {
|
|
430
524
|
const resolvedAttributeEntries = attributeEntries.reduce(
|
|
431
525
|
(accum, [name, val]) => {
|
|
@@ -437,27 +531,20 @@ function aggregateAsciiDoc (
|
|
|
437
531
|
} else if (val !== initialVal) {
|
|
438
532
|
accum.push(`:${name}:${initialVal ? ' ' + initialVal : ''}`)
|
|
439
533
|
}
|
|
440
|
-
} else if (
|
|
441
|
-
!(
|
|
442
|
-
val == null ||
|
|
443
|
-
doc.isAttributeLocked(name) ||
|
|
444
|
-
name === 'doctype' ||
|
|
445
|
-
name === 'leveloffset' ||
|
|
446
|
-
name === 'underscore'
|
|
447
|
-
)
|
|
448
|
-
) {
|
|
534
|
+
} else if (!(val == null || doc.isAttributeLocked(name) || DiscardAttributes.includes(name))) {
|
|
449
535
|
accum.push(`:!${name}:`)
|
|
450
536
|
}
|
|
451
537
|
return accum
|
|
452
538
|
},
|
|
453
539
|
['']
|
|
454
540
|
)
|
|
455
|
-
if (resolvedAttributeEntries.length > 1) buffer
|
|
541
|
+
if (resolvedAttributeEntries.length > 1) safePush(buffer, resolvedAttributeEntries)
|
|
456
542
|
}
|
|
457
543
|
} else if (level) {
|
|
458
|
-
if (
|
|
544
|
+
if (atDocumentRoot && navtitlePlain === componentVersion.title) {
|
|
459
545
|
level--
|
|
460
546
|
} else {
|
|
547
|
+
buffer.inBody = true
|
|
461
548
|
buffer.push('')
|
|
462
549
|
// NOTE: try to toggle sectids; otherwise, fallback to globally unique synthetic ID
|
|
463
550
|
// Q: should we unset docname, page-module, etc?
|
|
@@ -475,13 +562,10 @@ function aggregateAsciiDoc (
|
|
|
475
562
|
}
|
|
476
563
|
let sectionTitle = navtitleAsciiDoc
|
|
477
564
|
if (urlType === 'external') {
|
|
478
|
-
sectionTitle = `${url}[${
|
|
565
|
+
sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
479
566
|
} else if (urlType === 'internal' && !unresolved && siteUrl) {
|
|
480
|
-
const resource =
|
|
481
|
-
if (resource
|
|
482
|
-
sectionTitle = navtitlePass ? navtitleAsciiDoc : navtitleAsciiDoc.replace(/\]/g, '\\]')
|
|
483
|
-
sectionTitle = `${siteUrl}${resource.pub.url}[${sectionTitle}]`
|
|
484
|
-
}
|
|
567
|
+
const resource = files.find((it) => it.pub.url === url)
|
|
568
|
+
if (resource) sectionTitle = `${siteUrl}${resource.pub.url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
|
|
485
569
|
}
|
|
486
570
|
let hlevel = level + 1
|
|
487
571
|
if (hlevel > 6) {
|
|
@@ -495,50 +579,112 @@ function aggregateAsciiDoc (
|
|
|
495
579
|
}
|
|
496
580
|
}
|
|
497
581
|
|
|
498
|
-
|
|
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
|