@asciidoctor/core 4.0.0-alpha.2 → 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 +410 -34
- package/build/node/index.cjs +411 -35
- package/package.json +3 -2
- package/src/attribute_entry.js +17 -1
- package/src/browser/reader.js +2 -2
- 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/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 +8 -0
- package/types/index.d.ts +8 -0
- package/types/logging.d.ts +15 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asciidoctor/core",
|
|
3
|
-
"version": "4.0.0-alpha.
|
|
3
|
+
"version": "4.0.0-alpha.3",
|
|
4
4
|
"description": "Asciidoctor.js: AsciiDoc in JavaScript powered by Asciidoctor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/node/index.cjs",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"build:types": "tsc && node scripts/strip-internal.js && node scripts/patch-jsdoc.js",
|
|
29
29
|
"docs": "typedoc --tsconfig tsconfig.docs.json",
|
|
30
30
|
"test": "node --test test/**.test.js",
|
|
31
|
+
"test:deno": "deno test --allow-env --no-check --allow-read --allow-sys --allow-write --allow-run --allow-net test/*.test.js",
|
|
31
32
|
"test:coverage": "node --test --experimental-test-coverage --test-coverage-include='src/**' test/**.test.js",
|
|
32
33
|
"test:browser": "vitest run --config vitest.browser.config.js",
|
|
33
34
|
"lint": "biome lint src test",
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
},
|
|
66
67
|
"homepage": "https://github.com/asciidoctor/asciidoctor.js",
|
|
67
68
|
"volta": {
|
|
68
|
-
"node": "24.
|
|
69
|
+
"node": "24.15.0"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
72
|
"@biomejs/biome": "^2.4.13",
|
package/src/attribute_entry.js
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
// Symbol key used to store attribute entries on a block-attributes object without
|
|
2
|
+
// polluting the public attributes (invisible to Object.keys / for-in / JSON.stringify).
|
|
3
|
+
// Spread ({ ...attrs }) copies Symbol-keyed properties, so the entry survives the
|
|
4
|
+
// shallow clone made in AbstractNode's constructor.
|
|
5
|
+
export const ATTR_ENTRIES_KEY = Symbol('attribute_entries')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Return the attribute entries stored for the given block attributes object,
|
|
9
|
+
* or undefined if none have been saved.
|
|
10
|
+
* @param {Object} blockAttributes
|
|
11
|
+
* @returns {AttributeEntry[]|undefined}
|
|
12
|
+
*/
|
|
13
|
+
export function getAttributeEntries(blockAttributes) {
|
|
14
|
+
return blockAttributes[ATTR_ENTRIES_KEY]
|
|
15
|
+
}
|
|
16
|
+
|
|
1
17
|
export class AttributeEntry {
|
|
2
18
|
constructor(name, value, negate = null) {
|
|
3
19
|
this.name = name
|
|
@@ -6,7 +22,7 @@ export class AttributeEntry {
|
|
|
6
22
|
}
|
|
7
23
|
|
|
8
24
|
saveTo(blockAttributes) {
|
|
9
|
-
;(blockAttributes
|
|
25
|
+
;(blockAttributes[ATTR_ENTRIES_KEY] ??= []).push(this)
|
|
10
26
|
return this
|
|
11
27
|
}
|
|
12
28
|
}
|
package/src/browser/reader.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
//
|
|
6
6
|
// This logic is specific to Asciidoctor.js and has no equivalent in the upstream Ruby asciidoctor
|
|
7
7
|
// implementation. It handles the case where the document is loaded in a browser environment
|
|
8
|
-
//
|
|
8
|
+
// where paths can be file:// or http(s):// URIs.
|
|
9
9
|
//
|
|
10
10
|
// The key behavioural differences from the standard file-system resolver:
|
|
11
11
|
// - Relative targets are resolved by string concatenation against a URI context dir,
|
|
@@ -55,7 +55,7 @@ function _linkReplacement(reader, target, attrlist) {
|
|
|
55
55
|
* 1. target starts with file:// → inc_path = relpath = target
|
|
56
56
|
* 2. target is a URI → must descend from baseDir or allow-uri-read; else → link
|
|
57
57
|
* 3. target is an absolute OS path → prepend file:// (or file:///)
|
|
58
|
-
* 4. baseDir == '.' → inc_path = relpath = target (resolved by
|
|
58
|
+
* 4. baseDir == '.' → inc_path = relpath = target (resolved by fetch)
|
|
59
59
|
* 5. baseDir starts with file:// OR baseDir is not a URI → inc_path = baseDir/target; relpath = target
|
|
60
60
|
* 6. baseDir is an absolute URL → inc_path = baseDir/target; relpath = target
|
|
61
61
|
*
|
package/src/document.js
CHANGED
|
@@ -68,7 +68,11 @@ export class ImageReference {
|
|
|
68
68
|
import { Footnote } from './footnote.js'
|
|
69
69
|
export { Footnote }
|
|
70
70
|
|
|
71
|
-
import {
|
|
71
|
+
import {
|
|
72
|
+
AttributeEntry,
|
|
73
|
+
getAttributeEntries,
|
|
74
|
+
ATTR_ENTRIES_KEY,
|
|
75
|
+
} from './attribute_entry.js'
|
|
72
76
|
export { AttributeEntry }
|
|
73
77
|
|
|
74
78
|
/**
|
|
@@ -927,8 +931,9 @@ export class Document extends AbstractBlock {
|
|
|
927
931
|
* @param {Object} blockAttributes
|
|
928
932
|
*/
|
|
929
933
|
playbackAttributes(blockAttributes) {
|
|
930
|
-
|
|
931
|
-
|
|
934
|
+
const entries = getAttributeEntries(blockAttributes)
|
|
935
|
+
if (!entries) return
|
|
936
|
+
for (const entry of entries) {
|
|
932
937
|
if (entry.negate) {
|
|
933
938
|
delete this.attributes[entry.name]
|
|
934
939
|
if (entry.name === 'compat-mode') this.compatMode = false
|
|
@@ -1657,6 +1662,12 @@ export class Document extends AbstractBlock {
|
|
|
1657
1662
|
* Handles titles (AbstractBlock), list item text, table cell text, and reftexts.
|
|
1658
1663
|
*/
|
|
1659
1664
|
async _resolveAllTexts(block) {
|
|
1665
|
+
// The header section lives outside document.blocks; pre-compute its title here so
|
|
1666
|
+
// that doc.doctitle() returns the fully-substituted title (with replacements applied,
|
|
1667
|
+
// e.g. ' → ’) rather than the header-subs-only fallback.
|
|
1668
|
+
if (block === this && this.header) {
|
|
1669
|
+
await this.header.precomputeTitle?.()
|
|
1670
|
+
}
|
|
1660
1671
|
// Skip title pre-computation for blocks with an explicit empty id ([id=]).
|
|
1661
1672
|
// In Ruby, apply_title_subs is lazy: it is never called during parsing for such
|
|
1662
1673
|
// blocks because section.title is never accessed. An explicit empty id is
|
|
@@ -1740,7 +1751,7 @@ export class Document extends AbstractBlock {
|
|
|
1740
1751
|
* @internal
|
|
1741
1752
|
*/
|
|
1742
1753
|
_clearPlaybackAttributes(attributes) {
|
|
1743
|
-
delete attributes
|
|
1754
|
+
delete attributes[ATTR_ENTRIES_KEY]
|
|
1744
1755
|
}
|
|
1745
1756
|
|
|
1746
1757
|
/**
|
package/src/extensions.js
CHANGED
|
@@ -27,6 +27,94 @@ import { basename } from './helpers.js'
|
|
|
27
27
|
import { ATTR_REF_HEAD } from './constants.js'
|
|
28
28
|
import { MacroNameRx, CC_ANY } from './rx.js'
|
|
29
29
|
|
|
30
|
+
// Type-only imports for JSDoc
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {import('./document.js').Document} Document
|
|
33
|
+
* @typedef {import('./abstract_block.js').AbstractBlock} AbstractBlock
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
// ── DSL interface types ───────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* DSL interface for configuring a {@link Processor} instance.
|
|
40
|
+
* Applied to a processor instance via `Object.assign(instance, DslMixin)`.
|
|
41
|
+
*
|
|
42
|
+
* The `process` property behaves as a setter when called with a single Function
|
|
43
|
+
* argument (stores the process block), or as a passthrough caller otherwise.
|
|
44
|
+
*
|
|
45
|
+
* @typedef {object} ProcessorDslInterface
|
|
46
|
+
* @property {(key: string, value: unknown) => void} option - Set a config option.
|
|
47
|
+
* @property {(fn: (...args: unknown[]) => unknown) => void} process - Register the process function.
|
|
48
|
+
* @property {() => boolean} processBlockGiven - Returns true if a process function has been registered.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* DSL interface for document processors (Preprocessor, TreeProcessor, Postprocessor, DocinfoProcessor).
|
|
53
|
+
*
|
|
54
|
+
* @typedef {ProcessorDslInterface & { prefer(): void; prepend(): void }} DocumentProcessorDslInterface
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* DSL interface for syntax processors (BlockProcessor, BlockMacroProcessor, InlineMacroProcessor).
|
|
59
|
+
*
|
|
60
|
+
* @typedef {ProcessorDslInterface & {
|
|
61
|
+
* named(value: string): void;
|
|
62
|
+
* contentModel(value: string): void;
|
|
63
|
+
* parseContentAs(value: string): void;
|
|
64
|
+
* positionalAttributes(...value: string[]): void;
|
|
65
|
+
* namePositionalAttributes(...value: string[]): void;
|
|
66
|
+
* positionalAttrs(...value: string[]): void;
|
|
67
|
+
* defaultAttributes(value: Record<string, string>): void;
|
|
68
|
+
* defaultAttrs(value: Record<string, string>): void;
|
|
69
|
+
* resolveAttributes(...args: unknown[]): void;
|
|
70
|
+
* resolvesAttributes(...args: unknown[]): void;
|
|
71
|
+
* }} SyntaxProcessorDslInterface
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* DSL interface for include processors.
|
|
76
|
+
*
|
|
77
|
+
* @typedef {DocumentProcessorDslInterface & {
|
|
78
|
+
* handles(fn: (doc: Document, target: string) => boolean): void;
|
|
79
|
+
* }} IncludeProcessorDslInterface
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* DSL interface for docinfo processors.
|
|
84
|
+
*
|
|
85
|
+
* @typedef {DocumentProcessorDslInterface & {
|
|
86
|
+
* atLocation(value: string): void;
|
|
87
|
+
* }} DocinfoProcessorDslInterface
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* DSL interface for block processors.
|
|
92
|
+
*
|
|
93
|
+
* @typedef {SyntaxProcessorDslInterface & {
|
|
94
|
+
* contexts(...value: (string | string[])[]): void;
|
|
95
|
+
* onContexts(...value: (string | string[])[]): void;
|
|
96
|
+
* onContext(...value: (string | string[])[]): void;
|
|
97
|
+
* bindTo(...value: (string | string[])[]): void;
|
|
98
|
+
* }} BlockProcessorDslInterface
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* DSL interface for macro processors (block and inline macros).
|
|
103
|
+
*
|
|
104
|
+
* @typedef {SyntaxProcessorDslInterface} MacroProcessorDslInterface
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* DSL interface for inline macro processors.
|
|
109
|
+
*
|
|
110
|
+
* @typedef {MacroProcessorDslInterface & {
|
|
111
|
+
* format(value: string): void;
|
|
112
|
+
* matchFormat(value: string): void;
|
|
113
|
+
* usingFormat(value: string): void;
|
|
114
|
+
* match(value: RegExp): void;
|
|
115
|
+
* }} InlineMacroProcessorDslInterface
|
|
116
|
+
*/
|
|
117
|
+
|
|
30
118
|
// ── DSL Mixins ────────────────────────────────────────────────────────────────
|
|
31
119
|
|
|
32
120
|
/**
|
|
@@ -625,7 +713,12 @@ export class Processor {
|
|
|
625
713
|
* Extensions.register(function () { this.preprocessor(CommentStripPreprocessor) })
|
|
626
714
|
*/
|
|
627
715
|
export class Preprocessor extends Processor {
|
|
628
|
-
|
|
716
|
+
/**
|
|
717
|
+
* @param {Document} document - The document being parsed.
|
|
718
|
+
* @param {Reader} reader - The reader positioned at the beginning of the source.
|
|
719
|
+
* @returns {Reader|undefined} The same or a substitute Reader, or undefined to use the original.
|
|
720
|
+
*/
|
|
721
|
+
process(document, reader) {
|
|
629
722
|
throw new Error(
|
|
630
723
|
`${this.constructor.name} must implement the process method`
|
|
631
724
|
)
|
|
@@ -650,7 +743,11 @@ Preprocessor.DSL = DocumentProcessorDsl
|
|
|
650
743
|
* Extensions.register(function () { this.treeProcessor(ShoutTreeProcessor) })
|
|
651
744
|
*/
|
|
652
745
|
export class TreeProcessor extends Processor {
|
|
653
|
-
|
|
746
|
+
/**
|
|
747
|
+
* @param {Document} document - The parsed document.
|
|
748
|
+
* @returns {void}
|
|
749
|
+
*/
|
|
750
|
+
process(document) {
|
|
654
751
|
throw new Error(
|
|
655
752
|
`${this.constructor.name} must implement the process method`
|
|
656
753
|
)
|
|
@@ -679,7 +776,12 @@ export const Treeprocessor = TreeProcessor
|
|
|
679
776
|
* Extensions.register(function () { this.postprocessor(FooterPostprocessor) })
|
|
680
777
|
*/
|
|
681
778
|
export class Postprocessor extends Processor {
|
|
682
|
-
|
|
779
|
+
/**
|
|
780
|
+
* @param {Document} document - The converted document.
|
|
781
|
+
* @param {string} output - The converted output string.
|
|
782
|
+
* @returns {string} The (possibly modified) output string.
|
|
783
|
+
*/
|
|
784
|
+
process(document, output) {
|
|
683
785
|
throw new Error(
|
|
684
786
|
`${this.constructor.name} must implement the process method`
|
|
685
787
|
)
|
|
@@ -693,13 +795,25 @@ Postprocessor.DSL = DocumentProcessorDsl
|
|
|
693
795
|
* Implementations must extend IncludeProcessor.
|
|
694
796
|
*/
|
|
695
797
|
export class IncludeProcessor extends Processor {
|
|
696
|
-
|
|
798
|
+
/**
|
|
799
|
+
* @param {Document} document - The document being parsed.
|
|
800
|
+
* @param {Reader} reader - The reader for the including document.
|
|
801
|
+
* @param {string} target - The target of the include directive.
|
|
802
|
+
* @param {Record<string, string>} attributes - The parsed include attributes.
|
|
803
|
+
* @returns {void}
|
|
804
|
+
*/
|
|
805
|
+
process(document, reader, target, attributes) {
|
|
697
806
|
throw new Error(
|
|
698
807
|
`${this.constructor.name} must implement the process method`
|
|
699
808
|
)
|
|
700
809
|
}
|
|
701
810
|
|
|
702
|
-
|
|
811
|
+
/**
|
|
812
|
+
* @param {Document} doc - The document being parsed.
|
|
813
|
+
* @param {string} target - The target of the include directive.
|
|
814
|
+
* @returns {boolean} true if this processor handles the given target.
|
|
815
|
+
*/
|
|
816
|
+
handles(doc, target) {
|
|
703
817
|
return true
|
|
704
818
|
}
|
|
705
819
|
}
|
|
@@ -717,7 +831,11 @@ export class DocinfoProcessor extends Processor {
|
|
|
717
831
|
this.config.location ??= 'head'
|
|
718
832
|
}
|
|
719
833
|
|
|
720
|
-
|
|
834
|
+
/**
|
|
835
|
+
* @param {Document} document - The document being converted.
|
|
836
|
+
* @returns {string} The docinfo content to inject into the document.
|
|
837
|
+
*/
|
|
838
|
+
process(document) {
|
|
721
839
|
throw new Error(
|
|
722
840
|
`${this.constructor.name} must implement the process method`
|
|
723
841
|
)
|
|
@@ -773,7 +891,13 @@ export class BlockProcessor extends Processor {
|
|
|
773
891
|
this.config.content_model ??= 'compound'
|
|
774
892
|
}
|
|
775
893
|
|
|
776
|
-
|
|
894
|
+
/**
|
|
895
|
+
* @param {AbstractBlock} parent - The enclosing block.
|
|
896
|
+
* @param {Reader} reader - The reader positioned at the block content.
|
|
897
|
+
* @param {Record<string, unknown>} attributes - The parsed block attributes.
|
|
898
|
+
* @returns {Block|void} A block node, or void to let the parser handle it.
|
|
899
|
+
*/
|
|
900
|
+
process(parent, reader, attributes) {
|
|
777
901
|
throw new Error(
|
|
778
902
|
`${this.constructor.name} must implement the process method`
|
|
779
903
|
)
|
|
@@ -791,7 +915,13 @@ export class MacroProcessor extends Processor {
|
|
|
791
915
|
this.config.content_model ??= 'attributes'
|
|
792
916
|
}
|
|
793
917
|
|
|
794
|
-
|
|
918
|
+
/**
|
|
919
|
+
* @param {AbstractBlock} parent - The enclosing block.
|
|
920
|
+
* @param {string} target - The macro target (text between `name:` and `[`).
|
|
921
|
+
* @param {Record<string, unknown>} attributes - The parsed macro attributes.
|
|
922
|
+
* @returns {Block|Inline|void}
|
|
923
|
+
*/
|
|
924
|
+
process(parent, target, attributes) {
|
|
795
925
|
throw new Error(
|
|
796
926
|
`${this.constructor.name} must implement the process method`
|
|
797
927
|
)
|
|
@@ -837,6 +967,16 @@ export class BlockMacroProcessor extends MacroProcessor {
|
|
|
837
967
|
set name(value) {
|
|
838
968
|
this._name = value
|
|
839
969
|
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* @param {AbstractBlock} parent - The enclosing block.
|
|
973
|
+
* @param {string} target - The macro target.
|
|
974
|
+
* @param {Record<string, unknown>} attributes - The parsed macro attributes.
|
|
975
|
+
* @returns {Block} A block node created with one of the `createBlock` helpers.
|
|
976
|
+
*/
|
|
977
|
+
process(parent, target, attributes) {
|
|
978
|
+
return super.process(parent, target, attributes)
|
|
979
|
+
}
|
|
840
980
|
}
|
|
841
981
|
BlockMacroProcessor.DSL = MacroProcessorDsl
|
|
842
982
|
|
|
@@ -880,6 +1020,16 @@ export class InlineMacroProcessor extends MacroProcessor {
|
|
|
880
1020
|
))
|
|
881
1021
|
}
|
|
882
1022
|
|
|
1023
|
+
/**
|
|
1024
|
+
* @param {AbstractBlock} parent - The enclosing block.
|
|
1025
|
+
* @param {string} target - The macro target.
|
|
1026
|
+
* @param {Record<string, unknown>} attributes - The parsed macro attributes.
|
|
1027
|
+
* @returns {Inline} An Inline node created with `this.createInline(...)`.
|
|
1028
|
+
*/
|
|
1029
|
+
process(parent, target, attributes) {
|
|
1030
|
+
return super.process(parent, target, attributes)
|
|
1031
|
+
}
|
|
1032
|
+
|
|
883
1033
|
resolveRegexp(name, format) {
|
|
884
1034
|
if (!MacroNameRx.test(name)) {
|
|
885
1035
|
throw new Error(`invalid name for inline macro: ${name}`)
|
|
@@ -1263,6 +1413,15 @@ export class Registry {
|
|
|
1263
1413
|
return !!this._block_extensions
|
|
1264
1414
|
}
|
|
1265
1415
|
|
|
1416
|
+
/**
|
|
1417
|
+
* Retrieve all BlockProcessor Extension proxy objects.
|
|
1418
|
+
*
|
|
1419
|
+
* @returns {ProcessorExtension[]}
|
|
1420
|
+
*/
|
|
1421
|
+
blocks() {
|
|
1422
|
+
return this._block_extensions ? Object.values(this._block_extensions) : []
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1266
1425
|
/**
|
|
1267
1426
|
* Check whether a BlockProcessor is registered for the given name and context.
|
|
1268
1427
|
*
|
|
@@ -1309,6 +1468,17 @@ export class Registry {
|
|
|
1309
1468
|
return !!this._block_macro_extensions
|
|
1310
1469
|
}
|
|
1311
1470
|
|
|
1471
|
+
/**
|
|
1472
|
+
* Retrieve all BlockMacroProcessor Extension proxy objects.
|
|
1473
|
+
*
|
|
1474
|
+
* @returns {ProcessorExtension[]}
|
|
1475
|
+
*/
|
|
1476
|
+
blockMacros() {
|
|
1477
|
+
return this._block_macro_extensions
|
|
1478
|
+
? Object.values(this._block_macro_extensions)
|
|
1479
|
+
: []
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1312
1482
|
/**
|
|
1313
1483
|
* Check whether a BlockMacroProcessor is registered for the given name.
|
|
1314
1484
|
*
|
|
@@ -1414,6 +1584,85 @@ export class Registry {
|
|
|
1414
1584
|
return extension
|
|
1415
1585
|
}
|
|
1416
1586
|
|
|
1587
|
+
// ── JavaScript-style accessors ───────────────────────────────────────────────
|
|
1588
|
+
|
|
1589
|
+
/** @returns {object} the plain Object that maps names to groups for this registry. */
|
|
1590
|
+
getGroups() {
|
|
1591
|
+
return this.groups
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
/** Alias for {@link preprocessors}. */
|
|
1595
|
+
getPreprocessors() {
|
|
1596
|
+
return this.preprocessors()
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
/** Alias for {@link treeProcessors}. */
|
|
1600
|
+
getTreeProcessors() {
|
|
1601
|
+
return this.treeProcessors()
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
/** Alias for {@link includeProcessors}. */
|
|
1605
|
+
getIncludeProcessors() {
|
|
1606
|
+
return this.includeProcessors()
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
/** Alias for {@link postprocessors}. */
|
|
1610
|
+
getPostprocessors() {
|
|
1611
|
+
return this.postprocessors()
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
/**
|
|
1615
|
+
* Alias for {@link docinfoProcessors}.
|
|
1616
|
+
*
|
|
1617
|
+
* @param {string|null} [location=null]
|
|
1618
|
+
*/
|
|
1619
|
+
getDocinfoProcessors(location = null) {
|
|
1620
|
+
return this.docinfoProcessors(location)
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
/** Alias for {@link blocks}. */
|
|
1624
|
+
getBlocks() {
|
|
1625
|
+
return this.blocks()
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
/** Alias for {@link blockMacros}. */
|
|
1629
|
+
getBlockMacros() {
|
|
1630
|
+
return this.blockMacros()
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
/** Alias for {@link inlineMacros}. */
|
|
1634
|
+
getInlineMacros() {
|
|
1635
|
+
return this.inlineMacros()
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
/**
|
|
1639
|
+
* Alias for {@link registeredForInlineMacro}.
|
|
1640
|
+
*
|
|
1641
|
+
* @param {string} name
|
|
1642
|
+
*/
|
|
1643
|
+
getInlineMacroFor(name) {
|
|
1644
|
+
return this.registeredForInlineMacro(name)
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* Alias for {@link registeredForBlock}.
|
|
1649
|
+
*
|
|
1650
|
+
* @param {string} name
|
|
1651
|
+
* @param {string} context
|
|
1652
|
+
*/
|
|
1653
|
+
getBlockFor(name, context) {
|
|
1654
|
+
return this.registeredForBlock(name, context)
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
/**
|
|
1658
|
+
* Alias for {@link registeredForBlockMacro}.
|
|
1659
|
+
*
|
|
1660
|
+
* @param {string} name
|
|
1661
|
+
*/
|
|
1662
|
+
getBlockMacroFor(name) {
|
|
1663
|
+
return this.registeredForBlockMacro(name)
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1417
1666
|
// ── Private helpers ──────────────────────────────────────────────────────────
|
|
1418
1667
|
|
|
1419
1668
|
/** @internal */
|
|
@@ -1639,6 +1888,15 @@ export const Extensions = {
|
|
|
1639
1888
|
return _groups
|
|
1640
1889
|
},
|
|
1641
1890
|
|
|
1891
|
+
/**
|
|
1892
|
+
* Alias for {@link groups}.
|
|
1893
|
+
*
|
|
1894
|
+
* @returns {object}
|
|
1895
|
+
*/
|
|
1896
|
+
getGroups() {
|
|
1897
|
+
return this.groups()
|
|
1898
|
+
},
|
|
1899
|
+
|
|
1642
1900
|
/**
|
|
1643
1901
|
* Create a new Registry, optionally pre-populated with a named block.
|
|
1644
1902
|
*
|
package/src/helpers.js
CHANGED
|
@@ -38,7 +38,16 @@ const rstrip = (line) => line.replace(/[ \t\r\n\f\v]+$/, '')
|
|
|
38
38
|
*/
|
|
39
39
|
export function prepareSourceArray(data, trimEnd = true) {
|
|
40
40
|
if (!data.length) return []
|
|
41
|
-
if (data[0].startsWith(BOM))
|
|
41
|
+
if (data[0].startsWith(BOM)) {
|
|
42
|
+
data[0] = data[0].slice(1)
|
|
43
|
+
} else if (
|
|
44
|
+
data[0].charCodeAt(0) === 0xef &&
|
|
45
|
+
data[0].charCodeAt(1) === 0xbb &&
|
|
46
|
+
data[0].charCodeAt(2) === 0xbf
|
|
47
|
+
) {
|
|
48
|
+
// Strip UTF-8 BOM encoded as three raw characters ( / \xEF\xBB\xBF) if not already decoded to U+FEFF
|
|
49
|
+
data[0] = data[0].slice(3)
|
|
50
|
+
}
|
|
42
51
|
// Strip trailing \r to normalize Windows CRLF line endings (lines were split on \n, leaving \r).
|
|
43
52
|
return trimEnd
|
|
44
53
|
? data.map(rstrip)
|
|
@@ -58,7 +67,16 @@ export function prepareSourceArray(data, trimEnd = true) {
|
|
|
58
67
|
*/
|
|
59
68
|
export function prepareSourceString(data, trimEnd = true) {
|
|
60
69
|
if (!data) return []
|
|
61
|
-
if (data.startsWith(BOM))
|
|
70
|
+
if (data.startsWith(BOM)) {
|
|
71
|
+
data = data.slice(1)
|
|
72
|
+
} else if (
|
|
73
|
+
data.charCodeAt(0) === 0xef &&
|
|
74
|
+
data.charCodeAt(1) === 0xbb &&
|
|
75
|
+
data.charCodeAt(2) === 0xbf
|
|
76
|
+
) {
|
|
77
|
+
// Strip UTF-8 BOM encoded as three raw characters ( / \xEF\xBB\xBF) if not already decoded to U+FEFF
|
|
78
|
+
data = data.slice(3)
|
|
79
|
+
}
|
|
62
80
|
// Normalize Windows CRLF to LF so that split('\n') does not leave trailing \r on each line.
|
|
63
81
|
if (data.includes('\r'))
|
|
64
82
|
data = data.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
|
package/src/index.js
CHANGED
|
@@ -28,6 +28,18 @@ import {
|
|
|
28
28
|
BlockMacroProcessor,
|
|
29
29
|
Extensions,
|
|
30
30
|
} from './extensions.js'
|
|
31
|
+
|
|
32
|
+
// Re-export DSL interface types for TypeScript consumers.
|
|
33
|
+
/**
|
|
34
|
+
* @typedef {import('./extensions.js').ProcessorDslInterface} ProcessorDslInterface
|
|
35
|
+
* @typedef {import('./extensions.js').DocumentProcessorDslInterface} DocumentProcessorDslInterface
|
|
36
|
+
* @typedef {import('./extensions.js').SyntaxProcessorDslInterface} SyntaxProcessorDslInterface
|
|
37
|
+
* @typedef {import('./extensions.js').IncludeProcessorDslInterface} IncludeProcessorDslInterface
|
|
38
|
+
* @typedef {import('./extensions.js').DocinfoProcessorDslInterface} DocinfoProcessorDslInterface
|
|
39
|
+
* @typedef {import('./extensions.js').BlockProcessorDslInterface} BlockProcessorDslInterface
|
|
40
|
+
* @typedef {import('./extensions.js').MacroProcessorDslInterface} MacroProcessorDslInterface
|
|
41
|
+
* @typedef {import('./extensions.js').InlineMacroProcessorDslInterface} InlineMacroProcessorDslInterface
|
|
42
|
+
*/
|
|
31
43
|
import {
|
|
32
44
|
Converter,
|
|
33
45
|
ConverterBase,
|
package/src/load.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import { SpaceDelimiterRx, EscapedSpaceRx } from './rx.js'
|
|
19
19
|
import { NULL, BACKEND_ALIASES } from './constants.js'
|
|
20
|
-
import {
|
|
20
|
+
import { NullLogger, withLogger, getContextLogger } from './logging.js'
|
|
21
21
|
import { basename, extname } from './helpers.js'
|
|
22
22
|
import { Document } from './document.js'
|
|
23
23
|
import { Converter } from './converter.js'
|
|
@@ -58,17 +58,23 @@ export async function load(input, options = {}) {
|
|
|
58
58
|
// Shallow-copy options so we don't mutate the caller's object.
|
|
59
59
|
options = Object.assign({}, options)
|
|
60
60
|
|
|
61
|
-
const timings = options.timings ?? null
|
|
62
|
-
if (timings) timings.start('read')
|
|
63
|
-
|
|
64
61
|
// ── Logger override ───────────────────────────────────────────────────────
|
|
62
|
+
// When a logger option is supplied, run the conversion in an async-local
|
|
63
|
+
// context so the logger is scoped to this call only — no global mutation,
|
|
64
|
+
// which makes concurrent callers (e.g. parallel Deno tests) safe.
|
|
65
65
|
if ('logger' in options) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
66
|
+
const newLogger = options.logger ?? new NullLogger()
|
|
67
|
+
delete options.logger
|
|
68
|
+
return withLogger(newLogger, () => _doLoad(input, options, newLogger))
|
|
70
69
|
}
|
|
71
70
|
|
|
71
|
+
return _doLoad(input, options)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function _doLoad(input, options, explicitLogger = null) {
|
|
75
|
+
const timings = options.timings ?? null
|
|
76
|
+
if (timings) timings.start('read')
|
|
77
|
+
|
|
72
78
|
// ── Attributes normalisation ──────────────────────────────────────────────
|
|
73
79
|
let attrs = options.attributes
|
|
74
80
|
if (!attrs) {
|
|
@@ -100,11 +106,9 @@ export async function load(input, options = {}) {
|
|
|
100
106
|
attrs.docname = basename(inputPath, docfilesuffix)
|
|
101
107
|
}
|
|
102
108
|
source = await _readStream(input)
|
|
103
|
-
} else if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
) {
|
|
107
|
-
source = input.toString('utf8')
|
|
109
|
+
} else if (input instanceof Uint8Array) {
|
|
110
|
+
// Covers both Node.js Buffer (a Uint8Array subclass) and browser Uint8Array shims.
|
|
111
|
+
source = new TextDecoder('utf-8').decode(input)
|
|
108
112
|
} else if (typeof input === 'string') {
|
|
109
113
|
source = input
|
|
110
114
|
} else if (Array.isArray(input)) {
|
|
@@ -167,6 +171,19 @@ export async function load(input, options = {}) {
|
|
|
167
171
|
}
|
|
168
172
|
|
|
169
173
|
if (timings) timings.record('parse')
|
|
174
|
+
|
|
175
|
+
// Persist the logger on the Document instance so that doc.convert()
|
|
176
|
+
// (called by convert.js after the async-local context ends) still routes
|
|
177
|
+
// logging through the caller-supplied logger.
|
|
178
|
+
// ALS provides it in Node.js/Deno; the explicit parameter covers browser fallback.
|
|
179
|
+
const contextLogger = getContextLogger() ?? explicitLogger
|
|
180
|
+
if (contextLogger) {
|
|
181
|
+
Object.defineProperty(doc, 'logger', {
|
|
182
|
+
get: () => contextLogger,
|
|
183
|
+
configurable: true,
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
170
187
|
return doc
|
|
171
188
|
}
|
|
172
189
|
|