@asciidoctor/core 4.0.0-alpha.5 → 4.0.0
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 +123 -25
- package/build/node/index.cjs +123 -25
- package/package.json +6 -6
- package/src/abstract_block.js +1 -1
- package/src/document.js +3 -3
- package/src/extensions.js +125 -22
- package/src/parser.js +2 -2
- package/src/table.js +20 -0
- package/types/document.d.ts +1 -1
- package/types/extensions.d.ts +68 -6
package/build/browser/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var version = "4.0.0
|
|
1
|
+
var version = "4.0.0";
|
|
2
2
|
const packageJson = {
|
|
3
3
|
version: version};
|
|
4
4
|
|
|
@@ -4079,7 +4079,7 @@ class AbstractBlock extends AbstractNode {
|
|
|
4079
4079
|
if (anyContext || contextSelector !== 'section') {
|
|
4080
4080
|
// optimisation
|
|
4081
4081
|
// NOTE dlist items can be null
|
|
4082
|
-
for (const b of this.blocks.flat()) {
|
|
4082
|
+
for (const b of this.blocks.flat(Infinity)) {
|
|
4083
4083
|
if (b) b.#findByInternal(selector, result, filter);
|
|
4084
4084
|
}
|
|
4085
4085
|
}
|
|
@@ -8921,6 +8921,25 @@ class Cell extends AbstractBlock {
|
|
|
8921
8921
|
if (cell._innerDocSetup) {
|
|
8922
8922
|
const { lines, parentDoc, parentDoctitle, options } = cell._innerDocSetup;
|
|
8923
8923
|
cell._innerDocSetup = null;
|
|
8924
|
+
// If the first line may be a preprocessor directive (include, ifdef…), expand it using a
|
|
8925
|
+
// temporary PreprocessorReader — matching the Ruby behaviour in table.rb.
|
|
8926
|
+
if (lines.length > 0 && lines[0].includes('::')) {
|
|
8927
|
+
const firstLine = lines[0];
|
|
8928
|
+
const tmpReader = new PreprocessorReader(
|
|
8929
|
+
parentDoc,
|
|
8930
|
+
[firstLine],
|
|
8931
|
+
options.cursor
|
|
8932
|
+
);
|
|
8933
|
+
const preprocessedLines = await tmpReader.readLines();
|
|
8934
|
+
if (
|
|
8935
|
+
!(
|
|
8936
|
+
preprocessedLines.length === 1 && preprocessedLines[0] === firstLine
|
|
8937
|
+
)
|
|
8938
|
+
) {
|
|
8939
|
+
lines.shift();
|
|
8940
|
+
if (preprocessedLines.length > 0) lines.unshift(...preprocessedLines);
|
|
8941
|
+
}
|
|
8942
|
+
}
|
|
8924
8943
|
const innerDoc = await parentDoc.constructor.create(lines, options);
|
|
8925
8944
|
if (parentDoctitle) parentDoc.attributes.doctitle = parentDoctitle;
|
|
8926
8945
|
cell._innerDocument = innerDoc;
|
|
@@ -11012,8 +11031,8 @@ class Parser {
|
|
|
11012
11031
|
const admStyle = attributes.style ?? blockContext;
|
|
11013
11032
|
attributes.name = admStyle.toLowerCase();
|
|
11014
11033
|
attributes.textlabel =
|
|
11015
|
-
|
|
11016
|
-
|
|
11034
|
+
attributes.caption ?? docAttrs[`${attributes.name}-caption`];
|
|
11035
|
+
delete attributes.caption;
|
|
11017
11036
|
block = await Parser.buildBlock(
|
|
11018
11037
|
blockContext,
|
|
11019
11038
|
'compound',
|
|
@@ -13447,6 +13466,7 @@ function _uniform(str, chr, len) {
|
|
|
13447
13466
|
* DSL interface for include processors.
|
|
13448
13467
|
*
|
|
13449
13468
|
* @typedef {DocumentProcessorDslInterface & {
|
|
13469
|
+
* handles(fn: (target: string) => boolean): void;
|
|
13450
13470
|
* handles(fn: (doc: Document, target: string) => boolean): void;
|
|
13451
13471
|
* }} IncludeProcessorDslInterface
|
|
13452
13472
|
*/
|
|
@@ -13497,7 +13517,7 @@ function _uniform(str, chr, len) {
|
|
|
13497
13517
|
* - Called with a single Function argument → stores it as the process block.
|
|
13498
13518
|
* - Called with non-Function arguments → invokes the stored process block.
|
|
13499
13519
|
*
|
|
13500
|
-
* The this context inside a stored process function is bound to the processor
|
|
13520
|
+
* The `this` context inside a stored process function is bound to the processor
|
|
13501
13521
|
* instance at definition time.
|
|
13502
13522
|
*/
|
|
13503
13523
|
const ProcessorDsl = {
|
|
@@ -13671,6 +13691,35 @@ const SyntaxProcessorDsl = {
|
|
|
13671
13691
|
const IncludeProcessorDsl = {
|
|
13672
13692
|
...DocumentProcessorDsl,
|
|
13673
13693
|
|
|
13694
|
+
/**
|
|
13695
|
+
* @overload
|
|
13696
|
+
* @param {(target: string) => boolean} fn - Predicate that receives only the include target.
|
|
13697
|
+
* @returns {void}
|
|
13698
|
+
*/
|
|
13699
|
+
/**
|
|
13700
|
+
* @overload
|
|
13701
|
+
* @param {(doc: Document, target: string) => boolean} fn - Predicate that receives the document and the include target.
|
|
13702
|
+
* @returns {void}
|
|
13703
|
+
*/
|
|
13704
|
+
/**
|
|
13705
|
+
* @overload
|
|
13706
|
+
* @param {Document} doc - The document being parsed.
|
|
13707
|
+
* @param {string} target - The include target.
|
|
13708
|
+
* @returns {boolean}
|
|
13709
|
+
*/
|
|
13710
|
+
/**
|
|
13711
|
+
* Register a function that decides whether this include processor handles a given target,
|
|
13712
|
+
* or invoke the registered handler.
|
|
13713
|
+
*
|
|
13714
|
+
* **Setter form** — register a predicate. The callback may accept either just the target
|
|
13715
|
+
* string (arity 1) or both the document and the target string (arity 2).
|
|
13716
|
+
*
|
|
13717
|
+
* **Invoker form** — call the registered predicate with `(doc, target)` and return its
|
|
13718
|
+
* result, or return `true` if no predicate has been registered.
|
|
13719
|
+
*
|
|
13720
|
+
* @param {...*} args
|
|
13721
|
+
* @returns {boolean | void}
|
|
13722
|
+
*/
|
|
13674
13723
|
handles(...args) {
|
|
13675
13724
|
if (args.length === 1 && typeof args[0] === 'function') {
|
|
13676
13725
|
const fn = args[0];
|
|
@@ -14438,6 +14487,8 @@ class ProcessorExtension extends Extension {
|
|
|
14438
14487
|
super(kind, instance, instance.config);
|
|
14439
14488
|
this.processMethod =
|
|
14440
14489
|
processMethod || ((...args) => instance.process(...args));
|
|
14490
|
+
/** @internal */
|
|
14491
|
+
this._direct = false;
|
|
14441
14492
|
}
|
|
14442
14493
|
}
|
|
14443
14494
|
|
|
@@ -14465,10 +14516,24 @@ const SYNTAX_PROCESSOR_CLASSES = {
|
|
|
14465
14516
|
* Registry holds the extensions which have been registered and activated, has
|
|
14466
14517
|
* methods for registering or defining a processor and looks up extensions
|
|
14467
14518
|
* stored in the registry during parsing.
|
|
14519
|
+
*
|
|
14520
|
+
* A registry can be reused across multiple conversions. Extensions registered
|
|
14521
|
+
* via a group block (passed to {@link Extensions.create} or
|
|
14522
|
+
* {@link Extensions.register}) are re-executed on every activation. Extensions
|
|
14523
|
+
* registered directly on the registry instance (e.g. `registry.preprocessor(fn)`)
|
|
14524
|
+
* are preserved across activations.
|
|
14525
|
+
*
|
|
14526
|
+
* @example
|
|
14527
|
+
* const registry = Extensions.create('my-ext', function () {
|
|
14528
|
+
* this.preprocessor(function () { ... })
|
|
14529
|
+
* })
|
|
14530
|
+
* // registry can be passed to multiple conversions safely
|
|
14468
14531
|
*/
|
|
14469
14532
|
class Registry {
|
|
14470
14533
|
constructor(groups = {}) {
|
|
14471
14534
|
this.groups = groups;
|
|
14535
|
+
/** @internal */
|
|
14536
|
+
this._activating = false;
|
|
14472
14537
|
this._reset();
|
|
14473
14538
|
}
|
|
14474
14539
|
|
|
@@ -14485,18 +14550,26 @@ class Registry {
|
|
|
14485
14550
|
...Object.values(Extensions.groups()),
|
|
14486
14551
|
...Object.values(this.groups),
|
|
14487
14552
|
];
|
|
14488
|
-
|
|
14489
|
-
|
|
14490
|
-
|
|
14491
|
-
if (
|
|
14492
|
-
|
|
14493
|
-
|
|
14494
|
-
|
|
14495
|
-
|
|
14553
|
+
this._activating = true;
|
|
14554
|
+
try {
|
|
14555
|
+
for (const group of extGroups) {
|
|
14556
|
+
if (typeof group === 'function') {
|
|
14557
|
+
// Check if it is a class (constructor) with an activate prototype method.
|
|
14558
|
+
if (
|
|
14559
|
+
group.prototype &&
|
|
14560
|
+
typeof group.prototype.activate === 'function'
|
|
14561
|
+
) {
|
|
14562
|
+
new group().activate(this);
|
|
14563
|
+
} else {
|
|
14564
|
+
// Plain function — call in the context of this registry (like instance_exec).
|
|
14565
|
+
group.length === 0 ? group.call(this) : group(this);
|
|
14566
|
+
}
|
|
14567
|
+
} else if (group && typeof group.activate === 'function') {
|
|
14568
|
+
group.activate(this);
|
|
14496
14569
|
}
|
|
14497
|
-
} else if (group && typeof group.activate === 'function') {
|
|
14498
|
-
group.activate(this);
|
|
14499
14570
|
}
|
|
14571
|
+
} finally {
|
|
14572
|
+
this._activating = false;
|
|
14500
14573
|
}
|
|
14501
14574
|
return this
|
|
14502
14575
|
}
|
|
@@ -15072,6 +15145,7 @@ class Registry {
|
|
|
15072
15145
|
}
|
|
15073
15146
|
|
|
15074
15147
|
const extension = new ProcessorExtension(kind, processorInstance);
|
|
15148
|
+
extension._direct = !this._activating;
|
|
15075
15149
|
extension.config.position === '>>'
|
|
15076
15150
|
? store.unshift(extension)
|
|
15077
15151
|
: store.push(extension);
|
|
@@ -15143,27 +15217,44 @@ class Registry {
|
|
|
15143
15217
|
}
|
|
15144
15218
|
|
|
15145
15219
|
store[name] = new ProcessorExtension(kind, processorInstance);
|
|
15220
|
+
store[name]._direct = !this._activating;
|
|
15146
15221
|
return store[name]
|
|
15147
15222
|
}
|
|
15148
15223
|
|
|
15149
15224
|
/** @internal */
|
|
15150
15225
|
_reset() {
|
|
15226
|
+
// Keep extensions registered directly (outside a group); only clear group-registered ones.
|
|
15227
|
+
// Extensions tagged with _direct=true survive across activations.
|
|
15228
|
+
const keepArr = (arr) => {
|
|
15229
|
+
const kept = arr?.filter((e) => e._direct) ?? [];
|
|
15230
|
+
return kept.length ? kept : null
|
|
15231
|
+
};
|
|
15232
|
+
const keepMap = (map) => {
|
|
15233
|
+
if (!map) return null
|
|
15234
|
+
const kept = Object.create(null);
|
|
15235
|
+
for (const [k, v] of Object.entries(map)) if (v._direct) kept[k] = v;
|
|
15236
|
+
return Object.keys(kept).length ? kept : null
|
|
15237
|
+
};
|
|
15151
15238
|
/** @internal */
|
|
15152
|
-
this._preprocessor_extensions =
|
|
15239
|
+
this._preprocessor_extensions = keepArr(this._preprocessor_extensions);
|
|
15153
15240
|
/** @internal */
|
|
15154
|
-
this._tree_processor_extensions =
|
|
15241
|
+
this._tree_processor_extensions = keepArr(this._tree_processor_extensions);
|
|
15155
15242
|
/** @internal */
|
|
15156
|
-
this._postprocessor_extensions =
|
|
15243
|
+
this._postprocessor_extensions = keepArr(this._postprocessor_extensions);
|
|
15157
15244
|
/** @internal */
|
|
15158
|
-
this._include_processor_extensions =
|
|
15245
|
+
this._include_processor_extensions = keepArr(
|
|
15246
|
+
this._include_processor_extensions
|
|
15247
|
+
);
|
|
15159
15248
|
/** @internal */
|
|
15160
|
-
this._docinfo_processor_extensions =
|
|
15249
|
+
this._docinfo_processor_extensions = keepArr(
|
|
15250
|
+
this._docinfo_processor_extensions
|
|
15251
|
+
);
|
|
15161
15252
|
/** @internal */
|
|
15162
|
-
this._block_extensions =
|
|
15253
|
+
this._block_extensions = keepMap(this._block_extensions);
|
|
15163
15254
|
/** @internal */
|
|
15164
|
-
this._block_macro_extensions =
|
|
15255
|
+
this._block_macro_extensions = keepMap(this._block_macro_extensions);
|
|
15165
15256
|
/** @internal */
|
|
15166
|
-
this._inline_macro_extensions =
|
|
15257
|
+
this._inline_macro_extensions = keepMap(this._inline_macro_extensions);
|
|
15167
15258
|
this.document = null;
|
|
15168
15259
|
}
|
|
15169
15260
|
|
|
@@ -15246,6 +15337,13 @@ const Extensions = {
|
|
|
15246
15337
|
/**
|
|
15247
15338
|
* Create a new Registry, optionally pre-populated with a named block.
|
|
15248
15339
|
*
|
|
15340
|
+
* When a `block` is provided it is stored as a group and re-executed on every
|
|
15341
|
+
* activation, making the registry safe to reuse across multiple conversions.
|
|
15342
|
+
* Without a `block`, any extensions registered directly on the returned registry
|
|
15343
|
+
* (e.g. `registry.preprocessor(fn)`) are stored in transient state that is
|
|
15344
|
+
* cleared on every activation — those registrations will be lost from the second
|
|
15345
|
+
* conversion onwards. Prefer the block form when the registry may be reused.
|
|
15346
|
+
*
|
|
15249
15347
|
* @param {string|null} [name=null] - Optional name for the group; auto-generated if omitted.
|
|
15250
15348
|
* @param {Function|null} [block=null] - Optional function to register as the group.
|
|
15251
15349
|
* @returns {Registry}
|
|
@@ -16022,8 +16120,8 @@ class Document extends AbstractBlock {
|
|
|
16022
16120
|
if ((this.doctype = attrs.doctype = parentDoctype) !== DEFAULT_DOCTYPE) {
|
|
16023
16121
|
this._updateDoctypeAttributes(DEFAULT_DOCTYPE);
|
|
16024
16122
|
}
|
|
16025
|
-
//
|
|
16026
|
-
this.reader = new
|
|
16123
|
+
// Nested documents use a plain Reader (no include/conditional processing), matching Ruby behaviour.
|
|
16124
|
+
this.reader = new Reader(data, options.cursor, { document: this });
|
|
16027
16125
|
if (this.sourcemap) this.sourceLocation = this.reader.cursor;
|
|
16028
16126
|
} else {
|
|
16029
16127
|
this.backend = null;
|
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
|
|
8
|
+
var version = "4.0.0";
|
|
9
9
|
const packageJson = {
|
|
10
10
|
version: version};
|
|
11
11
|
|
|
@@ -4086,7 +4086,7 @@ class AbstractBlock extends AbstractNode {
|
|
|
4086
4086
|
if (anyContext || contextSelector !== 'section') {
|
|
4087
4087
|
// optimisation
|
|
4088
4088
|
// NOTE dlist items can be null
|
|
4089
|
-
for (const b of this.blocks.flat()) {
|
|
4089
|
+
for (const b of this.blocks.flat(Infinity)) {
|
|
4090
4090
|
if (b) b.#findByInternal(selector, result, filter);
|
|
4091
4091
|
}
|
|
4092
4092
|
}
|
|
@@ -8928,6 +8928,25 @@ class Cell extends AbstractBlock {
|
|
|
8928
8928
|
if (cell._innerDocSetup) {
|
|
8929
8929
|
const { lines, parentDoc, parentDoctitle, options } = cell._innerDocSetup;
|
|
8930
8930
|
cell._innerDocSetup = null;
|
|
8931
|
+
// If the first line may be a preprocessor directive (include, ifdef…), expand it using a
|
|
8932
|
+
// temporary PreprocessorReader — matching the Ruby behaviour in table.rb.
|
|
8933
|
+
if (lines.length > 0 && lines[0].includes('::')) {
|
|
8934
|
+
const firstLine = lines[0];
|
|
8935
|
+
const tmpReader = new PreprocessorReader(
|
|
8936
|
+
parentDoc,
|
|
8937
|
+
[firstLine],
|
|
8938
|
+
options.cursor
|
|
8939
|
+
);
|
|
8940
|
+
const preprocessedLines = await tmpReader.readLines();
|
|
8941
|
+
if (
|
|
8942
|
+
!(
|
|
8943
|
+
preprocessedLines.length === 1 && preprocessedLines[0] === firstLine
|
|
8944
|
+
)
|
|
8945
|
+
) {
|
|
8946
|
+
lines.shift();
|
|
8947
|
+
if (preprocessedLines.length > 0) lines.unshift(...preprocessedLines);
|
|
8948
|
+
}
|
|
8949
|
+
}
|
|
8931
8950
|
const innerDoc = await parentDoc.constructor.create(lines, options);
|
|
8932
8951
|
if (parentDoctitle) parentDoc.attributes.doctitle = parentDoctitle;
|
|
8933
8952
|
cell._innerDocument = innerDoc;
|
|
@@ -11019,8 +11038,8 @@ class Parser {
|
|
|
11019
11038
|
const admStyle = attributes.style ?? blockContext;
|
|
11020
11039
|
attributes.name = admStyle.toLowerCase();
|
|
11021
11040
|
attributes.textlabel =
|
|
11022
|
-
|
|
11023
|
-
|
|
11041
|
+
attributes.caption ?? docAttrs[`${attributes.name}-caption`];
|
|
11042
|
+
delete attributes.caption;
|
|
11024
11043
|
block = await Parser.buildBlock(
|
|
11025
11044
|
blockContext,
|
|
11026
11045
|
'compound',
|
|
@@ -13454,6 +13473,7 @@ function _uniform(str, chr, len) {
|
|
|
13454
13473
|
* DSL interface for include processors.
|
|
13455
13474
|
*
|
|
13456
13475
|
* @typedef {DocumentProcessorDslInterface & {
|
|
13476
|
+
* handles(fn: (target: string) => boolean): void;
|
|
13457
13477
|
* handles(fn: (doc: Document, target: string) => boolean): void;
|
|
13458
13478
|
* }} IncludeProcessorDslInterface
|
|
13459
13479
|
*/
|
|
@@ -13504,7 +13524,7 @@ function _uniform(str, chr, len) {
|
|
|
13504
13524
|
* - Called with a single Function argument → stores it as the process block.
|
|
13505
13525
|
* - Called with non-Function arguments → invokes the stored process block.
|
|
13506
13526
|
*
|
|
13507
|
-
* The this context inside a stored process function is bound to the processor
|
|
13527
|
+
* The `this` context inside a stored process function is bound to the processor
|
|
13508
13528
|
* instance at definition time.
|
|
13509
13529
|
*/
|
|
13510
13530
|
const ProcessorDsl = {
|
|
@@ -13678,6 +13698,35 @@ const SyntaxProcessorDsl = {
|
|
|
13678
13698
|
const IncludeProcessorDsl = {
|
|
13679
13699
|
...DocumentProcessorDsl,
|
|
13680
13700
|
|
|
13701
|
+
/**
|
|
13702
|
+
* @overload
|
|
13703
|
+
* @param {(target: string) => boolean} fn - Predicate that receives only the include target.
|
|
13704
|
+
* @returns {void}
|
|
13705
|
+
*/
|
|
13706
|
+
/**
|
|
13707
|
+
* @overload
|
|
13708
|
+
* @param {(doc: Document, target: string) => boolean} fn - Predicate that receives the document and the include target.
|
|
13709
|
+
* @returns {void}
|
|
13710
|
+
*/
|
|
13711
|
+
/**
|
|
13712
|
+
* @overload
|
|
13713
|
+
* @param {Document} doc - The document being parsed.
|
|
13714
|
+
* @param {string} target - The include target.
|
|
13715
|
+
* @returns {boolean}
|
|
13716
|
+
*/
|
|
13717
|
+
/**
|
|
13718
|
+
* Register a function that decides whether this include processor handles a given target,
|
|
13719
|
+
* or invoke the registered handler.
|
|
13720
|
+
*
|
|
13721
|
+
* **Setter form** — register a predicate. The callback may accept either just the target
|
|
13722
|
+
* string (arity 1) or both the document and the target string (arity 2).
|
|
13723
|
+
*
|
|
13724
|
+
* **Invoker form** — call the registered predicate with `(doc, target)` and return its
|
|
13725
|
+
* result, or return `true` if no predicate has been registered.
|
|
13726
|
+
*
|
|
13727
|
+
* @param {...*} args
|
|
13728
|
+
* @returns {boolean | void}
|
|
13729
|
+
*/
|
|
13681
13730
|
handles(...args) {
|
|
13682
13731
|
if (args.length === 1 && typeof args[0] === 'function') {
|
|
13683
13732
|
const fn = args[0];
|
|
@@ -14445,6 +14494,8 @@ class ProcessorExtension extends Extension {
|
|
|
14445
14494
|
super(kind, instance, instance.config);
|
|
14446
14495
|
this.processMethod =
|
|
14447
14496
|
processMethod || ((...args) => instance.process(...args));
|
|
14497
|
+
/** @internal */
|
|
14498
|
+
this._direct = false;
|
|
14448
14499
|
}
|
|
14449
14500
|
}
|
|
14450
14501
|
|
|
@@ -14472,10 +14523,24 @@ const SYNTAX_PROCESSOR_CLASSES = {
|
|
|
14472
14523
|
* Registry holds the extensions which have been registered and activated, has
|
|
14473
14524
|
* methods for registering or defining a processor and looks up extensions
|
|
14474
14525
|
* stored in the registry during parsing.
|
|
14526
|
+
*
|
|
14527
|
+
* A registry can be reused across multiple conversions. Extensions registered
|
|
14528
|
+
* via a group block (passed to {@link Extensions.create} or
|
|
14529
|
+
* {@link Extensions.register}) are re-executed on every activation. Extensions
|
|
14530
|
+
* registered directly on the registry instance (e.g. `registry.preprocessor(fn)`)
|
|
14531
|
+
* are preserved across activations.
|
|
14532
|
+
*
|
|
14533
|
+
* @example
|
|
14534
|
+
* const registry = Extensions.create('my-ext', function () {
|
|
14535
|
+
* this.preprocessor(function () { ... })
|
|
14536
|
+
* })
|
|
14537
|
+
* // registry can be passed to multiple conversions safely
|
|
14475
14538
|
*/
|
|
14476
14539
|
class Registry {
|
|
14477
14540
|
constructor(groups = {}) {
|
|
14478
14541
|
this.groups = groups;
|
|
14542
|
+
/** @internal */
|
|
14543
|
+
this._activating = false;
|
|
14479
14544
|
this._reset();
|
|
14480
14545
|
}
|
|
14481
14546
|
|
|
@@ -14492,18 +14557,26 @@ class Registry {
|
|
|
14492
14557
|
...Object.values(Extensions.groups()),
|
|
14493
14558
|
...Object.values(this.groups),
|
|
14494
14559
|
];
|
|
14495
|
-
|
|
14496
|
-
|
|
14497
|
-
|
|
14498
|
-
if (
|
|
14499
|
-
|
|
14500
|
-
|
|
14501
|
-
|
|
14502
|
-
|
|
14560
|
+
this._activating = true;
|
|
14561
|
+
try {
|
|
14562
|
+
for (const group of extGroups) {
|
|
14563
|
+
if (typeof group === 'function') {
|
|
14564
|
+
// Check if it is a class (constructor) with an activate prototype method.
|
|
14565
|
+
if (
|
|
14566
|
+
group.prototype &&
|
|
14567
|
+
typeof group.prototype.activate === 'function'
|
|
14568
|
+
) {
|
|
14569
|
+
new group().activate(this);
|
|
14570
|
+
} else {
|
|
14571
|
+
// Plain function — call in the context of this registry (like instance_exec).
|
|
14572
|
+
group.length === 0 ? group.call(this) : group(this);
|
|
14573
|
+
}
|
|
14574
|
+
} else if (group && typeof group.activate === 'function') {
|
|
14575
|
+
group.activate(this);
|
|
14503
14576
|
}
|
|
14504
|
-
} else if (group && typeof group.activate === 'function') {
|
|
14505
|
-
group.activate(this);
|
|
14506
14577
|
}
|
|
14578
|
+
} finally {
|
|
14579
|
+
this._activating = false;
|
|
14507
14580
|
}
|
|
14508
14581
|
return this
|
|
14509
14582
|
}
|
|
@@ -15079,6 +15152,7 @@ class Registry {
|
|
|
15079
15152
|
}
|
|
15080
15153
|
|
|
15081
15154
|
const extension = new ProcessorExtension(kind, processorInstance);
|
|
15155
|
+
extension._direct = !this._activating;
|
|
15082
15156
|
extension.config.position === '>>'
|
|
15083
15157
|
? store.unshift(extension)
|
|
15084
15158
|
: store.push(extension);
|
|
@@ -15150,27 +15224,44 @@ class Registry {
|
|
|
15150
15224
|
}
|
|
15151
15225
|
|
|
15152
15226
|
store[name] = new ProcessorExtension(kind, processorInstance);
|
|
15227
|
+
store[name]._direct = !this._activating;
|
|
15153
15228
|
return store[name]
|
|
15154
15229
|
}
|
|
15155
15230
|
|
|
15156
15231
|
/** @internal */
|
|
15157
15232
|
_reset() {
|
|
15233
|
+
// Keep extensions registered directly (outside a group); only clear group-registered ones.
|
|
15234
|
+
// Extensions tagged with _direct=true survive across activations.
|
|
15235
|
+
const keepArr = (arr) => {
|
|
15236
|
+
const kept = arr?.filter((e) => e._direct) ?? [];
|
|
15237
|
+
return kept.length ? kept : null
|
|
15238
|
+
};
|
|
15239
|
+
const keepMap = (map) => {
|
|
15240
|
+
if (!map) return null
|
|
15241
|
+
const kept = Object.create(null);
|
|
15242
|
+
for (const [k, v] of Object.entries(map)) if (v._direct) kept[k] = v;
|
|
15243
|
+
return Object.keys(kept).length ? kept : null
|
|
15244
|
+
};
|
|
15158
15245
|
/** @internal */
|
|
15159
|
-
this._preprocessor_extensions =
|
|
15246
|
+
this._preprocessor_extensions = keepArr(this._preprocessor_extensions);
|
|
15160
15247
|
/** @internal */
|
|
15161
|
-
this._tree_processor_extensions =
|
|
15248
|
+
this._tree_processor_extensions = keepArr(this._tree_processor_extensions);
|
|
15162
15249
|
/** @internal */
|
|
15163
|
-
this._postprocessor_extensions =
|
|
15250
|
+
this._postprocessor_extensions = keepArr(this._postprocessor_extensions);
|
|
15164
15251
|
/** @internal */
|
|
15165
|
-
this._include_processor_extensions =
|
|
15252
|
+
this._include_processor_extensions = keepArr(
|
|
15253
|
+
this._include_processor_extensions
|
|
15254
|
+
);
|
|
15166
15255
|
/** @internal */
|
|
15167
|
-
this._docinfo_processor_extensions =
|
|
15256
|
+
this._docinfo_processor_extensions = keepArr(
|
|
15257
|
+
this._docinfo_processor_extensions
|
|
15258
|
+
);
|
|
15168
15259
|
/** @internal */
|
|
15169
|
-
this._block_extensions =
|
|
15260
|
+
this._block_extensions = keepMap(this._block_extensions);
|
|
15170
15261
|
/** @internal */
|
|
15171
|
-
this._block_macro_extensions =
|
|
15262
|
+
this._block_macro_extensions = keepMap(this._block_macro_extensions);
|
|
15172
15263
|
/** @internal */
|
|
15173
|
-
this._inline_macro_extensions =
|
|
15264
|
+
this._inline_macro_extensions = keepMap(this._inline_macro_extensions);
|
|
15174
15265
|
this.document = null;
|
|
15175
15266
|
}
|
|
15176
15267
|
|
|
@@ -15253,6 +15344,13 @@ const Extensions = {
|
|
|
15253
15344
|
/**
|
|
15254
15345
|
* Create a new Registry, optionally pre-populated with a named block.
|
|
15255
15346
|
*
|
|
15347
|
+
* When a `block` is provided it is stored as a group and re-executed on every
|
|
15348
|
+
* activation, making the registry safe to reuse across multiple conversions.
|
|
15349
|
+
* Without a `block`, any extensions registered directly on the returned registry
|
|
15350
|
+
* (e.g. `registry.preprocessor(fn)`) are stored in transient state that is
|
|
15351
|
+
* cleared on every activation — those registrations will be lost from the second
|
|
15352
|
+
* conversion onwards. Prefer the block form when the registry may be reused.
|
|
15353
|
+
*
|
|
15256
15354
|
* @param {string|null} [name=null] - Optional name for the group; auto-generated if omitted.
|
|
15257
15355
|
* @param {Function|null} [block=null] - Optional function to register as the group.
|
|
15258
15356
|
* @returns {Registry}
|
|
@@ -16029,8 +16127,8 @@ class Document extends AbstractBlock {
|
|
|
16029
16127
|
if ((this.doctype = attrs.doctype = parentDoctype) !== DEFAULT_DOCTYPE) {
|
|
16030
16128
|
this._updateDoctypeAttributes(DEFAULT_DOCTYPE);
|
|
16031
16129
|
}
|
|
16032
|
-
//
|
|
16033
|
-
this.reader = new
|
|
16130
|
+
// Nested documents use a plain Reader (no include/conditional processing), matching Ruby behaviour.
|
|
16131
|
+
this.reader = new Reader(data, options.cursor, { document: this });
|
|
16034
16132
|
if (this.sourcemap) this.sourceLocation = this.reader.cursor;
|
|
16035
16133
|
} else {
|
|
16036
16134
|
this.backend = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asciidoctor/core",
|
|
3
|
-
"version": "4.0.0
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Asciidoctor.js: AsciiDoc in JavaScript powered by Asciidoctor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/node/index.cjs",
|
|
@@ -66,16 +66,16 @@
|
|
|
66
66
|
},
|
|
67
67
|
"homepage": "https://github.com/asciidoctor/asciidoctor.js",
|
|
68
68
|
"volta": {
|
|
69
|
-
"node": "24.
|
|
69
|
+
"node": "24.16.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@biomejs/biome": "^2.4.13",
|
|
73
73
|
"typedoc": "^0.28.4",
|
|
74
74
|
"@rollup/plugin-json": "^6.1.0",
|
|
75
75
|
"@types/node": "^25.6.0",
|
|
76
|
-
"@vitest/browser": "^4.1.
|
|
77
|
-
"@vitest/browser-playwright": "^4.1.
|
|
78
|
-
"@vitest/ui": "^4.1.
|
|
76
|
+
"@vitest/browser": "^4.1.7",
|
|
77
|
+
"@vitest/browser-playwright": "^4.1.7",
|
|
78
|
+
"@vitest/ui": "^4.1.7",
|
|
79
79
|
"@xmldom/xmldom": "^0.9.9",
|
|
80
80
|
"dot": "^1.1.3",
|
|
81
81
|
"ejs": "^5.0.2",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"linkedom": "^0.18.12",
|
|
84
84
|
"node-html-parser": "^7.1.0",
|
|
85
85
|
"nunjucks": "^3.2.4",
|
|
86
|
-
"playwright": "^1.
|
|
86
|
+
"playwright": "^1.60.0",
|
|
87
87
|
"pug": "^3.0.4",
|
|
88
88
|
"rollup": "^4.60.2",
|
|
89
89
|
"typescript": "^6.0.3",
|
package/src/abstract_block.js
CHANGED
|
@@ -626,7 +626,7 @@ export class AbstractBlock extends AbstractNode {
|
|
|
626
626
|
if (anyContext || contextSelector !== 'section') {
|
|
627
627
|
// optimisation
|
|
628
628
|
// NOTE dlist items can be null
|
|
629
|
-
for (const b of this.blocks.flat()) {
|
|
629
|
+
for (const b of this.blocks.flat(Infinity)) {
|
|
630
630
|
if (b) b.#findByInternal(selector, result, filter)
|
|
631
631
|
}
|
|
632
632
|
}
|
package/src/document.js
CHANGED
|
@@ -34,7 +34,7 @@ import { XmlSanitizeRx, AttributeEntryPassMacroRx } from './rx.js'
|
|
|
34
34
|
import { LF } from './constants.js'
|
|
35
35
|
import { applyLogging } from './logging.js'
|
|
36
36
|
import { SyntaxHighlighter, DefaultFactoryProxy } from './syntax_highlighter.js'
|
|
37
|
-
import { PreprocessorReader, Cursor } from './reader.js'
|
|
37
|
+
import { Reader, PreprocessorReader, Cursor } from './reader.js'
|
|
38
38
|
import { Parser } from './parser.js'
|
|
39
39
|
import { Extensions, Registry } from './extensions.js'
|
|
40
40
|
|
|
@@ -483,8 +483,8 @@ export class Document extends AbstractBlock {
|
|
|
483
483
|
if ((this.doctype = attrs.doctype = parentDoctype) !== DEFAULT_DOCTYPE) {
|
|
484
484
|
this._updateDoctypeAttributes(DEFAULT_DOCTYPE)
|
|
485
485
|
}
|
|
486
|
-
//
|
|
487
|
-
this.reader = new
|
|
486
|
+
// Nested documents use a plain Reader (no include/conditional processing), matching Ruby behaviour.
|
|
487
|
+
this.reader = new Reader(data, options.cursor, { document: this })
|
|
488
488
|
if (this.sourcemap) this.sourceLocation = this.reader.cursor
|
|
489
489
|
} else {
|
|
490
490
|
this.backend = null
|
package/src/extensions.js
CHANGED
|
@@ -75,6 +75,7 @@ import { MacroNameRx, CC_ANY } from './rx.js'
|
|
|
75
75
|
* DSL interface for include processors.
|
|
76
76
|
*
|
|
77
77
|
* @typedef {DocumentProcessorDslInterface & {
|
|
78
|
+
* handles(fn: (target: string) => boolean): void;
|
|
78
79
|
* handles(fn: (doc: Document, target: string) => boolean): void;
|
|
79
80
|
* }} IncludeProcessorDslInterface
|
|
80
81
|
*/
|
|
@@ -125,7 +126,7 @@ import { MacroNameRx, CC_ANY } from './rx.js'
|
|
|
125
126
|
* - Called with a single Function argument → stores it as the process block.
|
|
126
127
|
* - Called with non-Function arguments → invokes the stored process block.
|
|
127
128
|
*
|
|
128
|
-
* The this context inside a stored process function is bound to the processor
|
|
129
|
+
* The `this` context inside a stored process function is bound to the processor
|
|
129
130
|
* instance at definition time.
|
|
130
131
|
*/
|
|
131
132
|
export const ProcessorDsl = {
|
|
@@ -299,6 +300,35 @@ export const SyntaxProcessorDsl = {
|
|
|
299
300
|
export const IncludeProcessorDsl = {
|
|
300
301
|
...DocumentProcessorDsl,
|
|
301
302
|
|
|
303
|
+
/**
|
|
304
|
+
* @overload
|
|
305
|
+
* @param {(target: string) => boolean} fn - Predicate that receives only the include target.
|
|
306
|
+
* @returns {void}
|
|
307
|
+
*/
|
|
308
|
+
/**
|
|
309
|
+
* @overload
|
|
310
|
+
* @param {(doc: Document, target: string) => boolean} fn - Predicate that receives the document and the include target.
|
|
311
|
+
* @returns {void}
|
|
312
|
+
*/
|
|
313
|
+
/**
|
|
314
|
+
* @overload
|
|
315
|
+
* @param {Document} doc - The document being parsed.
|
|
316
|
+
* @param {string} target - The include target.
|
|
317
|
+
* @returns {boolean}
|
|
318
|
+
*/
|
|
319
|
+
/**
|
|
320
|
+
* Register a function that decides whether this include processor handles a given target,
|
|
321
|
+
* or invoke the registered handler.
|
|
322
|
+
*
|
|
323
|
+
* **Setter form** — register a predicate. The callback may accept either just the target
|
|
324
|
+
* string (arity 1) or both the document and the target string (arity 2).
|
|
325
|
+
*
|
|
326
|
+
* **Invoker form** — call the registered predicate with `(doc, target)` and return its
|
|
327
|
+
* result, or return `true` if no predicate has been registered.
|
|
328
|
+
*
|
|
329
|
+
* @param {...*} args
|
|
330
|
+
* @returns {boolean | void}
|
|
331
|
+
*/
|
|
302
332
|
handles(...args) {
|
|
303
333
|
if (args.length === 1 && typeof args[0] === 'function') {
|
|
304
334
|
const fn = args[0]
|
|
@@ -1072,22 +1102,48 @@ export class ProcessorExtension extends Extension {
|
|
|
1072
1102
|
super(kind, instance, instance.config)
|
|
1073
1103
|
this.processMethod =
|
|
1074
1104
|
processMethod || ((...args) => instance.process(...args))
|
|
1105
|
+
/** @internal */
|
|
1106
|
+
this._direct = false
|
|
1075
1107
|
}
|
|
1076
1108
|
}
|
|
1077
1109
|
|
|
1078
1110
|
// ── Group ─────────────────────────────────────────────────────────────────────
|
|
1079
1111
|
|
|
1080
1112
|
/**
|
|
1081
|
-
* A Group
|
|
1113
|
+
* A Group bundles one or more extension registrations that are re-executed on
|
|
1114
|
+
* every document conversion, making the registry safe to reuse.
|
|
1115
|
+
*
|
|
1116
|
+
* Subclass Group, override {@link Group#activate}, and either call
|
|
1117
|
+
* {@link Group.register} to add it globally or pass the subclass to
|
|
1118
|
+
* {@link Extensions.create} / {@link Extensions.register}.
|
|
1082
1119
|
*
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1120
|
+
* @example
|
|
1121
|
+
* class MyGroup extends Group {
|
|
1122
|
+
* activate (registry) {
|
|
1123
|
+
* registry.preprocessor(MyPreprocessor)
|
|
1124
|
+
* }
|
|
1125
|
+
* }
|
|
1126
|
+
* MyGroup.register()
|
|
1085
1127
|
*/
|
|
1086
1128
|
export class Group {
|
|
1129
|
+
/**
|
|
1130
|
+
* Register this Group class globally under the given name.
|
|
1131
|
+
*
|
|
1132
|
+
* Equivalent to calling `Extensions.register(name, this)`.
|
|
1133
|
+
*
|
|
1134
|
+
* @param {string|null} [name] - Optional name for the group.
|
|
1135
|
+
*/
|
|
1087
1136
|
static register(name = null) {
|
|
1088
1137
|
Extensions.register(name, this)
|
|
1089
1138
|
}
|
|
1090
1139
|
|
|
1140
|
+
/**
|
|
1141
|
+
* Called by {@link Registry#activate} on every document conversion.
|
|
1142
|
+
*
|
|
1143
|
+
* Override this method to register extensions with the provided registry.
|
|
1144
|
+
*
|
|
1145
|
+
* @param {Registry} _registry - The registry to register extensions with.
|
|
1146
|
+
*/
|
|
1091
1147
|
activate(_registry) {
|
|
1092
1148
|
throw new Error(
|
|
1093
1149
|
`${this.constructor.name} must implement the activate method`
|
|
@@ -1119,10 +1175,24 @@ const SYNTAX_PROCESSOR_CLASSES = {
|
|
|
1119
1175
|
* Registry holds the extensions which have been registered and activated, has
|
|
1120
1176
|
* methods for registering or defining a processor and looks up extensions
|
|
1121
1177
|
* stored in the registry during parsing.
|
|
1178
|
+
*
|
|
1179
|
+
* A registry can be reused across multiple conversions. Extensions registered
|
|
1180
|
+
* via a group block (passed to {@link Extensions.create} or
|
|
1181
|
+
* {@link Extensions.register}) are re-executed on every activation. Extensions
|
|
1182
|
+
* registered directly on the registry instance (e.g. `registry.preprocessor(fn)`)
|
|
1183
|
+
* are preserved across activations.
|
|
1184
|
+
*
|
|
1185
|
+
* @example
|
|
1186
|
+
* const registry = Extensions.create('my-ext', function () {
|
|
1187
|
+
* this.preprocessor(function () { ... })
|
|
1188
|
+
* })
|
|
1189
|
+
* // registry can be passed to multiple conversions safely
|
|
1122
1190
|
*/
|
|
1123
1191
|
export class Registry {
|
|
1124
1192
|
constructor(groups = {}) {
|
|
1125
1193
|
this.groups = groups
|
|
1194
|
+
/** @internal */
|
|
1195
|
+
this._activating = false
|
|
1126
1196
|
this._reset()
|
|
1127
1197
|
}
|
|
1128
1198
|
|
|
@@ -1139,18 +1209,26 @@ export class Registry {
|
|
|
1139
1209
|
...Object.values(Extensions.groups()),
|
|
1140
1210
|
...Object.values(this.groups),
|
|
1141
1211
|
]
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
if (
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1212
|
+
this._activating = true
|
|
1213
|
+
try {
|
|
1214
|
+
for (const group of extGroups) {
|
|
1215
|
+
if (typeof group === 'function') {
|
|
1216
|
+
// Check if it is a class (constructor) with an activate prototype method.
|
|
1217
|
+
if (
|
|
1218
|
+
group.prototype &&
|
|
1219
|
+
typeof group.prototype.activate === 'function'
|
|
1220
|
+
) {
|
|
1221
|
+
new group().activate(this)
|
|
1222
|
+
} else {
|
|
1223
|
+
// Plain function — call in the context of this registry (like instance_exec).
|
|
1224
|
+
group.length === 0 ? group.call(this) : group(this)
|
|
1225
|
+
}
|
|
1226
|
+
} else if (group && typeof group.activate === 'function') {
|
|
1227
|
+
group.activate(this)
|
|
1150
1228
|
}
|
|
1151
|
-
} else if (group && typeof group.activate === 'function') {
|
|
1152
|
-
group.activate(this)
|
|
1153
1229
|
}
|
|
1230
|
+
} finally {
|
|
1231
|
+
this._activating = false
|
|
1154
1232
|
}
|
|
1155
1233
|
return this
|
|
1156
1234
|
}
|
|
@@ -1726,6 +1804,7 @@ export class Registry {
|
|
|
1726
1804
|
}
|
|
1727
1805
|
|
|
1728
1806
|
const extension = new ProcessorExtension(kind, processorInstance)
|
|
1807
|
+
extension._direct = !this._activating
|
|
1729
1808
|
extension.config.position === '>>'
|
|
1730
1809
|
? store.unshift(extension)
|
|
1731
1810
|
: store.push(extension)
|
|
@@ -1797,27 +1876,44 @@ export class Registry {
|
|
|
1797
1876
|
}
|
|
1798
1877
|
|
|
1799
1878
|
store[name] = new ProcessorExtension(kind, processorInstance)
|
|
1879
|
+
store[name]._direct = !this._activating
|
|
1800
1880
|
return store[name]
|
|
1801
1881
|
}
|
|
1802
1882
|
|
|
1803
1883
|
/** @internal */
|
|
1804
1884
|
_reset() {
|
|
1885
|
+
// Keep extensions registered directly (outside a group); only clear group-registered ones.
|
|
1886
|
+
// Extensions tagged with _direct=true survive across activations.
|
|
1887
|
+
const keepArr = (arr) => {
|
|
1888
|
+
const kept = arr?.filter((e) => e._direct) ?? []
|
|
1889
|
+
return kept.length ? kept : null
|
|
1890
|
+
}
|
|
1891
|
+
const keepMap = (map) => {
|
|
1892
|
+
if (!map) return null
|
|
1893
|
+
const kept = Object.create(null)
|
|
1894
|
+
for (const [k, v] of Object.entries(map)) if (v._direct) kept[k] = v
|
|
1895
|
+
return Object.keys(kept).length ? kept : null
|
|
1896
|
+
}
|
|
1805
1897
|
/** @internal */
|
|
1806
|
-
this._preprocessor_extensions =
|
|
1898
|
+
this._preprocessor_extensions = keepArr(this._preprocessor_extensions)
|
|
1807
1899
|
/** @internal */
|
|
1808
|
-
this._tree_processor_extensions =
|
|
1900
|
+
this._tree_processor_extensions = keepArr(this._tree_processor_extensions)
|
|
1809
1901
|
/** @internal */
|
|
1810
|
-
this._postprocessor_extensions =
|
|
1902
|
+
this._postprocessor_extensions = keepArr(this._postprocessor_extensions)
|
|
1811
1903
|
/** @internal */
|
|
1812
|
-
this._include_processor_extensions =
|
|
1904
|
+
this._include_processor_extensions = keepArr(
|
|
1905
|
+
this._include_processor_extensions
|
|
1906
|
+
)
|
|
1813
1907
|
/** @internal */
|
|
1814
|
-
this._docinfo_processor_extensions =
|
|
1908
|
+
this._docinfo_processor_extensions = keepArr(
|
|
1909
|
+
this._docinfo_processor_extensions
|
|
1910
|
+
)
|
|
1815
1911
|
/** @internal */
|
|
1816
|
-
this._block_extensions =
|
|
1912
|
+
this._block_extensions = keepMap(this._block_extensions)
|
|
1817
1913
|
/** @internal */
|
|
1818
|
-
this._block_macro_extensions =
|
|
1914
|
+
this._block_macro_extensions = keepMap(this._block_macro_extensions)
|
|
1819
1915
|
/** @internal */
|
|
1820
|
-
this._inline_macro_extensions =
|
|
1916
|
+
this._inline_macro_extensions = keepMap(this._inline_macro_extensions)
|
|
1821
1917
|
this.document = null
|
|
1822
1918
|
}
|
|
1823
1919
|
|
|
@@ -1900,6 +1996,13 @@ export const Extensions = {
|
|
|
1900
1996
|
/**
|
|
1901
1997
|
* Create a new Registry, optionally pre-populated with a named block.
|
|
1902
1998
|
*
|
|
1999
|
+
* When a `block` is provided it is stored as a group and re-executed on every
|
|
2000
|
+
* activation, making the registry safe to reuse across multiple conversions.
|
|
2001
|
+
* Without a `block`, any extensions registered directly on the returned registry
|
|
2002
|
+
* (e.g. `registry.preprocessor(fn)`) are stored in transient state that is
|
|
2003
|
+
* cleared on every activation — those registrations will be lost from the second
|
|
2004
|
+
* conversion onwards. Prefer the block form when the registry may be reused.
|
|
2005
|
+
*
|
|
1903
2006
|
* @param {string|null} [name=null] - Optional name for the group; auto-generated if omitted.
|
|
1904
2007
|
* @param {Function|null} [block=null] - Optional function to register as the group.
|
|
1905
2008
|
* @returns {Registry}
|
package/src/parser.js
CHANGED
|
@@ -1284,8 +1284,8 @@ export class Parser {
|
|
|
1284
1284
|
const admStyle = attributes.style ?? blockContext
|
|
1285
1285
|
attributes.name = admStyle.toLowerCase()
|
|
1286
1286
|
attributes.textlabel =
|
|
1287
|
-
|
|
1288
|
-
|
|
1287
|
+
attributes.caption ?? docAttrs[`${attributes.name}-caption`]
|
|
1288
|
+
delete attributes.caption
|
|
1289
1289
|
block = await Parser.buildBlock(
|
|
1290
1290
|
blockContext,
|
|
1291
1291
|
'compound',
|
package/src/table.js
CHANGED
|
@@ -14,6 +14,7 @@ import { Inline } from './inline.js'
|
|
|
14
14
|
import { applyLogging } from './logging.js'
|
|
15
15
|
import { LF, ATTR_REF_HEAD, BASIC_SUBS, NORMAL_SUBS } from './constants.js'
|
|
16
16
|
import { BlankLineRx, LeadingInlineAnchorRx } from './rx.js'
|
|
17
|
+
import { PreprocessorReader } from './reader.js'
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Truncate a float to `precision` decimal places (like Ruby's Float#truncate).
|
|
@@ -474,6 +475,25 @@ class Cell extends AbstractBlock {
|
|
|
474
475
|
if (cell._innerDocSetup) {
|
|
475
476
|
const { lines, parentDoc, parentDoctitle, options } = cell._innerDocSetup
|
|
476
477
|
cell._innerDocSetup = null
|
|
478
|
+
// If the first line may be a preprocessor directive (include, ifdef…), expand it using a
|
|
479
|
+
// temporary PreprocessorReader — matching the Ruby behaviour in table.rb.
|
|
480
|
+
if (lines.length > 0 && lines[0].includes('::')) {
|
|
481
|
+
const firstLine = lines[0]
|
|
482
|
+
const tmpReader = new PreprocessorReader(
|
|
483
|
+
parentDoc,
|
|
484
|
+
[firstLine],
|
|
485
|
+
options.cursor
|
|
486
|
+
)
|
|
487
|
+
const preprocessedLines = await tmpReader.readLines()
|
|
488
|
+
if (
|
|
489
|
+
!(
|
|
490
|
+
preprocessedLines.length === 1 && preprocessedLines[0] === firstLine
|
|
491
|
+
)
|
|
492
|
+
) {
|
|
493
|
+
lines.shift()
|
|
494
|
+
if (preprocessedLines.length > 0) lines.unshift(...preprocessedLines)
|
|
495
|
+
}
|
|
496
|
+
}
|
|
477
497
|
const innerDoc = await parentDoc.constructor.create(lines, options)
|
|
478
498
|
if (parentDoctitle) parentDoc.attributes.doctitle = parentDoctitle
|
|
479
499
|
cell._innerDocument = innerDoc
|
package/types/document.d.ts
CHANGED
|
@@ -489,7 +489,7 @@ export namespace Document {
|
|
|
489
489
|
import { Footnote } from './footnote.js';
|
|
490
490
|
import { AttributeEntry } from './attribute_entry.js';
|
|
491
491
|
import { AbstractBlock } from './abstract_block.js';
|
|
492
|
-
import
|
|
492
|
+
import { Reader } from './reader.js';
|
|
493
493
|
import { Section } from './section.js';
|
|
494
494
|
import { Inline } from './inline.js';
|
|
495
495
|
export { Footnote, AttributeEntry };
|
package/types/extensions.d.ts
CHANGED
|
@@ -38,7 +38,25 @@ export namespace SyntaxProcessorDsl {
|
|
|
38
38
|
function resolvesAttributes(...args: any[]): void;
|
|
39
39
|
}
|
|
40
40
|
export namespace IncludeProcessorDsl {
|
|
41
|
-
|
|
41
|
+
/**
|
|
42
|
+
* @overload
|
|
43
|
+
* @param {(target: string) => boolean} fn - Predicate that receives only the include target.
|
|
44
|
+
* @returns {void}
|
|
45
|
+
*/
|
|
46
|
+
function handles(fn: (target: string) => boolean): void;
|
|
47
|
+
/**
|
|
48
|
+
* @overload
|
|
49
|
+
* @param {(doc: Document, target: string) => boolean} fn - Predicate that receives the document and the include target.
|
|
50
|
+
* @returns {void}
|
|
51
|
+
*/
|
|
52
|
+
function handles(fn: (doc: Document, target: string) => boolean): void;
|
|
53
|
+
/**
|
|
54
|
+
* @overload
|
|
55
|
+
* @param {Document} doc - The document being parsed.
|
|
56
|
+
* @param {string} target - The include target.
|
|
57
|
+
* @returns {boolean}
|
|
58
|
+
*/
|
|
59
|
+
function handles(doc: Document, target: string): boolean;
|
|
42
60
|
}
|
|
43
61
|
export namespace DocinfoProcessorDsl {
|
|
44
62
|
function atLocation(value: any): void;
|
|
@@ -486,14 +504,38 @@ export class ProcessorExtension extends Extension {
|
|
|
486
504
|
processMethod: any;
|
|
487
505
|
}
|
|
488
506
|
/**
|
|
489
|
-
* A Group
|
|
507
|
+
* A Group bundles one or more extension registrations that are re-executed on
|
|
508
|
+
* every document conversion, making the registry safe to reuse.
|
|
490
509
|
*
|
|
491
|
-
* Subclass Group
|
|
492
|
-
*
|
|
510
|
+
* Subclass Group, override {@link Group#activate}, and either call
|
|
511
|
+
* {@link Group.register} to add it globally or pass the subclass to
|
|
512
|
+
* {@link Extensions.create} / {@link Extensions.register}.
|
|
513
|
+
*
|
|
514
|
+
* @example
|
|
515
|
+
* class MyGroup extends Group {
|
|
516
|
+
* activate (registry) {
|
|
517
|
+
* registry.preprocessor(MyPreprocessor)
|
|
518
|
+
* }
|
|
519
|
+
* }
|
|
520
|
+
* MyGroup.register()
|
|
493
521
|
*/
|
|
494
522
|
export class Group {
|
|
495
|
-
|
|
496
|
-
|
|
523
|
+
/**
|
|
524
|
+
* Register this Group class globally under the given name.
|
|
525
|
+
*
|
|
526
|
+
* Equivalent to calling `Extensions.register(name, this)`.
|
|
527
|
+
*
|
|
528
|
+
* @param {string|null} [name] - Optional name for the group.
|
|
529
|
+
*/
|
|
530
|
+
static register(name?: string | null): void;
|
|
531
|
+
/**
|
|
532
|
+
* Called by {@link Registry#activate} on every document conversion.
|
|
533
|
+
*
|
|
534
|
+
* Override this method to register extensions with the provided registry.
|
|
535
|
+
*
|
|
536
|
+
* @param {Registry} _registry - The registry to register extensions with.
|
|
537
|
+
*/
|
|
538
|
+
activate(_registry: Registry): void;
|
|
497
539
|
}
|
|
498
540
|
/**
|
|
499
541
|
* The primary entry point into the extension system.
|
|
@@ -501,6 +543,18 @@ export class Group {
|
|
|
501
543
|
* Registry holds the extensions which have been registered and activated, has
|
|
502
544
|
* methods for registering or defining a processor and looks up extensions
|
|
503
545
|
* stored in the registry during parsing.
|
|
546
|
+
*
|
|
547
|
+
* A registry can be reused across multiple conversions. Extensions registered
|
|
548
|
+
* via a group block (passed to {@link Extensions.create} or
|
|
549
|
+
* {@link Extensions.register}) are re-executed on every activation. Extensions
|
|
550
|
+
* registered directly on the registry instance (e.g. `registry.preprocessor(fn)`)
|
|
551
|
+
* are preserved across activations.
|
|
552
|
+
*
|
|
553
|
+
* @example
|
|
554
|
+
* const registry = Extensions.create('my-ext', function () {
|
|
555
|
+
* this.preprocessor(function () { ... })
|
|
556
|
+
* })
|
|
557
|
+
* // registry can be passed to multiple conversions safely
|
|
504
558
|
*/
|
|
505
559
|
export class Registry {
|
|
506
560
|
constructor(groups?: {});
|
|
@@ -826,6 +880,13 @@ export namespace Extensions {
|
|
|
826
880
|
/**
|
|
827
881
|
* Create a new Registry, optionally pre-populated with a named block.
|
|
828
882
|
*
|
|
883
|
+
* When a `block` is provided it is stored as a group and re-executed on every
|
|
884
|
+
* activation, making the registry safe to reuse across multiple conversions.
|
|
885
|
+
* Without a `block`, any extensions registered directly on the returned registry
|
|
886
|
+
* (e.g. `registry.preprocessor(fn)`) are stored in transient state that is
|
|
887
|
+
* cleared on every activation — those registrations will be lost from the second
|
|
888
|
+
* conversion onwards. Prefer the block form when the registry may be reused.
|
|
889
|
+
*
|
|
829
890
|
* @param {string|null} [name=null] - Optional name for the group; auto-generated if omitted.
|
|
830
891
|
* @param {Function|null} [block=null] - Optional function to register as the group.
|
|
831
892
|
* @returns {Registry}
|
|
@@ -1032,6 +1093,7 @@ export type SyntaxProcessorDslInterface = ProcessorDslInterface & {
|
|
|
1032
1093
|
* DSL interface for include processors.
|
|
1033
1094
|
*/
|
|
1034
1095
|
export type IncludeProcessorDslInterface = DocumentProcessorDslInterface & {
|
|
1096
|
+
handles(fn: (target: string) => boolean): void;
|
|
1035
1097
|
handles(fn: (doc: Document, target: string) => boolean): void;
|
|
1036
1098
|
};
|
|
1037
1099
|
/**
|