@asciidoctor/core 4.0.0 → 4.0.2
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/build/browser/index.js +230 -81
- package/build/node/index.cjs +277 -93
- package/package.json +2 -2
- package/src/abstract_node.js +6 -3
- package/src/browser/reader.js +2 -2
- package/src/browser.js +8 -1
- package/src/converter/html5.js +39 -9
- package/src/converter/manpage.js +1 -1
- package/src/converter/template.js +47 -13
- package/src/converter.js +22 -9
- package/src/document.js +71 -21
- package/src/index.js +8 -1
- package/src/logging.js +41 -10
- package/src/parser.js +13 -2
- package/src/reader.js +25 -13
- package/src/substitutors.js +9 -9
- package/types/abstract_node.d.ts +4 -3
- package/types/document.d.ts +16 -0
- package/types/index.d.cts +2 -1
- package/types/index.d.ts +2 -1
- package/types/logging.d.ts +49 -5
- package/types/reader.d.ts +2 -2
- package/types/substitutors.d.ts +18 -18
package/build/browser/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var version = "4.0.
|
|
1
|
+
var version = "4.0.2";
|
|
2
2
|
const packageJson = {
|
|
3
3
|
version: version};
|
|
4
4
|
|
|
@@ -1745,14 +1745,23 @@ Logger.BasicFormatter = class {
|
|
|
1745
1745
|
|
|
1746
1746
|
Logger.AutoFormattingMessage = {
|
|
1747
1747
|
/**
|
|
1748
|
-
* Attach auto-formatting to any plain object carrying
|
|
1749
|
-
*
|
|
1748
|
+
* Attach auto-formatting to any plain object carrying
|
|
1749
|
+
* { text, source_location, include_location }.
|
|
1750
|
+
*
|
|
1751
|
+
* The location(s) are rendered only by inspect()/toString() (used when a
|
|
1752
|
+
* stderr Logger formats the line); the structured `source_location` /
|
|
1753
|
+
* `include_location` remain on the object so a MemoryLogger can record them
|
|
1754
|
+
* on the resulting LogMessage without duplicating them inside `text`.
|
|
1755
|
+
* @param {{text: string, source_location?: any, include_location?: any}} obj
|
|
1750
1756
|
* @returns {typeof obj} The same object with inspect() and toString() added.
|
|
1751
1757
|
*/
|
|
1752
1758
|
attach(obj) {
|
|
1753
1759
|
obj.inspect = function () {
|
|
1754
1760
|
const sloc = this.source_location;
|
|
1755
|
-
|
|
1761
|
+
const iloc = this.include_location;
|
|
1762
|
+
let text = sloc ? `${sloc}: ${this.text}` : this.text;
|
|
1763
|
+
if (iloc) text += ` (${iloc})`;
|
|
1764
|
+
return text
|
|
1756
1765
|
};
|
|
1757
1766
|
obj.toString = obj.inspect;
|
|
1758
1767
|
return obj
|
|
@@ -1763,27 +1772,45 @@ Logger.AutoFormattingMessage = {
|
|
|
1763
1772
|
|
|
1764
1773
|
/** Wrapper stored by MemoryLogger; provides getSeverity/getText/getSourceLocation. */
|
|
1765
1774
|
class LogMessage {
|
|
1775
|
+
/**
|
|
1776
|
+
* @param {string} severity - Severity label, e.g. 'ERROR'.
|
|
1777
|
+
* @param {string|{text: string, source_location?: import('./reader.js').Cursor}|null} message
|
|
1778
|
+
*/
|
|
1766
1779
|
constructor(severity, message) {
|
|
1767
1780
|
this.message = message;
|
|
1781
|
+
/** @type {string} */
|
|
1768
1782
|
this.severity = severity; // string label, e.g. 'ERROR'
|
|
1769
1783
|
// AutoFormattingMessage objects carry { text, source_location }
|
|
1770
1784
|
if (message !== null && typeof message === 'object' && 'text' in message) {
|
|
1771
|
-
|
|
1772
|
-
this.
|
|
1785
|
+
/** @type {string} */
|
|
1786
|
+
this.text = message.text;
|
|
1787
|
+
/** @type {import('./reader.js').Cursor|null} */
|
|
1788
|
+
this.sourceLocation = message.source_location ?? null;
|
|
1773
1789
|
} else {
|
|
1774
|
-
this.
|
|
1775
|
-
this.
|
|
1790
|
+
this.text = message != null ? String(message) : '';
|
|
1791
|
+
this.sourceLocation = null;
|
|
1776
1792
|
}
|
|
1777
1793
|
}
|
|
1778
1794
|
|
|
1795
|
+
/**
|
|
1796
|
+
* @returns {string} The severity label, e.g. 'ERROR'.
|
|
1797
|
+
*/
|
|
1779
1798
|
getSeverity() {
|
|
1780
1799
|
return this.severity
|
|
1781
1800
|
}
|
|
1801
|
+
|
|
1802
|
+
/**
|
|
1803
|
+
* @returns {string} The message text.
|
|
1804
|
+
*/
|
|
1782
1805
|
getText() {
|
|
1783
|
-
return this.
|
|
1806
|
+
return this.text
|
|
1784
1807
|
}
|
|
1808
|
+
|
|
1809
|
+
/**
|
|
1810
|
+
* @returns {import('./reader.js').Cursor|undefined} The source location, if any.
|
|
1811
|
+
*/
|
|
1785
1812
|
getSourceLocation() {
|
|
1786
|
-
return this.
|
|
1813
|
+
return this.sourceLocation ?? undefined
|
|
1787
1814
|
}
|
|
1788
1815
|
}
|
|
1789
1816
|
|
|
@@ -1796,6 +1823,7 @@ class MemoryLogger {
|
|
|
1796
1823
|
// matching Ruby's MemoryLogger (level: UNKNOWN). The add() method stores all
|
|
1797
1824
|
// messages unconditionally — level is only used by the isDebug() guard.
|
|
1798
1825
|
this.level = Severity.UNKNOWN;
|
|
1826
|
+
/** @type {LogMessage[]} */
|
|
1799
1827
|
this.messages = [];
|
|
1800
1828
|
}
|
|
1801
1829
|
|
|
@@ -1803,6 +1831,9 @@ class MemoryLogger {
|
|
|
1803
1831
|
return new MemoryLogger()
|
|
1804
1832
|
}
|
|
1805
1833
|
|
|
1834
|
+
/**
|
|
1835
|
+
* @returns {LogMessage[]} The log messages recorded so far, in order.
|
|
1836
|
+
*/
|
|
1806
1837
|
getMessages() {
|
|
1807
1838
|
return this.messages
|
|
1808
1839
|
}
|
|
@@ -3001,9 +3032,10 @@ class AbstractNode {
|
|
|
3001
3032
|
* Construct a URI reference or data URI to the target image.
|
|
3002
3033
|
*
|
|
3003
3034
|
* If the target image is already a URI it is left untouched (unless data-uri
|
|
3004
|
-
* conversion is requested).
|
|
3005
|
-
*
|
|
3006
|
-
* the
|
|
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.
|
|
3007
3039
|
*
|
|
3008
3040
|
* NOTE: When the document has both 'data-uri' and 'allow-uri-read' enabled
|
|
3009
3041
|
* and the resolved image URL is a remote URI, this method returns a Promise
|
|
@@ -3014,6 +3046,8 @@ class AbstractNode {
|
|
|
3014
3046
|
* @returns {Promise<string>} a Promise resolving to a String reference or data URI.
|
|
3015
3047
|
*/
|
|
3016
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
|
|
3017
3051
|
const doc = this.document;
|
|
3018
3052
|
if (doc.safe < SafeMode.SECURE && doc.hasAttribute('data-uri')) {
|
|
3019
3053
|
let imagesBase;
|
|
@@ -5421,7 +5455,18 @@ class SecurityError extends Error {
|
|
|
5421
5455
|
function applyBackendTraits(instance) {
|
|
5422
5456
|
instance._backendTraits = null;
|
|
5423
5457
|
|
|
5424
|
-
|
|
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) {
|
|
5425
5470
|
if (value) {
|
|
5426
5471
|
const traits = (this._backendTraits ??= {});
|
|
5427
5472
|
traits.basebackend = value;
|
|
@@ -5435,19 +5480,19 @@ function applyBackendTraits(instance) {
|
|
|
5435
5480
|
return value
|
|
5436
5481
|
}
|
|
5437
5482
|
return this._getBackendTraits().basebackend
|
|
5438
|
-
};
|
|
5439
|
-
|
|
5483
|
+
});
|
|
5484
|
+
defineTraitAccessor('filetype', function (value = null) {
|
|
5440
5485
|
if (value) return (this._getBackendTraits().filetype = value)
|
|
5441
5486
|
return this._getBackendTraits().filetype
|
|
5442
|
-
};
|
|
5443
|
-
|
|
5487
|
+
});
|
|
5488
|
+
defineTraitAccessor('htmlsyntax', function (value = null) {
|
|
5444
5489
|
if (value) return (this._getBackendTraits().htmlsyntax = value)
|
|
5445
5490
|
return this._getBackendTraits().htmlsyntax
|
|
5446
|
-
};
|
|
5447
|
-
|
|
5491
|
+
});
|
|
5492
|
+
defineTraitAccessor('outfilesuffix', function (value = null) {
|
|
5448
5493
|
if (value) return (this._getBackendTraits().outfilesuffix = value)
|
|
5449
5494
|
return this._getBackendTraits().outfilesuffix
|
|
5450
|
-
};
|
|
5495
|
+
});
|
|
5451
5496
|
instance.supportsTemplates = function (value = true) {
|
|
5452
5497
|
this._getBackendTraits().supportsTemplates = value;
|
|
5453
5498
|
};
|
|
@@ -5531,7 +5576,9 @@ function normalizeConverter(converter, backend) {
|
|
|
5531
5576
|
}
|
|
5532
5577
|
}
|
|
5533
5578
|
|
|
5534
|
-
// Apply the BackendTraits mixin so Document can
|
|
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.
|
|
5535
5582
|
applyBackendTraits(converter);
|
|
5536
5583
|
if (traits) {
|
|
5537
5584
|
converter._backendTraits = traits;
|
|
@@ -6268,7 +6315,7 @@ function resolveBrowserIncludePath(reader, target, attrlist) {
|
|
|
6268
6315
|
// ── Rule 2: target is an absolute URL (http:// / https:// / …) ───────────
|
|
6269
6316
|
} else if (isUriish(pTarget)) {
|
|
6270
6317
|
const descends = pathResolver.descendsFrom(pTarget, baseDir);
|
|
6271
|
-
if (descends === false && !doc.
|
|
6318
|
+
if (descends === false && !doc.hasAttribute('allow-uri-read')) {
|
|
6272
6319
|
return reader.replaceNextLine(_linkReplacement(reader, target, attrlist))
|
|
6273
6320
|
}
|
|
6274
6321
|
incPath = relpath = pTarget;
|
|
@@ -6302,7 +6349,7 @@ function resolveBrowserIncludePath(reader, target, attrlist) {
|
|
|
6302
6349
|
} else {
|
|
6303
6350
|
// Nested include: context dir is an absolute URL.
|
|
6304
6351
|
const ctxDescends = pathResolver.descendsFrom(ctxDir, baseDir);
|
|
6305
|
-
if (ctxDescends !== false || doc.
|
|
6352
|
+
if (ctxDescends !== false || doc.hasAttribute('allow-uri-read')) {
|
|
6306
6353
|
incPath = `${ctxDir}/${pTarget}`;
|
|
6307
6354
|
relpath = ctxDescends !== false ? incPath.slice(ctxDescends) : pTarget;
|
|
6308
6355
|
} else {
|
|
@@ -6966,22 +7013,34 @@ class Reader {
|
|
|
6966
7013
|
}
|
|
6967
7014
|
|
|
6968
7015
|
/** @param {string} msg @param {{ sourceLocation?: any, includeLocation?: any }} [opts] */
|
|
6969
|
-
_logWarn(msg,
|
|
6970
|
-
|
|
6971
|
-
if (includeLocation) text += ` (${includeLocation.lineInfo})`;
|
|
6972
|
-
this.logger.warn(text);
|
|
7016
|
+
_logWarn(msg, opts = {}) {
|
|
7017
|
+
this.logger.warn(this._messageWithContext(msg, opts));
|
|
6973
7018
|
}
|
|
6974
7019
|
_logError(msg, opts = {}) {
|
|
6975
|
-
|
|
6976
|
-
? `${opts.sourceLocation.lineInfo}: ${msg}`
|
|
6977
|
-
: msg;
|
|
6978
|
-
if (opts.includeLocation) text += ` (${opts.includeLocation.lineInfo})`;
|
|
6979
|
-
this.logger.error(text);
|
|
7020
|
+
this.logger.error(this._messageWithContext(msg, opts));
|
|
6980
7021
|
}
|
|
6981
7022
|
/** @param {string} msg @param {{ sourceLocation?: any }} [opts] */
|
|
6982
|
-
_logInfo(msg,
|
|
6983
|
-
|
|
6984
|
-
|
|
7023
|
+
_logInfo(msg, opts = {}) {
|
|
7024
|
+
this.logger.info(this._messageWithContext(msg, opts));
|
|
7025
|
+
}
|
|
7026
|
+
|
|
7027
|
+
/**
|
|
7028
|
+
* Build an auto-formatting message that keeps the cursor as a structured
|
|
7029
|
+
* source_location (rather than baking it into the text). When displayed by a
|
|
7030
|
+
* stderr Logger the location is rendered as a "<path>: line <N>: " prefix, but
|
|
7031
|
+
* a MemoryLogger records it separately on the LogMessage so consumers can call
|
|
7032
|
+
* getSourceLocation(). Mirrors Ruby's Logging#message_with_context.
|
|
7033
|
+
* @param {string} msg
|
|
7034
|
+
* @param {{ sourceLocation?: any, includeLocation?: any }} [opts]
|
|
7035
|
+
* @internal
|
|
7036
|
+
*/
|
|
7037
|
+
_messageWithContext(msg, { sourceLocation, includeLocation } = {}) {
|
|
7038
|
+
if (!sourceLocation && !includeLocation) return msg
|
|
7039
|
+
return Logger.AutoFormattingMessage.attach({
|
|
7040
|
+
text: msg,
|
|
7041
|
+
source_location: sourceLocation ?? null,
|
|
7042
|
+
include_location: includeLocation ?? null,
|
|
7043
|
+
})
|
|
6985
7044
|
}
|
|
6986
7045
|
}
|
|
6987
7046
|
|
|
@@ -7739,7 +7798,7 @@ class PreprocessorReader extends Reader {
|
|
|
7739
7798
|
}
|
|
7740
7799
|
|
|
7741
7800
|
if (isUriish(target) || typeof this._dir !== 'string') {
|
|
7742
|
-
if (!doc.
|
|
7801
|
+
if (!doc.hasAttribute('allow-uri-read')) {
|
|
7743
7802
|
this._logWarn(
|
|
7744
7803
|
`cannot include contents of URI: ${target} (allow-uri-read attribute not enabled)`,
|
|
7745
7804
|
{ sourceLocation: this.cursor }
|
|
@@ -10349,7 +10408,12 @@ class Parser {
|
|
|
10349
10408
|
}
|
|
10350
10409
|
}
|
|
10351
10410
|
(intro ?? section).blocks.push(newBlock);
|
|
10352
|
-
|
|
10411
|
+
// Reset the shared attributes object for the next block. Use Reflect.ownKeys
|
|
10412
|
+
// (not Object.keys) so the Symbol-keyed attribute entries (ATTR_ENTRIES_KEY)
|
|
10413
|
+
// are cleared too; otherwise the array of AttributeEntry objects leaks and
|
|
10414
|
+
// accumulates across blocks, causing reassigned attributes (e.g. a body-level
|
|
10415
|
+
// `:name:` redefined later) to all resolve to the final value at playback time.
|
|
10416
|
+
for (const key of Reflect.ownKeys(attributes)) delete attributes[key];
|
|
10353
10417
|
}
|
|
10354
10418
|
}
|
|
10355
10419
|
|
|
@@ -11196,7 +11260,13 @@ class Parser {
|
|
|
11196
11260
|
}
|
|
11197
11261
|
}
|
|
11198
11262
|
|
|
11199
|
-
|
|
11263
|
+
// Reflect.ownKeys (not Object.keys) so a block carrying only Symbol-keyed
|
|
11264
|
+
// attribute entries (ATTR_ENTRIES_KEY) — e.g. an `:attr:` entry immediately
|
|
11265
|
+
// preceding a list or table — still receives them, matching Ruby's
|
|
11266
|
+
// `attributes.empty?` where the `:attribute_entries` key is counted. Without this
|
|
11267
|
+
// the entries are dropped and the attribute is not played back for that block.
|
|
11268
|
+
if (Reflect.ownKeys(attributes).length > 0)
|
|
11269
|
+
block.updateAttributes(attributes);
|
|
11200
11270
|
block.commitSubs();
|
|
11201
11271
|
|
|
11202
11272
|
if (block.hasSub('callouts')) {
|
|
@@ -16037,7 +16107,7 @@ class Document extends AbstractBlock {
|
|
|
16037
16107
|
|
|
16038
16108
|
const attrOverrides = this._attributeOverrides;
|
|
16039
16109
|
attrOverrides.asciidoctor = '';
|
|
16040
|
-
attrOverrides['asciidoctor-version'] =
|
|
16110
|
+
attrOverrides['asciidoctor-version'] = packageJson.version;
|
|
16041
16111
|
|
|
16042
16112
|
const safeModeName = SafeMode.nameForValue(this.safe);
|
|
16043
16113
|
attrOverrides['safe-mode-name'] = safeModeName;
|
|
@@ -16224,13 +16294,21 @@ class Document extends AbstractBlock {
|
|
|
16224
16294
|
}
|
|
16225
16295
|
|
|
16226
16296
|
// Pre-compute all async text values (titles, list item text, cell text, reftexts)
|
|
16227
|
-
// so that synchronous getters work correctly during conversion.
|
|
16228
|
-
|
|
16229
|
-
//
|
|
16230
|
-
//
|
|
16231
|
-
//
|
|
16232
|
-
|
|
16233
|
-
|
|
16297
|
+
// so that synchronous getters work correctly during conversion. _resolveAllTexts
|
|
16298
|
+
// replays attribute entries in document order (mirroring conversion) so body-level
|
|
16299
|
+
// attribute (re)assignments are in scope; snapshot and restore the document
|
|
16300
|
+
// attributes so the downstream steps and conversion still start from the restored
|
|
16301
|
+
// (header) state, matching Ruby's restore_attributes-before-convert invariant.
|
|
16302
|
+
//
|
|
16303
|
+
// This runs in two passes because list item / table cell / dlist text may contain
|
|
16304
|
+
// natural cross-references (e.g. <<Some section title>>) that resolve against the
|
|
16305
|
+
// reftext→id map. Pass 1 substitutes titles and reftexts only, so every section
|
|
16306
|
+
// reftext is known; the map is then built; pass 2 substitutes the block content
|
|
16307
|
+
// text, where resolveId() can now match those natural references.
|
|
16308
|
+
const attributesSnapshot = { ...this.attributes };
|
|
16309
|
+
// Pass 1: titles + reftexts (no block content text yet).
|
|
16310
|
+
await this._resolveAllTexts(this, false);
|
|
16311
|
+
this._restoreAttributeSnapshot(attributesSnapshot);
|
|
16234
16312
|
// Pre-compute reftext for all registered inline anchor nodes.
|
|
16235
16313
|
for (const ref of Object.values(this.catalog.refs)) {
|
|
16236
16314
|
if (ref && typeof ref.precomputeReftext === 'function') {
|
|
@@ -16239,6 +16317,15 @@ class Document extends AbstractBlock {
|
|
|
16239
16317
|
}
|
|
16240
16318
|
// Build the reftext→id lookup map so that resolveId() is synchronous.
|
|
16241
16319
|
await this._buildReftextsMap();
|
|
16320
|
+
// Pass 2: list item / table cell / dlist text, now that natural cross-references
|
|
16321
|
+
// can be resolved against the reftext→id map.
|
|
16322
|
+
await this._resolveAllTexts(this, true);
|
|
16323
|
+
this._restoreAttributeSnapshot(attributesSnapshot);
|
|
16324
|
+
// Reset the footnote counter so that body-content footnotes (processed during conversion)
|
|
16325
|
+
// start numbering from 1, reproducing Ruby's "out of sequence" quirk: title footnotes are
|
|
16326
|
+
// numbered during parsing via apply_title_subs, then the counter restarts for body content.
|
|
16327
|
+
delete this.attributes['footnote-number'];
|
|
16328
|
+
delete this._counters['footnote-number'];
|
|
16242
16329
|
|
|
16243
16330
|
this._parsed = true;
|
|
16244
16331
|
return this
|
|
@@ -17293,12 +17380,39 @@ class Document extends AbstractBlock {
|
|
|
17293
17380
|
: ['attributes']
|
|
17294
17381
|
}
|
|
17295
17382
|
|
|
17383
|
+
/**
|
|
17384
|
+
* @private
|
|
17385
|
+
* Restore the document attributes to a previously captured snapshot, discarding any
|
|
17386
|
+
* body-level (re)assignments replayed while pre-computing text. Mirrors Ruby's
|
|
17387
|
+
* restore_attributes-before-convert invariant.
|
|
17388
|
+
* @param {Object} snapshot - The attributes snapshot to restore.
|
|
17389
|
+
*/
|
|
17390
|
+
_restoreAttributeSnapshot(snapshot) {
|
|
17391
|
+
for (const key of Reflect.ownKeys(this.attributes))
|
|
17392
|
+
delete this.attributes[key];
|
|
17393
|
+
Object.assign(this.attributes, snapshot);
|
|
17394
|
+
}
|
|
17395
|
+
|
|
17296
17396
|
/**
|
|
17297
17397
|
* @private
|
|
17298
17398
|
* Walk the block tree and pre-compute all async text values.
|
|
17299
17399
|
* Handles titles (AbstractBlock), list item text, table cell text, and reftexts.
|
|
17300
|
-
|
|
17301
|
-
|
|
17400
|
+
*
|
|
17401
|
+
* Runs in two passes (see {@link parse}): with `resolveContent` false only titles and
|
|
17402
|
+
* reftexts are substituted (so the reftext→id map can be built); with `resolveContent`
|
|
17403
|
+
* true the list item / table cell / dlist text is substituted, resolving any natural
|
|
17404
|
+
* cross-references against the now-complete map. Title/reftext pre-computation is
|
|
17405
|
+
* idempotent (results are cached), so running it in both passes is a no-op the second time.
|
|
17406
|
+
* @param {AbstractBlock} block - The block to resolve.
|
|
17407
|
+
* @param {boolean} resolveContent - Whether to substitute list item / cell / dlist text.
|
|
17408
|
+
*/
|
|
17409
|
+
async _resolveAllTexts(block, resolveContent) {
|
|
17410
|
+
// Replay this block's attribute entries (in document order, since the walk is
|
|
17411
|
+
// depth-first pre-order like conversion) so that body-level attribute assignments
|
|
17412
|
+
// and reassignments are in scope when the block's — and its descendants' and later
|
|
17413
|
+
// siblings' — title / list item / table cell / reftext values are substituted.
|
|
17414
|
+
// Mirrors AbstractBlock#convert, which calls playbackAttributes before converting.
|
|
17415
|
+
this.playbackAttributes(block.attributes);
|
|
17302
17416
|
// The header section lives outside document.blocks; pre-compute its title here so
|
|
17303
17417
|
// that doc.doctitle() returns the fully-substituted title (with replacements applied,
|
|
17304
17418
|
// e.g. ' → ’) rather than the header-subs-only fallback.
|
|
@@ -17318,12 +17432,12 @@ class Document extends AbstractBlock {
|
|
|
17318
17432
|
// dlist.blocks is an array of [[term, ...], item_or_null] pairs.
|
|
17319
17433
|
for (const [terms, item] of block.blocks ?? []) {
|
|
17320
17434
|
for (const term of terms ?? []) {
|
|
17321
|
-
await term.precomputeText?.();
|
|
17322
|
-
await this._resolveAllTexts(term);
|
|
17435
|
+
if (resolveContent) await term.precomputeText?.();
|
|
17436
|
+
await this._resolveAllTexts(term, resolveContent);
|
|
17323
17437
|
}
|
|
17324
17438
|
if (item) {
|
|
17325
|
-
await item.precomputeText?.();
|
|
17326
|
-
await this._resolveAllTexts(item);
|
|
17439
|
+
if (resolveContent) await item.precomputeText?.();
|
|
17440
|
+
await this._resolveAllTexts(item, resolveContent);
|
|
17327
17441
|
}
|
|
17328
17442
|
}
|
|
17329
17443
|
} else if (ctx === 'table') {
|
|
@@ -17333,14 +17447,14 @@ class Document extends AbstractBlock {
|
|
|
17333
17447
|
...(block.rows?.foot ?? []),
|
|
17334
17448
|
]) {
|
|
17335
17449
|
for (const cell of row) {
|
|
17336
|
-
await cell.precomputeText?.();
|
|
17450
|
+
if (resolveContent) await cell.precomputeText?.();
|
|
17337
17451
|
await cell.precomputeReftext?.();
|
|
17338
17452
|
}
|
|
17339
17453
|
}
|
|
17340
17454
|
} else {
|
|
17341
17455
|
for (const child of block.blocks ?? []) {
|
|
17342
|
-
await child.precomputeText?.();
|
|
17343
|
-
await this._resolveAllTexts(child);
|
|
17456
|
+
if (resolveContent) await child.precomputeText?.();
|
|
17457
|
+
await this._resolveAllTexts(child, resolveContent);
|
|
17344
17458
|
}
|
|
17345
17459
|
}
|
|
17346
17460
|
}
|
|
@@ -17632,14 +17746,19 @@ class Document extends AbstractBlock {
|
|
|
17632
17746
|
let newBasebackend, newFiletype;
|
|
17633
17747
|
|
|
17634
17748
|
if (converter && typeof converter._getBackendTraits === 'function') {
|
|
17635
|
-
|
|
17636
|
-
|
|
17637
|
-
|
|
17749
|
+
// Read the traits object directly rather than the same-named accessor
|
|
17750
|
+
// methods. A user converter that declares flat string properties
|
|
17751
|
+
// (`converter.outfilesuffix = '.html'`, convention #2) keeps them intact,
|
|
17752
|
+
// so callers reading `converter.outfilesuffix` still see the string.
|
|
17753
|
+
const traits = converter._getBackendTraits();
|
|
17754
|
+
newBasebackend = traits.basebackend;
|
|
17755
|
+
newFiletype = traits.filetype;
|
|
17756
|
+
const htmlsyntax = traits.htmlsyntax;
|
|
17638
17757
|
if (htmlsyntax) attrs.htmlsyntax = htmlsyntax;
|
|
17639
17758
|
if (init) {
|
|
17640
|
-
attrs.outfilesuffix ??=
|
|
17759
|
+
attrs.outfilesuffix ??= traits.outfilesuffix;
|
|
17641
17760
|
} else if (!this.isAttributeLocked('outfilesuffix')) {
|
|
17642
|
-
attrs.outfilesuffix =
|
|
17761
|
+
attrs.outfilesuffix = traits.outfilesuffix;
|
|
17643
17762
|
}
|
|
17644
17763
|
} else if (converter) {
|
|
17645
17764
|
const traits = deriveBackendTraits(newBackend);
|
|
@@ -18027,7 +18146,7 @@ const Substitutors = {
|
|
|
18027
18146
|
*
|
|
18028
18147
|
* @param {string|string[]} text - The text to process; must not be null.
|
|
18029
18148
|
* @param {string[]} [subs=NORMAL_SUBS] - The substitutions to perform.
|
|
18030
|
-
* @returns {string|string[]} Text with substitutions applied.
|
|
18149
|
+
* @returns {Promise<string|string[]>} Text with substitutions applied.
|
|
18031
18150
|
*/
|
|
18032
18151
|
async applySubs(text, subs = NORMAL_SUBS) {
|
|
18033
18152
|
const isEmpty = Array.isArray(text) ? text.length === 0 : text.length === 0;
|
|
@@ -18141,7 +18260,7 @@ const Substitutors = {
|
|
|
18141
18260
|
* Substitute quoted text (emphasis, strong, monospaced, etc.)
|
|
18142
18261
|
*
|
|
18143
18262
|
* @param {string} text
|
|
18144
|
-
* @returns {string}
|
|
18263
|
+
* @returns {Promise<string>}
|
|
18145
18264
|
*/
|
|
18146
18265
|
async subQuotes(text) {
|
|
18147
18266
|
const compat = this.document.compatMode;
|
|
@@ -18313,7 +18432,7 @@ const Substitutors = {
|
|
|
18313
18432
|
* Substitute inline macros (links, images, etc.)
|
|
18314
18433
|
*
|
|
18315
18434
|
* @param {string} text
|
|
18316
|
-
* @returns {string}
|
|
18435
|
+
* @returns {Promise<string>}
|
|
18317
18436
|
*/
|
|
18318
18437
|
async subMacros(text) {
|
|
18319
18438
|
const foundSquareBracket = text.includes('[');
|
|
@@ -19208,7 +19327,7 @@ const Substitutors = {
|
|
|
19208
19327
|
* Substitute post replacements (hard line breaks).
|
|
19209
19328
|
*
|
|
19210
19329
|
* @param {string} text
|
|
19211
|
-
* @returns {string}
|
|
19330
|
+
* @returns {Promise<string>}
|
|
19212
19331
|
*/
|
|
19213
19332
|
async subPostReplacements(text) {
|
|
19214
19333
|
if (
|
|
@@ -19246,7 +19365,7 @@ const Substitutors = {
|
|
|
19246
19365
|
*
|
|
19247
19366
|
* @param {string} source
|
|
19248
19367
|
* @param {boolean} processCallouts
|
|
19249
|
-
* @returns {string}
|
|
19368
|
+
* @returns {Promise<string>}
|
|
19250
19369
|
*/
|
|
19251
19370
|
async subSource(source, processCallouts) {
|
|
19252
19371
|
return processCallouts
|
|
@@ -19258,7 +19377,7 @@ const Substitutors = {
|
|
|
19258
19377
|
* Substitute callout source references.
|
|
19259
19378
|
*
|
|
19260
19379
|
* @param {string} text
|
|
19261
|
-
* @returns {string}
|
|
19380
|
+
* @returns {Promise<string>}
|
|
19262
19381
|
*/
|
|
19263
19382
|
async subCallouts(text) {
|
|
19264
19383
|
const calloutRx = this.hasAttribute('line-comment')
|
|
@@ -19287,7 +19406,7 @@ const Substitutors = {
|
|
|
19287
19406
|
*
|
|
19288
19407
|
* @param {string} source
|
|
19289
19408
|
* @param {boolean} processCallouts
|
|
19290
|
-
* @returns {string}
|
|
19409
|
+
* @returns {Promise<string>}
|
|
19291
19410
|
*/
|
|
19292
19411
|
async highlightSource(source, processCallouts) {
|
|
19293
19412
|
const syntaxHl = this.document.syntaxHighlighter;
|
|
@@ -19633,7 +19752,7 @@ const Substitutors = {
|
|
|
19633
19752
|
* Restore passthrough text by reinserting into placeholder positions.
|
|
19634
19753
|
*
|
|
19635
19754
|
* @param {string} text
|
|
19636
|
-
* @returns {string}
|
|
19755
|
+
* @returns {Promise<string>}
|
|
19637
19756
|
*/
|
|
19638
19757
|
async restorePassthroughs(text) {
|
|
19639
19758
|
if (!text.includes(PASS_START)) return text
|
|
@@ -19825,7 +19944,7 @@ const Substitutors = {
|
|
|
19825
19944
|
* @param {string} attrlist
|
|
19826
19945
|
* @param {string[]} [posattrs=[]]
|
|
19827
19946
|
* @param {Object} [opts={}]
|
|
19828
|
-
* @returns {Object}
|
|
19947
|
+
* @returns {Promise<Object>}
|
|
19829
19948
|
*/
|
|
19830
19949
|
async parseAttributes(attrlist, posattrs = [], opts = {}) {
|
|
19831
19950
|
if (!attrlist || attrlist.length === 0) return {}
|
|
@@ -20886,7 +21005,7 @@ class Html5Converter extends ConverterBase {
|
|
|
20886
21005
|
let reproducible;
|
|
20887
21006
|
if (!(reproducible = node.hasAttribute('reproducible'))) {
|
|
20888
21007
|
result.push(
|
|
20889
|
-
`<meta name="generator" content="Asciidoctor ${node.getAttribute('asciidoctor-version')}"${slash}>`
|
|
21008
|
+
`<meta name="generator" content="Asciidoctor.js ${node.getAttribute('asciidoctor-version')}"${slash}>`
|
|
20890
21009
|
);
|
|
20891
21010
|
}
|
|
20892
21011
|
if (node.hasAttribute('app-name')) {
|
|
@@ -21603,7 +21722,9 @@ ${await node.content()}
|
|
|
21603
21722
|
const slash = this._voidSlash;
|
|
21604
21723
|
let img, src;
|
|
21605
21724
|
if (
|
|
21606
|
-
(node.hasAttribute('format', 'svg') ||
|
|
21725
|
+
(node.hasAttribute('format', 'svg') ||
|
|
21726
|
+
target.includes('.svg') ||
|
|
21727
|
+
target.startsWith('data:image/svg+xml')) &&
|
|
21607
21728
|
node.document.safe < SafeMode.SECURE
|
|
21608
21729
|
) {
|
|
21609
21730
|
if (node.hasOption('inline')) {
|
|
@@ -22444,7 +22565,9 @@ Your browser does not support the video tag.
|
|
|
22444
22565
|
if (node.hasAttribute('title'))
|
|
22445
22566
|
attrs += ` title="${node.getAttribute('title')}"`;
|
|
22446
22567
|
if (
|
|
22447
|
-
(node.hasAttribute('format', 'svg') ||
|
|
22568
|
+
(node.hasAttribute('format', 'svg') ||
|
|
22569
|
+
target.includes('.svg') ||
|
|
22570
|
+
target.startsWith('data:image/svg+xml')) &&
|
|
22448
22571
|
node.document.safe < SafeMode.SECURE
|
|
22449
22572
|
) {
|
|
22450
22573
|
if (node.hasOption('inline')) {
|
|
@@ -22537,12 +22660,17 @@ Your browser does not support the video tag.
|
|
|
22537
22660
|
|
|
22538
22661
|
// NOTE expose readSvgContents for Bespoke converter
|
|
22539
22662
|
async readSvgContents(node, target) {
|
|
22540
|
-
|
|
22541
|
-
|
|
22542
|
-
|
|
22543
|
-
|
|
22544
|
-
|
|
22545
|
-
|
|
22663
|
+
// A data-URI carries the SVG in the target itself (e.g. an embedded diagram
|
|
22664
|
+
// produced with `:data-uri:`), so decode it directly rather than trying to
|
|
22665
|
+
// read it as a file or remote URI.
|
|
22666
|
+
let svg = target.startsWith('data:')
|
|
22667
|
+
? this._decodeDataUri(target)
|
|
22668
|
+
: await node.readContents(target, {
|
|
22669
|
+
start: node.document.getAttribute('imagesdir'),
|
|
22670
|
+
normalize: true,
|
|
22671
|
+
label: 'SVG',
|
|
22672
|
+
warnIfEmpty: true,
|
|
22673
|
+
});
|
|
22546
22674
|
if (!svg) return null
|
|
22547
22675
|
if (!svg.startsWith('<svg')) svg = svg.replace(SvgPreambleRx, '');
|
|
22548
22676
|
// Fix incomplete SVG start tag (missing closing >) by inserting > before the first child element.
|
|
@@ -22574,6 +22702,27 @@ Your browser does not support the video tag.
|
|
|
22574
22702
|
|
|
22575
22703
|
// ── Private helpers ─────────────────────────────────────────────────────────
|
|
22576
22704
|
|
|
22705
|
+
/**
|
|
22706
|
+
* Decode an inline `data:` URI to its text contents (e.g. an SVG document) so
|
|
22707
|
+
* an image whose target is a data-URI can be embedded inline. Supports both
|
|
22708
|
+
* Base64 (`;base64,`) and percent-encoded payloads. Returns null when the
|
|
22709
|
+
* payload is missing.
|
|
22710
|
+
*
|
|
22711
|
+
* @internal
|
|
22712
|
+
* @private
|
|
22713
|
+
*/
|
|
22714
|
+
_decodeDataUri(target) {
|
|
22715
|
+
const comma = target.indexOf(',');
|
|
22716
|
+
if (comma === -1) return null
|
|
22717
|
+
const meta = target.slice('data:'.length, comma);
|
|
22718
|
+
const data = target.slice(comma + 1);
|
|
22719
|
+
if (/;base64\b/i.test(meta)) {
|
|
22720
|
+
const bytes = Uint8Array.from(atob(data), (c) => c.charCodeAt(0));
|
|
22721
|
+
return new TextDecoder('utf-8').decode(bytes)
|
|
22722
|
+
}
|
|
22723
|
+
return decodeURIComponent(data)
|
|
22724
|
+
}
|
|
22725
|
+
|
|
22577
22726
|
/**
|
|
22578
22727
|
* @internal
|
|
22579
22728
|
* @private
|
|
@@ -23938,7 +24087,7 @@ class ManPageConverter extends ConverterBase {
|
|
|
23938
24087
|
`'\\" t
|
|
23939
24088
|
.\\" Title: ${mantitle}
|
|
23940
24089
|
.\\" Author: ${node.hasAttribute('authors') ? node.getAttribute('authors') : '[see the "AUTHOR(S)" section]'}
|
|
23941
|
-
.\\" Generator: Asciidoctor ${node.getAttribute('asciidoctor-version')}`,
|
|
24090
|
+
.\\" Generator: Asciidoctor.js ${node.getAttribute('asciidoctor-version')}`,
|
|
23942
24091
|
];
|
|
23943
24092
|
|
|
23944
24093
|
if (docdate) result.push(`.\\" Date: ${docdate}`);
|
|
@@ -24769,4 +24918,4 @@ const manpage = /*#__PURE__*/Object.freeze({
|
|
|
24769
24918
|
default: ManPageConverter
|
|
24770
24919
|
});
|
|
24771
24920
|
|
|
24772
|
-
export { AbstractBlock, AbstractNode, Author, Block, BlockMacroProcessor, BlockProcessor, ContentModel, ConverterBase, CustomFactory$1 as ConverterCustomFactory, Converter as ConverterFactory, Cursor, DefaultFactory$1 as DefaultConverterFactory, DefaultFactory as DefaultSyntaxHighlighterFactory, DocinfoProcessor, Document, DocumentTitle, Extensions, Footnote, Html5Converter, HttpCache, HttpCacheManager, ImageReference, IncludeProcessor, Inline, InlineMacroProcessor, List, ListItem, Logger, LoggerManager, MemoryHttpCache, MemoryLogger, NullLogger, Postprocessor, Preprocessor, Processor, ProcessorExtension, Reader, Registry, RevisionInfo, SafeMode, Section, SyntaxHighlighter, SyntaxHighlighterBase, Timings, TreeProcessor, convert, deriveBackendTraits, getCoreVersion, getVersion, load };
|
|
24921
|
+
export { AbstractBlock, AbstractNode, Author, Block, BlockMacroProcessor, BlockProcessor, ContentModel, ConverterBase, CustomFactory$1 as ConverterCustomFactory, Converter as ConverterFactory, Cursor, DefaultFactory$1 as DefaultConverterFactory, DefaultFactory as DefaultSyntaxHighlighterFactory, DocinfoProcessor, Document, DocumentTitle, Extensions, Footnote, Html5Converter, HttpCache, HttpCacheManager, ImageReference, IncludeProcessor, Inline, InlineMacroProcessor, List, ListItem, LogMessage, Logger, LoggerManager, MemoryHttpCache, MemoryLogger, NullLogger, Postprocessor, Preprocessor, Processor, ProcessorExtension, Reader, Registry, RevisionInfo, SafeMode, Section, SyntaxHighlighter, SyntaxHighlighterBase, Timings, TreeProcessor, convert, deriveBackendTraits, getCoreVersion, getVersion, load };
|