@asciidoctor/core 4.0.0-alpha.1 → 4.0.0-alpha.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.
- package/build/browser/index.js +423 -45
- package/build/node/index.cjs +424 -46
- package/package.json +11 -5
- package/src/abstract_block.js +11 -3
- package/src/attribute_entry.js +17 -1
- package/src/browser/reader.js +2 -2
- package/src/converter/html5.js +2 -8
- package/src/converter/template.js +1 -1
- package/src/document.js +15 -4
- package/src/extensions.js +266 -8
- package/src/helpers.js +20 -2
- package/src/index.js +12 -0
- package/src/load.js +30 -13
- package/src/logging.js +65 -5
- package/src/parser.js +1 -1
- package/src/reader.js +1 -1
- package/types/abstract_block.d.ts +5 -2
- package/types/attribute_entry.d.ts +8 -0
- package/types/browser/reader.d.ts +1 -1
- package/types/extensions.d.ts +203 -9
- package/types/index.d.cts +83 -0
- package/types/index.d.ts +8 -0
- package/types/logging.d.ts +15 -4
package/build/node/index.cjs
CHANGED
|
@@ -5,7 +5,7 @@ const node_fs = require('node:fs');
|
|
|
5
5
|
const path = require('node:path');
|
|
6
6
|
|
|
7
7
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
8
|
-
var version = "4.0.0-alpha.
|
|
8
|
+
var version = "4.0.0-alpha.3";
|
|
9
9
|
const packageJson = {
|
|
10
10
|
version: version};
|
|
11
11
|
|
|
@@ -1514,6 +1514,65 @@ function resolveSeverity(severity) {
|
|
|
1514
1514
|
return severity ?? Severity.UNKNOWN
|
|
1515
1515
|
}
|
|
1516
1516
|
|
|
1517
|
+
// ── Per-execution logger context ─────────────────────────────────────────────
|
|
1518
|
+
|
|
1519
|
+
// Holds an AsyncLocalStorage instance once it is lazily initialised.
|
|
1520
|
+
// Allows per-execution logger isolation without mutating the global singleton,
|
|
1521
|
+
// making concurrent test execution (e.g. Deno's node:test) safe.
|
|
1522
|
+
let _loggerStore = null;
|
|
1523
|
+
|
|
1524
|
+
// Promise singleton — ensures AsyncLocalStorage is initialised at most once.
|
|
1525
|
+
let _loggerStorePromise = null;
|
|
1526
|
+
|
|
1527
|
+
/**
|
|
1528
|
+
* Lazily initialise an AsyncLocalStorage for per-execution logger context.
|
|
1529
|
+
* Falls back to null in environments that do not support node:async_hooks (e.g. browsers).
|
|
1530
|
+
* @returns {Promise<import('node:async_hooks').AsyncLocalStorage|null>}
|
|
1531
|
+
*/
|
|
1532
|
+
async function _ensureLoggerStore() {
|
|
1533
|
+
if (_loggerStorePromise === null) {
|
|
1534
|
+
_loggerStorePromise = import('node:async_hooks')
|
|
1535
|
+
.then(({ AsyncLocalStorage }) => {
|
|
1536
|
+
const store = new AsyncLocalStorage();
|
|
1537
|
+
_loggerStore = store;
|
|
1538
|
+
return store
|
|
1539
|
+
})
|
|
1540
|
+
.catch(() => null);
|
|
1541
|
+
}
|
|
1542
|
+
return _loggerStorePromise
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
/** @internal — returns the logger bound to the current async context, or null */
|
|
1546
|
+
function getContextLogger() {
|
|
1547
|
+
return _loggerStore?.getStore() ?? null
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
/**
|
|
1551
|
+
* Run fn() within an async-local logger context so that all log calls via
|
|
1552
|
+
* `this.logger` (from applyLogging) automatically route to the provided logger
|
|
1553
|
+
* for the duration of the async execution chain.
|
|
1554
|
+
*
|
|
1555
|
+
* Falls back to global mutation in environments without node:async_hooks (e.g. browsers).
|
|
1556
|
+
*
|
|
1557
|
+
* @param {Logger|MemoryLogger|NullLogger} logger - The logger to activate.
|
|
1558
|
+
* @param {() => any} fn - The function to execute within the logger context.
|
|
1559
|
+
* @returns {Promise<any>}
|
|
1560
|
+
*/
|
|
1561
|
+
async function withLogger(logger, fn) {
|
|
1562
|
+
const store = await _ensureLoggerStore();
|
|
1563
|
+
if (store) {
|
|
1564
|
+
return store.run(logger, fn)
|
|
1565
|
+
}
|
|
1566
|
+
// Fallback for environments without node:async_hooks (browsers).
|
|
1567
|
+
const prev = LoggerManager.logger;
|
|
1568
|
+
if (logger !== prev) LoggerManager.logger = logger;
|
|
1569
|
+
try {
|
|
1570
|
+
return await fn()
|
|
1571
|
+
} finally {
|
|
1572
|
+
if (logger !== prev) LoggerManager.logger = prev;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1517
1576
|
// ── Logger ────────────────────────────────────────────────────────────────────
|
|
1518
1577
|
|
|
1519
1578
|
/** Standard logger that writes formatted messages to stderr or a custom pipe. */
|
|
@@ -1822,8 +1881,9 @@ class MemoryLogger {
|
|
|
1822
1881
|
// ── NullLogger ────────────────────────────────────────────────────────────────
|
|
1823
1882
|
|
|
1824
1883
|
/** Logger that discards all messages but still tracks the maximum severity. */
|
|
1825
|
-
class NullLogger {
|
|
1884
|
+
class NullLogger extends Logger {
|
|
1826
1885
|
constructor() {
|
|
1886
|
+
super();
|
|
1827
1887
|
this.level = Severity.UNKNOWN;
|
|
1828
1888
|
this._maxSeverity = null;
|
|
1829
1889
|
}
|
|
@@ -1968,12 +2028,12 @@ const LoggerManager = (() => {
|
|
|
1968
2028
|
function applyLogging(proto) {
|
|
1969
2029
|
Object.defineProperty(proto, 'logger', {
|
|
1970
2030
|
get() {
|
|
1971
|
-
return LoggerManager.logger
|
|
2031
|
+
return _loggerStore?.getStore() ?? LoggerManager.logger
|
|
1972
2032
|
},
|
|
1973
2033
|
configurable: true,
|
|
1974
2034
|
});
|
|
1975
2035
|
|
|
1976
|
-
proto.getLogger = () => LoggerManager.logger;
|
|
2036
|
+
proto.getLogger = () => _loggerStore?.getStore() ?? LoggerManager.logger;
|
|
1977
2037
|
|
|
1978
2038
|
proto.messageWithContext = (text, context = {}) =>
|
|
1979
2039
|
Logger.AutoFormattingMessage.attach({ text, ...context });
|
|
@@ -2020,7 +2080,16 @@ const rstrip = (line) => line.replace(/[ \t\r\n\f\v]+$/, '');
|
|
|
2020
2080
|
*/
|
|
2021
2081
|
function prepareSourceArray(data, trimEnd = true) {
|
|
2022
2082
|
if (!data.length) return []
|
|
2023
|
-
if (data[0].startsWith(BOM))
|
|
2083
|
+
if (data[0].startsWith(BOM)) {
|
|
2084
|
+
data[0] = data[0].slice(1);
|
|
2085
|
+
} else if (
|
|
2086
|
+
data[0].charCodeAt(0) === 0xef &&
|
|
2087
|
+
data[0].charCodeAt(1) === 0xbb &&
|
|
2088
|
+
data[0].charCodeAt(2) === 0xbf
|
|
2089
|
+
) {
|
|
2090
|
+
// Strip UTF-8 BOM encoded as three raw characters ( / \xEF\xBB\xBF) if not already decoded to U+FEFF
|
|
2091
|
+
data[0] = data[0].slice(3);
|
|
2092
|
+
}
|
|
2024
2093
|
// Strip trailing \r to normalize Windows CRLF line endings (lines were split on \n, leaving \r).
|
|
2025
2094
|
return trimEnd
|
|
2026
2095
|
? data.map(rstrip)
|
|
@@ -2040,7 +2109,16 @@ function prepareSourceArray(data, trimEnd = true) {
|
|
|
2040
2109
|
*/
|
|
2041
2110
|
function prepareSourceString(data, trimEnd = true) {
|
|
2042
2111
|
if (!data) return []
|
|
2043
|
-
if (data.startsWith(BOM))
|
|
2112
|
+
if (data.startsWith(BOM)) {
|
|
2113
|
+
data = data.slice(1);
|
|
2114
|
+
} else if (
|
|
2115
|
+
data.charCodeAt(0) === 0xef &&
|
|
2116
|
+
data.charCodeAt(1) === 0xbb &&
|
|
2117
|
+
data.charCodeAt(2) === 0xbf
|
|
2118
|
+
) {
|
|
2119
|
+
// Strip UTF-8 BOM encoded as three raw characters ( / \xEF\xBB\xBF) if not already decoded to U+FEFF
|
|
2120
|
+
data = data.slice(3);
|
|
2121
|
+
}
|
|
2044
2122
|
// Normalize Windows CRLF to LF so that split('\n') does not leave trailing \r on each line.
|
|
2045
2123
|
if (data.includes('\r'))
|
|
2046
2124
|
data = data.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
@@ -3750,7 +3828,7 @@ class AbstractBlock extends AbstractNode {
|
|
|
3750
3828
|
* - `'reject'` → skip the node and its children
|
|
3751
3829
|
* - `'stop'` → include the node (if it matched) and stop the entire traversal
|
|
3752
3830
|
*
|
|
3753
|
-
* @param {Object} [selector={}] - Selector criteria object
|
|
3831
|
+
* @param {Object|Function} [selector={}] - Selector criteria object, or a filter callback when called as `findBy(callback)`.
|
|
3754
3832
|
* @param {Function|null} [filter=null] - Per-node filter callback.
|
|
3755
3833
|
* @returns {AbstractBlock[]} array of matching block-level nodes.
|
|
3756
3834
|
*
|
|
@@ -3765,6 +3843,9 @@ class AbstractBlock extends AbstractNode {
|
|
|
3765
3843
|
*
|
|
3766
3844
|
* @example <caption>All image blocks including those inside AsciiDoc table cells</caption>
|
|
3767
3845
|
* const images = doc.findBy({ context: 'image', traverseDocuments: true })
|
|
3846
|
+
*
|
|
3847
|
+
* @example <caption>Filter-only shorthand (no selector)</caption>
|
|
3848
|
+
* const verbatim = doc.findBy((b) => b.contentModel === ContentModel.VERBATIM)
|
|
3768
3849
|
*/
|
|
3769
3850
|
findBy(selector = {}, filter = null) {
|
|
3770
3851
|
const result = [];
|
|
@@ -3773,8 +3854,13 @@ class AbstractBlock extends AbstractNode {
|
|
|
3773
3854
|
selector && typeof selector === 'object' && !Array.isArray(selector)
|
|
3774
3855
|
? selector
|
|
3775
3856
|
: {};
|
|
3776
|
-
// Normalise:
|
|
3777
|
-
const normFilter =
|
|
3857
|
+
// Normalise: support findBy(callback) shorthand — selector is the filter when it's a function.
|
|
3858
|
+
const normFilter =
|
|
3859
|
+
typeof filter === 'function'
|
|
3860
|
+
? filter
|
|
3861
|
+
: typeof selector === 'function'
|
|
3862
|
+
? selector
|
|
3863
|
+
: null;
|
|
3778
3864
|
try {
|
|
3779
3865
|
this.#findByInternal(normSelector, result, normFilter);
|
|
3780
3866
|
} catch (e) {
|
|
@@ -5964,7 +6050,7 @@ const SyntaxHighlighter = new DefaultFactory();
|
|
|
5964
6050
|
//
|
|
5965
6051
|
// This logic is specific to Asciidoctor.js and has no equivalent in the upstream Ruby asciidoctor
|
|
5966
6052
|
// implementation. It handles the case where the document is loaded in a browser environment
|
|
5967
|
-
//
|
|
6053
|
+
// where paths can be file:// or http(s):// URIs.
|
|
5968
6054
|
//
|
|
5969
6055
|
// The key behavioural differences from the standard file-system resolver:
|
|
5970
6056
|
// - Relative targets are resolved by string concatenation against a URI context dir,
|
|
@@ -6013,7 +6099,7 @@ function _linkReplacement(reader, target, attrlist) {
|
|
|
6013
6099
|
* 1. target starts with file:// → inc_path = relpath = target
|
|
6014
6100
|
* 2. target is a URI → must descend from baseDir or allow-uri-read; else → link
|
|
6015
6101
|
* 3. target is an absolute OS path → prepend file:// (or file:///)
|
|
6016
|
-
* 4. baseDir == '.' → inc_path = relpath = target (resolved by
|
|
6102
|
+
* 4. baseDir == '.' → inc_path = relpath = target (resolved by fetch)
|
|
6017
6103
|
* 5. baseDir starts with file:// OR baseDir is not a URI → inc_path = baseDir/target; relpath = target
|
|
6018
6104
|
* 6. baseDir is an absolute URL → inc_path = baseDir/target; relpath = target
|
|
6019
6105
|
*
|
|
@@ -6735,7 +6821,7 @@ class Reader {
|
|
|
6735
6821
|
return this.source()
|
|
6736
6822
|
}
|
|
6737
6823
|
getLogger() {
|
|
6738
|
-
return LoggerManager.logger
|
|
6824
|
+
return this._document?.logger ?? LoggerManager.logger
|
|
6739
6825
|
}
|
|
6740
6826
|
createLogMessage(text, context = {}) {
|
|
6741
6827
|
return Logger.AutoFormattingMessage.attach({ text, ...context })
|
|
@@ -9560,6 +9646,22 @@ class AttributeList {
|
|
|
9560
9646
|
}
|
|
9561
9647
|
}
|
|
9562
9648
|
|
|
9649
|
+
// Symbol key used to store attribute entries on a block-attributes object without
|
|
9650
|
+
// polluting the public attributes (invisible to Object.keys / for-in / JSON.stringify).
|
|
9651
|
+
// Spread ({ ...attrs }) copies Symbol-keyed properties, so the entry survives the
|
|
9652
|
+
// shallow clone made in AbstractNode's constructor.
|
|
9653
|
+
const ATTR_ENTRIES_KEY = Symbol('attribute_entries');
|
|
9654
|
+
|
|
9655
|
+
/**
|
|
9656
|
+
* Return the attribute entries stored for the given block attributes object,
|
|
9657
|
+
* or undefined if none have been saved.
|
|
9658
|
+
* @param {Object} blockAttributes
|
|
9659
|
+
* @returns {AttributeEntry[]|undefined}
|
|
9660
|
+
*/
|
|
9661
|
+
function getAttributeEntries(blockAttributes) {
|
|
9662
|
+
return blockAttributes[ATTR_ENTRIES_KEY]
|
|
9663
|
+
}
|
|
9664
|
+
|
|
9563
9665
|
class AttributeEntry {
|
|
9564
9666
|
constructor(name, value, negate = null) {
|
|
9565
9667
|
this.name = name;
|
|
@@ -9568,7 +9670,7 @@ class AttributeEntry {
|
|
|
9568
9670
|
}
|
|
9569
9671
|
|
|
9570
9672
|
saveTo(blockAttributes) {
|
|
9571
|
-
(blockAttributes
|
|
9673
|
+
(blockAttributes[ATTR_ENTRIES_KEY] ??= []).push(this);
|
|
9572
9674
|
return this
|
|
9573
9675
|
}
|
|
9574
9676
|
}
|
|
@@ -11043,7 +11145,7 @@ class Parser {
|
|
|
11043
11145
|
const Rdr = Reader;
|
|
11044
11146
|
const result = await extension.processMethod(
|
|
11045
11147
|
parent,
|
|
11046
|
-
blockReader ?? new Rdr(lines),
|
|
11148
|
+
blockReader ?? new Rdr(lines, null, { document: parent.document }),
|
|
11047
11149
|
{ ...attributes }
|
|
11048
11150
|
);
|
|
11049
11151
|
if (!result || result === parent) return null
|
|
@@ -13165,6 +13267,94 @@ function _uniform(str, chr, len) {
|
|
|
13165
13267
|
// references; they will be resolved when those modules implement the methods.
|
|
13166
13268
|
|
|
13167
13269
|
|
|
13270
|
+
// Type-only imports for JSDoc
|
|
13271
|
+
/**
|
|
13272
|
+
* @typedef {import('./document.js').Document} Document
|
|
13273
|
+
* @typedef {import('./abstract_block.js').AbstractBlock} AbstractBlock
|
|
13274
|
+
*/
|
|
13275
|
+
|
|
13276
|
+
// ── DSL interface types ───────────────────────────────────────────────────────
|
|
13277
|
+
|
|
13278
|
+
/**
|
|
13279
|
+
* DSL interface for configuring a {@link Processor} instance.
|
|
13280
|
+
* Applied to a processor instance via `Object.assign(instance, DslMixin)`.
|
|
13281
|
+
*
|
|
13282
|
+
* The `process` property behaves as a setter when called with a single Function
|
|
13283
|
+
* argument (stores the process block), or as a passthrough caller otherwise.
|
|
13284
|
+
*
|
|
13285
|
+
* @typedef {object} ProcessorDslInterface
|
|
13286
|
+
* @property {(key: string, value: unknown) => void} option - Set a config option.
|
|
13287
|
+
* @property {(fn: (...args: unknown[]) => unknown) => void} process - Register the process function.
|
|
13288
|
+
* @property {() => boolean} processBlockGiven - Returns true if a process function has been registered.
|
|
13289
|
+
*/
|
|
13290
|
+
|
|
13291
|
+
/**
|
|
13292
|
+
* DSL interface for document processors (Preprocessor, TreeProcessor, Postprocessor, DocinfoProcessor).
|
|
13293
|
+
*
|
|
13294
|
+
* @typedef {ProcessorDslInterface & { prefer(): void; prepend(): void }} DocumentProcessorDslInterface
|
|
13295
|
+
*/
|
|
13296
|
+
|
|
13297
|
+
/**
|
|
13298
|
+
* DSL interface for syntax processors (BlockProcessor, BlockMacroProcessor, InlineMacroProcessor).
|
|
13299
|
+
*
|
|
13300
|
+
* @typedef {ProcessorDslInterface & {
|
|
13301
|
+
* named(value: string): void;
|
|
13302
|
+
* contentModel(value: string): void;
|
|
13303
|
+
* parseContentAs(value: string): void;
|
|
13304
|
+
* positionalAttributes(...value: string[]): void;
|
|
13305
|
+
* namePositionalAttributes(...value: string[]): void;
|
|
13306
|
+
* positionalAttrs(...value: string[]): void;
|
|
13307
|
+
* defaultAttributes(value: Record<string, string>): void;
|
|
13308
|
+
* defaultAttrs(value: Record<string, string>): void;
|
|
13309
|
+
* resolveAttributes(...args: unknown[]): void;
|
|
13310
|
+
* resolvesAttributes(...args: unknown[]): void;
|
|
13311
|
+
* }} SyntaxProcessorDslInterface
|
|
13312
|
+
*/
|
|
13313
|
+
|
|
13314
|
+
/**
|
|
13315
|
+
* DSL interface for include processors.
|
|
13316
|
+
*
|
|
13317
|
+
* @typedef {DocumentProcessorDslInterface & {
|
|
13318
|
+
* handles(fn: (doc: Document, target: string) => boolean): void;
|
|
13319
|
+
* }} IncludeProcessorDslInterface
|
|
13320
|
+
*/
|
|
13321
|
+
|
|
13322
|
+
/**
|
|
13323
|
+
* DSL interface for docinfo processors.
|
|
13324
|
+
*
|
|
13325
|
+
* @typedef {DocumentProcessorDslInterface & {
|
|
13326
|
+
* atLocation(value: string): void;
|
|
13327
|
+
* }} DocinfoProcessorDslInterface
|
|
13328
|
+
*/
|
|
13329
|
+
|
|
13330
|
+
/**
|
|
13331
|
+
* DSL interface for block processors.
|
|
13332
|
+
*
|
|
13333
|
+
* @typedef {SyntaxProcessorDslInterface & {
|
|
13334
|
+
* contexts(...value: (string | string[])[]): void;
|
|
13335
|
+
* onContexts(...value: (string | string[])[]): void;
|
|
13336
|
+
* onContext(...value: (string | string[])[]): void;
|
|
13337
|
+
* bindTo(...value: (string | string[])[]): void;
|
|
13338
|
+
* }} BlockProcessorDslInterface
|
|
13339
|
+
*/
|
|
13340
|
+
|
|
13341
|
+
/**
|
|
13342
|
+
* DSL interface for macro processors (block and inline macros).
|
|
13343
|
+
*
|
|
13344
|
+
* @typedef {SyntaxProcessorDslInterface} MacroProcessorDslInterface
|
|
13345
|
+
*/
|
|
13346
|
+
|
|
13347
|
+
/**
|
|
13348
|
+
* DSL interface for inline macro processors.
|
|
13349
|
+
*
|
|
13350
|
+
* @typedef {MacroProcessorDslInterface & {
|
|
13351
|
+
* format(value: string): void;
|
|
13352
|
+
* matchFormat(value: string): void;
|
|
13353
|
+
* usingFormat(value: string): void;
|
|
13354
|
+
* match(value: RegExp): void;
|
|
13355
|
+
* }} InlineMacroProcessorDslInterface
|
|
13356
|
+
*/
|
|
13357
|
+
|
|
13168
13358
|
// ── DSL Mixins ────────────────────────────────────────────────────────────────
|
|
13169
13359
|
|
|
13170
13360
|
/**
|
|
@@ -13760,7 +13950,12 @@ class Processor {
|
|
|
13760
13950
|
* Extensions.register(function () { this.preprocessor(CommentStripPreprocessor) })
|
|
13761
13951
|
*/
|
|
13762
13952
|
class Preprocessor extends Processor {
|
|
13763
|
-
|
|
13953
|
+
/**
|
|
13954
|
+
* @param {Document} document - The document being parsed.
|
|
13955
|
+
* @param {Reader} reader - The reader positioned at the beginning of the source.
|
|
13956
|
+
* @returns {Reader|undefined} The same or a substitute Reader, or undefined to use the original.
|
|
13957
|
+
*/
|
|
13958
|
+
process(document, reader) {
|
|
13764
13959
|
throw new Error(
|
|
13765
13960
|
`${this.constructor.name} must implement the process method`
|
|
13766
13961
|
)
|
|
@@ -13785,7 +13980,11 @@ Preprocessor.DSL = DocumentProcessorDsl;
|
|
|
13785
13980
|
* Extensions.register(function () { this.treeProcessor(ShoutTreeProcessor) })
|
|
13786
13981
|
*/
|
|
13787
13982
|
class TreeProcessor extends Processor {
|
|
13788
|
-
|
|
13983
|
+
/**
|
|
13984
|
+
* @param {Document} document - The parsed document.
|
|
13985
|
+
* @returns {void}
|
|
13986
|
+
*/
|
|
13987
|
+
process(document) {
|
|
13789
13988
|
throw new Error(
|
|
13790
13989
|
`${this.constructor.name} must implement the process method`
|
|
13791
13990
|
)
|
|
@@ -13811,7 +14010,12 @@ TreeProcessor.DSL = DocumentProcessorDsl;
|
|
|
13811
14010
|
* Extensions.register(function () { this.postprocessor(FooterPostprocessor) })
|
|
13812
14011
|
*/
|
|
13813
14012
|
class Postprocessor extends Processor {
|
|
13814
|
-
|
|
14013
|
+
/**
|
|
14014
|
+
* @param {Document} document - The converted document.
|
|
14015
|
+
* @param {string} output - The converted output string.
|
|
14016
|
+
* @returns {string} The (possibly modified) output string.
|
|
14017
|
+
*/
|
|
14018
|
+
process(document, output) {
|
|
13815
14019
|
throw new Error(
|
|
13816
14020
|
`${this.constructor.name} must implement the process method`
|
|
13817
14021
|
)
|
|
@@ -13825,13 +14029,25 @@ Postprocessor.DSL = DocumentProcessorDsl;
|
|
|
13825
14029
|
* Implementations must extend IncludeProcessor.
|
|
13826
14030
|
*/
|
|
13827
14031
|
class IncludeProcessor extends Processor {
|
|
13828
|
-
|
|
14032
|
+
/**
|
|
14033
|
+
* @param {Document} document - The document being parsed.
|
|
14034
|
+
* @param {Reader} reader - The reader for the including document.
|
|
14035
|
+
* @param {string} target - The target of the include directive.
|
|
14036
|
+
* @param {Record<string, string>} attributes - The parsed include attributes.
|
|
14037
|
+
* @returns {void}
|
|
14038
|
+
*/
|
|
14039
|
+
process(document, reader, target, attributes) {
|
|
13829
14040
|
throw new Error(
|
|
13830
14041
|
`${this.constructor.name} must implement the process method`
|
|
13831
14042
|
)
|
|
13832
14043
|
}
|
|
13833
14044
|
|
|
13834
|
-
|
|
14045
|
+
/**
|
|
14046
|
+
* @param {Document} doc - The document being parsed.
|
|
14047
|
+
* @param {string} target - The target of the include directive.
|
|
14048
|
+
* @returns {boolean} true if this processor handles the given target.
|
|
14049
|
+
*/
|
|
14050
|
+
handles(doc, target) {
|
|
13835
14051
|
return true
|
|
13836
14052
|
}
|
|
13837
14053
|
}
|
|
@@ -13849,7 +14065,11 @@ class DocinfoProcessor extends Processor {
|
|
|
13849
14065
|
this.config.location ??= 'head';
|
|
13850
14066
|
}
|
|
13851
14067
|
|
|
13852
|
-
|
|
14068
|
+
/**
|
|
14069
|
+
* @param {Document} document - The document being converted.
|
|
14070
|
+
* @returns {string} The docinfo content to inject into the document.
|
|
14071
|
+
*/
|
|
14072
|
+
process(document) {
|
|
13853
14073
|
throw new Error(
|
|
13854
14074
|
`${this.constructor.name} must implement the process method`
|
|
13855
14075
|
)
|
|
@@ -13905,7 +14125,13 @@ class BlockProcessor extends Processor {
|
|
|
13905
14125
|
this.config.content_model ??= 'compound';
|
|
13906
14126
|
}
|
|
13907
14127
|
|
|
13908
|
-
|
|
14128
|
+
/**
|
|
14129
|
+
* @param {AbstractBlock} parent - The enclosing block.
|
|
14130
|
+
* @param {Reader} reader - The reader positioned at the block content.
|
|
14131
|
+
* @param {Record<string, unknown>} attributes - The parsed block attributes.
|
|
14132
|
+
* @returns {Block|void} A block node, or void to let the parser handle it.
|
|
14133
|
+
*/
|
|
14134
|
+
process(parent, reader, attributes) {
|
|
13909
14135
|
throw new Error(
|
|
13910
14136
|
`${this.constructor.name} must implement the process method`
|
|
13911
14137
|
)
|
|
@@ -13923,7 +14149,13 @@ class MacroProcessor extends Processor {
|
|
|
13923
14149
|
this.config.content_model ??= 'attributes';
|
|
13924
14150
|
}
|
|
13925
14151
|
|
|
13926
|
-
|
|
14152
|
+
/**
|
|
14153
|
+
* @param {AbstractBlock} parent - The enclosing block.
|
|
14154
|
+
* @param {string} target - The macro target (text between `name:` and `[`).
|
|
14155
|
+
* @param {Record<string, unknown>} attributes - The parsed macro attributes.
|
|
14156
|
+
* @returns {Block|Inline|void}
|
|
14157
|
+
*/
|
|
14158
|
+
process(parent, target, attributes) {
|
|
13927
14159
|
throw new Error(
|
|
13928
14160
|
`${this.constructor.name} must implement the process method`
|
|
13929
14161
|
)
|
|
@@ -13969,6 +14201,16 @@ class BlockMacroProcessor extends MacroProcessor {
|
|
|
13969
14201
|
set name(value) {
|
|
13970
14202
|
this._name = value;
|
|
13971
14203
|
}
|
|
14204
|
+
|
|
14205
|
+
/**
|
|
14206
|
+
* @param {AbstractBlock} parent - The enclosing block.
|
|
14207
|
+
* @param {string} target - The macro target.
|
|
14208
|
+
* @param {Record<string, unknown>} attributes - The parsed macro attributes.
|
|
14209
|
+
* @returns {Block} A block node created with one of the `createBlock` helpers.
|
|
14210
|
+
*/
|
|
14211
|
+
process(parent, target, attributes) {
|
|
14212
|
+
return super.process(parent, target, attributes)
|
|
14213
|
+
}
|
|
13972
14214
|
}
|
|
13973
14215
|
BlockMacroProcessor.DSL = MacroProcessorDsl;
|
|
13974
14216
|
|
|
@@ -14012,6 +14254,16 @@ class InlineMacroProcessor extends MacroProcessor {
|
|
|
14012
14254
|
))
|
|
14013
14255
|
}
|
|
14014
14256
|
|
|
14257
|
+
/**
|
|
14258
|
+
* @param {AbstractBlock} parent - The enclosing block.
|
|
14259
|
+
* @param {string} target - The macro target.
|
|
14260
|
+
* @param {Record<string, unknown>} attributes - The parsed macro attributes.
|
|
14261
|
+
* @returns {Inline} An Inline node created with `this.createInline(...)`.
|
|
14262
|
+
*/
|
|
14263
|
+
process(parent, target, attributes) {
|
|
14264
|
+
return super.process(parent, target, attributes)
|
|
14265
|
+
}
|
|
14266
|
+
|
|
14015
14267
|
resolveRegexp(name, format) {
|
|
14016
14268
|
if (!MacroNameRx.test(name)) {
|
|
14017
14269
|
throw new Error(`invalid name for inline macro: ${name}`)
|
|
@@ -14375,6 +14627,15 @@ class Registry {
|
|
|
14375
14627
|
return !!this._block_extensions
|
|
14376
14628
|
}
|
|
14377
14629
|
|
|
14630
|
+
/**
|
|
14631
|
+
* Retrieve all BlockProcessor Extension proxy objects.
|
|
14632
|
+
*
|
|
14633
|
+
* @returns {ProcessorExtension[]}
|
|
14634
|
+
*/
|
|
14635
|
+
blocks() {
|
|
14636
|
+
return this._block_extensions ? Object.values(this._block_extensions) : []
|
|
14637
|
+
}
|
|
14638
|
+
|
|
14378
14639
|
/**
|
|
14379
14640
|
* Check whether a BlockProcessor is registered for the given name and context.
|
|
14380
14641
|
*
|
|
@@ -14421,6 +14682,17 @@ class Registry {
|
|
|
14421
14682
|
return !!this._block_macro_extensions
|
|
14422
14683
|
}
|
|
14423
14684
|
|
|
14685
|
+
/**
|
|
14686
|
+
* Retrieve all BlockMacroProcessor Extension proxy objects.
|
|
14687
|
+
*
|
|
14688
|
+
* @returns {ProcessorExtension[]}
|
|
14689
|
+
*/
|
|
14690
|
+
blockMacros() {
|
|
14691
|
+
return this._block_macro_extensions
|
|
14692
|
+
? Object.values(this._block_macro_extensions)
|
|
14693
|
+
: []
|
|
14694
|
+
}
|
|
14695
|
+
|
|
14424
14696
|
/**
|
|
14425
14697
|
* Check whether a BlockMacroProcessor is registered for the given name.
|
|
14426
14698
|
*
|
|
@@ -14526,6 +14798,85 @@ class Registry {
|
|
|
14526
14798
|
return extension
|
|
14527
14799
|
}
|
|
14528
14800
|
|
|
14801
|
+
// ── JavaScript-style accessors ───────────────────────────────────────────────
|
|
14802
|
+
|
|
14803
|
+
/** @returns {object} the plain Object that maps names to groups for this registry. */
|
|
14804
|
+
getGroups() {
|
|
14805
|
+
return this.groups
|
|
14806
|
+
}
|
|
14807
|
+
|
|
14808
|
+
/** Alias for {@link preprocessors}. */
|
|
14809
|
+
getPreprocessors() {
|
|
14810
|
+
return this.preprocessors()
|
|
14811
|
+
}
|
|
14812
|
+
|
|
14813
|
+
/** Alias for {@link treeProcessors}. */
|
|
14814
|
+
getTreeProcessors() {
|
|
14815
|
+
return this.treeProcessors()
|
|
14816
|
+
}
|
|
14817
|
+
|
|
14818
|
+
/** Alias for {@link includeProcessors}. */
|
|
14819
|
+
getIncludeProcessors() {
|
|
14820
|
+
return this.includeProcessors()
|
|
14821
|
+
}
|
|
14822
|
+
|
|
14823
|
+
/** Alias for {@link postprocessors}. */
|
|
14824
|
+
getPostprocessors() {
|
|
14825
|
+
return this.postprocessors()
|
|
14826
|
+
}
|
|
14827
|
+
|
|
14828
|
+
/**
|
|
14829
|
+
* Alias for {@link docinfoProcessors}.
|
|
14830
|
+
*
|
|
14831
|
+
* @param {string|null} [location=null]
|
|
14832
|
+
*/
|
|
14833
|
+
getDocinfoProcessors(location = null) {
|
|
14834
|
+
return this.docinfoProcessors(location)
|
|
14835
|
+
}
|
|
14836
|
+
|
|
14837
|
+
/** Alias for {@link blocks}. */
|
|
14838
|
+
getBlocks() {
|
|
14839
|
+
return this.blocks()
|
|
14840
|
+
}
|
|
14841
|
+
|
|
14842
|
+
/** Alias for {@link blockMacros}. */
|
|
14843
|
+
getBlockMacros() {
|
|
14844
|
+
return this.blockMacros()
|
|
14845
|
+
}
|
|
14846
|
+
|
|
14847
|
+
/** Alias for {@link inlineMacros}. */
|
|
14848
|
+
getInlineMacros() {
|
|
14849
|
+
return this.inlineMacros()
|
|
14850
|
+
}
|
|
14851
|
+
|
|
14852
|
+
/**
|
|
14853
|
+
* Alias for {@link registeredForInlineMacro}.
|
|
14854
|
+
*
|
|
14855
|
+
* @param {string} name
|
|
14856
|
+
*/
|
|
14857
|
+
getInlineMacroFor(name) {
|
|
14858
|
+
return this.registeredForInlineMacro(name)
|
|
14859
|
+
}
|
|
14860
|
+
|
|
14861
|
+
/**
|
|
14862
|
+
* Alias for {@link registeredForBlock}.
|
|
14863
|
+
*
|
|
14864
|
+
* @param {string} name
|
|
14865
|
+
* @param {string} context
|
|
14866
|
+
*/
|
|
14867
|
+
getBlockFor(name, context) {
|
|
14868
|
+
return this.registeredForBlock(name, context)
|
|
14869
|
+
}
|
|
14870
|
+
|
|
14871
|
+
/**
|
|
14872
|
+
* Alias for {@link registeredForBlockMacro}.
|
|
14873
|
+
*
|
|
14874
|
+
* @param {string} name
|
|
14875
|
+
*/
|
|
14876
|
+
getBlockMacroFor(name) {
|
|
14877
|
+
return this.registeredForBlockMacro(name)
|
|
14878
|
+
}
|
|
14879
|
+
|
|
14529
14880
|
// ── Private helpers ──────────────────────────────────────────────────────────
|
|
14530
14881
|
|
|
14531
14882
|
/** @internal */
|
|
@@ -14751,6 +15102,15 @@ const Extensions = {
|
|
|
14751
15102
|
return _groups
|
|
14752
15103
|
},
|
|
14753
15104
|
|
|
15105
|
+
/**
|
|
15106
|
+
* Alias for {@link groups}.
|
|
15107
|
+
*
|
|
15108
|
+
* @returns {object}
|
|
15109
|
+
*/
|
|
15110
|
+
getGroups() {
|
|
15111
|
+
return this.groups()
|
|
15112
|
+
},
|
|
15113
|
+
|
|
14754
15114
|
/**
|
|
14755
15115
|
* Create a new Registry, optionally pre-populated with a named block.
|
|
14756
15116
|
*
|
|
@@ -15978,8 +16338,9 @@ class Document extends AbstractBlock {
|
|
|
15978
16338
|
* @param {Object} blockAttributes
|
|
15979
16339
|
*/
|
|
15980
16340
|
playbackAttributes(blockAttributes) {
|
|
15981
|
-
|
|
15982
|
-
|
|
16341
|
+
const entries = getAttributeEntries(blockAttributes);
|
|
16342
|
+
if (!entries) return
|
|
16343
|
+
for (const entry of entries) {
|
|
15983
16344
|
if (entry.negate) {
|
|
15984
16345
|
delete this.attributes[entry.name];
|
|
15985
16346
|
if (entry.name === 'compat-mode') this.compatMode = false;
|
|
@@ -16708,6 +17069,12 @@ class Document extends AbstractBlock {
|
|
|
16708
17069
|
* Handles titles (AbstractBlock), list item text, table cell text, and reftexts.
|
|
16709
17070
|
*/
|
|
16710
17071
|
async _resolveAllTexts(block) {
|
|
17072
|
+
// The header section lives outside document.blocks; pre-compute its title here so
|
|
17073
|
+
// that doc.doctitle() returns the fully-substituted title (with replacements applied,
|
|
17074
|
+
// e.g. ' → ’) rather than the header-subs-only fallback.
|
|
17075
|
+
if (block === this && this.header) {
|
|
17076
|
+
await this.header.precomputeTitle?.();
|
|
17077
|
+
}
|
|
16711
17078
|
// Skip title pre-computation for blocks with an explicit empty id ([id=]).
|
|
16712
17079
|
// In Ruby, apply_title_subs is lazy: it is never called during parsing for such
|
|
16713
17080
|
// blocks because section.title is never accessed. An explicit empty id is
|
|
@@ -16791,7 +17158,7 @@ class Document extends AbstractBlock {
|
|
|
16791
17158
|
* @internal
|
|
16792
17159
|
*/
|
|
16793
17160
|
_clearPlaybackAttributes(attributes) {
|
|
16794
|
-
delete attributes
|
|
17161
|
+
delete attributes[ATTR_ENTRIES_KEY];
|
|
16795
17162
|
}
|
|
16796
17163
|
|
|
16797
17164
|
/**
|
|
@@ -19519,17 +19886,23 @@ async function load$1(input, options = {}) {
|
|
|
19519
19886
|
// Shallow-copy options so we don't mutate the caller's object.
|
|
19520
19887
|
options = Object.assign({}, options);
|
|
19521
19888
|
|
|
19522
|
-
const timings = options.timings ?? null;
|
|
19523
|
-
if (timings) timings.start('read');
|
|
19524
|
-
|
|
19525
19889
|
// ── Logger override ───────────────────────────────────────────────────────
|
|
19890
|
+
// When a logger option is supplied, run the conversion in an async-local
|
|
19891
|
+
// context so the logger is scoped to this call only — no global mutation,
|
|
19892
|
+
// which makes concurrent callers (e.g. parallel Deno tests) safe.
|
|
19526
19893
|
if ('logger' in options) {
|
|
19527
|
-
const
|
|
19528
|
-
|
|
19529
|
-
|
|
19530
|
-
}
|
|
19894
|
+
const newLogger = options.logger ?? new NullLogger();
|
|
19895
|
+
delete options.logger;
|
|
19896
|
+
return withLogger(newLogger, () => _doLoad(input, options, newLogger))
|
|
19531
19897
|
}
|
|
19532
19898
|
|
|
19899
|
+
return _doLoad(input, options)
|
|
19900
|
+
}
|
|
19901
|
+
|
|
19902
|
+
async function _doLoad(input, options, explicitLogger = null) {
|
|
19903
|
+
const timings = options.timings ?? null;
|
|
19904
|
+
if (timings) timings.start('read');
|
|
19905
|
+
|
|
19533
19906
|
// ── Attributes normalisation ──────────────────────────────────────────────
|
|
19534
19907
|
let attrs = options.attributes;
|
|
19535
19908
|
if (!attrs) {
|
|
@@ -19561,11 +19934,9 @@ async function load$1(input, options = {}) {
|
|
|
19561
19934
|
attrs.docname = basename(inputPath, docfilesuffix);
|
|
19562
19935
|
}
|
|
19563
19936
|
source = await _readStream(input);
|
|
19564
|
-
} else if (
|
|
19565
|
-
|
|
19566
|
-
|
|
19567
|
-
) {
|
|
19568
|
-
source = input.toString('utf8');
|
|
19937
|
+
} else if (input instanceof Uint8Array) {
|
|
19938
|
+
// Covers both Node.js Buffer (a Uint8Array subclass) and browser Uint8Array shims.
|
|
19939
|
+
source = new TextDecoder('utf-8').decode(input);
|
|
19569
19940
|
} else if (typeof input === 'string') {
|
|
19570
19941
|
source = input;
|
|
19571
19942
|
} else if (Array.isArray(input)) {
|
|
@@ -19628,6 +19999,19 @@ async function load$1(input, options = {}) {
|
|
|
19628
19999
|
}
|
|
19629
20000
|
|
|
19630
20001
|
if (timings) timings.record('parse');
|
|
20002
|
+
|
|
20003
|
+
// Persist the logger on the Document instance so that doc.convert()
|
|
20004
|
+
// (called by convert.js after the async-local context ends) still routes
|
|
20005
|
+
// logging through the caller-supplied logger.
|
|
20006
|
+
// ALS provides it in Node.js/Deno; the explicit parameter covers browser fallback.
|
|
20007
|
+
const contextLogger = getContextLogger() ?? explicitLogger;
|
|
20008
|
+
if (contextLogger) {
|
|
20009
|
+
Object.defineProperty(doc, 'logger', {
|
|
20010
|
+
get: () => contextLogger,
|
|
20011
|
+
configurable: true,
|
|
20012
|
+
});
|
|
20013
|
+
}
|
|
20014
|
+
|
|
19631
20015
|
return doc
|
|
19632
20016
|
}
|
|
19633
20017
|
|
|
@@ -21038,10 +21422,7 @@ ${await node.content()}
|
|
|
21038
21422
|
img =
|
|
21039
21423
|
(await this.readSvgContents(node, target)) ||
|
|
21040
21424
|
`<span class="alt">${node.getAlt()}</span>`;
|
|
21041
|
-
} else if (
|
|
21042
|
-
node.hasOption('interactive') &&
|
|
21043
|
-
node.document.safe >= SafeMode.SERVER
|
|
21044
|
-
) {
|
|
21425
|
+
} else if (node.hasOption('interactive')) {
|
|
21045
21426
|
const fallback = node.hasAttribute('fallback')
|
|
21046
21427
|
? `<img src="${await node.imageUri(node.getAttribute('fallback'))}" alt="${this._encodeAttrValue(node.getAlt())}"${widthAttr}${heightAttr}${slash}>`
|
|
21047
21428
|
: `<span class="alt">${node.getAlt()}</span>`;
|
|
@@ -21882,10 +22263,7 @@ Your browser does not support the video tag.
|
|
|
21882
22263
|
img =
|
|
21883
22264
|
(await this.readSvgContents(node, target)) ||
|
|
21884
22265
|
`<span class="alt">${node.getAlt()}</span>`;
|
|
21885
|
-
} else if (
|
|
21886
|
-
node.hasOption('interactive') &&
|
|
21887
|
-
node.document.safe >= SafeMode.SERVER
|
|
21888
|
-
) {
|
|
22266
|
+
} else if (node.hasOption('interactive')) {
|
|
21889
22267
|
const fallback = node.hasAttribute('fallback')
|
|
21890
22268
|
? `<img src="${await node.imageUri(node.getAttribute('fallback'))}" alt="${this._encodeAttrValue(node.getAlt())}"${attrs}${this._voidSlash}>`
|
|
21891
22269
|
: `<span class="alt">${node.getAlt()}</span>`;
|
|
@@ -22532,7 +22910,7 @@ class TemplateConverter extends ConverterBase {
|
|
|
22532
22910
|
|
|
22533
22911
|
let entries;
|
|
22534
22912
|
try {
|
|
22535
|
-
entries = await node_fs.promises.readdir(templateDir);
|
|
22913
|
+
entries = (await node_fs.promises.readdir(templateDir)).sort();
|
|
22536
22914
|
} catch {
|
|
22537
22915
|
return result
|
|
22538
22916
|
}
|