@antora/assembler 1.0.0-alpha.9 → 1.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,599 +0,0 @@
1
- 'use strict'
2
-
3
- const File = require('vinyl')
4
- const path = require('node:path/posix')
5
- const sanitize = require('./util/sanitize')
6
- const unconvertInlineAsciiDoc = require('./util/unconvert-inline-asciidoc')
7
-
8
- function produceAggregateDocument (
9
- loadAsciiDoc,
10
- contentCatalog,
11
- componentVersion,
12
- outline,
13
- doctype,
14
- pages,
15
- asciidocConfig,
16
- mutableAttributes,
17
- sectionMergeStrategy = 'discrete'
18
- ) {
19
- const pagesInOutline = selectPagesInOutline(outline, pages)
20
- const navtitle = outline.content
21
- const templateFile = contentCatalog.addFile({
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(
35
- loadAsciiDoc,
36
- contentCatalog,
37
- header,
38
- componentVersion,
39
- outline,
40
- pagesInOutline,
41
- asciidocConfig,
42
- mutableAttributes,
43
- sectionMergeStrategy
44
- )
45
- return new File({
46
- aggregate: true,
47
- asciidoc: asciidocConfig,
48
- contents: Buffer.from([...header, ...body].join('\n') + '\n'),
49
- mediaType: 'text/asciidoc',
50
- path: path_,
51
- src: {
52
- component: componentVersion.name,
53
- version: componentVersion.version,
54
- basename: path.basename(path_),
55
- stem: path.basename(path_, '.adoc'),
56
- extname: '.adoc',
57
- },
58
- })
59
- }
60
-
61
- function buildAsciiDocHeader (componentVersion, navtitle, doctype = 'book') {
62
- const navtitlePlain = sanitize(navtitle)
63
- const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
64
- let doctitle = navtitleAsciiDoc
65
- if (navtitlePlain !== componentVersion.title) doctitle = `${componentVersion.title}: ${doctitle}`
66
- const version = componentVersion.version === 'master' ? '' : componentVersion.version
67
- return [
68
- `= ${doctitle}`,
69
- ...(version ? [`:revnumber: ${version}`] : []),
70
- ...(doctype === 'article' ? [] : [`:doctype: ${doctype}`]),
71
- ':underscore: _',
72
- // Q: should we pass these via the CLI so they cannot be modified?
73
- `:page-component-name: ${componentVersion.name}`,
74
- `:page-component-version:${version ? ' ' + version : ''}`,
75
- ':page-version: {page-component-version}',
76
- `:page-component-display-version: ${componentVersion.displayVersion}`,
77
- `:page-component-title: ${componentVersion.title}`,
78
- ]
79
- }
80
-
81
- function selectPagesInOutline (outlineEntry, pages) {
82
- const page = outlineEntry.urlType === 'internal' ? pages.find((it) => it.pub.url === outlineEntry.url) : undefined
83
- return (outlineEntry.items || []).reduce(
84
- (accum, item) => new Map([...accum, ...selectPagesInOutline(item, pages)]),
85
- new Map(
86
- page && [
87
- [`${page.src.module === 'ROOT' ? '' : page.src.module + ':'}${page.src.relative}`, page],
88
- [page.pub.url, page],
89
- ]
90
- )
91
- )
92
- }
93
-
94
- function aggregateAsciiDoc (
95
- loadAsciiDoc,
96
- contentCatalog,
97
- header,
98
- componentVersion,
99
- outlineEntry,
100
- pagesInOutline,
101
- asciidocConfig,
102
- mutableAttributes,
103
- sectionMergeStrategy,
104
- lastComponentVersion = componentVersion,
105
- level = 0
106
- ) {
107
- const buffer = []
108
- // TODO: we could try to be smart about it and make sure the page with fragment is included at least once
109
- if (outlineEntry.hash) return buffer
110
- const { content: navtitle, items = [], unresolved, urlType, url } = outlineEntry
111
- const hasItems = items.length > 0
112
- const navtitlePlain = sanitize(navtitle)
113
- const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
114
- const siteUrl = ((val) => {
115
- if (!val || val === '/') return ''
116
- return val.charAt(val.length - 1) === '/' ? val.slice(0, val.length - 1) : val
117
- })(asciidocConfig.attributes['site-url'])
118
- // FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
119
- let page = urlType === 'internal' && !unresolved ? pagesInOutline.get(url) : undefined
120
- if (page && pagesInOutline.aggregated?.includes(page)) page = undefined
121
- if (page) {
122
- let contents = page.src.contents
123
- if (contents == null) return buffer
124
- // NOTE: blank lines at top and bottom of document create mismatch when using line numbers to navigate source lines
125
- // IMPORTANT: this must not leave behind lines the parser will drop!
126
- // IDEA: another option is to capture initial lineno of reader and use as offset (but preseves those blank lines)
127
- contents = Buffer.from(
128
- contents
129
- .toString()
130
- .replace(/^(?:[ \t]*\r\n?|[ \t]*\n)+/, '')
131
- .trimRight()
132
- )
133
- ;(pagesInOutline.aggregated ??= []).push(page)
134
- page = new page.constructor(Object.assign({}, page, { contents, mediaType: 'text/asciidoc' }))
135
- const { component, version, module: module_, relative, origin } = page.src
136
- const doc = loadAsciiDoc(page, contentCatalog, asciidocConfig)
137
- const refs = doc.getCatalog().refs
138
- // NOTE: in Antora, docname is relative src path from module without file extension
139
- const docname = doc.getAttribute('docname')
140
- const docnameForId = docname.replace(/[/]/g, '::').replace(/[.]/g, '-')
141
- const scopeId = component !== componentVersion.name
142
- const idprefix =
143
- (scopeId ? component + ':' : '') +
144
- (module_ === 'ROOT' ? (scopeId ? ':' : '') : module_ + ':') +
145
- docnameForId +
146
- ':::'
147
- buffer.push('')
148
- buffer.push(`:docname: ${docname}`)
149
- if (component !== lastComponentVersion.name) {
150
- const thisComponentVersion =
151
- component === componentVersion.name && version === componentVersion.version
152
- ? componentVersion
153
- : contentCatalog.getComponentVersion(component, version)
154
- if (thisComponentVersion) {
155
- buffer.push(`:page-component-name: ${thisComponentVersion.name}`)
156
- buffer.push(`:page-component-version:${thisComponentVersion.version ? ' ' + thisComponentVersion.version : ''}`)
157
- buffer.push(':page-version: {page-component-version}')
158
- buffer.push(`:page-component-display-version: ${thisComponentVersion.displayVersion}`)
159
- buffer.push(`:page-component-title: ${thisComponentVersion.title}`)
160
- lastComponentVersion = thisComponentVersion
161
- }
162
- }
163
- buffer.push(`:page-module: ${module_}`)
164
- buffer.push(`:page-relative-src-path: ${relative}`)
165
- //buffer.push(`:page-origin-type: ${origin.type}`)
166
- buffer.push(`:page-origin-url: ${origin.url}`)
167
- buffer.push(`:page-origin-start-path:${origin.startPath && ' '}${origin.startPath}`)
168
- buffer.push(`:page-origin-refname: ${origin.branch || origin.tag}`)
169
- buffer.push(`:page-origin-reftype: ${origin.branch ? 'branch' : 'tag'}`)
170
- buffer.push(`:page-origin-refhash: ${origin.worktree ? '(worktree)' : origin.refhash}`)
171
- let enclosed
172
- // NOTE: if level is 0, doctitle has already been added and we're in the document header
173
- if (level) {
174
- if (level === 1 && navtitlePlain === componentVersion.title) {
175
- level--
176
- } else {
177
- let hlevel = level + 1
178
- if (hlevel > 6) {
179
- hlevel = 6
180
- buffer.push(`[discrete#${idprefix}]`)
181
- } else {
182
- buffer.push(`[#${idprefix}]`)
183
- }
184
- buffer.push(`${'='.repeat(hlevel)} ${navtitleAsciiDoc}`)
185
- }
186
- } else {
187
- header.unshift(`[#${idprefix}]`)
188
- }
189
- if (sectionMergeStrategy === 'enclose' && hasItems && doc.hasSections()) {
190
- enclosed = true
191
- // TODO: make overview section title configurable
192
- //let overviewTitle = doc.getDocumentTitle()
193
- //if (overviewTitle === navtitle) overviewTitle = doc.getAttribute('overview-title', 'Overview')
194
- const overviewTitle = doc.getAttribute('overview-title', 'Overview')
195
- buffer.push('')
196
- // NOTE: try to toggle sectids; otherwise, fallback to globally unique synthetic ID
197
- let toggleSectids, syntheticId
198
- if (doc.isAttribute('sectids')) {
199
- if (doc.isAttributeLocked('sectids')) {
200
- syntheticId = `__object-id-${getObjectId(outlineEntry)}`
201
- } else {
202
- buffer.push(':!sectids:')
203
- toggleSectids = true
204
- }
205
- }
206
- let hlevel = level + 2
207
- if (hlevel > 6) {
208
- hlevel = 6
209
- buffer.push(syntheticId ? `[discrete#${syntheticId}]` : '[discrete]')
210
- } else if (syntheticId) {
211
- buffer.push(`[#${syntheticId}]`)
212
- }
213
- buffer.push(`${'='.repeat(hlevel)} ${overviewTitle}`)
214
- if (toggleSectids) buffer.push(':sectids:')
215
- }
216
- const lines = doc.getSourceLines()
217
- const ignoreLines = []
218
- // TODO: think more about when multipart is allowed; perhaps configurable
219
- if (doc.hasSections()) fixSectionLevels(doc.getSections(), level === 0)
220
- const allBlocks = doc.findBy({ traverse_documents: true }, (it) =>
221
- it.getContext() === 'document'
222
- ? it.getDocument().isNested()
223
- : !(it.getContext() === 'table_cell' && it.getStyle() === 'asciidoc')
224
- )
225
- allBlocks.forEach((block) => {
226
- const contentModel = block.content_model
227
- if (
228
- ((contentModel === 'verbatim' && block.getContext() !== 'table_cell') ||
229
- contentModel === 'simple' ||
230
- contentModel === 'pass') &&
231
- !block.hasSubstitution('macros')
232
- ) {
233
- const lineno = block.getLineNumber()
234
- const idx = typeof lineno === 'number' ? lineno - 1 : undefined
235
- const startLine = lines[idx]
236
- // NOTE: one case this happens if when sourcemap isn't enabled when reducing
237
- if (startLine == null) {
238
- console.log(`null startLine for ${block.getContext()} at ${lineno} in ${relative}`)
239
- return
240
- }
241
- const char0 = startLine.charAt()
242
- // FIXME: needs to be more robust; move logic to helper
243
- const delimited =
244
- startLine.length > 3 &&
245
- startLine === char0.repeat(startLine.length) &&
246
- (char0 === '-' || char0 === '.' || char0 === '+')
247
- // QUESTION: exclude block attribute lines too? what about attribute entries?
248
- for (let i = idx; i < block.lines.length + (delimited ? idx + 2 : idx); i++) ignoreLines.push(i)
249
- }
250
- })
251
- let skipping
252
- for (let idx = 0, len = lines.length; idx < len; idx++) {
253
- if (~ignoreLines.indexOf(idx)) continue
254
- let line = lines[idx]
255
- if (line.startsWith('//')) {
256
- if (line[2] !== '/') continue
257
- if (line.length > 3 && line === '/'.repeat(line.length)) {
258
- if (skipping) {
259
- if (line === skipping) skipping = undefined
260
- } else {
261
- skipping = line
262
- }
263
- continue
264
- }
265
- } else if (skipping) {
266
- continue
267
- }
268
- if (line.charAt() === ':' && /^:(?:leveloffset: .*|!leveloffset:|leveloffset!:)$/.test(line)) {
269
- if (lines[idx - 1] === '') lines[idx - 1] = undefined
270
- lines[idx] = undefined
271
- continue
272
- }
273
- if (~line.indexOf('<<')) {
274
- line = line.replace(/(?<![\\+])<<#?([\p{Alpha}0-9_/.:{][^>,]*?)(?:|, *([^>]+?))?>>/gu, (m, refid, text) => {
275
- // support natural xref
276
- if (!refs['$key?'](refid) && (~refid.indexOf(' ') || refid.toLowerCase() !== refid)) {
277
- if ((refid = doc.$resolve_id(refid))['$nil?']()) return m
278
- }
279
- return `<<${idprefix}${refid}${text ? ',' + text : ''}>>`
280
- })
281
- }
282
- // NOTE: the next check takes care of inline and block anchors
283
- if (~line.indexOf('[[')) {
284
- line = line.replace(/\[\[([\p{Alpha}_:][\p{Alpha}0-9_\-:.]*)(|, *.+?)\]\]/gu, `[[${idprefix}$1$2]]`)
285
- }
286
- if (~line.indexOf('xref:')) {
287
- // Q: should we allow : as first character of target?
288
- line = line.replace(/(?<![\\+])xref:([\p{Alpha}0-9_/.{#].*?)\[(|.*?[^\\])\]/gu, (m, target, text) => {
289
- let pagePart, fragment, targetPage
290
- const hashIdx = target.indexOf('#')
291
- if (~hashIdx) {
292
- pagePart = target.slice(0, hashIdx)
293
- fragment = target.slice(hashIdx + 1)
294
- // TODO: for now, assume .adoc; in the future, consider other file extensions
295
- if (pagePart && !pagePart.endsWith('.adoc')) pagePart += '.adoc'
296
- } else if (target.endsWith('.adoc')) {
297
- pagePart = target
298
- fragment = ''
299
- } else {
300
- fragment = target
301
- }
302
- if (!pagePart) {
303
- // Q: should we validate the internal ID here?
304
- return text && ~text.indexOf('=')
305
- ? `xref:${idprefix}${fragment}[${text}]`
306
- : `<<${idprefix}${fragment}${text ? ',' + text.replace(/\\]/g, ']') : ''}>>`
307
- }
308
- if (~pagePart.indexOf('@') || /:.*:/.test(pagePart)) {
309
- if (siteUrl && (targetPage = contentCatalog.resolvePage(pagePart, page.src)) && targetPage.out) {
310
- text ||= targetPage.asciidoc?.xreftext || target
311
- return `${siteUrl}${targetPage.pub.url}${fragment && '#' + fragment}[${text}]`
312
- }
313
- // TODO: handle unresolved page better
314
- 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
- }
320
- if (!(targetPage = pagesInOutline.get(pagePart))) {
321
- if (siteUrl && (targetPage = contentCatalog.resolvePage(pagePart, page.src)) && targetPage.out) {
322
- text ||= targetPage.asciidoc?.xreftext || target
323
- return `${siteUrl}${targetPage.pub.url}${fragment && '#' + fragment}[${text}]`
324
- }
325
- // TODO: handle unresolved page better
326
- return m
327
- }
328
- pagePart = pagePart
329
- .replace(/[/]/g, '::')
330
- .replace(/\.adoc$/, '')
331
- .replace(/[.]/g, '-')
332
- const refid = `${pagePart}:::${fragment}`
333
- return `<<${refid}${text && text !== targetPage.title ? ',' + text.replace(/\\]/g, ']') : ''}>>`
334
- })
335
- }
336
- if (~line.indexOf('link:{attachmentsdir}/')) {
337
- line = line.replace(/(?<![\\+])link:\{attachmentsdir\}\/([^\s[]+)\[(|.*?[^\\])\]/g, (m, relative, text) => {
338
- const attachment =
339
- siteUrl &&
340
- contentCatalog.getById({
341
- component: componentVersion.name,
342
- version: componentVersion.version,
343
- module: module_,
344
- family: 'attachment',
345
- relative,
346
- })
347
- // TODO: handle unresolved attachment page
348
- return attachment?.out ? `${siteUrl}${attachment.pub.url.replace(/_/g, '{underscore}')}[${text}]` : m
349
- })
350
- }
351
- if (~line.indexOf('image:') && !line.startsWith('image::')) {
352
- line = line.replace(/(?<![\\+])image:([^:\s[](?:[^[]*[^\s[])?)\[([^\]]*)\]/g, (m, target, attrlist) => {
353
- if (isResourceSpec(target)) {
354
- const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
355
- // TODO: handle (or report) unresolved image better
356
- if (image?.out) {
357
- image.out.assembled = true
358
- return `image:${image.out.path.replace(/_/g, '{underscore}')}[${attrlist}]`
359
- }
360
- }
361
- return m
362
- })
363
- }
364
- lines[idx] = line
365
- }
366
- // NOTE: need to do this last since it modifies the line numbers
367
- // we could remap the line numbers to make them resilient
368
- // or we could mark which lines to remove and filter them after
369
- let lastImageMacroAt
370
- ;[...allBlocks].reverse().forEach((block) => {
371
- const lineno = block.getLineNumber()
372
- // NOTE: lineno is not defined for preamble
373
- if (typeof lineno !== 'number') return
374
- const context = block.getContext()
375
- let idx = lineno - 1
376
- if (context === 'section' && !block.getDocument().isNested()) {
377
- if (block.getSectionName() === 'header') {
378
- lines[idx] = undefined
379
- return
380
- }
381
- let blockStyle = sectionMergeStrategy === 'discrete' ? 'discrete' : undefined
382
- lines[idx] = lines[idx].replace(/^=+ (.+)/, (_, rest) => {
383
- let targetMarkerLength = block.level + 1 + level + (enclosed ? 1 : 0)
384
- if (targetMarkerLength > 6) {
385
- targetMarkerLength = 6
386
- blockStyle = 'discrete'
387
- }
388
- return '='.repeat(targetMarkerLength) + ' ' + rest
389
- })
390
- // NOTE: ID will be undefined if sectids are turned off
391
- if (block.getId()) rewriteStyleAttribute(block, lines, idx, idprefix, blockStyle)
392
- } else {
393
- if (context === 'image') {
394
- let line = lines[idx] || ''
395
- let prefix = ''
396
- // Q: can we use startsWith('image::') in certain cases?
397
- let imageMacroOffset = (
398
- lastImageMacroAt?.[0] === idx ? line.slice(0, lastImageMacroAt[1]) : line
399
- ).lastIndexOf('image::')
400
- if (imageMacroOffset > 0) {
401
- if (
402
- block.getDocument().isNested() &&
403
- (prefix = line.slice(0, imageMacroOffset)).trimRight().endsWith('|')
404
- ) {
405
- line = line.slice(prefix.length)
406
- } else {
407
- imageMacroOffset = -1
408
- }
409
- }
410
- if (imageMacroOffset >= 0) {
411
- const target = block.getAttribute('target')
412
- if (isResourceSpec(target)) {
413
- const image = contentCatalog.resolveResource(target, page.src, 'image', ['image'])
414
- // FIXME: handle (or report) case when image is not resolved
415
- if (image?.out) {
416
- const boxedAttrlist = line.slice(line.indexOf('['))
417
- image.out.assembled = true
418
- lines[idx] = `${prefix}image::${image.out.path}${boxedAttrlist}`
419
- }
420
- }
421
- lastImageMacroAt = [idx, imageMacroOffset]
422
- }
423
- } else if (context === 'document' && block.hasHeader()) {
424
- // nested document
425
- idx = (block.getHeader().getLineNumber() || idx + 1) - 1
426
- }
427
- if (block.getId()) rewriteStyleAttribute(block, lines, idx, idprefix)
428
- }
429
- })
430
- buffer.push(...lines.filter((it) => it !== undefined))
431
- const attributeEntries = Object.entries(doc.attributes_defined_in_header || {})
432
- if (attributeEntries.length) {
433
- const resolvedAttributeEntries = attributeEntries.reduce(
434
- (accum, [name, val]) => {
435
- // Q: couldn't we just check if attribute is locked?
436
- if (name in mutableAttributes) {
437
- const initialVal = mutableAttributes[name]
438
- if (initialVal == null) {
439
- if (val != null) accum.push(`:!${name}:`)
440
- } else if (val !== initialVal) {
441
- accum.push(`:${name}:${initialVal ? ' ' + initialVal : ''}`)
442
- }
443
- } else if (
444
- !(
445
- val == null ||
446
- doc.isAttributeLocked(name) ||
447
- name === 'doctype' ||
448
- name === 'leveloffset' ||
449
- name === 'underscore'
450
- )
451
- ) {
452
- accum.push(`:!${name}:`)
453
- }
454
- return accum
455
- },
456
- ['']
457
- )
458
- if (resolvedAttributeEntries.length > 1) buffer.push(...resolvedAttributeEntries)
459
- }
460
- } else if (level) {
461
- if (level === 1 && navtitlePlain === componentVersion.title) {
462
- level--
463
- } else {
464
- buffer.push('')
465
- // NOTE: try to toggle sectids; otherwise, fallback to globally unique synthetic ID
466
- // Q: should we unset docname, page-module, etc?
467
- let toggleSectids, syntheticId
468
- if (!('sectids' in asciidocConfig.attributes)) {
469
- buffer.push(':!sectids:')
470
- toggleSectids = true
471
- } else if (typeof asciidocConfig.attributes.sectids === 'string') {
472
- if ('sectids' in mutableAttributes) {
473
- buffer.push(':!sectids:')
474
- toggleSectids = true
475
- } else {
476
- syntheticId = `__object-id-${global.Opal.hash(outlineEntry).$object_id()}`
477
- }
478
- }
479
- let sectionTitle = navtitleAsciiDoc
480
- if (urlType === 'external') {
481
- sectionTitle = `${url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
482
- } else if (urlType === 'internal' && !unresolved && siteUrl) {
483
- const resource = contentCatalog.getFiles().find((it) => it.out && it.pub.url === url)
484
- if (resource) sectionTitle = `${siteUrl}${resource.pub.url}[${navtitleAsciiDoc.replace(/\]/g, '\\]')}]`
485
- }
486
- let hlevel = level + 1
487
- if (hlevel > 6) {
488
- hlevel = 6
489
- buffer.push(syntheticId ? `[discrete#${syntheticId}]` : '[discrete]')
490
- } else if (syntheticId) {
491
- buffer.push(`[#${syntheticId}]`)
492
- }
493
- buffer.push(`${'='.repeat(hlevel)} ${sectionTitle}`)
494
- if (toggleSectids) buffer.push(':sectids:')
495
- }
496
- }
497
-
498
- const nextLevel = level + 1
499
- if (hasItems) {
500
- // NOTE: drop first child if same as parent; should we keep if content is different?
501
- ;(urlType === 'internal' && urlType === items[0].urlType && url === items[0].url && !items[0].items
502
- ? items.slice(1)
503
- : items
504
- ).forEach((item) => {
505
- buffer.push(
506
- ...aggregateAsciiDoc(
507
- loadAsciiDoc,
508
- contentCatalog,
509
- header,
510
- componentVersion,
511
- item,
512
- pagesInOutline,
513
- asciidocConfig,
514
- mutableAttributes,
515
- sectionMergeStrategy,
516
- lastComponentVersion,
517
- nextLevel
518
- )
519
- )
520
- })
521
- }
522
- return buffer
523
- }
524
-
525
- function generateSlug (title) {
526
- return title
527
- .toLowerCase()
528
- .replace(/&.+?;|[^ \p{Alpha}0-9_\-.]/gu, '')
529
- .replace(/[ _.]/g, '-')
530
- .replace(/--+/g, '-')
531
- }
532
-
533
- function fixSectionLevels (sections, multipart) {
534
- sections.forEach((sect) => {
535
- const targetLevel = sect.getParent().getLevel() + 1
536
- if (multipart ? sect.getLevel() > targetLevel : sect.getLevel() !== targetLevel) sect.level = targetLevel
537
- if (sect.hasSections()) fixSectionLevels(sect.getSections(), multipart)
538
- })
539
- }
540
-
541
- function rewriteStyleAttribute (block, lines, idx, idprefix, replacementStyle = '') {
542
- let prevLine = lines[idx - 1]
543
- const char0 = prevLine?.charAt()
544
- if (char0) {
545
- if (
546
- (char0 === '.' && /^\.\.?[^ \t.]/.test(prevLine)) ||
547
- (char0 === '[' &&
548
- prevLine.charAt(1) === '[' &&
549
- /^\[\[(?:|[\p{Alpha}_:][\p{Alpha}0-9_\-:.]*(?:, *.+)?)\]\]$/u.test(prevLine))
550
- ) {
551
- return rewriteStyleAttribute(block, lines, idx - 1, idprefix, replacementStyle)
552
- }
553
- }
554
- let cellSpec
555
- if (
556
- char0 &&
557
- (char0 === '[' || (block.getDocument().isNested() && (cellSpec = prevLine.match(/^([^[|]*)\| *(\[.+)/)))) &&
558
- prevLine.charAt(prevLine.length - 1) === ']'
559
- ) {
560
- if (cellSpec) {
561
- prevLine = cellSpec[2]
562
- cellSpec = cellSpec[1]
563
- }
564
- let rawStyle
565
- const commaIdx = prevLine.indexOf(',')
566
- if (~commaIdx) {
567
- rawStyle = prevLine.slice(1, commaIdx - 1)
568
- if (~rawStyle.indexOf('=')) rawStyle = undefined
569
- } else if (!~prevLine.indexOf('=')) {
570
- rawStyle = prevLine.slice(1, prevLine.length - 2)
571
- }
572
- if (rawStyle) {
573
- if (~rawStyle.indexOf('#')) {
574
- prevLine = prevLine.replace(/#[^.%,\]]+/, `#${idprefix}${block.getId()}`)
575
- if (replacementStyle) prevLine = prevLine.replace(/^[^#.%,\]]+/, `[${replacementStyle}`)
576
- } else {
577
- prevLine = `[${
578
- replacementStyle ? rawStyle.replace(/^[^.%]*/, replacementStyle) : rawStyle
579
- }#${idprefix}${block.getId()}${prevLine.slice(rawStyle.length + 1)}`
580
- }
581
- } else {
582
- prevLine = `[${replacementStyle}#${idprefix}${block.getId()}${rawStyle == null ? ',' : ''}${prevLine.slice(1)}`
583
- }
584
- if (cellSpec) prevLine = `${cellSpec}|${prevLine}`
585
- lines[idx - 1] = prevLine
586
- } else {
587
- lines.splice(idx, 0, `[${replacementStyle}#${idprefix}${block.getId()}]`)
588
- }
589
- }
590
-
591
- function isResourceSpec (str) {
592
- return !(~str.indexOf(':') && (~str.indexOf('://') || (str.startsWith('data:') && ~str.indexOf(','))))
593
- }
594
-
595
- function getObjectId (obj) {
596
- return global.Opal.uid()
597
- }
598
-
599
- module.exports = produceAggregateDocument