@asciidoctor/core 4.0.1 → 4.0.3

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,4 +1,4 @@
1
- var version = "4.0.1";
1
+ var version = "4.0.3";
2
2
  const packageJson = {
3
3
  version: version};
4
4
 
@@ -3032,9 +3032,10 @@ class AbstractNode {
3032
3032
  * Construct a URI reference or data URI to the target image.
3033
3033
  *
3034
3034
  * If the target image is already a URI it is left untouched (unless data-uri
3035
- * conversion is requested). The image is resolved relative to the directory
3036
- * named by assetDirKey. When data-uri is enabled and the safe level permits,
3037
- * the image is embedded as a Base64 data URI.
3035
+ * conversion is requested). If the target image is a data URI, then it is
3036
+ * already an embedded image, so it is returned as-is. The image is resolved
3037
+ * relative to the directory named by assetDirKey. When data-uri is enabled and
3038
+ * the safe level permits, the image is embedded as a Base64 data URI.
3038
3039
  *
3039
3040
  * NOTE: When the document has both 'data-uri' and 'allow-uri-read' enabled
3040
3041
  * and the resolved image URL is a remote URI, this method returns a Promise
@@ -3045,6 +3046,8 @@ class AbstractNode {
3045
3046
  * @returns {Promise<string>} a Promise resolving to a String reference or data URI.
3046
3047
  */
3047
3048
  async imageUri(targetImage, assetDirKey = 'imagesdir') {
3049
+ // A data URI is already an embedded image, so use it as-is rather than reading or re-encoding it.
3050
+ if (targetImage.startsWith('data:')) return targetImage
3048
3051
  const doc = this.document;
3049
3052
  if (doc.safe < SafeMode.SECURE && doc.hasAttribute('data-uri')) {
3050
3053
  let imagesBase;
@@ -5452,7 +5455,18 @@ class SecurityError extends Error {
5452
5455
  function applyBackendTraits(instance) {
5453
5456
  instance._backendTraits = null;
5454
5457
 
5455
- instance.basebackend = function (value = null) {
5458
+ // Install a Ruby-style trait accessor method, but never clobber a flat string
5459
+ // property the converter already declared (convention #2). Overwriting e.g.
5460
+ // `converter.outfilesuffix = '.html'` with a method would silently turn the
5461
+ // author's string into a function; the backend traits stay reachable through
5462
+ // `_getBackendTraits()` instead.
5463
+ const defineTraitAccessor = (name, fn) => {
5464
+ const existing = Object.getOwnPropertyDescriptor(instance, name);
5465
+ if (existing && typeof existing.value !== 'function') return
5466
+ instance[name] = fn;
5467
+ };
5468
+
5469
+ defineTraitAccessor('basebackend', function (value = null) {
5456
5470
  if (value) {
5457
5471
  const traits = (this._backendTraits ??= {});
5458
5472
  traits.basebackend = value;
@@ -5466,19 +5480,19 @@ function applyBackendTraits(instance) {
5466
5480
  return value
5467
5481
  }
5468
5482
  return this._getBackendTraits().basebackend
5469
- };
5470
- instance.filetype = function (value = null) {
5483
+ });
5484
+ defineTraitAccessor('filetype', function (value = null) {
5471
5485
  if (value) return (this._getBackendTraits().filetype = value)
5472
5486
  return this._getBackendTraits().filetype
5473
- };
5474
- instance.htmlsyntax = function (value = null) {
5487
+ });
5488
+ defineTraitAccessor('htmlsyntax', function (value = null) {
5475
5489
  if (value) return (this._getBackendTraits().htmlsyntax = value)
5476
5490
  return this._getBackendTraits().htmlsyntax
5477
- };
5478
- instance.outfilesuffix = function (value = null) {
5491
+ });
5492
+ defineTraitAccessor('outfilesuffix', function (value = null) {
5479
5493
  if (value) return (this._getBackendTraits().outfilesuffix = value)
5480
5494
  return this._getBackendTraits().outfilesuffix
5481
- };
5495
+ });
5482
5496
  instance.supportsTemplates = function (value = true) {
5483
5497
  this._getBackendTraits().supportsTemplates = value;
5484
5498
  };
@@ -5562,7 +5576,9 @@ function normalizeConverter(converter, backend) {
5562
5576
  }
5563
5577
  }
5564
5578
 
5565
- // Apply the BackendTraits mixin so Document can call the standard accessor methods.
5579
+ // Apply the BackendTraits mixin so Document can read traits via
5580
+ // _getBackendTraits(). Flat string properties (convention #2) are preserved:
5581
+ // applyBackendTraits does not overwrite an existing same-named data property.
5566
5582
  applyBackendTraits(converter);
5567
5583
  if (traits) {
5568
5584
  converter._backendTraits = traits;
@@ -13499,6 +13515,30 @@ function _uniform(str, chr, len) {
13499
13515
  * @typedef {ProcessorDslInterface & { prefer(): void; prepend(): void }} DocumentProcessorDslInterface
13500
13516
  */
13501
13517
 
13518
+ /**
13519
+ * DSL interface for preprocessors.
13520
+ *
13521
+ * @typedef {Omit<DocumentProcessorDslInterface, 'process'> & {
13522
+ * process(fn: (this: PreprocessorDslInterface, document: Document, reader: Reader) => Reader | void): void;
13523
+ * }} PreprocessorDslInterface
13524
+ */
13525
+
13526
+ /**
13527
+ * DSL interface for tree processors.
13528
+ *
13529
+ * @typedef {Omit<DocumentProcessorDslInterface, 'process'> & {
13530
+ * process(fn: (this: TreeProcessorDslInterface, document: Document) => Document | void): void;
13531
+ * }} TreeProcessorDslInterface
13532
+ */
13533
+
13534
+ /**
13535
+ * DSL interface for postprocessors.
13536
+ *
13537
+ * @typedef {Omit<DocumentProcessorDslInterface, 'process'> & {
13538
+ * process(fn: (this: PostprocessorDslInterface, document: Document, output: string) => string): void;
13539
+ * }} PostprocessorDslInterface
13540
+ */
13541
+
13502
13542
  /**
13503
13543
  * DSL interface for syntax processors (BlockProcessor, BlockMacroProcessor, InlineMacroProcessor).
13504
13544
  *
@@ -13519,28 +13559,35 @@ function _uniform(str, chr, len) {
13519
13559
  /**
13520
13560
  * DSL interface for include processors.
13521
13561
  *
13522
- * @typedef {DocumentProcessorDslInterface & {
13562
+ * @typedef {Omit<DocumentProcessorDslInterface, 'process'> & {
13523
13563
  * handles(fn: (target: string) => boolean): void;
13524
13564
  * handles(fn: (doc: Document, target: string) => boolean): void;
13565
+ * process(fn: (this: IncludeProcessorDslInterface, document: Document, reader: Reader, target: string, attributes: Record<string, string>) => void): void;
13525
13566
  * }} IncludeProcessorDslInterface
13526
13567
  */
13527
13568
 
13528
13569
  /**
13529
13570
  * DSL interface for docinfo processors.
13530
13571
  *
13531
- * @typedef {DocumentProcessorDslInterface & {
13572
+ * @typedef {Omit<DocumentProcessorDslInterface, 'process'> & {
13532
13573
  * atLocation(value: string): void;
13574
+ * process(fn: (this: DocinfoProcessorDslInterface, document: Document) => string): void;
13533
13575
  * }} DocinfoProcessorDslInterface
13534
13576
  */
13535
13577
 
13536
13578
  /**
13537
13579
  * DSL interface for block processors.
13538
13580
  *
13539
- * @typedef {SyntaxProcessorDslInterface & {
13581
+ * The `process` callback is bound to the processor instance, so `this` inside it
13582
+ * (and inside the registration function) exposes the `createBlock` helpers.
13583
+ *
13584
+ * @typedef {Omit<SyntaxProcessorDslInterface, 'process'> & {
13540
13585
  * contexts(...value: (string | string[])[]): void;
13541
13586
  * onContexts(...value: (string | string[])[]): void;
13542
13587
  * onContext(...value: (string | string[])[]): void;
13543
13588
  * bindTo(...value: (string | string[])[]): void;
13589
+ * createBlock(parent: AbstractBlock, context: string, source?: string | string[] | null, attrs?: object, opts?: object): Block;
13590
+ * process(fn: (this: BlockProcessorDslInterface, parent: AbstractBlock, reader: Reader, attributes: Record<string, unknown>) => AbstractBlock | void): void;
13544
13591
  * }} BlockProcessorDslInterface
13545
13592
  */
13546
13593
 
@@ -13550,14 +13597,31 @@ function _uniform(str, chr, len) {
13550
13597
  * @typedef {SyntaxProcessorDslInterface} MacroProcessorDslInterface
13551
13598
  */
13552
13599
 
13600
+ /**
13601
+ * DSL interface for block macro processors.
13602
+ *
13603
+ * The `process` callback is bound to the processor instance, so `this` inside it
13604
+ * (and inside the registration function) exposes the `createBlock` helpers.
13605
+ *
13606
+ * @typedef {Omit<MacroProcessorDslInterface, 'process'> & {
13607
+ * createBlock(parent: AbstractBlock, context: string, source?: string | string[] | null, attrs?: object, opts?: object): Block;
13608
+ * process(fn: (this: BlockMacroProcessorDslInterface, parent: AbstractBlock, target: string, attributes: Record<string, unknown>) => AbstractBlock | void): void;
13609
+ * }} BlockMacroProcessorDslInterface
13610
+ */
13611
+
13553
13612
  /**
13554
13613
  * DSL interface for inline macro processors.
13555
13614
  *
13556
- * @typedef {MacroProcessorDslInterface & {
13615
+ * The `process` callback is bound to the processor instance, so `this` inside it
13616
+ * (and inside the registration function) exposes the `createInline` helper.
13617
+ *
13618
+ * @typedef {Omit<MacroProcessorDslInterface, 'process'> & {
13557
13619
  * format(value: string): void;
13558
13620
  * matchFormat(value: string): void;
13559
13621
  * usingFormat(value: string): void;
13560
13622
  * match(value: RegExp): void;
13623
+ * createInline(parent: AbstractBlock, context: string, text: string, opts?: object): Inline;
13624
+ * process(fn: (this: InlineMacroProcessorDslInterface, parent: AbstractBlock, target: string, attributes: Record<string, unknown>) => Inline | void): void;
13561
13625
  * }} InlineMacroProcessorDslInterface
13562
13626
  */
13563
13627
 
@@ -14278,7 +14342,15 @@ class IncludeProcessor extends Processor {
14278
14342
  }
14279
14343
 
14280
14344
  /**
14281
- * @param {Document} doc - The document being parsed.
14345
+ * Decide whether this include processor handles the given target.
14346
+ *
14347
+ * Override this method in a subclass. The override may accept either just the
14348
+ * target string (Ruby-style, arity 1) or both the document and the target
14349
+ * (arity 2) — an arity-1 override is adapted at registration time so the
14350
+ * parser can always invoke it as `handles(doc, target)`. The first parameter
14351
+ * is therefore typed `Document | string` so both override shapes type-check.
14352
+ *
14353
+ * @param {Document|string} doc - The document being parsed, or (for a Ruby-style arity-1 override) the include target.
14282
14354
  * @param {string} target - The target of the include directive.
14283
14355
  * @returns {boolean} true if this processor handles the given target.
14284
14356
  */
@@ -14646,6 +14718,14 @@ class Registry {
14646
14718
  * this.process(function (doc, reader) { ... })
14647
14719
  * })
14648
14720
  *
14721
+ * @overload
14722
+ * @param {typeof Preprocessor} processor - A Preprocessor subclass or instance.
14723
+ * @returns {ProcessorExtension} the Extension stored in the registry.
14724
+ *
14725
+ * @overload
14726
+ * @param {(this: PreprocessorDslInterface) => void} fn - Registration function bound to the preprocessor DSL.
14727
+ * @returns {ProcessorExtension} the Extension stored in the registry.
14728
+ *
14649
14729
  * @param {...*} args - Class constructor, instance, or block function.
14650
14730
  * @returns {ProcessorExtension} the Extension stored in the registry.
14651
14731
  */
@@ -14679,6 +14759,14 @@ class Registry {
14679
14759
  /**
14680
14760
  * Register a TreeProcessor with the extension registry.
14681
14761
  *
14762
+ * @overload
14763
+ * @param {typeof TreeProcessor} processor - A TreeProcessor subclass or instance.
14764
+ * @returns {ProcessorExtension} the Extension stored in the registry.
14765
+ *
14766
+ * @overload
14767
+ * @param {(this: TreeProcessorDslInterface) => void} fn - Registration function bound to the tree processor DSL.
14768
+ * @returns {ProcessorExtension} the Extension stored in the registry.
14769
+ *
14682
14770
  * @param {...*} args - Class constructor, instance, or block function.
14683
14771
  * @returns {ProcessorExtension} the Extension stored in the registry.
14684
14772
  */
@@ -14732,6 +14820,14 @@ class Registry {
14732
14820
  /**
14733
14821
  * Register a Postprocessor with the extension registry.
14734
14822
  *
14823
+ * @overload
14824
+ * @param {typeof Postprocessor} processor - A Postprocessor subclass or instance.
14825
+ * @returns {ProcessorExtension} the Extension stored in the registry.
14826
+ *
14827
+ * @overload
14828
+ * @param {(this: PostprocessorDslInterface) => void} fn - Registration function bound to the postprocessor DSL.
14829
+ * @returns {ProcessorExtension} the Extension stored in the registry.
14830
+ *
14735
14831
  * @param {...*} args - Class constructor, instance, or block function.
14736
14832
  * @returns {ProcessorExtension} the Extension stored in the registry.
14737
14833
  */
@@ -14765,6 +14861,14 @@ class Registry {
14765
14861
  /**
14766
14862
  * Register an IncludeProcessor with the extension registry.
14767
14863
  *
14864
+ * @overload
14865
+ * @param {typeof IncludeProcessor} processor - An IncludeProcessor subclass or instance.
14866
+ * @returns {ProcessorExtension} the Extension stored in the registry.
14867
+ *
14868
+ * @overload
14869
+ * @param {(this: IncludeProcessorDslInterface) => void} fn - Registration function bound to the include processor DSL.
14870
+ * @returns {ProcessorExtension} the Extension stored in the registry.
14871
+ *
14768
14872
  * @param {...*} args - Class constructor, instance, or block function.
14769
14873
  * @returns {ProcessorExtension} the Extension stored in the registry.
14770
14874
  */
@@ -14803,6 +14907,14 @@ class Registry {
14803
14907
  /**
14804
14908
  * Register a DocinfoProcessor with the extension registry.
14805
14909
  *
14910
+ * @overload
14911
+ * @param {typeof DocinfoProcessor} processor - A DocinfoProcessor subclass or instance.
14912
+ * @returns {ProcessorExtension} the Extension stored in the registry.
14913
+ *
14914
+ * @overload
14915
+ * @param {(this: DocinfoProcessorDslInterface) => void} fn - Registration function bound to the docinfo processor DSL.
14916
+ * @returns {ProcessorExtension} the Extension stored in the registry.
14917
+ *
14806
14918
  * @param {...*} args - Class constructor, instance, or block function.
14807
14919
  * @returns {ProcessorExtension} the Extension stored in the registry.
14808
14920
  */
@@ -14870,6 +14982,20 @@ class Registry {
14870
14982
  * this.process(function (parent, reader, attrs) { ... })
14871
14983
  * })
14872
14984
  *
14985
+ * @overload
14986
+ * @param {typeof BlockProcessor} processor - A BlockProcessor subclass.
14987
+ * @param {string} [name] - Optional explicit block name.
14988
+ * @returns {ProcessorExtension} an Extension proxy object.
14989
+ *
14990
+ * @overload
14991
+ * @param {string} name - The block name.
14992
+ * @param {(this: BlockProcessorDslInterface) => void} fn - Registration function bound to the block DSL.
14993
+ * @returns {ProcessorExtension} an Extension proxy object.
14994
+ *
14995
+ * @overload
14996
+ * @param {(this: BlockProcessorDslInterface) => void} fn - Registration function bound to the block DSL.
14997
+ * @returns {ProcessorExtension} an Extension proxy object.
14998
+ *
14873
14999
  * @param {...*} args - Class constructor, instance, block function, or name + one of those.
14874
15000
  * @returns {ProcessorExtension} an Extension proxy object.
14875
15001
  */
@@ -14920,6 +15046,20 @@ class Registry {
14920
15046
  /**
14921
15047
  * Register a BlockMacroProcessor with the extension registry.
14922
15048
  *
15049
+ * @overload
15050
+ * @param {typeof BlockMacroProcessor} processor - A BlockMacroProcessor subclass.
15051
+ * @param {string} [name] - Optional explicit macro name.
15052
+ * @returns {ProcessorExtension} the Extension stored in the registry.
15053
+ *
15054
+ * @overload
15055
+ * @param {string} name - The macro name.
15056
+ * @param {(this: BlockMacroProcessorDslInterface) => void} fn - Registration function bound to the block macro DSL.
15057
+ * @returns {ProcessorExtension} the Extension stored in the registry.
15058
+ *
15059
+ * @overload
15060
+ * @param {(this: BlockMacroProcessorDslInterface) => void} fn - Registration function bound to the block macro DSL.
15061
+ * @returns {ProcessorExtension} the Extension stored in the registry.
15062
+ *
14923
15063
  * @param {...*} args - Class constructor, instance, or block function.
14924
15064
  * @returns {ProcessorExtension} the Extension stored in the registry.
14925
15065
  */
@@ -14975,6 +15115,20 @@ class Registry {
14975
15115
  /**
14976
15116
  * Register an InlineMacroProcessor with the extension registry.
14977
15117
  *
15118
+ * @overload
15119
+ * @param {typeof InlineMacroProcessor} processor - An InlineMacroProcessor subclass.
15120
+ * @param {string} [name] - Optional explicit macro name.
15121
+ * @returns {ProcessorExtension} the Extension stored in the registry.
15122
+ *
15123
+ * @overload
15124
+ * @param {string} name - The macro name.
15125
+ * @param {(this: InlineMacroProcessorDslInterface) => void} fn - Registration function bound to the inline macro DSL.
15126
+ * @returns {ProcessorExtension} the Extension stored in the registry.
15127
+ *
15128
+ * @overload
15129
+ * @param {(this: InlineMacroProcessorDslInterface) => void} fn - Registration function bound to the inline macro DSL.
15130
+ * @returns {ProcessorExtension} the Extension stored in the registry.
15131
+ *
14978
15132
  * @param {...*} args - Class constructor, instance, or block function.
14979
15133
  * @returns {ProcessorExtension} the Extension stored in the registry.
14980
15134
  */
@@ -16091,7 +16245,7 @@ class Document extends AbstractBlock {
16091
16245
 
16092
16246
  const attrOverrides = this._attributeOverrides;
16093
16247
  attrOverrides.asciidoctor = '';
16094
- attrOverrides['asciidoctor-version'] = '3.0.0.dev'; // matches Ruby VERSION
16248
+ attrOverrides['asciidoctor-version'] = packageJson.version;
16095
16249
 
16096
16250
  const safeModeName = SafeMode.nameForValue(this.safe);
16097
16251
  attrOverrides['safe-mode-name'] = safeModeName;
@@ -16283,16 +16437,16 @@ class Document extends AbstractBlock {
16283
16437
  // attribute (re)assignments are in scope; snapshot and restore the document
16284
16438
  // attributes so the downstream steps and conversion still start from the restored
16285
16439
  // (header) state, matching Ruby's restore_attributes-before-convert invariant.
16440
+ //
16441
+ // This runs in two passes because list item / table cell / dlist text may contain
16442
+ // natural cross-references (e.g. <<Some section title>>) that resolve against the
16443
+ // reftext→id map. Pass 1 substitutes titles and reftexts only, so every section
16444
+ // reftext is known; the map is then built; pass 2 substitutes the block content
16445
+ // text, where resolveId() can now match those natural references.
16286
16446
  const attributesSnapshot = { ...this.attributes };
16287
- await this._resolveAllTexts(this);
16288
- for (const key of Reflect.ownKeys(this.attributes))
16289
- delete this.attributes[key];
16290
- Object.assign(this.attributes, attributesSnapshot);
16291
- // Reset the footnote counter so that body-content footnotes (processed during conversion)
16292
- // start numbering from 1, reproducing Ruby's "out of sequence" quirk: title footnotes are
16293
- // numbered during parsing via apply_title_subs, then the counter restarts for body content.
16294
- delete this.attributes['footnote-number'];
16295
- delete this._counters['footnote-number'];
16447
+ // Pass 1: titles + reftexts (no block content text yet).
16448
+ await this._resolveAllTexts(this, false);
16449
+ this._restoreAttributeSnapshot(attributesSnapshot);
16296
16450
  // Pre-compute reftext for all registered inline anchor nodes.
16297
16451
  for (const ref of Object.values(this.catalog.refs)) {
16298
16452
  if (ref && typeof ref.precomputeReftext === 'function') {
@@ -16301,6 +16455,15 @@ class Document extends AbstractBlock {
16301
16455
  }
16302
16456
  // Build the reftext→id lookup map so that resolveId() is synchronous.
16303
16457
  await this._buildReftextsMap();
16458
+ // Pass 2: list item / table cell / dlist text, now that natural cross-references
16459
+ // can be resolved against the reftext→id map.
16460
+ await this._resolveAllTexts(this, true);
16461
+ this._restoreAttributeSnapshot(attributesSnapshot);
16462
+ // Reset the footnote counter so that body-content footnotes (processed during conversion)
16463
+ // start numbering from 1, reproducing Ruby's "out of sequence" quirk: title footnotes are
16464
+ // numbered during parsing via apply_title_subs, then the counter restarts for body content.
16465
+ delete this.attributes['footnote-number'];
16466
+ delete this._counters['footnote-number'];
16304
16467
 
16305
16468
  this._parsed = true;
16306
16469
  return this
@@ -17275,6 +17438,10 @@ class Document extends AbstractBlock {
17275
17438
  cell._innerDocument &&
17276
17439
  cell._innerContent == null
17277
17440
  ) {
17441
+ // Recurse into the inner document first so that AsciiDoc cells
17442
+ // of any nested table have their content computed before the
17443
+ // inner document (and the nested table) is rendered.
17444
+ await this._convertAsciiDocCells(cell._innerDocument);
17278
17445
  cell._innerContent = await cell._innerDocument.convert();
17279
17446
  }
17280
17447
  }
@@ -17355,12 +17522,33 @@ class Document extends AbstractBlock {
17355
17522
  : ['attributes']
17356
17523
  }
17357
17524
 
17525
+ /**
17526
+ * @private
17527
+ * Restore the document attributes to a previously captured snapshot, discarding any
17528
+ * body-level (re)assignments replayed while pre-computing text. Mirrors Ruby's
17529
+ * restore_attributes-before-convert invariant.
17530
+ * @param {Object} snapshot - The attributes snapshot to restore.
17531
+ */
17532
+ _restoreAttributeSnapshot(snapshot) {
17533
+ for (const key of Reflect.ownKeys(this.attributes))
17534
+ delete this.attributes[key];
17535
+ Object.assign(this.attributes, snapshot);
17536
+ }
17537
+
17358
17538
  /**
17359
17539
  * @private
17360
17540
  * Walk the block tree and pre-compute all async text values.
17361
17541
  * Handles titles (AbstractBlock), list item text, table cell text, and reftexts.
17362
- */
17363
- async _resolveAllTexts(block) {
17542
+ *
17543
+ * Runs in two passes (see {@link parse}): with `resolveContent` false only titles and
17544
+ * reftexts are substituted (so the reftext→id map can be built); with `resolveContent`
17545
+ * true the list item / table cell / dlist text is substituted, resolving any natural
17546
+ * cross-references against the now-complete map. Title/reftext pre-computation is
17547
+ * idempotent (results are cached), so running it in both passes is a no-op the second time.
17548
+ * @param {AbstractBlock} block - The block to resolve.
17549
+ * @param {boolean} resolveContent - Whether to substitute list item / cell / dlist text.
17550
+ */
17551
+ async _resolveAllTexts(block, resolveContent) {
17364
17552
  // Replay this block's attribute entries (in document order, since the walk is
17365
17553
  // depth-first pre-order like conversion) so that body-level attribute assignments
17366
17554
  // and reassignments are in scope when the block's — and its descendants' and later
@@ -17386,12 +17574,12 @@ class Document extends AbstractBlock {
17386
17574
  // dlist.blocks is an array of [[term, ...], item_or_null] pairs.
17387
17575
  for (const [terms, item] of block.blocks ?? []) {
17388
17576
  for (const term of terms ?? []) {
17389
- await term.precomputeText?.();
17390
- await this._resolveAllTexts(term);
17577
+ if (resolveContent) await term.precomputeText?.();
17578
+ await this._resolveAllTexts(term, resolveContent);
17391
17579
  }
17392
17580
  if (item) {
17393
- await item.precomputeText?.();
17394
- await this._resolveAllTexts(item);
17581
+ if (resolveContent) await item.precomputeText?.();
17582
+ await this._resolveAllTexts(item, resolveContent);
17395
17583
  }
17396
17584
  }
17397
17585
  } else if (ctx === 'table') {
@@ -17401,14 +17589,14 @@ class Document extends AbstractBlock {
17401
17589
  ...(block.rows?.foot ?? []),
17402
17590
  ]) {
17403
17591
  for (const cell of row) {
17404
- await cell.precomputeText?.();
17592
+ if (resolveContent) await cell.precomputeText?.();
17405
17593
  await cell.precomputeReftext?.();
17406
17594
  }
17407
17595
  }
17408
17596
  } else {
17409
17597
  for (const child of block.blocks ?? []) {
17410
- await child.precomputeText?.();
17411
- await this._resolveAllTexts(child);
17598
+ if (resolveContent) await child.precomputeText?.();
17599
+ await this._resolveAllTexts(child, resolveContent);
17412
17600
  }
17413
17601
  }
17414
17602
  }
@@ -17700,14 +17888,19 @@ class Document extends AbstractBlock {
17700
17888
  let newBasebackend, newFiletype;
17701
17889
 
17702
17890
  if (converter && typeof converter._getBackendTraits === 'function') {
17703
- newBasebackend = converter.basebackend();
17704
- newFiletype = converter.filetype();
17705
- const htmlsyntax = converter.htmlsyntax();
17891
+ // Read the traits object directly rather than the same-named accessor
17892
+ // methods. A user converter that declares flat string properties
17893
+ // (`converter.outfilesuffix = '.html'`, convention #2) keeps them intact,
17894
+ // so callers reading `converter.outfilesuffix` still see the string.
17895
+ const traits = converter._getBackendTraits();
17896
+ newBasebackend = traits.basebackend;
17897
+ newFiletype = traits.filetype;
17898
+ const htmlsyntax = traits.htmlsyntax;
17706
17899
  if (htmlsyntax) attrs.htmlsyntax = htmlsyntax;
17707
17900
  if (init) {
17708
- attrs.outfilesuffix ??= converter.outfilesuffix();
17901
+ attrs.outfilesuffix ??= traits.outfilesuffix;
17709
17902
  } else if (!this.isAttributeLocked('outfilesuffix')) {
17710
- attrs.outfilesuffix = converter.outfilesuffix();
17903
+ attrs.outfilesuffix = traits.outfilesuffix;
17711
17904
  }
17712
17905
  } else if (converter) {
17713
17906
  const traits = deriveBackendTraits(newBackend);
@@ -20954,7 +21147,7 @@ class Html5Converter extends ConverterBase {
20954
21147
  let reproducible;
20955
21148
  if (!(reproducible = node.hasAttribute('reproducible'))) {
20956
21149
  result.push(
20957
- `<meta name="generator" content="Asciidoctor ${node.getAttribute('asciidoctor-version')}"${slash}>`
21150
+ `<meta name="generator" content="Asciidoctor.js ${node.getAttribute('asciidoctor-version')}"${slash}>`
20958
21151
  );
20959
21152
  }
20960
21153
  if (node.hasAttribute('app-name')) {
@@ -21671,7 +21864,9 @@ ${await node.content()}
21671
21864
  const slash = this._voidSlash;
21672
21865
  let img, src;
21673
21866
  if (
21674
- (node.hasAttribute('format', 'svg') || target.includes('.svg')) &&
21867
+ (node.hasAttribute('format', 'svg') ||
21868
+ target.includes('.svg') ||
21869
+ target.startsWith('data:image/svg+xml')) &&
21675
21870
  node.document.safe < SafeMode.SECURE
21676
21871
  ) {
21677
21872
  if (node.hasOption('inline')) {
@@ -22512,7 +22707,9 @@ Your browser does not support the video tag.
22512
22707
  if (node.hasAttribute('title'))
22513
22708
  attrs += ` title="${node.getAttribute('title')}"`;
22514
22709
  if (
22515
- (node.hasAttribute('format', 'svg') || target.includes('.svg')) &&
22710
+ (node.hasAttribute('format', 'svg') ||
22711
+ target.includes('.svg') ||
22712
+ target.startsWith('data:image/svg+xml')) &&
22516
22713
  node.document.safe < SafeMode.SECURE
22517
22714
  ) {
22518
22715
  if (node.hasOption('inline')) {
@@ -22605,12 +22802,17 @@ Your browser does not support the video tag.
22605
22802
 
22606
22803
  // NOTE expose readSvgContents for Bespoke converter
22607
22804
  async readSvgContents(node, target) {
22608
- let svg = await node.readContents(target, {
22609
- start: node.document.getAttribute('imagesdir'),
22610
- normalize: true,
22611
- label: 'SVG',
22612
- warnIfEmpty: true,
22613
- });
22805
+ // A data-URI carries the SVG in the target itself (e.g. an embedded diagram
22806
+ // produced with `:data-uri:`), so decode it directly rather than trying to
22807
+ // read it as a file or remote URI.
22808
+ let svg = target.startsWith('data:')
22809
+ ? this._decodeDataUri(target)
22810
+ : await node.readContents(target, {
22811
+ start: node.document.getAttribute('imagesdir'),
22812
+ normalize: true,
22813
+ label: 'SVG',
22814
+ warnIfEmpty: true,
22815
+ });
22614
22816
  if (!svg) return null
22615
22817
  if (!svg.startsWith('<svg')) svg = svg.replace(SvgPreambleRx, '');
22616
22818
  // Fix incomplete SVG start tag (missing closing >) by inserting > before the first child element.
@@ -22642,6 +22844,27 @@ Your browser does not support the video tag.
22642
22844
 
22643
22845
  // ── Private helpers ─────────────────────────────────────────────────────────
22644
22846
 
22847
+ /**
22848
+ * Decode an inline `data:` URI to its text contents (e.g. an SVG document) so
22849
+ * an image whose target is a data-URI can be embedded inline. Supports both
22850
+ * Base64 (`;base64,`) and percent-encoded payloads. Returns null when the
22851
+ * payload is missing.
22852
+ *
22853
+ * @internal
22854
+ * @private
22855
+ */
22856
+ _decodeDataUri(target) {
22857
+ const comma = target.indexOf(',');
22858
+ if (comma === -1) return null
22859
+ const meta = target.slice('data:'.length, comma);
22860
+ const data = target.slice(comma + 1);
22861
+ if (/;base64\b/i.test(meta)) {
22862
+ const bytes = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
22863
+ return new TextDecoder('utf-8').decode(bytes)
22864
+ }
22865
+ return decodeURIComponent(data)
22866
+ }
22867
+
22645
22868
  /**
22646
22869
  * @internal
22647
22870
  * @private
@@ -24006,7 +24229,7 @@ class ManPageConverter extends ConverterBase {
24006
24229
  `'\\" t
24007
24230
  .\\" Title: ${mantitle}
24008
24231
  .\\" Author: ${node.hasAttribute('authors') ? node.getAttribute('authors') : '[see the "AUTHOR(S)" section]'}
24009
- .\\" Generator: Asciidoctor ${node.getAttribute('asciidoctor-version')}`,
24232
+ .\\" Generator: Asciidoctor.js ${node.getAttribute('asciidoctor-version')}`,
24010
24233
  ];
24011
24234
 
24012
24235
  if (docdate) result.push(`.\\" Date: ${docdate}`);