@antora/assembler 1.0.0-rc.6 → 1.0.0-rc.8
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 +34 -88
- package/lib/compile-conversion-attributes.js +75 -0
- package/lib/configure.js +6 -4
- package/lib/constants.js +1 -0
- package/lib/index.js +1 -1
- package/lib/load-config.js +5 -5
- package/lib/log-command.js +4 -6
- package/lib/produce-assembly-file.js +151 -142
- package/lib/produce-assembly-files.js +55 -77
- package/lib/util/collate-asciidoc-attributes.js +32 -0
- package/lib/util/deep-clone.js +18 -0
- package/lib/util/identify-mutable-attributes.js +25 -0
- package/lib/util/lazy-readable.js +12 -9
- package/lib/util/rewriter.js +34 -9
- package/lib/util/to-hash.js +7 -0
- package/package.json +3 -5
- package/lib/select-mutable-attributes.js +0 -27
|
@@ -11,20 +11,22 @@ const {
|
|
|
11
11
|
rewriteStyleAttribute,
|
|
12
12
|
} = require('./util/rewriter')
|
|
13
13
|
const sanitize = require('./util/sanitize')
|
|
14
|
+
const toHash = require('./util/to-hash')
|
|
14
15
|
const unconvertInlineAsciiDoc = require('./util/unconvert-inline-asciidoc')
|
|
15
16
|
|
|
16
17
|
const ATTR_ENTRY_RX = /^:(!?[\p{Alpha}0-9_][^:]*):(?: |$)/u
|
|
17
18
|
const BUILT_IN_NAMED_ENTITIES = { amp: '&', apos: "'", gt: '>', lt: '<', nbsp: ' ', quot: '"' }
|
|
18
19
|
const CHAR_REF_RX = /&(?:([a-z][a-z]+\d{0,2})|#(?:(\d{2,6})|x([a-z\d]{2,5})));/g
|
|
19
20
|
const DISCARD_ATTRIBUTE_NAMES = [
|
|
20
|
-
'doctype',
|
|
21
|
-
'leveloffset',
|
|
22
|
-
'preface-title',
|
|
23
21
|
'assembly-doctype',
|
|
24
22
|
'assembly-header-attributes',
|
|
25
23
|
'assembly-navtitle',
|
|
26
24
|
'assembly-slug',
|
|
27
25
|
'assembly-style',
|
|
26
|
+
'doctype',
|
|
27
|
+
'leveloffset',
|
|
28
|
+
'page-aliases',
|
|
29
|
+
'preface-title',
|
|
28
30
|
'underscore',
|
|
29
31
|
]
|
|
30
32
|
const { NAMED_ID_ATTR_RX } = require('./util/rx')
|
|
@@ -42,33 +44,37 @@ function produceAssemblyFile (
|
|
|
42
44
|
const pagesByUrl = files.reduce((map, it) => (it.src.family === 'page' ? map.set(it.pub.url, it) : map), new Map())
|
|
43
45
|
const pagesInOutline = selectPagesInOutline(outline, pagesByUrl)
|
|
44
46
|
if (outline.urlType === 'internal' && !pagesByUrl.has(outline.pathname) && !outline.items?.length) return
|
|
47
|
+
const { name: component, version } = componentVersion
|
|
45
48
|
const { rootLevel, xmlIds } = assemblyModel
|
|
49
|
+
const scratchDoc = loadAsciiDoc(
|
|
50
|
+
{
|
|
51
|
+
contents: Buffer.alloc(0),
|
|
52
|
+
src: { component, version, module: 'ROOT', family: 'page', relative: '_.adoc', path: '_.adoc' },
|
|
53
|
+
},
|
|
54
|
+
undefined,
|
|
55
|
+
asciidocConfig
|
|
56
|
+
)
|
|
46
57
|
const idSeparators = {
|
|
47
58
|
prefix:
|
|
48
59
|
'assembler-idprefix' in asciidocConfig.attributes ? (asciidocConfig.attributes['assembler-idprefix'] ?? '') : '_',
|
|
49
60
|
scope: xmlIds ? '---' : ':::',
|
|
50
61
|
coordinate: xmlIds ? '----' : ':',
|
|
51
|
-
generateIdFromTitle: generateIdFromTitle.bind(
|
|
52
|
-
loadAsciiDoc(
|
|
53
|
-
{ contents: Buffer.alloc(0), src: { family: 'page', relative: 'generate-id-from-title.adoc' } },
|
|
54
|
-
undefined,
|
|
55
|
-
asciidocConfig
|
|
56
|
-
)
|
|
57
|
-
),
|
|
62
|
+
generateIdFromTitle: generateIdFromTitle.bind(scratchDoc),
|
|
58
63
|
}
|
|
59
|
-
const { name: component, version } = componentVersion
|
|
60
64
|
asciidocConfig = prepareAsciiDocConfig(
|
|
61
65
|
contentCatalog,
|
|
62
66
|
{ component, version },
|
|
63
67
|
pagesInOutline,
|
|
64
68
|
asciidocConfig,
|
|
65
69
|
assemblyModel,
|
|
66
|
-
idSeparators
|
|
70
|
+
idSeparators,
|
|
71
|
+
scratchDoc
|
|
67
72
|
)
|
|
68
|
-
const
|
|
73
|
+
const builder = buildAsciiDocHeader(componentVersion, outline.content, assemblyModel)
|
|
74
|
+
mergeAsciiDoc(
|
|
69
75
|
loadAsciiDoc,
|
|
70
76
|
contentCatalog,
|
|
71
|
-
|
|
77
|
+
builder,
|
|
72
78
|
componentVersion,
|
|
73
79
|
outline,
|
|
74
80
|
files,
|
|
@@ -78,24 +84,23 @@ function produceAssemblyFile (
|
|
|
78
84
|
mutableAttributes,
|
|
79
85
|
assemblyModel
|
|
80
86
|
)
|
|
81
|
-
if (buffer[buffer.doctypeIdx] === ':doctype: article') buffer.splice(buffer.doctypeIdx, 1)
|
|
82
87
|
const stem =
|
|
83
|
-
(
|
|
88
|
+
(builder.slug ? sanitizeSlug(builder.slug) : generateSlug(rootLevel === 0 ? 'index' : builder.navtitle)) ||
|
|
84
89
|
'export-' + (assemblyModel.stemSeq = (assemblyModel.exportSeq ?? 0) + 1)
|
|
85
90
|
const downloadStem = [component, version, stem === 'index' ? '' : stem].filter((it) => it).join('-')
|
|
86
91
|
const file = contentCatalog.createFile({
|
|
87
92
|
asciidoc: asciidocConfig,
|
|
88
93
|
assembler: { assembled: pagesInOutline.assembled, downloadStem, rootLevel },
|
|
89
|
-
contents:
|
|
94
|
+
contents: builder.serialize(),
|
|
90
95
|
src: { component, version, componentVersion, module: 'ROOT', family: 'export', relative: stem + '.adoc' },
|
|
91
96
|
pub: false,
|
|
92
97
|
})
|
|
93
|
-
file.path = file.
|
|
98
|
+
file.path = file.out.path // use out path as path so assets can be published to same hierarchy
|
|
94
99
|
delete file.out
|
|
95
100
|
return file
|
|
96
101
|
}
|
|
97
102
|
|
|
98
|
-
function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocConfig, assemblyModel, idSeparators) {
|
|
103
|
+
function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocConfig, assemblyModel, idSeparators, doc) {
|
|
99
104
|
let attributesModified
|
|
100
105
|
const configShared = asciidocConfig.$shared
|
|
101
106
|
const sharedAttributes = asciidocConfig.attributes
|
|
@@ -116,7 +121,17 @@ function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocCon
|
|
|
116
121
|
for (const [name, val] of Object.entries(sharedAttributes)) {
|
|
117
122
|
if (!(val?.constructor === String && ~val.indexOf(':'))) continue
|
|
118
123
|
if (~val.indexOf('xref:')) {
|
|
119
|
-
const newVal = rewriteXrefs(
|
|
124
|
+
const newVal = rewriteXrefs(
|
|
125
|
+
val,
|
|
126
|
+
contentCatalog,
|
|
127
|
+
assemblyModel,
|
|
128
|
+
ctx,
|
|
129
|
+
false,
|
|
130
|
+
pagesInOutline,
|
|
131
|
+
idSeparators,
|
|
132
|
+
null,
|
|
133
|
+
doc
|
|
134
|
+
)
|
|
120
135
|
if (newVal !== val) (attributesModified ??= {})[name] = newVal
|
|
121
136
|
}
|
|
122
137
|
}
|
|
@@ -126,25 +141,44 @@ function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocCon
|
|
|
126
141
|
}
|
|
127
142
|
|
|
128
143
|
function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
|
|
144
|
+
const { name: componentName, version, displayVersion, title } = componentVersion
|
|
129
145
|
const navtitlePlain = sanitize(navtitle)
|
|
130
146
|
const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
131
|
-
const doctitle = (
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
const doctitle = (title === navtitlePlain ? '' : title + ': ') + navtitleAsciiDoc
|
|
148
|
+
const versioned = !!(version && version !== 'master')
|
|
149
|
+
return {
|
|
150
|
+
header: [
|
|
151
|
+
`= ${doctitle}`,
|
|
152
|
+
...(versioned ? [`:revnumber: ${displayVersion}`] : []),
|
|
153
|
+
`:doctype: ${(assemblyModel.doctype ??= 'book')}`,
|
|
154
|
+
':underscore: _',
|
|
155
|
+
`:page-component-name: ${componentName}`,
|
|
156
|
+
`:page-component-version:${versioned ? ' ' + version : ''}`,
|
|
157
|
+
':page-version: {page-component-version}',
|
|
158
|
+
`:page-component-display-version: ${displayVersion}`,
|
|
159
|
+
`:page-component-title: ${title}`,
|
|
160
|
+
],
|
|
161
|
+
currentComponentVersion: componentVersion,
|
|
162
|
+
navtitle,
|
|
163
|
+
setAuthor (author) {
|
|
164
|
+
this.header.splice(1, 0, author)
|
|
165
|
+
},
|
|
166
|
+
setDoctitle (doctitle_) {
|
|
167
|
+
this.header[0] = `= ${doctitle_}`
|
|
168
|
+
},
|
|
169
|
+
setDoctype (doctype) {
|
|
170
|
+
this.header[this.header.findIndex((line) => line.startsWith(':doctype: '))] = `:doctype: ${doctype}`
|
|
171
|
+
},
|
|
172
|
+
setDocid (docid) {
|
|
173
|
+
this.header.unshift(`[#${docid}]`)
|
|
174
|
+
},
|
|
175
|
+
serialize () {
|
|
176
|
+
const normalizedHeader = this.header.filter((it) => it !== ':doctype: article')
|
|
177
|
+
return this.body
|
|
178
|
+
? Buffer.from(normalizedHeader.join('\n') + '\n\n' + this.body.join('\n') + '\n')
|
|
179
|
+
: Buffer.from(normalizedHeader.join('\n') + '\n')
|
|
180
|
+
},
|
|
181
|
+
}
|
|
148
182
|
}
|
|
149
183
|
|
|
150
184
|
function selectPagesInOutline (outlineEntry, pagesByUrl, accum) {
|
|
@@ -165,7 +199,7 @@ function selectPagesInOutline (outlineEntry, pagesByUrl, accum) {
|
|
|
165
199
|
function mergeAsciiDoc (
|
|
166
200
|
loadAsciiDoc,
|
|
167
201
|
contentCatalog,
|
|
168
|
-
|
|
202
|
+
builder,
|
|
169
203
|
componentVersion,
|
|
170
204
|
outlineEntry,
|
|
171
205
|
files,
|
|
@@ -174,15 +208,14 @@ function mergeAsciiDoc (
|
|
|
174
208
|
asciidocConfig,
|
|
175
209
|
mutableAttributes,
|
|
176
210
|
assemblyModel,
|
|
177
|
-
lastComponentVersion = componentVersion,
|
|
178
211
|
level = 0,
|
|
179
212
|
supportsParts = false
|
|
180
213
|
) {
|
|
181
214
|
const { items = [], roles = [], unresolved, urlType, url, pathname, hash } = outlineEntry
|
|
182
215
|
// TODO: we could try to be smart about it and make sure the page with fragment is included at least once
|
|
183
|
-
if (roles.includes('site-only')) {
|
|
184
|
-
|
|
185
|
-
return
|
|
216
|
+
if (roles.includes('site-only') || (urlType === 'external' && !URL.canParse(url))) {
|
|
217
|
+
builder.body ??= []
|
|
218
|
+
return builder
|
|
186
219
|
}
|
|
187
220
|
const assembled = pagesInOutline.assembled
|
|
188
221
|
// FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
|
|
@@ -191,18 +224,18 @@ function mergeAsciiDoc (
|
|
|
191
224
|
let navtitlePlain = sanitize(navtitle)
|
|
192
225
|
let navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
|
|
193
226
|
const { filetype, linkReferenceStyle, pubRoot, sectionMergeStrategy, siteRoot, logger, rootLevel } = assemblyModel
|
|
194
|
-
const doctype = assemblyModel.doctype
|
|
195
|
-
const atDocumentRoot = !
|
|
227
|
+
const doctype = assemblyModel.doctype
|
|
228
|
+
const atDocumentRoot = !builder.body?.length
|
|
196
229
|
let atBookRoot = atDocumentRoot && !level && doctype === 'book' && (supportsParts = true)
|
|
197
230
|
const hasItems = items.length > 0
|
|
198
231
|
if (page && !assembled.pages.has(page)) {
|
|
199
232
|
const contents = page.src.contents
|
|
200
233
|
if (contents == null) {
|
|
201
|
-
|
|
202
|
-
return
|
|
234
|
+
builder.body ??= []
|
|
235
|
+
return builder
|
|
203
236
|
}
|
|
204
237
|
const pageAsAsciiDoc = new page.constructor(
|
|
205
|
-
Object.assign({}, page, { contents:
|
|
238
|
+
Object.assign({}, page, { contents: Buffer.from(contents.toString().trimEnd()), mediaType: page.src.mediaType })
|
|
206
239
|
)
|
|
207
240
|
const doc = loadAsciiDoc(pageAsAsciiDoc, contentCatalog, asciidocConfig)
|
|
208
241
|
doc.catalog.syntheticIds = {}
|
|
@@ -215,25 +248,25 @@ function mergeAsciiDoc (
|
|
|
215
248
|
},
|
|
216
249
|
})
|
|
217
250
|
: doc.getLogger()
|
|
218
|
-
doc.source_header_attributes ??=
|
|
251
|
+
doc.source_header_attributes ??= toHash()
|
|
219
252
|
const isRootPage = atDocumentRoot && !level
|
|
220
253
|
const { component, version, module: module_, relative, origin } = page.src
|
|
221
254
|
const doctypeOverride = doc.getAttribute('assembly-doctype')
|
|
222
255
|
if (doctypeOverride) {
|
|
223
|
-
if (doctypeOverride !== doctype)
|
|
256
|
+
if (doctypeOverride !== doctype) builder.setDoctype(doctypeOverride)
|
|
224
257
|
if (doctypeOverride !== 'book') atBookRoot = supportsParts = false
|
|
225
258
|
}
|
|
226
|
-
if (isRootPage && doc.hasAttribute('assembly-slug'))
|
|
259
|
+
if (isRootPage && doc.hasAttribute('assembly-slug')) builder.slug = doc.getAttribute('assembly-slug')
|
|
227
260
|
let hasAssemblyNavtitleAttr
|
|
228
261
|
if ((hasAssemblyNavtitleAttr = !!doc.getAttribute('assembly-navtitle'))) {
|
|
229
262
|
navtitleAsciiDoc = doc.getAttribute('assembly-navtitle')
|
|
230
263
|
navtitlePlain = sanitize((navtitle = doc.$apply_reftext_subs(navtitleAsciiDoc)))
|
|
231
|
-
if (
|
|
232
|
-
|
|
264
|
+
if (builder.body == null) {
|
|
265
|
+
builder.navtitle = navtitle
|
|
233
266
|
if (rootLevel) {
|
|
234
|
-
|
|
267
|
+
builder.setDoctitle(
|
|
235
268
|
(componentVersion.title === navtitlePlain ? '' : componentVersion.title + ': ') + navtitleAsciiDoc
|
|
236
|
-
|
|
269
|
+
)
|
|
237
270
|
}
|
|
238
271
|
}
|
|
239
272
|
}
|
|
@@ -241,7 +274,7 @@ function mergeAsciiDoc (
|
|
|
241
274
|
const docname = doc.getAttribute('docname')
|
|
242
275
|
const pageId = generateScopedId(page.src, componentVersion, idSeparators, filetype)
|
|
243
276
|
const pageIdLeader = pageId + idSeparators.scope
|
|
244
|
-
let pageFragment =
|
|
277
|
+
let pageFragment = `#${pageId}`
|
|
245
278
|
let pageRoles = []
|
|
246
279
|
let pageStyle = doc.getAttribute('assembly-style', '')
|
|
247
280
|
if (!pageStyle && atBookRoot && rootLevel === 0 && !doc.source_header_attributes['$key?']('assembly-style')) {
|
|
@@ -259,13 +292,10 @@ function mergeAsciiDoc (
|
|
|
259
292
|
let nextSectionLevel = 1
|
|
260
293
|
const lines = doc.getSourceLines()
|
|
261
294
|
const ignoreLines = []
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
buffer.endHeaderIdx = buffer.length - 1
|
|
265
|
-
}
|
|
266
|
-
buffer.push('')
|
|
295
|
+
const buffer = (builder.body ??= [])
|
|
296
|
+
if (buffer.length) buffer.push('')
|
|
267
297
|
buffer.push(`:docname: ${docname}`)
|
|
268
|
-
if (component !==
|
|
298
|
+
if (component !== builder.currentComponentVersion.name) {
|
|
269
299
|
const thisComponentVersion =
|
|
270
300
|
component === componentVersion.name && version === componentVersion.version
|
|
271
301
|
? componentVersion
|
|
@@ -276,7 +306,7 @@ function mergeAsciiDoc (
|
|
|
276
306
|
buffer.push(':page-version: {page-component-version}')
|
|
277
307
|
buffer.push(`:page-component-display-version: ${thisComponentVersion.displayVersion}`)
|
|
278
308
|
buffer.push(`:page-component-title: ${thisComponentVersion.title}`)
|
|
279
|
-
|
|
309
|
+
builder.currentComponentVersion = thisComponentVersion
|
|
280
310
|
}
|
|
281
311
|
}
|
|
282
312
|
buffer.push(`:page-module: ${module_}`)
|
|
@@ -286,13 +316,10 @@ function mergeAsciiDoc (
|
|
|
286
316
|
buffer.push(`:page-origin-refname: ${origin.branch || origin.tag}`)
|
|
287
317
|
buffer.push(`:page-origin-reftype: ${origin.branch ? 'branch' : 'tag'}`)
|
|
288
318
|
buffer.push(`:page-origin-refhash: ${origin.worktree ? '(worktree)' : origin.refhash}`)
|
|
289
|
-
let headerHasBlockAttrs
|
|
290
319
|
if (doc.hasHeader()) {
|
|
291
320
|
for (const entry of processDocumentHeader(doc, lines, ignoreLines, isRootPage)) {
|
|
292
321
|
if (entry.type === 'author_line') {
|
|
293
|
-
|
|
294
|
-
buffer.doctypeIdx += 1
|
|
295
|
-
buffer.endHeaderIdx += 1
|
|
322
|
+
builder.setAuthor(entry.lines[0])
|
|
296
323
|
} else if (entry.type === 'attribute_entry') {
|
|
297
324
|
const name = entry.name
|
|
298
325
|
if (!entry.negated && !doc.isAttributeLocked(name)) {
|
|
@@ -300,7 +327,7 @@ function mergeAsciiDoc (
|
|
|
300
327
|
if (
|
|
301
328
|
name.endsWith('-image') &&
|
|
302
329
|
(val = doc.getAttribute(name)) &&
|
|
303
|
-
(newVal = rewriteImageAttr(val, contentCatalog, assemblyModel, page.src, assembled.assets))
|
|
330
|
+
(newVal = rewriteImageAttr(val, contentCatalog, assemblyModel, page.src, assembled.assets)) != null
|
|
304
331
|
) {
|
|
305
332
|
if (newVal !== val) entry.lines = [`:${name}: ${newVal}`]
|
|
306
333
|
} else if (~(val = entry.lines.join('\n').substring(name.length + 3)).indexOf(':')) {
|
|
@@ -333,14 +360,9 @@ function mergeAsciiDoc (
|
|
|
333
360
|
if (newVal !== val) entry.lines = `:${entry.name}: ${newVal}`.split('\n')
|
|
334
361
|
}
|
|
335
362
|
}
|
|
336
|
-
|
|
337
|
-
buffer.splice(buffer.endHeaderIdx + 1, 0, ...entry.lines)
|
|
338
|
-
buffer.endHeaderIdx += entry.lines.length
|
|
339
|
-
} else {
|
|
340
|
-
buffer.push(...entry.lines)
|
|
341
|
-
}
|
|
363
|
+
;(entry.promote ? builder.header : buffer).push(...entry.lines)
|
|
342
364
|
} else {
|
|
343
|
-
if (entry.type === 'attrlist') headerHasBlockAttrs = true
|
|
365
|
+
if (entry.type === 'attrlist') builder.headerHasBlockAttrs = true
|
|
344
366
|
buffer.push(...entry.lines)
|
|
345
367
|
}
|
|
346
368
|
}
|
|
@@ -364,15 +386,15 @@ function mergeAsciiDoc (
|
|
|
364
386
|
if (htitleOverride != null) {
|
|
365
387
|
const keepSections = hasSections && sectionMergeStrategy !== 'discrete'
|
|
366
388
|
if (htitleOverride === '') {
|
|
367
|
-
if (headerHasBlockAttrs || keepSections) {
|
|
389
|
+
if (builder.headerHasBlockAttrs || keepSections) {
|
|
368
390
|
htitleAsciiDoc = '{empty}'
|
|
369
391
|
notitle = true
|
|
370
392
|
} else {
|
|
371
|
-
if (hasPrefaceTitleAttr)
|
|
393
|
+
if (hasPrefaceTitleAttr) builder.header.push(':preface-title:')
|
|
372
394
|
htitleAsciiDoc = undefined
|
|
373
395
|
}
|
|
374
|
-
} else if (hasPrefaceTitleAttr && !headerHasBlockAttrs && !keepSections) {
|
|
375
|
-
|
|
396
|
+
} else if (hasPrefaceTitleAttr && !builder.headerHasBlockAttrs && !keepSections) {
|
|
397
|
+
builder.header.push(`:preface-title: ${unconvertInlineAsciiDoc(htitleOverride)}`)
|
|
376
398
|
htitleAsciiDoc = undefined
|
|
377
399
|
} else {
|
|
378
400
|
htitleAsciiDoc = unconvertInlineAsciiDoc(htitleOverride)
|
|
@@ -383,10 +405,7 @@ function mergeAsciiDoc (
|
|
|
383
405
|
if (atDocumentRoot) {
|
|
384
406
|
// Q: should we use htitleAsciiDoc to generate ID if not {empty} instead of pageStyle
|
|
385
407
|
pageFragment = `#_${idSeparators.coordinate}${pageIdLeader}${pageStyle}`
|
|
386
|
-
|
|
387
|
-
buffer.doctypeIdx += 1
|
|
388
|
-
} else {
|
|
389
|
-
pageFragment = `#${pageId}`
|
|
408
|
+
builder.setDocid(pageId)
|
|
390
409
|
}
|
|
391
410
|
heading = { title: htitleAsciiDoc, level: part ? 1 : 2 }
|
|
392
411
|
if (notitle) heading.notitle = true
|
|
@@ -396,14 +415,12 @@ function mergeAsciiDoc (
|
|
|
396
415
|
if (atDocumentRoot && rootLevel === 0 && navtitlePlain === componentVersion.title && !hasAssemblyNavtitleAttr) {
|
|
397
416
|
level--
|
|
398
417
|
} else {
|
|
399
|
-
pageFragment = `#${pageId}`
|
|
400
418
|
if (part && level === 1) level--
|
|
401
419
|
if ((heading = { title: navtitleAsciiDoc, level: level + 1 }).level > 6) {
|
|
402
420
|
Object.assign(heading, { level: 6, style: `discrete.h${heading.level}` })
|
|
403
421
|
}
|
|
404
422
|
}
|
|
405
423
|
} else if (atDocumentRoot && rootLevel === 0 && hasAssemblyNavtitleAttr) {
|
|
406
|
-
pageFragment = `#${pageId}`
|
|
407
424
|
heading = { title: navtitleAsciiDoc, level: (nextSectionLevel = part ? 1 : 2) }
|
|
408
425
|
} else if (atBookRoot) {
|
|
409
426
|
nextSectionLevel = undefined
|
|
@@ -414,16 +431,17 @@ function mergeAsciiDoc (
|
|
|
414
431
|
buffer.push(`[${heading.style ?? pageStyle}${pageFragment}${rolesAttr}${notitleAttrs}]`)
|
|
415
432
|
buffer.push(`${'='.repeat(heading.level)} ${heading.title}`)
|
|
416
433
|
} else if (atDocumentRoot) {
|
|
417
|
-
|
|
418
|
-
buffer.doctypeIdx += 1
|
|
434
|
+
builder.setDocid(pageId)
|
|
419
435
|
}
|
|
436
|
+
if (isRootPage || (atDocumentRoot && !heading)) assembled.pages.rootPageFragment = pageFragment
|
|
437
|
+
assembled.pages.set(page, pageFragment)
|
|
420
438
|
if (sectionMergeStrategy === 'enclose' && hasItems && hasSections && !(atDocumentRoot && heading)) {
|
|
421
439
|
// TODO: make overview section title configurable
|
|
422
440
|
//let overviewTitle = doc.getDocumentTitle()
|
|
423
441
|
//if (overviewTitle === navtitle) overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
424
442
|
const overviewTitle = doc.getAttribute('overview-title', 'Overview')
|
|
425
443
|
const syntheticId = idSeparators.generateIdFromTitle(overviewTitle, idSeparators, pageIdLeader)
|
|
426
|
-
if (heading) buffer.push('')
|
|
444
|
+
if (heading && buffer.length) buffer.push('')
|
|
427
445
|
let hlevel = level + (nextSectionLevel = 2)
|
|
428
446
|
if (hlevel > 6) {
|
|
429
447
|
const blockStyle = `discrete.h${hlevel}`
|
|
@@ -434,7 +452,6 @@ function mergeAsciiDoc (
|
|
|
434
452
|
}
|
|
435
453
|
buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
|
|
436
454
|
}
|
|
437
|
-
assembled.pages.set(page, pageFragment)
|
|
438
455
|
if (hasSections) fixSectionLevels(doc.getSections(), nextSectionLevel)
|
|
439
456
|
const allBlocks = doc.findBy({ traverse_documents: true }, (it) =>
|
|
440
457
|
it.getContext() === 'document'
|
|
@@ -444,7 +461,7 @@ function mergeAsciiDoc (
|
|
|
444
461
|
if (doc.getDoctype() === 'manpage') {
|
|
445
462
|
const firstSectionIdx = hasSections ? doc.getSections()[0].getLineNumber() - 1 : lines.length
|
|
446
463
|
for (let idx = 0; idx < firstSectionIdx; idx++) {
|
|
447
|
-
if (
|
|
464
|
+
if (ignoreLines.includes(idx)) continue
|
|
448
465
|
const line = lines[idx]
|
|
449
466
|
if (line.startsWith('== ') && line.length > 3) {
|
|
450
467
|
allBlocks.unshift({
|
|
@@ -488,7 +505,7 @@ function mergeAsciiDoc (
|
|
|
488
505
|
})
|
|
489
506
|
let skipping
|
|
490
507
|
for (let idx = 0, lastIdx = lines.length - 1; idx <= lastIdx; idx++) {
|
|
491
|
-
if (
|
|
508
|
+
if (ignoreLines.includes(idx)) continue
|
|
492
509
|
let line = lines[idx]
|
|
493
510
|
if (line.startsWith('//')) {
|
|
494
511
|
if (line.charAt(2) !== '/') continue
|
|
@@ -604,10 +621,13 @@ function mergeAsciiDoc (
|
|
|
604
621
|
buffer,
|
|
605
622
|
lines.filter((it) => it !== undefined)
|
|
606
623
|
)
|
|
607
|
-
const attributeEntries =
|
|
624
|
+
const attributeEntries = doc.source_header_attributes.$entries()
|
|
608
625
|
if (attributeEntries.length) {
|
|
609
626
|
const resolvedAttributeEntries = attributeEntries.reduce(
|
|
610
627
|
(accum, [name, val]) => {
|
|
628
|
+
if (val) {
|
|
629
|
+
if (val['$nil?']()) val = null
|
|
630
|
+
}
|
|
611
631
|
// Q: couldn't we just check if attribute is locked?
|
|
612
632
|
if (name in mutableAttributes) {
|
|
613
633
|
const initialVal = mutableAttributes[name]
|
|
@@ -626,12 +646,11 @@ function mergeAsciiDoc (
|
|
|
626
646
|
if (resolvedAttributeEntries.length > 1) safePush(buffer, resolvedAttributeEntries)
|
|
627
647
|
}
|
|
628
648
|
} else if (level && !hash) {
|
|
649
|
+
const buffer = (builder.body ??= [])
|
|
629
650
|
if (atDocumentRoot && rootLevel === 0 && navtitlePlain === componentVersion.title) {
|
|
630
|
-
buffer.inBody ??= false
|
|
631
651
|
level--
|
|
632
652
|
} else {
|
|
633
|
-
buffer.
|
|
634
|
-
buffer.push('')
|
|
653
|
+
if (buffer.length) buffer.push('')
|
|
635
654
|
const syntheticId = idSeparators.generateIdFromTitle(navtitleAsciiDoc, idSeparators)
|
|
636
655
|
let sectionTitle = navtitleAsciiDoc
|
|
637
656
|
if (urlType === 'external') {
|
|
@@ -647,38 +666,39 @@ function mergeAsciiDoc (
|
|
|
647
666
|
}
|
|
648
667
|
}
|
|
649
668
|
}
|
|
669
|
+
if (supportsParts && level === 1 && roles.includes('part')) {
|
|
670
|
+
roles.splice(roles.indexOf('part', 1))
|
|
671
|
+
level--
|
|
672
|
+
}
|
|
650
673
|
const hlevel = level > 5 ? 6 : level + 1
|
|
651
674
|
const hroles = urlType === 'internal' ? roles.slice(1) : roles
|
|
652
675
|
buffer.push(`[${hlevel > 6 ? 'discrete' : ''}#${syntheticId}${hroles.reduce((str, it) => str + '.' + it, '')}]`)
|
|
653
676
|
buffer.push(`${'='.repeat(hlevel)} ${sectionTitle}`)
|
|
654
677
|
}
|
|
655
678
|
}
|
|
656
|
-
if (hasItems)
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
})
|
|
680
|
-
}
|
|
681
|
-
return buffer
|
|
679
|
+
if (!hasItems) return
|
|
680
|
+
const nextLevel = level + 1
|
|
681
|
+
// NOTE: drop first child if same as parent; should we keep if content is different?
|
|
682
|
+
;(urlType === 'internal' && items[0].urlType === 'internal' && pathname === items[0].url && !items[0].items?.length
|
|
683
|
+
? items.slice(1)
|
|
684
|
+
: items
|
|
685
|
+
).forEach((item) => {
|
|
686
|
+
mergeAsciiDoc(
|
|
687
|
+
loadAsciiDoc,
|
|
688
|
+
contentCatalog,
|
|
689
|
+
builder,
|
|
690
|
+
componentVersion,
|
|
691
|
+
item,
|
|
692
|
+
files,
|
|
693
|
+
pagesInOutline,
|
|
694
|
+
idSeparators,
|
|
695
|
+
asciidocConfig,
|
|
696
|
+
mutableAttributes,
|
|
697
|
+
assemblyModel,
|
|
698
|
+
nextLevel,
|
|
699
|
+
atBookRoot
|
|
700
|
+
)
|
|
701
|
+
})
|
|
682
702
|
}
|
|
683
703
|
|
|
684
704
|
function processDocumentHeader (doc, lines, ignoreLines, isRootPage) {
|
|
@@ -717,7 +737,7 @@ function processDocumentHeader (doc, lines, ignoreLines, isRootPage) {
|
|
|
717
737
|
}
|
|
718
738
|
const line = lines[idx]
|
|
719
739
|
if (open === ':' || open === '-:') {
|
|
720
|
-
if (open === ':') current.
|
|
740
|
+
if (open === ':') current.appendLine(line)
|
|
721
741
|
if (!line?.endsWith(' \\')) current = open = undefined
|
|
722
742
|
} else if (line) {
|
|
723
743
|
let attributeEntryMatch
|
|
@@ -725,13 +745,14 @@ function processDocumentHeader (doc, lines, ignoreLines, isRootPage) {
|
|
|
725
745
|
if (chr0 === '/' && line.charAt(1) === '/') {
|
|
726
746
|
if (line.startsWith('////') && line === '/'.repeat(line.length)) {
|
|
727
747
|
if (open) {
|
|
728
|
-
current.
|
|
748
|
+
current.appendLine(line)
|
|
729
749
|
if (open === line) current = open = undefined
|
|
730
750
|
} else {
|
|
731
751
|
entries.push((current = { type: 'block_comment', lines: [(open = line)] }))
|
|
752
|
+
current.appendLine = Array.prototype.push.bind(current.lines)
|
|
732
753
|
}
|
|
733
754
|
} else if (open) {
|
|
734
|
-
current.
|
|
755
|
+
current.appendLine(line)
|
|
735
756
|
} else if (belowDoctitle && line.charAt(2) === '/') {
|
|
736
757
|
break
|
|
737
758
|
} else {
|
|
@@ -739,7 +760,7 @@ function processDocumentHeader (doc, lines, ignoreLines, isRootPage) {
|
|
|
739
760
|
current = undefined
|
|
740
761
|
}
|
|
741
762
|
} else if (open) {
|
|
742
|
-
current.
|
|
763
|
+
current.appendLine(line)
|
|
743
764
|
} else if (chr0 === ':' && ~line.indexOf(':', 2) && (attributeEntryMatch = line.match(ATTR_ENTRY_RX))) {
|
|
744
765
|
let name = attributeEntryMatch[1]
|
|
745
766
|
const negated = name.charAt() === '!' || name.charAt(name.length - 1) === '!'
|
|
@@ -748,7 +769,9 @@ function processDocumentHeader (doc, lines, ignoreLines, isRootPage) {
|
|
|
748
769
|
if (line.endsWith(' \\')) open = '-:' // disallow value continuation
|
|
749
770
|
} else {
|
|
750
771
|
if (line.endsWith(' \\')) open = ':'
|
|
751
|
-
entries.push((current = { type: 'attribute_entry', name, negated, lines: [
|
|
772
|
+
entries.push((current = { type: 'attribute_entry', name, negated, lines: [], lineno: idx + 1 }))
|
|
773
|
+
current.lines.push(negated ? `:!${name}:` : line)
|
|
774
|
+
current.appendLine = Array.prototype.push.bind(negated ? [] : current.lines)
|
|
752
775
|
if (assemblyHeaderAttributes.has(name)) current.promote = true
|
|
753
776
|
}
|
|
754
777
|
} else if (belowDoctitle) {
|
|
@@ -799,36 +822,22 @@ function fixSectionLevels (sections, expectedLevel) {
|
|
|
799
822
|
})
|
|
800
823
|
}
|
|
801
824
|
|
|
802
|
-
// NOTE: blank lines at top and bottom of document create mismatch when using line numbers to navigate source lines
|
|
803
|
-
// IMPORTANT: this must not leave behind lines the parser will drop!
|
|
804
|
-
// IDEA: another option is to capture initial lineno of reader and use as offset (but preseves those blank lines)
|
|
805
|
-
function trimAsciiDoc (buffer) {
|
|
806
|
-
return Buffer.from(
|
|
807
|
-
buffer
|
|
808
|
-
.toString()
|
|
809
|
-
.replace(/^(?:[ \t]*\r\n?|[ \t]*\n)+/, '')
|
|
810
|
-
.trimRight()
|
|
811
|
-
)
|
|
812
|
-
}
|
|
813
|
-
|
|
814
825
|
function safePush (onto, entries) {
|
|
815
826
|
try {
|
|
816
827
|
onto.push(...entries)
|
|
817
|
-
} catch
|
|
818
|
-
/* istanbul ignore if */
|
|
819
|
-
if (!(err instanceof RangeError)) throw err
|
|
828
|
+
} catch {
|
|
820
829
|
for (const e of entries) onto.push(e)
|
|
821
830
|
}
|
|
822
831
|
}
|
|
823
832
|
|
|
824
833
|
function generateIdFromTitle (titleAsciiDoc, idSeparators, idLeader) {
|
|
825
|
-
const Section = this.$class()
|
|
834
|
+
const Section = this.$class().$$base_module.Section
|
|
826
835
|
const baseId = Section.$generate_id(titleAsciiDoc, this) // refs in catalog will always be empty
|
|
827
836
|
let id = `_${idSeparators.coordinate}${idLeader ?? ''}${baseId}`
|
|
828
837
|
const syntheticIds = (this.catalog.syntheticIds ??= {})
|
|
829
838
|
if (id in syntheticIds) {
|
|
830
839
|
const sep = (this.getAttribute('idseparator') ?? '_').charAt()
|
|
831
|
-
let cnt = this.$class()
|
|
840
|
+
let cnt = this.$class().$$base_module.Compliance.unique_id_start_index
|
|
832
841
|
let candidate
|
|
833
842
|
while ((candidate = `${id}${sep}${cnt}`) in syntheticIds) cnt++
|
|
834
843
|
id = candidate
|