@antora/assembler 1.0.0-rc.6 → 1.0.0-rc.7

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.
@@ -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')
@@ -43,18 +45,17 @@ function produceAssemblyFile (
43
45
  const pagesInOutline = selectPagesInOutline(outline, pagesByUrl)
44
46
  if (outline.urlType === 'internal' && !pagesByUrl.has(outline.pathname) && !outline.items?.length) return
45
47
  const { rootLevel, xmlIds } = assemblyModel
48
+ const scratchDoc = loadAsciiDoc(
49
+ { contents: Buffer.alloc(0), src: { family: 'page', relative: '_scratch.adoc' } },
50
+ undefined,
51
+ asciidocConfig
52
+ )
46
53
  const idSeparators = {
47
54
  prefix:
48
55
  'assembler-idprefix' in asciidocConfig.attributes ? (asciidocConfig.attributes['assembler-idprefix'] ?? '') : '_',
49
56
  scope: xmlIds ? '---' : ':::',
50
57
  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
- ),
58
+ generateIdFromTitle: generateIdFromTitle.bind(scratchDoc),
58
59
  }
59
60
  const { name: component, version } = componentVersion
60
61
  asciidocConfig = prepareAsciiDocConfig(
@@ -63,12 +64,14 @@ function produceAssemblyFile (
63
64
  pagesInOutline,
64
65
  asciidocConfig,
65
66
  assemblyModel,
66
- idSeparators
67
+ idSeparators,
68
+ scratchDoc
67
69
  )
68
- const buffer = mergeAsciiDoc(
70
+ const builder = buildAsciiDocHeader(componentVersion, outline.content, assemblyModel)
71
+ mergeAsciiDoc(
69
72
  loadAsciiDoc,
70
73
  contentCatalog,
71
- buildAsciiDocHeader(componentVersion, outline.content, assemblyModel),
74
+ builder,
72
75
  componentVersion,
73
76
  outline,
74
77
  files,
@@ -78,15 +81,14 @@ function produceAssemblyFile (
78
81
  mutableAttributes,
79
82
  assemblyModel
80
83
  )
81
- if (buffer[buffer.doctypeIdx] === ':doctype: article') buffer.splice(buffer.doctypeIdx, 1)
82
84
  const stem =
83
- (buffer.slug ? sanitizeSlug(buffer.slug) : generateSlug(rootLevel === 0 ? 'index' : buffer.navtitle)) ||
85
+ (builder.slug ? sanitizeSlug(builder.slug) : generateSlug(rootLevel === 0 ? 'index' : builder.navtitle)) ||
84
86
  'export-' + (assemblyModel.stemSeq = (assemblyModel.exportSeq ?? 0) + 1)
85
87
  const downloadStem = [component, version, stem === 'index' ? '' : stem].filter((it) => it).join('-')
86
88
  const file = contentCatalog.createFile({
87
89
  asciidoc: asciidocConfig,
88
90
  assembler: { assembled: pagesInOutline.assembled, downloadStem, rootLevel },
89
- contents: Buffer.from(buffer.join('\n') + '\n'),
91
+ contents: builder.serialize(),
90
92
  src: { component, version, componentVersion, module: 'ROOT', family: 'export', relative: stem + '.adoc' },
91
93
  pub: false,
92
94
  })
@@ -95,7 +97,7 @@ function produceAssemblyFile (
95
97
  return file
96
98
  }
97
99
 
98
- function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocConfig, assemblyModel, idSeparators) {
100
+ function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocConfig, assemblyModel, idSeparators, doc) {
99
101
  let attributesModified
100
102
  const configShared = asciidocConfig.$shared
101
103
  const sharedAttributes = asciidocConfig.attributes
@@ -116,7 +118,17 @@ function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocCon
116
118
  for (const [name, val] of Object.entries(sharedAttributes)) {
117
119
  if (!(val?.constructor === String && ~val.indexOf(':'))) continue
118
120
  if (~val.indexOf('xref:')) {
119
- const newVal = rewriteXrefs(val, contentCatalog, assemblyModel, ctx, false, pagesInOutline, idSeparators)
121
+ const newVal = rewriteXrefs(
122
+ val,
123
+ contentCatalog,
124
+ assemblyModel,
125
+ ctx,
126
+ false,
127
+ pagesInOutline,
128
+ idSeparators,
129
+ null,
130
+ doc
131
+ )
120
132
  if (newVal !== val) (attributesModified ??= {})[name] = newVal
121
133
  }
122
134
  }
@@ -126,25 +138,44 @@ function prepareAsciiDocConfig (contentCatalog, ctx, pagesInOutline, asciidocCon
126
138
  }
127
139
 
128
140
  function buildAsciiDocHeader (componentVersion, navtitle, assemblyModel) {
141
+ const { name: componentName, version, displayVersion, title } = componentVersion
129
142
  const navtitlePlain = sanitize(navtitle)
130
143
  const navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
131
- const doctitle = (componentVersion.title === navtitlePlain ? '' : componentVersion.title + ': ') + navtitleAsciiDoc
132
- const version = componentVersion.version === 'master' ? '' : componentVersion.version
133
- const displayVersion = componentVersion.displayVersion
134
- const buffer = [
135
- `= ${doctitle}`,
136
- ...(version ? [`:revnumber: ${displayVersion}`] : []),
137
- `:doctype: ${assemblyModel.doctype ?? 'book'}`,
138
- ':underscore: _',
139
- // Q: should we pass these via the CLI so they cannot be modified?
140
- `:page-component-name: ${componentVersion.name}`,
141
- `:page-component-version:${version ? ' ' + version : ''}`,
142
- ':page-version: {page-component-version}',
143
- `:page-component-display-version: ${displayVersion}`,
144
- `:page-component-title: ${componentVersion.title}`,
145
- ]
146
- buffer.doctypeIdx = version ? 2 : 1
147
- return Object.assign(buffer, { navtitle })
144
+ const doctitle = (title === navtitlePlain ? '' : title + ': ') + navtitleAsciiDoc
145
+ const versioned = !!(version && version !== 'master')
146
+ return {
147
+ header: [
148
+ `= ${doctitle}`,
149
+ ...(versioned ? [`:revnumber: ${displayVersion}`] : []),
150
+ `:doctype: ${(assemblyModel.doctype ??= 'book')}`,
151
+ ':underscore: _',
152
+ `:page-component-name: ${componentName}`,
153
+ `:page-component-version:${versioned ? ' ' + version : ''}`,
154
+ ':page-version: {page-component-version}',
155
+ `:page-component-display-version: ${displayVersion}`,
156
+ `:page-component-title: ${title}`,
157
+ ],
158
+ currentComponentVersion: componentVersion,
159
+ navtitle,
160
+ setAuthor (author) {
161
+ this.header.splice(1, 0, author)
162
+ },
163
+ setDoctitle (doctitle_) {
164
+ this.header[0] = `= ${doctitle_}`
165
+ },
166
+ setDoctype (doctype) {
167
+ this.header[this.header.findIndex((line) => line.startsWith(':doctype: '))] = `:doctype: ${doctype}`
168
+ },
169
+ setDocid (docid) {
170
+ this.header.unshift(`[#${docid}]`)
171
+ },
172
+ serialize () {
173
+ const normalizedHeader = this.header.filter((it) => it !== ':doctype: article')
174
+ return this.body
175
+ ? Buffer.from(normalizedHeader.join('\n') + '\n\n' + this.body.join('\n') + '\n')
176
+ : Buffer.from(normalizedHeader.join('\n') + '\n')
177
+ },
178
+ }
148
179
  }
149
180
 
150
181
  function selectPagesInOutline (outlineEntry, pagesByUrl, accum) {
@@ -165,7 +196,7 @@ function selectPagesInOutline (outlineEntry, pagesByUrl, accum) {
165
196
  function mergeAsciiDoc (
166
197
  loadAsciiDoc,
167
198
  contentCatalog,
168
- buffer,
199
+ builder,
169
200
  componentVersion,
170
201
  outlineEntry,
171
202
  files,
@@ -174,15 +205,14 @@ function mergeAsciiDoc (
174
205
  asciidocConfig,
175
206
  mutableAttributes,
176
207
  assemblyModel,
177
- lastComponentVersion = componentVersion,
178
208
  level = 0,
179
209
  supportsParts = false
180
210
  ) {
181
211
  const { items = [], roles = [], unresolved, urlType, url, pathname, hash } = outlineEntry
182
212
  // 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
- buffer.inBody ??= false
185
- return buffer
213
+ if (roles.includes('site-only') || (urlType === 'external' && !URL.canParse(url))) {
214
+ builder.body ??= []
215
+ return builder
186
216
  }
187
217
  const assembled = pagesInOutline.assembled
188
218
  // FIXME: ideally, resource ID would be stored in navigation so we can look up the page more efficiently
@@ -191,15 +221,15 @@ function mergeAsciiDoc (
191
221
  let navtitlePlain = sanitize(navtitle)
192
222
  let navtitleAsciiDoc = unconvertInlineAsciiDoc(navtitle)
193
223
  const { filetype, linkReferenceStyle, pubRoot, sectionMergeStrategy, siteRoot, logger, rootLevel } = assemblyModel
194
- const doctype = assemblyModel.doctype ?? 'book'
195
- const atDocumentRoot = !buffer.inBody
224
+ const doctype = assemblyModel.doctype
225
+ const atDocumentRoot = !builder.body?.length
196
226
  let atBookRoot = atDocumentRoot && !level && doctype === 'book' && (supportsParts = true)
197
227
  const hasItems = items.length > 0
198
228
  if (page && !assembled.pages.has(page)) {
199
229
  const contents = page.src.contents
200
230
  if (contents == null) {
201
- buffer.inBody ??= false
202
- return buffer
231
+ builder.body ??= []
232
+ return builder
203
233
  }
204
234
  const pageAsAsciiDoc = new page.constructor(
205
235
  Object.assign({}, page, { contents: trimAsciiDoc(contents), mediaType: page.src.mediaType })
@@ -215,25 +245,25 @@ function mergeAsciiDoc (
215
245
  },
216
246
  })
217
247
  : doc.getLogger()
218
- doc.source_header_attributes ??= doc.parent.$to_h()
248
+ doc.source_header_attributes ??= toHash()
219
249
  const isRootPage = atDocumentRoot && !level
220
250
  const { component, version, module: module_, relative, origin } = page.src
221
251
  const doctypeOverride = doc.getAttribute('assembly-doctype')
222
252
  if (doctypeOverride) {
223
- if (doctypeOverride !== doctype) buffer[buffer.doctypeIdx] = `:doctype: ${doctypeOverride}`
253
+ if (doctypeOverride !== doctype) builder.setDoctype(doctypeOverride)
224
254
  if (doctypeOverride !== 'book') atBookRoot = supportsParts = false
225
255
  }
226
- if (isRootPage && doc.hasAttribute('assembly-slug')) buffer.slug = doc.getAttribute('assembly-slug')
256
+ if (isRootPage && doc.hasAttribute('assembly-slug')) builder.slug = doc.getAttribute('assembly-slug')
227
257
  let hasAssemblyNavtitleAttr
228
258
  if ((hasAssemblyNavtitleAttr = !!doc.getAttribute('assembly-navtitle'))) {
229
259
  navtitleAsciiDoc = doc.getAttribute('assembly-navtitle')
230
260
  navtitlePlain = sanitize((navtitle = doc.$apply_reftext_subs(navtitleAsciiDoc)))
231
- if (buffer.inBody == null) {
232
- buffer.navtitle = navtitle
261
+ if (builder.body == null) {
262
+ builder.navtitle = navtitle
233
263
  if (rootLevel) {
234
- const doctitle =
264
+ builder.setDoctitle(
235
265
  (componentVersion.title === navtitlePlain ? '' : componentVersion.title + ': ') + navtitleAsciiDoc
236
- buffer[0] = `= ${doctitle}`
266
+ )
237
267
  }
238
268
  }
239
269
  }
@@ -259,13 +289,10 @@ function mergeAsciiDoc (
259
289
  let nextSectionLevel = 1
260
290
  const lines = doc.getSourceLines()
261
291
  const ignoreLines = []
262
- if (!buffer.inBody) {
263
- buffer.inBody = true
264
- buffer.endHeaderIdx = buffer.length - 1
265
- }
266
- buffer.push('')
292
+ const buffer = (builder.body ??= [])
293
+ if (buffer.length) buffer.push('')
267
294
  buffer.push(`:docname: ${docname}`)
268
- if (component !== lastComponentVersion.name) {
295
+ if (component !== builder.currentComponentVersion.name) {
269
296
  const thisComponentVersion =
270
297
  component === componentVersion.name && version === componentVersion.version
271
298
  ? componentVersion
@@ -276,7 +303,7 @@ function mergeAsciiDoc (
276
303
  buffer.push(':page-version: {page-component-version}')
277
304
  buffer.push(`:page-component-display-version: ${thisComponentVersion.displayVersion}`)
278
305
  buffer.push(`:page-component-title: ${thisComponentVersion.title}`)
279
- lastComponentVersion = thisComponentVersion
306
+ builder.currentComponentVersion = thisComponentVersion
280
307
  }
281
308
  }
282
309
  buffer.push(`:page-module: ${module_}`)
@@ -286,13 +313,10 @@ function mergeAsciiDoc (
286
313
  buffer.push(`:page-origin-refname: ${origin.branch || origin.tag}`)
287
314
  buffer.push(`:page-origin-reftype: ${origin.branch ? 'branch' : 'tag'}`)
288
315
  buffer.push(`:page-origin-refhash: ${origin.worktree ? '(worktree)' : origin.refhash}`)
289
- let headerHasBlockAttrs
290
316
  if (doc.hasHeader()) {
291
317
  for (const entry of processDocumentHeader(doc, lines, ignoreLines, isRootPage)) {
292
318
  if (entry.type === 'author_line') {
293
- buffer.splice(1, 0, entry.lines[0])
294
- buffer.doctypeIdx += 1
295
- buffer.endHeaderIdx += 1
319
+ builder.setAuthor(entry.lines[0])
296
320
  } else if (entry.type === 'attribute_entry') {
297
321
  const name = entry.name
298
322
  if (!entry.negated && !doc.isAttributeLocked(name)) {
@@ -333,14 +357,9 @@ function mergeAsciiDoc (
333
357
  if (newVal !== val) entry.lines = `:${entry.name}: ${newVal}`.split('\n')
334
358
  }
335
359
  }
336
- if (entry.promote) {
337
- buffer.splice(buffer.endHeaderIdx + 1, 0, ...entry.lines)
338
- buffer.endHeaderIdx += entry.lines.length
339
- } else {
340
- buffer.push(...entry.lines)
341
- }
360
+ ;(entry.promote ? builder.header : buffer).push(...entry.lines)
342
361
  } else {
343
- if (entry.type === 'attrlist') headerHasBlockAttrs = true
362
+ if (entry.type === 'attrlist') builder.headerHasBlockAttrs = true
344
363
  buffer.push(...entry.lines)
345
364
  }
346
365
  }
@@ -364,15 +383,15 @@ function mergeAsciiDoc (
364
383
  if (htitleOverride != null) {
365
384
  const keepSections = hasSections && sectionMergeStrategy !== 'discrete'
366
385
  if (htitleOverride === '') {
367
- if (headerHasBlockAttrs || keepSections) {
386
+ if (builder.headerHasBlockAttrs || keepSections) {
368
387
  htitleAsciiDoc = '{empty}'
369
388
  notitle = true
370
389
  } else {
371
- if (hasPrefaceTitleAttr) buffer.splice(++buffer.endHeaderIdx, 0, ':preface-title:')
390
+ if (hasPrefaceTitleAttr) builder.header.push(':preface-title:')
372
391
  htitleAsciiDoc = undefined
373
392
  }
374
- } else if (hasPrefaceTitleAttr && !headerHasBlockAttrs && !keepSections) {
375
- buffer.splice(++buffer.endHeaderIdx, 0, `:preface-title: ${unconvertInlineAsciiDoc(htitleOverride)}`)
393
+ } else if (hasPrefaceTitleAttr && !builder.headerHasBlockAttrs && !keepSections) {
394
+ builder.header.push(`:preface-title: ${unconvertInlineAsciiDoc(htitleOverride)}`)
376
395
  htitleAsciiDoc = undefined
377
396
  } else {
378
397
  htitleAsciiDoc = unconvertInlineAsciiDoc(htitleOverride)
@@ -383,8 +402,7 @@ function mergeAsciiDoc (
383
402
  if (atDocumentRoot) {
384
403
  // Q: should we use htitleAsciiDoc to generate ID if not {empty} instead of pageStyle
385
404
  pageFragment = `#_${idSeparators.coordinate}${pageIdLeader}${pageStyle}`
386
- buffer.unshift(`[#${pageId}]`)
387
- buffer.doctypeIdx += 1
405
+ builder.setDocid(pageId)
388
406
  } else {
389
407
  pageFragment = `#${pageId}`
390
408
  }
@@ -414,8 +432,7 @@ function mergeAsciiDoc (
414
432
  buffer.push(`[${heading.style ?? pageStyle}${pageFragment}${rolesAttr}${notitleAttrs}]`)
415
433
  buffer.push(`${'='.repeat(heading.level)} ${heading.title}`)
416
434
  } else if (atDocumentRoot) {
417
- buffer.unshift(`[#${pageId}]`)
418
- buffer.doctypeIdx += 1
435
+ builder.setDocid(pageId)
419
436
  }
420
437
  if (sectionMergeStrategy === 'enclose' && hasItems && hasSections && !(atDocumentRoot && heading)) {
421
438
  // TODO: make overview section title configurable
@@ -423,7 +440,7 @@ function mergeAsciiDoc (
423
440
  //if (overviewTitle === navtitle) overviewTitle = doc.getAttribute('overview-title', 'Overview')
424
441
  const overviewTitle = doc.getAttribute('overview-title', 'Overview')
425
442
  const syntheticId = idSeparators.generateIdFromTitle(overviewTitle, idSeparators, pageIdLeader)
426
- if (heading) buffer.push('')
443
+ if (heading && buffer.length) buffer.push('')
427
444
  let hlevel = level + (nextSectionLevel = 2)
428
445
  if (hlevel > 6) {
429
446
  const blockStyle = `discrete.h${hlevel}`
@@ -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 (~ignoreLines.indexOf(idx)) continue
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 (~ignoreLines.indexOf(idx)) continue
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 = Object.entries(doc.source_header_attributes.$$smap)
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.inBody = true
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
- const nextLevel = level + 1
658
- // NOTE: drop first child if same as parent; should we keep if content is different?
659
- ;(urlType === 'internal' && items[0].urlType === 'internal' && pathname === items[0].url && !items[0].items?.length
660
- ? items.slice(1)
661
- : items
662
- ).forEach((item) => {
663
- mergeAsciiDoc(
664
- loadAsciiDoc,
665
- contentCatalog,
666
- buffer,
667
- componentVersion,
668
- item,
669
- files,
670
- pagesInOutline,
671
- idSeparators,
672
- asciidocConfig,
673
- mutableAttributes,
674
- assemblyModel,
675
- lastComponentVersion,
676
- nextLevel,
677
- atBookRoot
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.lines.push(line)
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.lines.push(line)
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.lines.push(line)
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.lines.push(line)
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: [line], lineno: idx + 1 }))
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) {
@@ -822,13 +845,13 @@ function safePush (onto, entries) {
822
845
  }
823
846
 
824
847
  function generateIdFromTitle (titleAsciiDoc, idSeparators, idLeader) {
825
- const Section = this.$class().$const_get('::Asciidoctor::Section')
848
+ const Section = this.$class().$$base_module.Section
826
849
  const baseId = Section.$generate_id(titleAsciiDoc, this) // refs in catalog will always be empty
827
850
  let id = `_${idSeparators.coordinate}${idLeader ?? ''}${baseId}`
828
851
  const syntheticIds = (this.catalog.syntheticIds ??= {})
829
852
  if (id in syntheticIds) {
830
853
  const sep = (this.getAttribute('idseparator') ?? '_').charAt()
831
- let cnt = this.$class().$const_get('::Asciidoctor::Compliance').unique_id_start_index
854
+ let cnt = this.$class().$$base_module.Compliance.unique_id_start_index
832
855
  let candidate
833
856
  while ((candidate = `${id}${sep}${cnt}`) in syntheticIds) cnt++
834
857
  id = candidate