@asciidoctor/core 3.0.3 → 4.0.0-alpha.1
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/README.md +42 -10
- package/build/browser/index.js +24154 -0
- package/build/node/index.cjs +24735 -0
- package/{dist/css/asciidoctor.css → data/asciidoctor-default.css} +54 -53
- package/package.json +53 -100
- package/src/abstract_block.js +849 -0
- package/src/abstract_node.js +954 -0
- package/src/attribute_entry.js +12 -0
- package/src/attribute_list.js +380 -0
- package/src/block.js +168 -0
- package/src/browser/asset.js +22 -0
- package/src/browser/reader.js +138 -0
- package/src/browser.js +121 -0
- package/src/callouts.js +85 -0
- package/src/compliance.js +54 -0
- package/src/constants.js +665 -0
- package/src/convert.js +370 -0
- package/src/converter/composite.js +83 -0
- package/src/converter/docbook5.js +1031 -0
- package/src/converter/html5.js +1899 -0
- package/src/converter/manpage.js +935 -0
- package/src/converter/template.js +459 -0
- package/src/converter.js +478 -0
- package/src/data/stylesheet-data.js +2 -0
- package/src/document.js +2134 -0
- package/src/extensions.js +1952 -0
- package/src/footnote.js +28 -0
- package/src/helpers.js +355 -0
- package/src/index.js +138 -0
- package/src/inline.js +158 -0
- package/src/list.js +240 -0
- package/src/load.js +276 -0
- package/src/logging.js +526 -0
- package/src/parser.js +3661 -0
- package/src/path_resolver.js +472 -0
- package/src/reader.js +1755 -0
- package/src/rx.js +829 -0
- package/src/section.js +354 -0
- package/src/stylesheets.js +30 -0
- package/src/substitutors.js +2241 -0
- package/src/syntaxHighlighter/highlightjs.js +90 -0
- package/src/syntaxHighlighter/html_pipeline.js +33 -0
- package/src/syntax_highlighter.js +304 -0
- package/src/table.js +952 -0
- package/src/timings.js +78 -0
- package/types/abstract_block.d.ts +343 -0
- package/types/abstract_node.d.ts +471 -0
- package/types/attribute_entry.d.ts +7 -0
- package/types/attribute_list.d.ts +52 -0
- package/types/block.d.ts +55 -0
- package/types/browser/asset.d.ts +7 -0
- package/types/browser/reader.d.ts +29 -0
- package/types/callouts.d.ts +36 -0
- package/types/compliance.d.ts +23 -0
- package/types/constants.d.ts +268 -0
- package/types/convert.d.ts +34 -0
- package/types/converter/composite.d.ts +20 -0
- package/types/converter/docbook5.d.ts +41 -0
- package/types/converter/html5.d.ts +51 -0
- package/types/converter/manpage.d.ts +59 -0
- package/types/converter/template.d.ts +83 -0
- package/types/converter.d.ts +150 -0
- package/types/data/stylesheet-data.d.ts +2 -0
- package/types/document.d.ts +495 -0
- package/types/extensions.d.ts +876 -0
- package/types/footnote.d.ts +18 -0
- package/types/helpers.d.ts +146 -0
- package/types/index.d.ts +73 -3731
- package/types/inline.d.ts +69 -0
- package/types/list.d.ts +114 -0
- package/types/load.d.ts +39 -0
- package/types/logging.d.ts +187 -0
- package/types/parser.d.ts +114 -0
- package/types/path_resolver.d.ts +103 -0
- package/types/reader.d.ts +184 -0
- package/types/rx.d.ts +513 -0
- package/types/section.d.ts +122 -0
- package/types/stylesheets.d.ts +10 -0
- package/types/substitutors.d.ts +208 -0
- package/types/syntaxHighlighter/highlightjs.d.ts +33 -0
- package/types/syntaxHighlighter/html_pipeline.d.ts +16 -0
- package/types/syntax_highlighter.d.ts +167 -0
- package/types/table.d.ts +231 -0
- package/types/timings.d.ts +25 -0
- package/types/tsconfig.json +9 -0
- package/LICENSE +0 -21
- package/dist/browser/asciidoctor.js +0 -47654
- package/dist/browser/asciidoctor.min.js +0 -1452
- package/dist/graalvm/asciidoctor.js +0 -47402
- package/dist/node/asciidoctor.cjs +0 -21567
- package/dist/node/asciidoctor.js +0 -23037
|
@@ -0,0 +1,1952 @@
|
|
|
1
|
+
// ESM conversion of extensions.rb
|
|
2
|
+
//
|
|
3
|
+
// Ruby-to-JavaScript notes:
|
|
4
|
+
// - Ruby modules used as mixins → plain JS objects applied via Object.assign.
|
|
5
|
+
// - Ruby :symbols used as keys → plain strings throughout.
|
|
6
|
+
// - Ruby ::Set → JavaScript Set.
|
|
7
|
+
// - Ruby defined? @foo → this._foo !== undefined.
|
|
8
|
+
// - Ruby instance_exec(&block) → block.call(instance) or instance method call.
|
|
9
|
+
// - Ruby singleton_class.enable_dsl → Object.assign(instance, kindClass.DSL).
|
|
10
|
+
// - Ruby class << self → static methods.
|
|
11
|
+
// - Ruby Helpers.resolve_class → typeof fn === 'function' check.
|
|
12
|
+
// - Ruby @@class_var (InlineMacroProcessor.rx_cache) → static property.
|
|
13
|
+
// - Config option keys keep snake_case to match the Ruby/parser convention.
|
|
14
|
+
// - String class-name resolution (e.g. preprocessor 'MyClass') is not supported;
|
|
15
|
+
// pass the class constructor or an instance directly.
|
|
16
|
+
// - Parser.parseBlocks / block.subAttributes / block.assignCaption are forward
|
|
17
|
+
// references; they will be resolved when those modules implement the methods.
|
|
18
|
+
|
|
19
|
+
import { Section } from './section.js'
|
|
20
|
+
import { Block } from './block.js'
|
|
21
|
+
import { List, ListItem } from './list.js'
|
|
22
|
+
import { Inline } from './inline.js'
|
|
23
|
+
import { Reader } from './reader.js'
|
|
24
|
+
import { Parser } from './parser.js'
|
|
25
|
+
import { AttributeList } from './attribute_list.js'
|
|
26
|
+
import { basename } from './helpers.js'
|
|
27
|
+
import { ATTR_REF_HEAD } from './constants.js'
|
|
28
|
+
import { MacroNameRx, CC_ANY } from './rx.js'
|
|
29
|
+
|
|
30
|
+
// ── DSL Mixins ────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @internal Builder DSL mixin for configuring a Processor instance.
|
|
34
|
+
* Applied to a processor instance via Object.assign(instance, DslMixin).
|
|
35
|
+
*
|
|
36
|
+
* The process() method has dual behaviour (mirrors Ruby's block / no-block):
|
|
37
|
+
* - Called with a single Function argument → stores it as the process block.
|
|
38
|
+
* - Called with non-Function arguments → invokes the stored process block.
|
|
39
|
+
*
|
|
40
|
+
* The this context inside a stored process function is bound to the processor
|
|
41
|
+
* instance at definition time.
|
|
42
|
+
*/
|
|
43
|
+
export const ProcessorDsl = {
|
|
44
|
+
option(key, value) {
|
|
45
|
+
this.config[key] = value
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
process(...args) {
|
|
49
|
+
if (args.length === 1 && typeof args[0] === 'function') {
|
|
50
|
+
this._processBlock = args[0].bind(this)
|
|
51
|
+
} else if (this._processBlock !== undefined) {
|
|
52
|
+
return this._processBlock(...args)
|
|
53
|
+
} else {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`${this.constructor.name} #process method called before being registered`
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
processBlockGiven() {
|
|
61
|
+
return this._processBlock !== undefined
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const DocumentProcessorDsl = {
|
|
66
|
+
...ProcessorDsl,
|
|
67
|
+
|
|
68
|
+
prefer() {
|
|
69
|
+
this.option('position', '>>')
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/** Alias for {@link prefer}. */
|
|
73
|
+
prepend() {
|
|
74
|
+
this.prefer()
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const SyntaxProcessorDsl = {
|
|
79
|
+
...ProcessorDsl,
|
|
80
|
+
|
|
81
|
+
named(value) {
|
|
82
|
+
// When applied to a processor instance, set the name directly.
|
|
83
|
+
// When applied to a class (via static enableDsl), store in config.
|
|
84
|
+
if (this instanceof Processor) {
|
|
85
|
+
this.name = value
|
|
86
|
+
} else {
|
|
87
|
+
this.option('name', value)
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
contentModel(value) {
|
|
92
|
+
this.option('content_model', value)
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
/** Alias for {@link contentModel}. */
|
|
96
|
+
parseContentAs(value) {
|
|
97
|
+
this.option('content_model', value)
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
positionalAttributes(...value) {
|
|
101
|
+
this.option('positional_attrs', value.flat().map(String))
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
/** Alias for {@link positionalAttributes}. */
|
|
105
|
+
namePositionalAttributes(...value) {
|
|
106
|
+
this.option('positional_attrs', value.flat().map(String))
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
positionalAttrs(...value) {
|
|
110
|
+
this.option('positional_attrs', value.flat().map(String))
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
defaultAttributes(value) {
|
|
114
|
+
this.option('default_attrs', value)
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
/** @deprecated Alias for {@link defaultAttributes}. */
|
|
118
|
+
defaultAttrs(value) {
|
|
119
|
+
this.option('default_attrs', value)
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Resolve and register positional attribute names and default values.
|
|
124
|
+
*
|
|
125
|
+
* Accepts any of:
|
|
126
|
+
* resolveAttributes() → positional_attrs: [], default_attrs: {}
|
|
127
|
+
* resolveAttributes('foo', 'bar') → positional maps (Array-style)
|
|
128
|
+
* resolveAttributes({...}) → positional maps (Object-style)
|
|
129
|
+
*
|
|
130
|
+
* Array-style tokens understand positional-index notation (e.g. '1:name',
|
|
131
|
+
* '@:name') and default-value notation (e.g. 'name=value', '1:name=value').
|
|
132
|
+
*
|
|
133
|
+
* @param {...*} args - Positional attribute specifications.
|
|
134
|
+
*/
|
|
135
|
+
resolveAttributes(...args) {
|
|
136
|
+
// Normalise: if 0 or 1 argument given, unwrap into a single value.
|
|
137
|
+
if (args.length <= 1) {
|
|
138
|
+
const first = args.length === 0 ? true : args[0]
|
|
139
|
+
if (typeof first === 'string' || typeof first === 'symbol') {
|
|
140
|
+
args = [first]
|
|
141
|
+
} else {
|
|
142
|
+
args = first // true, Array, or plain Object
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (args === true) {
|
|
147
|
+
this.option('positional_attrs', [])
|
|
148
|
+
this.option('default_attrs', {})
|
|
149
|
+
} else if (Array.isArray(args)) {
|
|
150
|
+
const names = []
|
|
151
|
+
const defaults = {}
|
|
152
|
+
for (let arg of args) {
|
|
153
|
+
arg = String(arg)
|
|
154
|
+
if (arg.includes('=')) {
|
|
155
|
+
const eqIdx = arg.indexOf('=')
|
|
156
|
+
let name = arg.slice(0, eqIdx)
|
|
157
|
+
const value = arg.slice(eqIdx + 1)
|
|
158
|
+
if (name.includes(':')) {
|
|
159
|
+
const colonIdx = name.indexOf(':')
|
|
160
|
+
const idxStr = name.slice(0, colonIdx)
|
|
161
|
+
name = name.slice(colonIdx + 1)
|
|
162
|
+
const idx = idxStr === '@' ? names.length : parseInt(idxStr, 10)
|
|
163
|
+
names[idx] = name
|
|
164
|
+
}
|
|
165
|
+
defaults[name] = value
|
|
166
|
+
} else if (arg.includes(':')) {
|
|
167
|
+
const colonIdx = arg.indexOf(':')
|
|
168
|
+
const idxStr = arg.slice(0, colonIdx)
|
|
169
|
+
const name = arg.slice(colonIdx + 1)
|
|
170
|
+
const idx = idxStr === '@' ? names.length : parseInt(idxStr, 10)
|
|
171
|
+
names[idx] = name
|
|
172
|
+
} else {
|
|
173
|
+
names.push(arg)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
this.option(
|
|
177
|
+
'positional_attrs',
|
|
178
|
+
names.filter((n) => n != null)
|
|
179
|
+
)
|
|
180
|
+
this.option('default_attrs', defaults)
|
|
181
|
+
} else if (typeof args === 'object' && args !== null) {
|
|
182
|
+
const names = []
|
|
183
|
+
const defaults = {}
|
|
184
|
+
for (const [key, val] of Object.entries(args)) {
|
|
185
|
+
let name = String(key)
|
|
186
|
+
if (name.includes(':')) {
|
|
187
|
+
const colonIdx = name.indexOf(':')
|
|
188
|
+
const idxStr = name.slice(0, colonIdx)
|
|
189
|
+
name = name.slice(colonIdx + 1)
|
|
190
|
+
const idx = idxStr === '@' ? names.length : parseInt(idxStr, 10)
|
|
191
|
+
names[idx] = name
|
|
192
|
+
}
|
|
193
|
+
if (val) defaults[name] = val
|
|
194
|
+
}
|
|
195
|
+
this.option(
|
|
196
|
+
'positional_attrs',
|
|
197
|
+
names.filter((n) => n != null)
|
|
198
|
+
)
|
|
199
|
+
this.option('default_attrs', defaults)
|
|
200
|
+
} else {
|
|
201
|
+
throw new Error(`unsupported attributes specification for macro: ${args}`)
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
/** @deprecated Alias for {@link resolveAttributes}. */
|
|
206
|
+
resolvesAttributes(...args) {
|
|
207
|
+
this.resolveAttributes(...args)
|
|
208
|
+
},
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export const IncludeProcessorDsl = {
|
|
212
|
+
...DocumentProcessorDsl,
|
|
213
|
+
|
|
214
|
+
handles(...args) {
|
|
215
|
+
if (args.length === 1 && typeof args[0] === 'function') {
|
|
216
|
+
const fn = args[0]
|
|
217
|
+
// Normalise arity-1 handle blocks to accept (doc, target)
|
|
218
|
+
this._handlesBlock =
|
|
219
|
+
fn.length === 1 ? (_doc, target) => fn(target) : fn.bind(this)
|
|
220
|
+
} else if (this._handlesBlock !== undefined) {
|
|
221
|
+
return this._handlesBlock(args[0], args[1])
|
|
222
|
+
} else {
|
|
223
|
+
return true
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export const DocinfoProcessorDsl = {
|
|
229
|
+
...DocumentProcessorDsl,
|
|
230
|
+
|
|
231
|
+
atLocation(value) {
|
|
232
|
+
this.option('location', value)
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export const BlockProcessorDsl = {
|
|
237
|
+
...SyntaxProcessorDsl,
|
|
238
|
+
|
|
239
|
+
contexts(...value) {
|
|
240
|
+
this.option('contexts', new Set(value.flat()))
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
// aliases
|
|
244
|
+
onContexts(...value) {
|
|
245
|
+
this.contexts(...value)
|
|
246
|
+
},
|
|
247
|
+
onContext(...value) {
|
|
248
|
+
this.contexts(...value)
|
|
249
|
+
},
|
|
250
|
+
bindTo(...value) {
|
|
251
|
+
this.contexts(...value)
|
|
252
|
+
},
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export const MacroProcessorDsl = {
|
|
256
|
+
...SyntaxProcessorDsl,
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Override: passing a falsy value sets content_model to :text instead of
|
|
260
|
+
* configuring positional attributes.
|
|
261
|
+
*
|
|
262
|
+
* @param {...*} args - Positional attribute specifications.
|
|
263
|
+
*/
|
|
264
|
+
resolveAttributes(...args) {
|
|
265
|
+
if (args.length === 1 && !args[0]) {
|
|
266
|
+
this.option('content_model', 'text')
|
|
267
|
+
} else {
|
|
268
|
+
SyntaxProcessorDsl.resolveAttributes.call(this, ...args)
|
|
269
|
+
this.option('content_model', 'attributes')
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
/** @deprecated Alias for {@link resolveAttributes}. */
|
|
274
|
+
resolvesAttributes(...args) {
|
|
275
|
+
this.resolveAttributes(...args)
|
|
276
|
+
},
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export const InlineMacroProcessorDsl = {
|
|
280
|
+
...MacroProcessorDsl,
|
|
281
|
+
|
|
282
|
+
format(value) {
|
|
283
|
+
this.option('format', value)
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
/** Alias for {@link format}. */
|
|
287
|
+
matchFormat(value) {
|
|
288
|
+
this.option('format', value)
|
|
289
|
+
},
|
|
290
|
+
/** @deprecated Alias for {@link format}. */
|
|
291
|
+
usingFormat(value) {
|
|
292
|
+
this.option('format', value)
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
match(value) {
|
|
296
|
+
this.option('regexp', value)
|
|
297
|
+
},
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ── Processor ────────────────────────────────────────────────────────────────
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Abstract base class for document and syntax processors.
|
|
304
|
+
*
|
|
305
|
+
* Provides a class-level config map (via static config / static option) and a
|
|
306
|
+
* set of convenience factory methods for creating AST nodes.
|
|
307
|
+
*/
|
|
308
|
+
export class Processor {
|
|
309
|
+
/**
|
|
310
|
+
* Get the static configuration map for this processor class.
|
|
311
|
+
* Uses hasOwnProperty to avoid inheriting a parent class's config object
|
|
312
|
+
* through the prototype chain when a subclass first accesses config.
|
|
313
|
+
*
|
|
314
|
+
* @returns {object}
|
|
315
|
+
*/
|
|
316
|
+
static get config() {
|
|
317
|
+
if (!Object.hasOwn(this, '_config')) this._config = {}
|
|
318
|
+
return this._config
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Set a default option value for all instances of this processor class.
|
|
323
|
+
*
|
|
324
|
+
* @param {string} key - The option key.
|
|
325
|
+
* @param {*} value - The option value.
|
|
326
|
+
*/
|
|
327
|
+
static option(key, value) {
|
|
328
|
+
this.config[key] = value
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Mix the DSL object for this processor class into its prototype.
|
|
333
|
+
*/
|
|
334
|
+
static enableDsl() {
|
|
335
|
+
const DSL = this.DSL
|
|
336
|
+
if (DSL) Object.assign(this.prototype, DSL)
|
|
337
|
+
}
|
|
338
|
+
/** Alias for {@link enableDsl}. */
|
|
339
|
+
static useDsl() {
|
|
340
|
+
this.enableDsl()
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
constructor(config = {}) {
|
|
344
|
+
this.config = { ...this.constructor.config, ...config }
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
updateConfig(config) {
|
|
348
|
+
Object.assign(this.config, config)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
process(..._args) {
|
|
352
|
+
throw new Error(
|
|
353
|
+
`${this.constructor.name} subclass must implement the process method`
|
|
354
|
+
)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Create a Section node in the same manner as the parser.
|
|
359
|
+
*
|
|
360
|
+
* @param {Section|Document} parent - The parent Section or Document of this new Section.
|
|
361
|
+
* @param {string} title - The String title of the new Section.
|
|
362
|
+
* @param {object} attrs - A plain object of attributes to control how the section is built.
|
|
363
|
+
* Use the style attribute to set the name of a special section (e.g. appendix).
|
|
364
|
+
* Use the id attribute to assign an explicit ID, or set it to false to
|
|
365
|
+
* disable automatic ID generation (when sectids document attribute is set).
|
|
366
|
+
* @param {object} [opts={}] - An optional plain object of options:
|
|
367
|
+
* - level {number} - The Integer level to assign; defaults to parent.level + 1.
|
|
368
|
+
* - numbered {boolean} - Flag to force numbering.
|
|
369
|
+
* @returns {Section} a Section node with all properties properly initialized.
|
|
370
|
+
*/
|
|
371
|
+
createSection(parent, title, attrs, opts = {}) {
|
|
372
|
+
const doc = parent.document
|
|
373
|
+
const doctype = doc.doctype
|
|
374
|
+
const book = doctype === 'book'
|
|
375
|
+
const level = opts.level ?? parent.level + 1
|
|
376
|
+
|
|
377
|
+
let sectname,
|
|
378
|
+
special = false
|
|
379
|
+
const style = attrs.style
|
|
380
|
+
if (style) {
|
|
381
|
+
delete attrs.style
|
|
382
|
+
if (book && style === 'abstract') {
|
|
383
|
+
sectname = 'chapter'
|
|
384
|
+
// level intentionally set to 1 (overrides local const)
|
|
385
|
+
Object.defineProperty(opts, '_level', { value: 1 })
|
|
386
|
+
} else {
|
|
387
|
+
sectname = style
|
|
388
|
+
special = true
|
|
389
|
+
if (level === 0) {
|
|
390
|
+
// promote to level 1
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
} else if (book) {
|
|
394
|
+
sectname = level === 0 ? 'part' : level > 1 ? 'section' : 'chapter'
|
|
395
|
+
} else if (doctype === 'manpage' && title.toLowerCase() === 'synopsis') {
|
|
396
|
+
sectname = 'synopsis'
|
|
397
|
+
special = true
|
|
398
|
+
} else {
|
|
399
|
+
sectname = 'section'
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Re-derive level if style forced it (appendix/abstract style override)
|
|
403
|
+
const effectiveLevel =
|
|
404
|
+
style && book && style === 'abstract'
|
|
405
|
+
? 1
|
|
406
|
+
: style && special && level === 0
|
|
407
|
+
? 1
|
|
408
|
+
: level
|
|
409
|
+
|
|
410
|
+
const sect = new Section(parent, effectiveLevel)
|
|
411
|
+
sect.title = title
|
|
412
|
+
sect.sectname = sectname
|
|
413
|
+
|
|
414
|
+
if (special) {
|
|
415
|
+
sect.special = true
|
|
416
|
+
if ('numbered' in opts ? opts.numbered : style === 'appendix') {
|
|
417
|
+
sect.numbered = true
|
|
418
|
+
} else if (!('numbered' in opts) && doc.hasAttribute('sectnums', 'all')) {
|
|
419
|
+
sect.numbered = book && effectiveLevel === 1 ? 'chapter' : true
|
|
420
|
+
}
|
|
421
|
+
} else if (effectiveLevel > 0) {
|
|
422
|
+
if ('numbered' in opts ? opts.numbered : doc.hasAttribute('sectnums')) {
|
|
423
|
+
sect.numbered = sect.special ? !!parent.numbered : true
|
|
424
|
+
}
|
|
425
|
+
} else if (
|
|
426
|
+
'numbered' in opts ? opts.numbered : book && doc.hasAttribute('partnums')
|
|
427
|
+
) {
|
|
428
|
+
sect.numbered = true
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (attrs.id === false) {
|
|
432
|
+
delete attrs.id
|
|
433
|
+
} else {
|
|
434
|
+
sect.id = attrs.id =
|
|
435
|
+
attrs.id ||
|
|
436
|
+
(doc.hasAttribute('sectids')
|
|
437
|
+
? Section.generateId(sect.title, doc)
|
|
438
|
+
: null)
|
|
439
|
+
}
|
|
440
|
+
sect.updateAttributes(attrs)
|
|
441
|
+
return sect
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Create a generic block node and link it to the specified parent.
|
|
446
|
+
*
|
|
447
|
+
* @param {Block|Section} parent - The parent node.
|
|
448
|
+
* @param {string} context - The block context (e.g. 'paragraph', 'listing').
|
|
449
|
+
* @param {string|string[]|null} source - The source content.
|
|
450
|
+
* @param {object} attrs - A plain object of attributes.
|
|
451
|
+
* @param {object} [opts={}] - An optional plain object of options.
|
|
452
|
+
* @returns {Block} a Block node with all properties properly initialized.
|
|
453
|
+
*/
|
|
454
|
+
createBlock(parent, context, source, attrs, opts = {}) {
|
|
455
|
+
return new Block(parent, context, { source, attributes: attrs, ...opts })
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Create a list node and link it to the specified parent.
|
|
460
|
+
*
|
|
461
|
+
* @param {Block|Section|Document} parent - The parent of this new list.
|
|
462
|
+
* @param {string} context - The list context ('ulist', 'olist', 'colist', 'dlist').
|
|
463
|
+
* @param {object|null} [attrs=null] - A plain object of attributes to set on this list block.
|
|
464
|
+
* @returns {List} a List node with all properties properly initialized.
|
|
465
|
+
*/
|
|
466
|
+
createList(parent, context, attrs = null) {
|
|
467
|
+
const list = new List(parent, context)
|
|
468
|
+
if (attrs) list.updateAttributes(attrs)
|
|
469
|
+
return list
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Create a list item node and link it to the specified parent.
|
|
474
|
+
*
|
|
475
|
+
* @param {List} parent - The parent List of this new list item.
|
|
476
|
+
* @param {string|null} [text=null] - The text of the list item.
|
|
477
|
+
* @returns {ListItem} a ListItem node with all properties properly initialized.
|
|
478
|
+
*/
|
|
479
|
+
createListItem(parent, text = null) {
|
|
480
|
+
return new ListItem(parent, text)
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Create an image block node and link it to the specified parent.
|
|
485
|
+
*
|
|
486
|
+
* @param {Block|Section|Document} parent - The parent of this new image block.
|
|
487
|
+
* @param {object} attrs - A plain object of attributes to control how the image block is built.
|
|
488
|
+
* The target attribute sets the image source; alt sets the alt text.
|
|
489
|
+
* @param {object} [opts={}] - An optional plain object of options.
|
|
490
|
+
* @returns {Block} a Block node with all properties properly initialized.
|
|
491
|
+
*/
|
|
492
|
+
createImageBlock(parent, attrs, opts = {}) {
|
|
493
|
+
const target = attrs.target
|
|
494
|
+
if (!target)
|
|
495
|
+
throw new Error(
|
|
496
|
+
'Unable to create an image block, target attribute is required'
|
|
497
|
+
)
|
|
498
|
+
if (!attrs.alt)
|
|
499
|
+
attrs.alt = attrs['default-alt'] = basename(target, true).replace(
|
|
500
|
+
/[_-]/g,
|
|
501
|
+
' '
|
|
502
|
+
)
|
|
503
|
+
const title = 'title' in attrs ? attrs.title : null
|
|
504
|
+
if (title !== null) delete attrs.title
|
|
505
|
+
const block = this.createBlock(parent, 'image', null, attrs, opts)
|
|
506
|
+
if (title) {
|
|
507
|
+
block.title = title
|
|
508
|
+
const caption = attrs.caption
|
|
509
|
+
delete attrs.caption
|
|
510
|
+
block.assignCaption(caption, 'figure')
|
|
511
|
+
}
|
|
512
|
+
return block
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Create an inline node and bind it to the specified parent.
|
|
517
|
+
*
|
|
518
|
+
* @param {Block} parent - The parent Block of this new inline node.
|
|
519
|
+
* @param {string} context - The context of the inline node ('quoted', 'anchor', etc.).
|
|
520
|
+
* @param {string} text - The text of the inline node.
|
|
521
|
+
* @param {object} [opts={}] - An optional plain object of options:
|
|
522
|
+
* - type {string} - The subtype of the inline node context.
|
|
523
|
+
* - attributes {object} - Attributes to set on the inline node.
|
|
524
|
+
* @returns {Inline} an Inline node with all properties properly initialized.
|
|
525
|
+
*/
|
|
526
|
+
createInline(parent, context, text, opts = {}) {
|
|
527
|
+
const options = context === 'quoted' ? { type: 'unquoted', ...opts } : opts
|
|
528
|
+
return new Inline(parent, context, text, options)
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Parse blocks in the content and attach them to the parent.
|
|
533
|
+
*
|
|
534
|
+
* @param {Block|Section} parent - The parent node.
|
|
535
|
+
* @param {string[]|Reader} content - Lines or a Reader.
|
|
536
|
+
* @param {object|null} [attributes=null] - Attributes to pass to the parser.
|
|
537
|
+
* @returns {Promise<Block|Section>} the parent node into which the blocks are parsed.
|
|
538
|
+
*/
|
|
539
|
+
async parseContent(parent, content, attributes = null) {
|
|
540
|
+
const reader = content instanceof Reader ? content : new Reader(content)
|
|
541
|
+
await Parser.parseBlocks(reader, parent, attributes)
|
|
542
|
+
return parent
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Parse the attrlist String into a plain object of attributes.
|
|
547
|
+
*
|
|
548
|
+
* @param {Block|Section} block - The current block (used for applying subs).
|
|
549
|
+
* @param {string} attrlist - The list of attributes as a String.
|
|
550
|
+
* @param {object} [opts={}] - An optional plain object of options:
|
|
551
|
+
* - positional_attributes {string[]} - Array of attribute names to map positional args to.
|
|
552
|
+
* - sub_attributes {boolean} - Enables attribute substitution on attrlist.
|
|
553
|
+
* @returns {Promise<object>} a plain object of parsed attributes.
|
|
554
|
+
*/
|
|
555
|
+
async parseAttributes(block, attrlist, opts = {}) {
|
|
556
|
+
if (!attrlist || attrlist.length === 0) return {}
|
|
557
|
+
if (opts.sub_attributes && attrlist.includes(ATTR_REF_HEAD)) {
|
|
558
|
+
attrlist = block.subAttributes(attrlist)
|
|
559
|
+
}
|
|
560
|
+
return new AttributeList(attrlist).parse(opts.positional_attributes || [])
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/** Shorthand for {@link createBlock} with context 'paragraph'. */
|
|
564
|
+
createParagraph(parent, ...rest) {
|
|
565
|
+
return this.createBlock(parent, 'paragraph', ...rest)
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/** Shorthand for {@link createBlock} with context 'open'. */
|
|
569
|
+
createOpenBlock(parent, ...rest) {
|
|
570
|
+
return this.createBlock(parent, 'open', ...rest)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/** Shorthand for {@link createBlock} with context 'example'. */
|
|
574
|
+
createExampleBlock(parent, ...rest) {
|
|
575
|
+
return this.createBlock(parent, 'example', ...rest)
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/** Shorthand for {@link createBlock} with context 'pass'. */
|
|
579
|
+
createPassBlock(parent, ...rest) {
|
|
580
|
+
return this.createBlock(parent, 'pass', ...rest)
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/** Shorthand for {@link createBlock} with context 'listing'. */
|
|
584
|
+
createListingBlock(parent, ...rest) {
|
|
585
|
+
return this.createBlock(parent, 'listing', ...rest)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/** Shorthand for {@link createBlock} with context 'literal'. */
|
|
589
|
+
createLiteralBlock(parent, ...rest) {
|
|
590
|
+
return this.createBlock(parent, 'literal', ...rest)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/** Shorthand for {@link createInline} with context 'anchor'. */
|
|
594
|
+
createAnchor(parent, ...rest) {
|
|
595
|
+
return this.createInline(parent, 'anchor', ...rest)
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/** Shorthand for {@link createInline} with context 'quoted'. */
|
|
599
|
+
createInlinePass(parent, ...rest) {
|
|
600
|
+
return this.createInline(parent, 'quoted', ...rest)
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// ── Document processors ───────────────────────────────────────────────────────
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Preprocessors are run after the source text is split into lines and
|
|
608
|
+
* normalised, but before parsing begins.
|
|
609
|
+
*
|
|
610
|
+
* Asciidoctor passes the document and the document's Reader to the process
|
|
611
|
+
* method of the Preprocessor instance. The Preprocessor can modify the Reader
|
|
612
|
+
* as necessary and either return the same Reader (or falsy) or a substitute Reader.
|
|
613
|
+
*
|
|
614
|
+
* Implementations must extend Preprocessor and override {@link process}.
|
|
615
|
+
*
|
|
616
|
+
* @example
|
|
617
|
+
* class CommentStripPreprocessor extends Preprocessor {
|
|
618
|
+
* process(document, reader) {
|
|
619
|
+
* const lines = reader.getLines().filter((l) => !l.startsWith('//'))
|
|
620
|
+
* reader.pushInclude(lines, null, null, 1, {})
|
|
621
|
+
* return reader
|
|
622
|
+
* }
|
|
623
|
+
* }
|
|
624
|
+
* // Register:
|
|
625
|
+
* Extensions.register(function () { this.preprocessor(CommentStripPreprocessor) })
|
|
626
|
+
*/
|
|
627
|
+
export class Preprocessor extends Processor {
|
|
628
|
+
process(_document, _reader) {
|
|
629
|
+
throw new Error(
|
|
630
|
+
`${this.constructor.name} must implement the process method`
|
|
631
|
+
)
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
Preprocessor.DSL = DocumentProcessorDsl
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* TreeProcessors are run on the Document after the source has been
|
|
638
|
+
* parsed into an abstract syntax tree (AST).
|
|
639
|
+
*
|
|
640
|
+
* Implementations must extend TreeProcessor and override {@link process}.
|
|
641
|
+
*
|
|
642
|
+
* @example
|
|
643
|
+
* class ShoutTreeProcessor extends TreeProcessor {
|
|
644
|
+
* process(document) {
|
|
645
|
+
* for (const block of document.findBy({ context: 'paragraph' })) {
|
|
646
|
+
* block.source = block.source.toUpperCase()
|
|
647
|
+
* }
|
|
648
|
+
* }
|
|
649
|
+
* }
|
|
650
|
+
* Extensions.register(function () { this.treeProcessor(ShoutTreeProcessor) })
|
|
651
|
+
*/
|
|
652
|
+
export class TreeProcessor extends Processor {
|
|
653
|
+
process(_document) {
|
|
654
|
+
throw new Error(
|
|
655
|
+
`${this.constructor.name} must implement the process method`
|
|
656
|
+
)
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
TreeProcessor.DSL = DocumentProcessorDsl
|
|
660
|
+
|
|
661
|
+
/** @deprecated Alias for {@link TreeProcessor} kept for backwards compatibility. */
|
|
662
|
+
export const Treeprocessor = TreeProcessor
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Postprocessors are run after the document is converted, but before
|
|
666
|
+
* it is written to the output stream.
|
|
667
|
+
*
|
|
668
|
+
* The `process` method receives the document and the converted output string, and
|
|
669
|
+
* must return the (possibly modified) output string.
|
|
670
|
+
*
|
|
671
|
+
* Implementations must extend Postprocessor and override {@link process}.
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* class FooterPostprocessor extends Postprocessor {
|
|
675
|
+
* process(document, output) {
|
|
676
|
+
* return output.replace('</body>', '<footer>Generated by Acme</footer></body>')
|
|
677
|
+
* }
|
|
678
|
+
* }
|
|
679
|
+
* Extensions.register(function () { this.postprocessor(FooterPostprocessor) })
|
|
680
|
+
*/
|
|
681
|
+
export class Postprocessor extends Processor {
|
|
682
|
+
process(_document, _output) {
|
|
683
|
+
throw new Error(
|
|
684
|
+
`${this.constructor.name} must implement the process method`
|
|
685
|
+
)
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
Postprocessor.DSL = DocumentProcessorDsl
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* IncludeProcessors handle include::<target>[] directives.
|
|
692
|
+
*
|
|
693
|
+
* Implementations must extend IncludeProcessor.
|
|
694
|
+
*/
|
|
695
|
+
export class IncludeProcessor extends Processor {
|
|
696
|
+
process(_document, _reader, _target, _attributes) {
|
|
697
|
+
throw new Error(
|
|
698
|
+
`${this.constructor.name} must implement the process method`
|
|
699
|
+
)
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
handles(_doc, _target) {
|
|
703
|
+
return true
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
IncludeProcessor.DSL = IncludeProcessorDsl
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* DocinfoProcessors add additional content to the header and/or footer
|
|
710
|
+
* of the generated document.
|
|
711
|
+
*
|
|
712
|
+
* Implementations must extend DocinfoProcessor.
|
|
713
|
+
*/
|
|
714
|
+
export class DocinfoProcessor extends Processor {
|
|
715
|
+
constructor(config = {}) {
|
|
716
|
+
super(config)
|
|
717
|
+
this.config.location ??= 'head'
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
process(_document) {
|
|
721
|
+
throw new Error(
|
|
722
|
+
`${this.constructor.name} must implement the process method`
|
|
723
|
+
)
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
DocinfoProcessor.DSL = DocinfoProcessorDsl
|
|
727
|
+
|
|
728
|
+
// ── Syntax processors ─────────────────────────────────────────────────────────
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* BlockProcessors handle delimited blocks and paragraphs with a custom name.
|
|
732
|
+
*
|
|
733
|
+
* The `process(parent, reader, attributes)` method receives:
|
|
734
|
+
* - `parent` {AbstractBlock} — the enclosing block
|
|
735
|
+
* - `reader` {Reader} — positioned at the block content
|
|
736
|
+
* - `attributes` {Object} — parsed block attributes
|
|
737
|
+
*
|
|
738
|
+
* It must return a block node (created with `this.createBlock(...)`, etc.)
|
|
739
|
+
* or call `this.parseContent(parent, reader)` to delegate parsing.
|
|
740
|
+
*
|
|
741
|
+
* Use the static `config` object or the DSL helpers (`contexts()`,
|
|
742
|
+
* `contentModel()`, `resolveAttributes()`, …) to declare the block's behaviour
|
|
743
|
+
* before registering.
|
|
744
|
+
*
|
|
745
|
+
* Implementations must extend BlockProcessor and override {@link process}.
|
|
746
|
+
*
|
|
747
|
+
* @example <caption>Custom delimited block that wraps content in a div</caption>
|
|
748
|
+
* class ShoutBlock extends BlockProcessor {
|
|
749
|
+
* process(parent, reader, attrs) {
|
|
750
|
+
* const block = this.createBlock(parent, 'paragraph', reader.readLines().join('\n').toUpperCase(), attrs)
|
|
751
|
+
* return block
|
|
752
|
+
* }
|
|
753
|
+
* }
|
|
754
|
+
* ShoutBlock.config = { name: 'shout', contexts: ['paragraph'], content_model: 'simple' }
|
|
755
|
+
* Extensions.register(function () { this.block(ShoutBlock) })
|
|
756
|
+
* // AsciiDoc usage: [shout]\nHello world
|
|
757
|
+
*/
|
|
758
|
+
export class BlockProcessor extends Processor {
|
|
759
|
+
constructor(name = null, config = {}) {
|
|
760
|
+
super(config)
|
|
761
|
+
this.name = name || this.config.name || null
|
|
762
|
+
|
|
763
|
+
// Normalise contexts config to a Set.
|
|
764
|
+
const ctx = this.config.contexts
|
|
765
|
+
if (ctx == null) {
|
|
766
|
+
this.config.contexts = new Set(['open', 'paragraph'])
|
|
767
|
+
} else if (typeof ctx === 'string') {
|
|
768
|
+
this.config.contexts = new Set([ctx])
|
|
769
|
+
} else if (Array.isArray(ctx)) {
|
|
770
|
+
this.config.contexts = new Set(ctx)
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
this.config.content_model ??= 'compound'
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
process(_parent, _reader, _attributes) {
|
|
777
|
+
throw new Error(
|
|
778
|
+
`${this.constructor.name} must implement the process method`
|
|
779
|
+
)
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
BlockProcessor.DSL = BlockProcessorDsl
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Base class shared by BlockMacroProcessor and InlineMacroProcessor.
|
|
786
|
+
*/
|
|
787
|
+
export class MacroProcessor extends Processor {
|
|
788
|
+
constructor(name = null, config = {}) {
|
|
789
|
+
super(config)
|
|
790
|
+
this.name = name || this.config.name || null
|
|
791
|
+
this.config.content_model ??= 'attributes'
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
process(_parent, _target, _attributes) {
|
|
795
|
+
throw new Error(
|
|
796
|
+
`${this.constructor.name} must implement the process method`
|
|
797
|
+
)
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* BlockMacroProcessors handle block macros with a custom name.
|
|
803
|
+
*
|
|
804
|
+
* The `process(parent, target, attributes)` method receives:
|
|
805
|
+
* - `parent` {AbstractBlock} — the enclosing block
|
|
806
|
+
* - `target` {string} — the macro target (text between `name:` and `[`)
|
|
807
|
+
* - `attributes` {Object} — parsed macro attributes
|
|
808
|
+
*
|
|
809
|
+
* It must return a block node created with one of the `createBlock`,
|
|
810
|
+
* `createImage`, etc. helpers inherited from {@link Processor}.
|
|
811
|
+
*
|
|
812
|
+
* Implementations must extend BlockMacroProcessor and override {@link process}.
|
|
813
|
+
*
|
|
814
|
+
* @example <caption>Block macro that embeds a GitHub Gist</caption>
|
|
815
|
+
* class GistBlockMacro extends BlockMacroProcessor {
|
|
816
|
+
* process(parent, target, attrs) {
|
|
817
|
+
* const html = `<script src="https://gist.github.com/${target}.js"></script>`
|
|
818
|
+
* return this.createBlock(parent, 'pass', html)
|
|
819
|
+
* }
|
|
820
|
+
* }
|
|
821
|
+
* GistBlockMacro.config = { name: 'gist' }
|
|
822
|
+
* Extensions.register(function () { this.blockMacro(GistBlockMacro) })
|
|
823
|
+
* // AsciiDoc usage: gist::abc123[]
|
|
824
|
+
*/
|
|
825
|
+
export class BlockMacroProcessor extends MacroProcessor {
|
|
826
|
+
/**
|
|
827
|
+
* Get the name, validating the format.
|
|
828
|
+
* The setter stores without validation to avoid throwing during construction.
|
|
829
|
+
*/
|
|
830
|
+
get name() {
|
|
831
|
+
if (this._name != null && !MacroNameRx.test(String(this._name))) {
|
|
832
|
+
throw new Error(`invalid name for block macro: ${this._name}`)
|
|
833
|
+
}
|
|
834
|
+
return this._name
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
set name(value) {
|
|
838
|
+
this._name = value
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
BlockMacroProcessor.DSL = MacroProcessorDsl
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* InlineMacroProcessors handle inline macros with a custom name.
|
|
845
|
+
*
|
|
846
|
+
* The `process(parent, target, attributes)` method receives:
|
|
847
|
+
* - `parent` {AbstractBlock} — the enclosing block
|
|
848
|
+
* - `target` {string} — the macro target (text between `name:` and `[`)
|
|
849
|
+
* - `attributes` {Object} — parsed macro attributes (first positional attr is `attributes[1]`)
|
|
850
|
+
*
|
|
851
|
+
* It must return an {@link Inline} node created with `this.createInline(parent, 'quoted', text, opts)`.
|
|
852
|
+
*
|
|
853
|
+
* By default the macro format is `'long'` (`name:target[attrs]`). Set
|
|
854
|
+
* `config.format = 'short'` for a no-target form (`name:[attrs]`).
|
|
855
|
+
*
|
|
856
|
+
* Implementations must extend InlineMacroProcessor and override {@link process}.
|
|
857
|
+
*
|
|
858
|
+
* @example <caption>Inline macro that generates a keyboard shortcut span</caption>
|
|
859
|
+
* class KbdInlineMacro extends InlineMacroProcessor {
|
|
860
|
+
* process(parent, target, attrs) {
|
|
861
|
+
* return this.createInline(parent, 'quoted', target, { type: 'monospaced' })
|
|
862
|
+
* }
|
|
863
|
+
* }
|
|
864
|
+
* KbdInlineMacro.config = { name: 'kbd', format: 'short' }
|
|
865
|
+
* Extensions.register(function () { this.inlineMacro(KbdInlineMacro) })
|
|
866
|
+
* // AsciiDoc usage: kbd:[Ctrl+C]
|
|
867
|
+
*/
|
|
868
|
+
export class InlineMacroProcessor extends MacroProcessor {
|
|
869
|
+
static rxCache = new Map()
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Look up (and memoize) the regexp for this inline macro processor.
|
|
873
|
+
*
|
|
874
|
+
* @returns {RegExp}
|
|
875
|
+
*/
|
|
876
|
+
get regexp() {
|
|
877
|
+
return (this.config.regexp ??= this.resolveRegexp(
|
|
878
|
+
String(this.name),
|
|
879
|
+
this.config.format
|
|
880
|
+
))
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
resolveRegexp(name, format) {
|
|
884
|
+
if (!MacroNameRx.test(name)) {
|
|
885
|
+
throw new Error(`invalid name for inline macro: ${name}`)
|
|
886
|
+
}
|
|
887
|
+
const key = `${name}:${format}`
|
|
888
|
+
if (!InlineMacroProcessor.rxCache.has(key)) {
|
|
889
|
+
const targetPart = format === 'short' ? '(){0}' : '(\\S+?)'
|
|
890
|
+
InlineMacroProcessor.rxCache.set(
|
|
891
|
+
key,
|
|
892
|
+
new RegExp(
|
|
893
|
+
`\\\\?${name}:${targetPart}\\[(|(?:${CC_ANY})*?(?<!\\\\))\\]`
|
|
894
|
+
)
|
|
895
|
+
)
|
|
896
|
+
}
|
|
897
|
+
return InlineMacroProcessor.rxCache.get(key)
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
InlineMacroProcessor.DSL = InlineMacroProcessorDsl
|
|
901
|
+
|
|
902
|
+
// ── Extension proxy objects ───────────────────────────────────────────────────
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* Proxy that encapsulates the extension kind, config, and instance.
|
|
906
|
+
* This is what gets stored in the extension registry when activated.
|
|
907
|
+
*/
|
|
908
|
+
export class Extension {
|
|
909
|
+
constructor(kind, instance, config) {
|
|
910
|
+
this.kind = kind
|
|
911
|
+
this.instance = instance
|
|
912
|
+
this.config = config
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Specialisation of Extension that additionally stores a reference
|
|
918
|
+
* to the process method, accommodating both class-based processors and function blocks.
|
|
919
|
+
*/
|
|
920
|
+
export class ProcessorExtension extends Extension {
|
|
921
|
+
constructor(kind, instance, processMethod = null) {
|
|
922
|
+
super(kind, instance, instance.config)
|
|
923
|
+
this.processMethod =
|
|
924
|
+
processMethod || ((...args) => instance.process(...args))
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// ── Group ─────────────────────────────────────────────────────────────────────
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* A Group registers one or more extensions with a Registry.
|
|
932
|
+
*
|
|
933
|
+
* Subclass Group and pass the subclass to Extensions.register(), or call
|
|
934
|
+
* the static register() method directly.
|
|
935
|
+
*/
|
|
936
|
+
export class Group {
|
|
937
|
+
static register(name = null) {
|
|
938
|
+
Extensions.register(name, this)
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
activate(_registry) {
|
|
942
|
+
throw new Error(
|
|
943
|
+
`${this.constructor.name} must implement the activate method`
|
|
944
|
+
)
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// ── Registry ──────────────────────────────────────────────────────────────────
|
|
949
|
+
|
|
950
|
+
/** @internal Maps kind name → document-processor class. */
|
|
951
|
+
const DOCUMENT_PROCESSOR_CLASSES = {
|
|
952
|
+
preprocessor: Preprocessor,
|
|
953
|
+
tree_processor: TreeProcessor,
|
|
954
|
+
postprocessor: Postprocessor,
|
|
955
|
+
include_processor: IncludeProcessor,
|
|
956
|
+
docinfo_processor: DocinfoProcessor,
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/** @internal Maps kind name → syntax-processor class. */
|
|
960
|
+
const SYNTAX_PROCESSOR_CLASSES = {
|
|
961
|
+
block: BlockProcessor,
|
|
962
|
+
block_macro: BlockMacroProcessor,
|
|
963
|
+
inline_macro: InlineMacroProcessor,
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* The primary entry point into the extension system.
|
|
968
|
+
*
|
|
969
|
+
* Registry holds the extensions which have been registered and activated, has
|
|
970
|
+
* methods for registering or defining a processor and looks up extensions
|
|
971
|
+
* stored in the registry during parsing.
|
|
972
|
+
*/
|
|
973
|
+
export class Registry {
|
|
974
|
+
constructor(groups = {}) {
|
|
975
|
+
this.groups = groups
|
|
976
|
+
this._reset()
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Activate all global extension Groups and the Groups associated with this registry.
|
|
981
|
+
*
|
|
982
|
+
* @param {Document} document - The Document on which the extensions are to be used.
|
|
983
|
+
* @returns {Registry} this Registry.
|
|
984
|
+
*/
|
|
985
|
+
activate(document) {
|
|
986
|
+
if (this.document) this._reset()
|
|
987
|
+
this.document = document
|
|
988
|
+
const extGroups = [
|
|
989
|
+
...Object.values(Extensions.groups()),
|
|
990
|
+
...Object.values(this.groups),
|
|
991
|
+
]
|
|
992
|
+
for (const group of extGroups) {
|
|
993
|
+
if (typeof group === 'function') {
|
|
994
|
+
// Check if it is a class (constructor) with an activate prototype method.
|
|
995
|
+
if (group.prototype && typeof group.prototype.activate === 'function') {
|
|
996
|
+
new group().activate(this)
|
|
997
|
+
} else {
|
|
998
|
+
// Plain function — call in the context of this registry (like instance_exec).
|
|
999
|
+
group.length === 0 ? group.call(this) : group(this)
|
|
1000
|
+
}
|
|
1001
|
+
} else if (group && typeof group.activate === 'function') {
|
|
1002
|
+
group.activate(this)
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return this
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* Register a Preprocessor with the extension registry.
|
|
1010
|
+
*
|
|
1011
|
+
* The processor may be:
|
|
1012
|
+
* - A Preprocessor subclass (constructor function)
|
|
1013
|
+
* - An instance of a Preprocessor subclass
|
|
1014
|
+
* - A Function that configures the processor via the DSL (block style)
|
|
1015
|
+
*
|
|
1016
|
+
* @example
|
|
1017
|
+
* // class style
|
|
1018
|
+
* preprocessor(FrontMatterPreprocessor)
|
|
1019
|
+
* // instance style
|
|
1020
|
+
* preprocessor(new FrontMatterPreprocessor())
|
|
1021
|
+
* // block style
|
|
1022
|
+
* preprocessor(function () {
|
|
1023
|
+
* this.process(function (doc, reader) { ... })
|
|
1024
|
+
* })
|
|
1025
|
+
*
|
|
1026
|
+
* @param {...*} args - Class constructor, instance, or block function.
|
|
1027
|
+
* @returns {ProcessorExtension} the Extension stored in the registry.
|
|
1028
|
+
*/
|
|
1029
|
+
preprocessor(...args) {
|
|
1030
|
+
return this._addDocumentProcessor('preprocessor', args)
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* Return the registered Preprocessor extensions, or null if none.
|
|
1035
|
+
*
|
|
1036
|
+
* @returns {ProcessorExtension[]|null}
|
|
1037
|
+
*/
|
|
1038
|
+
preprocessors() {
|
|
1039
|
+
return this._preprocessor_extensions
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* Check whether any Preprocessor extensions have been registered.
|
|
1044
|
+
*
|
|
1045
|
+
* @returns {boolean}
|
|
1046
|
+
*/
|
|
1047
|
+
hasPreprocessors() {
|
|
1048
|
+
return !!this._preprocessor_extensions
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/** @internal Core API compatibility alias for preprocessors(). */
|
|
1052
|
+
get preprocessor_extensions() {
|
|
1053
|
+
return this._preprocessor_extensions
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* Register a TreeProcessor with the extension registry.
|
|
1058
|
+
*
|
|
1059
|
+
* @param {...*} args - Class constructor, instance, or block function.
|
|
1060
|
+
* @returns {ProcessorExtension} the Extension stored in the registry.
|
|
1061
|
+
*/
|
|
1062
|
+
treeProcessor(...args) {
|
|
1063
|
+
return this._addDocumentProcessor('tree_processor', args)
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/** @deprecated Alias for {@link treeProcessor}. */
|
|
1067
|
+
treeprocessor(...args) {
|
|
1068
|
+
return this.treeProcessor(...args)
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/** Alias for {@link treeProcessor} (snake_case for prefer() / Registry method dispatch). */
|
|
1072
|
+
tree_processor(...args) {
|
|
1073
|
+
return this.treeProcessor(...args)
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
/**
|
|
1077
|
+
* Return the registered TreeProcessor extensions, or null if none.
|
|
1078
|
+
*
|
|
1079
|
+
* @returns {ProcessorExtension[]|null}
|
|
1080
|
+
*/
|
|
1081
|
+
treeProcessors() {
|
|
1082
|
+
return this._tree_processor_extensions
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Check whether any TreeProcessor extensions have been registered.
|
|
1087
|
+
*
|
|
1088
|
+
* @returns {boolean}
|
|
1089
|
+
*/
|
|
1090
|
+
hasTreeProcessors() {
|
|
1091
|
+
return !!this._tree_processor_extensions
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/** @deprecated Typo alias kept for backward compatibility. Use {@link hasTreeProcessors}. */
|
|
1095
|
+
hasTeeProcessors() {
|
|
1096
|
+
return !!this._tree_processor_extensions
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/** @deprecated Alias for {@link treeProcessors}. */
|
|
1100
|
+
treeprocessors() {
|
|
1101
|
+
return this._tree_processor_extensions
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/** @internal Core API compatibility alias for treeProcessors(). */
|
|
1105
|
+
get tree_processor_extensions() {
|
|
1106
|
+
return this._tree_processor_extensions
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* Register a Postprocessor with the extension registry.
|
|
1111
|
+
*
|
|
1112
|
+
* @param {...*} args - Class constructor, instance, or block function.
|
|
1113
|
+
* @returns {ProcessorExtension} the Extension stored in the registry.
|
|
1114
|
+
*/
|
|
1115
|
+
postprocessor(...args) {
|
|
1116
|
+
return this._addDocumentProcessor('postprocessor', args)
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
/**
|
|
1120
|
+
* Return the registered Postprocessor extensions, or null if none.
|
|
1121
|
+
*
|
|
1122
|
+
* @returns {ProcessorExtension[]|null}
|
|
1123
|
+
*/
|
|
1124
|
+
postprocessors() {
|
|
1125
|
+
return this._postprocessor_extensions
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/**
|
|
1129
|
+
* Check whether any Postprocessor extensions have been registered.
|
|
1130
|
+
*
|
|
1131
|
+
* @returns {boolean}
|
|
1132
|
+
*/
|
|
1133
|
+
hasPostprocessors() {
|
|
1134
|
+
return !!this._postprocessor_extensions
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/** @internal Core API compatibility alias for postprocessors(). */
|
|
1138
|
+
get postprocessor_extensions() {
|
|
1139
|
+
return this._postprocessor_extensions
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* Register an IncludeProcessor with the extension registry.
|
|
1144
|
+
*
|
|
1145
|
+
* @param {...*} args - Class constructor, instance, or block function.
|
|
1146
|
+
* @returns {ProcessorExtension} the Extension stored in the registry.
|
|
1147
|
+
*/
|
|
1148
|
+
includeProcessor(...args) {
|
|
1149
|
+
return this._addDocumentProcessor('include_processor', args)
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
* Return the registered IncludeProcessor extensions, or null if none.
|
|
1154
|
+
*
|
|
1155
|
+
* @returns {ProcessorExtension[]|null}
|
|
1156
|
+
*/
|
|
1157
|
+
includeProcessors() {
|
|
1158
|
+
return this._include_processor_extensions
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* Check whether any IncludeProcessor extensions have been registered.
|
|
1163
|
+
*
|
|
1164
|
+
* @returns {boolean}
|
|
1165
|
+
*/
|
|
1166
|
+
hasIncludeProcessors() {
|
|
1167
|
+
return !!this._include_processor_extensions
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
/** Alias for {@link includeProcessor} (snake_case for prefer() / Registry method dispatch). */
|
|
1171
|
+
include_processor(...args) {
|
|
1172
|
+
return this.includeProcessor(...args)
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
/** @internal Core API compatibility alias for includeProcessors(). */
|
|
1176
|
+
get include_processor_extensions() {
|
|
1177
|
+
return this._include_processor_extensions
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/**
|
|
1181
|
+
* Register a DocinfoProcessor with the extension registry.
|
|
1182
|
+
*
|
|
1183
|
+
* @param {...*} args - Class constructor, instance, or block function.
|
|
1184
|
+
* @returns {ProcessorExtension} the Extension stored in the registry.
|
|
1185
|
+
*/
|
|
1186
|
+
docinfoProcessor(...args) {
|
|
1187
|
+
return this._addDocumentProcessor('docinfo_processor', args)
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* Check whether any DocinfoProcessor extensions have been registered.
|
|
1192
|
+
*
|
|
1193
|
+
* @param {string|null} [location=null] - Optional location ('head' or 'footer') to filter by.
|
|
1194
|
+
* @returns {boolean}
|
|
1195
|
+
*/
|
|
1196
|
+
hasDocinfoProcessors(location = null) {
|
|
1197
|
+
if (!this._docinfo_processor_extensions) return false
|
|
1198
|
+
if (location) {
|
|
1199
|
+
return this._docinfo_processor_extensions.some(
|
|
1200
|
+
(ext) => ext.config.location === location
|
|
1201
|
+
)
|
|
1202
|
+
}
|
|
1203
|
+
return true
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
/**
|
|
1207
|
+
* Retrieve Extension proxy objects for DocinfoProcessor instances.
|
|
1208
|
+
*
|
|
1209
|
+
* @param {string|null} [location=null] - Optional location ('head' or 'footer') to filter by.
|
|
1210
|
+
* @returns {ProcessorExtension[]} array of Extension proxy objects.
|
|
1211
|
+
*/
|
|
1212
|
+
docinfoProcessors(location = null) {
|
|
1213
|
+
if (!this._docinfo_processor_extensions) return []
|
|
1214
|
+
if (location) {
|
|
1215
|
+
return this._docinfo_processor_extensions.filter(
|
|
1216
|
+
(ext) => ext.config.location === location
|
|
1217
|
+
)
|
|
1218
|
+
}
|
|
1219
|
+
return this._docinfo_processor_extensions
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/** Alias for {@link docinfoProcessor} (snake_case for prefer() / Registry method dispatch). */
|
|
1223
|
+
docinfo_processor(...args) {
|
|
1224
|
+
return this.docinfoProcessor(...args)
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
/** @internal Core API compatibility alias for docinfoProcessors(). */
|
|
1228
|
+
get docinfo_processor_extensions() {
|
|
1229
|
+
return this._docinfo_processor_extensions
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* Register a BlockProcessor with the extension registry.
|
|
1234
|
+
*
|
|
1235
|
+
* @example
|
|
1236
|
+
* // class style
|
|
1237
|
+
* block(ShoutBlock)
|
|
1238
|
+
* // class style with explicit name
|
|
1239
|
+
* block(ShoutBlock, 'shout')
|
|
1240
|
+
* // block style
|
|
1241
|
+
* block(function () {
|
|
1242
|
+
* this.named('shout')
|
|
1243
|
+
* this.process(function (parent, reader, attrs) { ... })
|
|
1244
|
+
* })
|
|
1245
|
+
* // block style with explicit name
|
|
1246
|
+
* block('shout', function () {
|
|
1247
|
+
* this.process(function (parent, reader, attrs) { ... })
|
|
1248
|
+
* })
|
|
1249
|
+
*
|
|
1250
|
+
* @param {...*} args - Class constructor, instance, block function, or name + one of those.
|
|
1251
|
+
* @returns {ProcessorExtension} an Extension proxy object.
|
|
1252
|
+
*/
|
|
1253
|
+
block(...args) {
|
|
1254
|
+
return this._addSyntaxProcessor('block', args)
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* Check whether any BlockProcessor extensions have been registered.
|
|
1259
|
+
*
|
|
1260
|
+
* @returns {boolean}
|
|
1261
|
+
*/
|
|
1262
|
+
hasBlocks() {
|
|
1263
|
+
return !!this._block_extensions
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* Check whether a BlockProcessor is registered for the given name and context.
|
|
1268
|
+
*
|
|
1269
|
+
* @param {string} name - The block name.
|
|
1270
|
+
* @param {string} context - The block context.
|
|
1271
|
+
* @returns {ProcessorExtension|false} the Extension proxy or false.
|
|
1272
|
+
*/
|
|
1273
|
+
registeredForBlock(name, context) {
|
|
1274
|
+
const ext = this._block_extensions?.[String(name)]
|
|
1275
|
+
return ext ? ext.config.contexts.has(context) && ext : false
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
/**
|
|
1279
|
+
* Retrieve the Extension proxy for the BlockProcessor registered with the given name.
|
|
1280
|
+
*
|
|
1281
|
+
* @param {string} name - The block name.
|
|
1282
|
+
* @returns {ProcessorExtension|null}
|
|
1283
|
+
*/
|
|
1284
|
+
findBlockExtension(name) {
|
|
1285
|
+
return this._block_extensions?.[String(name)] ?? null
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Register a BlockMacroProcessor with the extension registry.
|
|
1290
|
+
*
|
|
1291
|
+
* @param {...*} args - Class constructor, instance, or block function.
|
|
1292
|
+
* @returns {ProcessorExtension} the Extension stored in the registry.
|
|
1293
|
+
*/
|
|
1294
|
+
blockMacro(...args) {
|
|
1295
|
+
return this._addSyntaxProcessor('block_macro', args)
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
/** @deprecated Alias for {@link blockMacro}. */
|
|
1299
|
+
block_macro(...args) {
|
|
1300
|
+
return this.blockMacro(...args)
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* Check whether any BlockMacroProcessor extensions have been registered.
|
|
1305
|
+
*
|
|
1306
|
+
* @returns {boolean}
|
|
1307
|
+
*/
|
|
1308
|
+
hasBlockMacros() {
|
|
1309
|
+
return !!this._block_macro_extensions
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
/**
|
|
1313
|
+
* Check whether a BlockMacroProcessor is registered for the given name.
|
|
1314
|
+
*
|
|
1315
|
+
* @param {string} name - The macro name.
|
|
1316
|
+
* @returns {ProcessorExtension|false}
|
|
1317
|
+
*/
|
|
1318
|
+
registeredForBlockMacro(name) {
|
|
1319
|
+
return this._block_macro_extensions?.[String(name)] || false
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
/**
|
|
1323
|
+
* Retrieve the Extension proxy for the BlockMacroProcessor registered with the given name.
|
|
1324
|
+
*
|
|
1325
|
+
* @param {string} name - The macro name.
|
|
1326
|
+
* @returns {ProcessorExtension|null}
|
|
1327
|
+
*/
|
|
1328
|
+
findBlockMacroExtension(name) {
|
|
1329
|
+
return this._block_macro_extensions?.[String(name)] ?? null
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
/**
|
|
1333
|
+
* Register an InlineMacroProcessor with the extension registry.
|
|
1334
|
+
*
|
|
1335
|
+
* @param {...*} args - Class constructor, instance, or block function.
|
|
1336
|
+
* @returns {ProcessorExtension} the Extension stored in the registry.
|
|
1337
|
+
*/
|
|
1338
|
+
inlineMacro(...args) {
|
|
1339
|
+
return this._addSyntaxProcessor('inline_macro', args)
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
/** @deprecated Alias for {@link inlineMacro}. */
|
|
1343
|
+
inline_macro(...args) {
|
|
1344
|
+
return this.inlineMacro(...args)
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
/**
|
|
1348
|
+
* Check whether any InlineMacroProcessor extensions have been registered.
|
|
1349
|
+
*
|
|
1350
|
+
* @returns {boolean}
|
|
1351
|
+
*/
|
|
1352
|
+
hasInlineMacros() {
|
|
1353
|
+
return !!this._inline_macro_extensions
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
/**
|
|
1357
|
+
* Check whether an InlineMacroProcessor is registered for the given name.
|
|
1358
|
+
*
|
|
1359
|
+
* @param {string} name - The macro name.
|
|
1360
|
+
* @returns {ProcessorExtension|false}
|
|
1361
|
+
*/
|
|
1362
|
+
registeredForInlineMacro(name) {
|
|
1363
|
+
return this._inline_macro_extensions?.[String(name)] || false
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
/**
|
|
1367
|
+
* Retrieve the Extension proxy for the InlineMacroProcessor registered with the given name.
|
|
1368
|
+
*
|
|
1369
|
+
* @param {string} name - The macro name.
|
|
1370
|
+
* @returns {ProcessorExtension|null}
|
|
1371
|
+
*/
|
|
1372
|
+
findInlineMacroExtension(name) {
|
|
1373
|
+
return this._inline_macro_extensions?.[String(name)] ?? null
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
/**
|
|
1377
|
+
* Retrieve all InlineMacroProcessor Extension proxy objects.
|
|
1378
|
+
*
|
|
1379
|
+
* @returns {ProcessorExtension[]}
|
|
1380
|
+
*/
|
|
1381
|
+
inlineMacros() {
|
|
1382
|
+
return this._inline_macro_extensions
|
|
1383
|
+
? Object.values(this._inline_macro_extensions)
|
|
1384
|
+
: []
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* Insert the document-processor Extension as the first of its kind in the extension registry.
|
|
1389
|
+
*
|
|
1390
|
+
* @example
|
|
1391
|
+
* registry.prefer('includeProcessor', function () {
|
|
1392
|
+
* this.process(function (document, reader, target, attrs) { ... })
|
|
1393
|
+
* })
|
|
1394
|
+
*
|
|
1395
|
+
* @param {...*} args - A ProcessorExtension, or a method name followed by processor args.
|
|
1396
|
+
* @returns {ProcessorExtension} the Extension stored in the registry.
|
|
1397
|
+
*/
|
|
1398
|
+
prefer(...args) {
|
|
1399
|
+
const arg0 = args.shift()
|
|
1400
|
+
let extension
|
|
1401
|
+
if (arg0 instanceof ProcessorExtension) {
|
|
1402
|
+
extension = arg0
|
|
1403
|
+
} else {
|
|
1404
|
+
// arg0 is a method name; remaining args include the processor and optional block.
|
|
1405
|
+
extension = this[arg0](...args)
|
|
1406
|
+
}
|
|
1407
|
+
const storeKey = `_${extension.kind}_extensions`
|
|
1408
|
+
const store = this[storeKey]
|
|
1409
|
+
if (Array.isArray(store)) {
|
|
1410
|
+
const idx = store.indexOf(extension)
|
|
1411
|
+
if (idx > -1) store.splice(idx, 1)
|
|
1412
|
+
store.unshift(extension)
|
|
1413
|
+
}
|
|
1414
|
+
return extension
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// ── Private helpers ──────────────────────────────────────────────────────────
|
|
1418
|
+
|
|
1419
|
+
/** @internal */
|
|
1420
|
+
_addDocumentProcessor(kind, args) {
|
|
1421
|
+
const kindName = kind.replace(/_/g, ' ')
|
|
1422
|
+
const kindClass = DOCUMENT_PROCESSOR_CLASSES[kind]
|
|
1423
|
+
if (!this[`_${kind}_extensions`]) this[`_${kind}_extensions`] = []
|
|
1424
|
+
const store = this[`_${kind}_extensions`]
|
|
1425
|
+
|
|
1426
|
+
// Detect block style: last argument is a function that is NOT a class constructor.
|
|
1427
|
+
// Class constructors (ES6 classes) have a non-writable prototype descriptor;
|
|
1428
|
+
// plain functions (used as DSL blocks) have a writable prototype.
|
|
1429
|
+
const lastArg = args[args.length - 1]
|
|
1430
|
+
const hasBlock =
|
|
1431
|
+
args.length > 0 &&
|
|
1432
|
+
typeof lastArg === 'function' &&
|
|
1433
|
+
!!(
|
|
1434
|
+
Object.getOwnPropertyDescriptor(lastArg, 'prototype')?.writable ?? true
|
|
1435
|
+
)
|
|
1436
|
+
|
|
1437
|
+
let processorInstance
|
|
1438
|
+
|
|
1439
|
+
if (hasBlock) {
|
|
1440
|
+
const block = args.pop()
|
|
1441
|
+
const config = this._resolveArgs(args, 1)
|
|
1442
|
+
const processor = new kindClass(config)
|
|
1443
|
+
Object.assign(processor, kindClass.DSL)
|
|
1444
|
+
block.length === 0 ? block.call(processor) : block(processor)
|
|
1445
|
+
if (!processor.processBlockGiven()) {
|
|
1446
|
+
throw new Error(`No block specified to process ${kindName} extension`)
|
|
1447
|
+
}
|
|
1448
|
+
processorInstance = processor
|
|
1449
|
+
} else {
|
|
1450
|
+
const [processorArg, config] = this._resolveArgs(args, 2)
|
|
1451
|
+
if (typeof processorArg === 'function') {
|
|
1452
|
+
// Style 2: class constructor
|
|
1453
|
+
if (!(processorArg.prototype instanceof kindClass)) {
|
|
1454
|
+
throw new Error(
|
|
1455
|
+
`Invalid type for ${kindName} extension: ${processorArg}`
|
|
1456
|
+
)
|
|
1457
|
+
}
|
|
1458
|
+
processorInstance = new processorArg(config)
|
|
1459
|
+
} else if (processorArg instanceof kindClass) {
|
|
1460
|
+
// Style 3: already an instance
|
|
1461
|
+
processorArg.updateConfig(config)
|
|
1462
|
+
processorInstance = processorArg
|
|
1463
|
+
} else {
|
|
1464
|
+
throw new Error(
|
|
1465
|
+
`Invalid arguments specified for registering ${kindName} extension: ${args}`
|
|
1466
|
+
)
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
// Apply legacy handles adapter for IncludeProcessors with arity-1 handles method.
|
|
1471
|
+
if (kind === 'include_processor') {
|
|
1472
|
+
const handlesFn = processorInstance.handles
|
|
1473
|
+
if (typeof handlesFn === 'function' && handlesFn.length === 1) {
|
|
1474
|
+
const original = handlesFn.bind(processorInstance)
|
|
1475
|
+
processorInstance.handles = (_doc, target) => original(target)
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
const extension = new ProcessorExtension(kind, processorInstance)
|
|
1480
|
+
extension.config.position === '>>'
|
|
1481
|
+
? store.unshift(extension)
|
|
1482
|
+
: store.push(extension)
|
|
1483
|
+
return extension
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
/** @internal */
|
|
1487
|
+
_addSyntaxProcessor(kind, args) {
|
|
1488
|
+
const kindName = kind.replace(/_/g, ' ')
|
|
1489
|
+
const kindClass = SYNTAX_PROCESSOR_CLASSES[kind]
|
|
1490
|
+
if (!this[`_${kind}_extensions`])
|
|
1491
|
+
this[`_${kind}_extensions`] = Object.create(null)
|
|
1492
|
+
const store = this[`_${kind}_extensions`]
|
|
1493
|
+
|
|
1494
|
+
// Detect block style (same heuristic as _addDocumentProcessor).
|
|
1495
|
+
const lastArg = args[args.length - 1]
|
|
1496
|
+
const hasBlock =
|
|
1497
|
+
args.length > 0 &&
|
|
1498
|
+
typeof lastArg === 'function' &&
|
|
1499
|
+
!!(
|
|
1500
|
+
Object.getOwnPropertyDescriptor(lastArg, 'prototype')?.writable ?? true
|
|
1501
|
+
)
|
|
1502
|
+
|
|
1503
|
+
let processorInstance, name
|
|
1504
|
+
|
|
1505
|
+
if (hasBlock) {
|
|
1506
|
+
const block = args.pop()
|
|
1507
|
+
const [nameArg, config] = this._resolveArgs(args, 2)
|
|
1508
|
+
const processor = new kindClass(this._asSymbol(nameArg), config)
|
|
1509
|
+
Object.assign(processor, kindClass.DSL)
|
|
1510
|
+
block.length === 0 ? block.call(processor) : block(processor)
|
|
1511
|
+
name = this._asSymbol(processor.name)
|
|
1512
|
+
if (!name) throw new Error(`No name specified for ${kindName} extension`)
|
|
1513
|
+
if (!processor.processBlockGiven()) {
|
|
1514
|
+
throw new Error(`No block specified to process ${kindName} extension`)
|
|
1515
|
+
}
|
|
1516
|
+
processorInstance = processor
|
|
1517
|
+
} else {
|
|
1518
|
+
const [processorArg, nameArg, config] = this._resolveArgs(args, 3)
|
|
1519
|
+
if (typeof processorArg === 'function') {
|
|
1520
|
+
// Style 2: class constructor
|
|
1521
|
+
if (!(processorArg.prototype instanceof kindClass)) {
|
|
1522
|
+
throw new Error(
|
|
1523
|
+
`Class specified for ${kindName} extension does not inherit from ${kindClass.name}: ${processorArg}`
|
|
1524
|
+
)
|
|
1525
|
+
}
|
|
1526
|
+
processorInstance = new processorArg(this._asSymbol(nameArg), config)
|
|
1527
|
+
name = this._asSymbol(processorInstance.name)
|
|
1528
|
+
if (!name)
|
|
1529
|
+
throw new Error(
|
|
1530
|
+
`No name specified for ${kindName} extension: ${processorArg}`
|
|
1531
|
+
)
|
|
1532
|
+
} else if (processorArg instanceof kindClass) {
|
|
1533
|
+
// Style 3: already an instance
|
|
1534
|
+
processorArg.updateConfig(config)
|
|
1535
|
+
name = nameArg
|
|
1536
|
+
? (processorArg.name = this._asSymbol(nameArg))
|
|
1537
|
+
: this._asSymbol(processorArg.name)
|
|
1538
|
+
if (!name)
|
|
1539
|
+
throw new Error(
|
|
1540
|
+
`No name specified for ${kindName} extension: ${processorArg}`
|
|
1541
|
+
)
|
|
1542
|
+
processorInstance = processorArg
|
|
1543
|
+
} else {
|
|
1544
|
+
throw new Error(
|
|
1545
|
+
`Invalid arguments specified for registering ${kindName} extension: ${args}`
|
|
1546
|
+
)
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
store[name] = new ProcessorExtension(kind, processorInstance)
|
|
1551
|
+
return store[name]
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
/** @internal */
|
|
1555
|
+
_reset() {
|
|
1556
|
+
/** @internal */
|
|
1557
|
+
this._preprocessor_extensions = null
|
|
1558
|
+
/** @internal */
|
|
1559
|
+
this._tree_processor_extensions = null
|
|
1560
|
+
/** @internal */
|
|
1561
|
+
this._postprocessor_extensions = null
|
|
1562
|
+
/** @internal */
|
|
1563
|
+
this._include_processor_extensions = null
|
|
1564
|
+
/** @internal */
|
|
1565
|
+
this._docinfo_processor_extensions = null
|
|
1566
|
+
/** @internal */
|
|
1567
|
+
this._block_extensions = null
|
|
1568
|
+
/** @internal */
|
|
1569
|
+
this._block_macro_extensions = null
|
|
1570
|
+
/** @internal */
|
|
1571
|
+
this._inline_macro_extensions = null
|
|
1572
|
+
this.document = null
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
/**
|
|
1576
|
+
* @internal Normalise an args array to the expected number of values.
|
|
1577
|
+
*
|
|
1578
|
+
* Pops a trailing plain-object as options (or uses {}), then pads / trims
|
|
1579
|
+
* the remaining args to (expect - 1) elements, then appends the options object.
|
|
1580
|
+
* If expect === 1, returns just the options object.
|
|
1581
|
+
*/
|
|
1582
|
+
_resolveArgs(args, expect) {
|
|
1583
|
+
const last = args[args.length - 1]
|
|
1584
|
+
const opts =
|
|
1585
|
+
args.length > 0 &&
|
|
1586
|
+
last !== null &&
|
|
1587
|
+
typeof last === 'object' &&
|
|
1588
|
+
!Array.isArray(last) &&
|
|
1589
|
+
!(last instanceof Processor)
|
|
1590
|
+
? args.pop()
|
|
1591
|
+
: {}
|
|
1592
|
+
|
|
1593
|
+
if (expect === 1) return opts
|
|
1594
|
+
|
|
1595
|
+
const missing = expect - 1 - args.length
|
|
1596
|
+
if (missing > 0) {
|
|
1597
|
+
for (let i = 0; i < missing; i++) args.push(undefined)
|
|
1598
|
+
} else if (missing < 0) {
|
|
1599
|
+
args.splice(args.length + missing, -missing)
|
|
1600
|
+
}
|
|
1601
|
+
args.push(opts)
|
|
1602
|
+
return args
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
/** @internal */
|
|
1606
|
+
_asSymbol(name) {
|
|
1607
|
+
return name != null ? String(name) : null
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// ── Extensions module namespace ───────────────────────────────────────────────
|
|
1612
|
+
|
|
1613
|
+
// Module-level state (mirrors Ruby module instance variables @auto_id / @groups).
|
|
1614
|
+
let _autoId = -1
|
|
1615
|
+
const _groups = Object.create(null)
|
|
1616
|
+
|
|
1617
|
+
/**
|
|
1618
|
+
* The primary entry point for registering extensions globally.
|
|
1619
|
+
*
|
|
1620
|
+
* Mirrors the class-level methods on the Ruby Asciidoctor::Extensions module.
|
|
1621
|
+
*/
|
|
1622
|
+
export const Extensions = {
|
|
1623
|
+
/** @internal Generate a unique name for an anonymous extension group. */
|
|
1624
|
+
generateName() {
|
|
1625
|
+
return `extgrp${this.nextAutoId()}`
|
|
1626
|
+
},
|
|
1627
|
+
|
|
1628
|
+
/** @internal Increment and return the global auto-id counter. */
|
|
1629
|
+
nextAutoId() {
|
|
1630
|
+
return ++_autoId
|
|
1631
|
+
},
|
|
1632
|
+
|
|
1633
|
+
/**
|
|
1634
|
+
* Return the plain Object that maps names to registered groups.
|
|
1635
|
+
*
|
|
1636
|
+
* @returns {object}
|
|
1637
|
+
*/
|
|
1638
|
+
groups() {
|
|
1639
|
+
return _groups
|
|
1640
|
+
},
|
|
1641
|
+
|
|
1642
|
+
/**
|
|
1643
|
+
* Create a new Registry, optionally pre-populated with a named block.
|
|
1644
|
+
*
|
|
1645
|
+
* @param {string|null} [name=null] - Optional name for the group; auto-generated if omitted.
|
|
1646
|
+
* @param {Function|null} [block=null] - Optional function to register as the group.
|
|
1647
|
+
* @returns {Registry}
|
|
1648
|
+
*/
|
|
1649
|
+
create(name = null, block = null) {
|
|
1650
|
+
if (block) {
|
|
1651
|
+
return new Registry({ [name || this.generateName()]: block })
|
|
1652
|
+
}
|
|
1653
|
+
return new Registry()
|
|
1654
|
+
},
|
|
1655
|
+
|
|
1656
|
+
/**
|
|
1657
|
+
* Register an extension Group that subsequently registers extensions.
|
|
1658
|
+
*
|
|
1659
|
+
* @example
|
|
1660
|
+
* Extensions.register(UmlExtensions)
|
|
1661
|
+
* Extensions.register('uml', UmlExtensions)
|
|
1662
|
+
* Extensions.register(function () { this.blockMacro('plantuml', PlantUmlBlock) })
|
|
1663
|
+
* Extensions.register('uml', function () { this.blockMacro('plantuml', PlantUmlBlock) })
|
|
1664
|
+
*
|
|
1665
|
+
* @param {...*} args - Optional name followed by a Group class, instance, or function.
|
|
1666
|
+
* @returns {Function|object} the registered group.
|
|
1667
|
+
*/
|
|
1668
|
+
register(...args) {
|
|
1669
|
+
const argc = args.length
|
|
1670
|
+
if (argc === 0) throw new Error('Extension group to register not specified')
|
|
1671
|
+
const group = args.pop()
|
|
1672
|
+
if (!group) throw new Error('Extension group to register not specified')
|
|
1673
|
+
const name = args.pop() ?? this.generateName()
|
|
1674
|
+
if (args.length > 0)
|
|
1675
|
+
throw new Error(`Wrong number of arguments (${argc} for 1..2)`)
|
|
1676
|
+
_groups[String(name)] = group
|
|
1677
|
+
return group
|
|
1678
|
+
},
|
|
1679
|
+
|
|
1680
|
+
/**
|
|
1681
|
+
* Unregister all statically-registered extension groups.
|
|
1682
|
+
*/
|
|
1683
|
+
unregisterAll() {
|
|
1684
|
+
for (const key of Object.keys(_groups)) delete _groups[key]
|
|
1685
|
+
},
|
|
1686
|
+
|
|
1687
|
+
/**
|
|
1688
|
+
* Unregister statically-registered extension groups by name.
|
|
1689
|
+
*
|
|
1690
|
+
* @param {...string} names - One or more group names to unregister.
|
|
1691
|
+
*/
|
|
1692
|
+
unregister(...names) {
|
|
1693
|
+
for (const name of names) delete _groups[String(name)]
|
|
1694
|
+
},
|
|
1695
|
+
|
|
1696
|
+
// ── Processor factory helpers (mirrors core API) ─────────────────────────────
|
|
1697
|
+
// Each pair: create<Kind>(name?, functions) → class constructor
|
|
1698
|
+
// new<Kind>(name?, functions) → instance of that class
|
|
1699
|
+
// The `name` argument is optional; if omitted the sole argument is `functions`.
|
|
1700
|
+
|
|
1701
|
+
/** @internal Build a subclass of BaseClass with the given prototype functions. */
|
|
1702
|
+
_buildProcessorClass(BaseClass, name, functions) {
|
|
1703
|
+
if (arguments.length === 2) {
|
|
1704
|
+
functions = name
|
|
1705
|
+
name = null
|
|
1706
|
+
}
|
|
1707
|
+
const klass = class extends BaseClass {}
|
|
1708
|
+
if (name) Object.defineProperty(klass, 'name', { value: name })
|
|
1709
|
+
Object.assign(klass.prototype, functions)
|
|
1710
|
+
return klass
|
|
1711
|
+
},
|
|
1712
|
+
|
|
1713
|
+
/**
|
|
1714
|
+
* Create a Preprocessor subclass with the given prototype functions.
|
|
1715
|
+
*
|
|
1716
|
+
* @param {string} [name] - Optional class name.
|
|
1717
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1718
|
+
* @returns {typeof Preprocessor}
|
|
1719
|
+
*/
|
|
1720
|
+
createPreprocessor(name, functions) {
|
|
1721
|
+
if (arguments.length === 1) {
|
|
1722
|
+
functions = name
|
|
1723
|
+
name = null
|
|
1724
|
+
}
|
|
1725
|
+
return this._buildProcessorClass(Preprocessor, name, functions)
|
|
1726
|
+
},
|
|
1727
|
+
|
|
1728
|
+
/**
|
|
1729
|
+
* Create and return a new Preprocessor instance with the given prototype functions.
|
|
1730
|
+
*
|
|
1731
|
+
* @param {string} [name] - Optional class name.
|
|
1732
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1733
|
+
* @returns {Preprocessor}
|
|
1734
|
+
*/
|
|
1735
|
+
newPreprocessor(name, functions) {
|
|
1736
|
+
if (arguments.length === 1) {
|
|
1737
|
+
functions = name
|
|
1738
|
+
name = null
|
|
1739
|
+
}
|
|
1740
|
+
return new (this.createPreprocessor(name, functions))()
|
|
1741
|
+
},
|
|
1742
|
+
|
|
1743
|
+
/**
|
|
1744
|
+
* Create a TreeProcessor subclass with the given prototype functions.
|
|
1745
|
+
*
|
|
1746
|
+
* @param {string} [name] - Optional class name.
|
|
1747
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1748
|
+
* @returns {typeof TreeProcessor}
|
|
1749
|
+
*/
|
|
1750
|
+
createTreeProcessor(name, functions) {
|
|
1751
|
+
if (arguments.length === 1) {
|
|
1752
|
+
functions = name
|
|
1753
|
+
name = null
|
|
1754
|
+
}
|
|
1755
|
+
return this._buildProcessorClass(TreeProcessor, name, functions)
|
|
1756
|
+
},
|
|
1757
|
+
|
|
1758
|
+
/**
|
|
1759
|
+
* Create and return a new TreeProcessor instance with the given prototype functions.
|
|
1760
|
+
*
|
|
1761
|
+
* @param {string} [name] - Optional class name.
|
|
1762
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1763
|
+
* @returns {TreeProcessor}
|
|
1764
|
+
*/
|
|
1765
|
+
newTreeProcessor(name, functions) {
|
|
1766
|
+
if (arguments.length === 1) {
|
|
1767
|
+
functions = name
|
|
1768
|
+
name = null
|
|
1769
|
+
}
|
|
1770
|
+
return new (this.createTreeProcessor(name, functions))()
|
|
1771
|
+
},
|
|
1772
|
+
|
|
1773
|
+
/**
|
|
1774
|
+
* Create a Postprocessor subclass with the given prototype functions.
|
|
1775
|
+
*
|
|
1776
|
+
* @param {string} [name] - Optional class name.
|
|
1777
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1778
|
+
* @returns {typeof Postprocessor}
|
|
1779
|
+
*/
|
|
1780
|
+
createPostprocessor(name, functions) {
|
|
1781
|
+
if (arguments.length === 1) {
|
|
1782
|
+
functions = name
|
|
1783
|
+
name = null
|
|
1784
|
+
}
|
|
1785
|
+
return this._buildProcessorClass(Postprocessor, name, functions)
|
|
1786
|
+
},
|
|
1787
|
+
|
|
1788
|
+
/**
|
|
1789
|
+
* Create and return a new Postprocessor instance with the given prototype functions.
|
|
1790
|
+
*
|
|
1791
|
+
* @param {string} [name] - Optional class name.
|
|
1792
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1793
|
+
* @returns {Postprocessor}
|
|
1794
|
+
*/
|
|
1795
|
+
newPostprocessor(name, functions) {
|
|
1796
|
+
if (arguments.length === 1) {
|
|
1797
|
+
functions = name
|
|
1798
|
+
name = null
|
|
1799
|
+
}
|
|
1800
|
+
return new (this.createPostprocessor(name, functions))()
|
|
1801
|
+
},
|
|
1802
|
+
|
|
1803
|
+
/**
|
|
1804
|
+
* Create an IncludeProcessor subclass with the given prototype functions.
|
|
1805
|
+
*
|
|
1806
|
+
* @param {string} [name] - Optional class name.
|
|
1807
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1808
|
+
* @returns {typeof IncludeProcessor}
|
|
1809
|
+
*/
|
|
1810
|
+
createIncludeProcessor(name, functions) {
|
|
1811
|
+
if (arguments.length === 1) {
|
|
1812
|
+
functions = name
|
|
1813
|
+
name = null
|
|
1814
|
+
}
|
|
1815
|
+
return this._buildProcessorClass(IncludeProcessor, name, functions)
|
|
1816
|
+
},
|
|
1817
|
+
|
|
1818
|
+
/**
|
|
1819
|
+
* Create and return a new IncludeProcessor instance with the given prototype functions.
|
|
1820
|
+
*
|
|
1821
|
+
* @param {string} [name] - Optional class name.
|
|
1822
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1823
|
+
* @returns {IncludeProcessor}
|
|
1824
|
+
*/
|
|
1825
|
+
newIncludeProcessor(name, functions) {
|
|
1826
|
+
if (arguments.length === 1) {
|
|
1827
|
+
functions = name
|
|
1828
|
+
name = null
|
|
1829
|
+
}
|
|
1830
|
+
return new (this.createIncludeProcessor(name, functions))()
|
|
1831
|
+
},
|
|
1832
|
+
|
|
1833
|
+
/**
|
|
1834
|
+
* Create a DocinfoProcessor subclass with the given prototype functions.
|
|
1835
|
+
*
|
|
1836
|
+
* @param {string} [name] - Optional class name.
|
|
1837
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1838
|
+
* @returns {typeof DocinfoProcessor}
|
|
1839
|
+
*/
|
|
1840
|
+
createDocinfoProcessor(name, functions) {
|
|
1841
|
+
if (arguments.length === 1) {
|
|
1842
|
+
functions = name
|
|
1843
|
+
name = null
|
|
1844
|
+
}
|
|
1845
|
+
return this._buildProcessorClass(DocinfoProcessor, name, functions)
|
|
1846
|
+
},
|
|
1847
|
+
|
|
1848
|
+
/**
|
|
1849
|
+
* Create and return a new DocinfoProcessor instance with the given prototype functions.
|
|
1850
|
+
*
|
|
1851
|
+
* @param {string} [name] - Optional class name.
|
|
1852
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1853
|
+
* @returns {DocinfoProcessor}
|
|
1854
|
+
*/
|
|
1855
|
+
newDocinfoProcessor(name, functions) {
|
|
1856
|
+
if (arguments.length === 1) {
|
|
1857
|
+
functions = name
|
|
1858
|
+
name = null
|
|
1859
|
+
}
|
|
1860
|
+
return new (this.createDocinfoProcessor(name, functions))()
|
|
1861
|
+
},
|
|
1862
|
+
|
|
1863
|
+
/**
|
|
1864
|
+
* Create a BlockProcessor subclass with the given prototype functions.
|
|
1865
|
+
*
|
|
1866
|
+
* @param {string} [name] - Optional class name.
|
|
1867
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1868
|
+
* @returns {typeof BlockProcessor}
|
|
1869
|
+
*/
|
|
1870
|
+
createBlockProcessor(name, functions) {
|
|
1871
|
+
if (arguments.length === 1) {
|
|
1872
|
+
functions = name
|
|
1873
|
+
name = null
|
|
1874
|
+
}
|
|
1875
|
+
return this._buildProcessorClass(BlockProcessor, name, functions)
|
|
1876
|
+
},
|
|
1877
|
+
|
|
1878
|
+
/**
|
|
1879
|
+
* Create and return a new BlockProcessor instance with the given prototype functions.
|
|
1880
|
+
*
|
|
1881
|
+
* @param {string} [name] - Optional class name.
|
|
1882
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1883
|
+
* @returns {BlockProcessor}
|
|
1884
|
+
*/
|
|
1885
|
+
newBlockProcessor(name, functions) {
|
|
1886
|
+
if (arguments.length === 1) {
|
|
1887
|
+
functions = name
|
|
1888
|
+
name = null
|
|
1889
|
+
}
|
|
1890
|
+
return new (this.createBlockProcessor(name, functions))()
|
|
1891
|
+
},
|
|
1892
|
+
|
|
1893
|
+
/**
|
|
1894
|
+
* Create an InlineMacroProcessor subclass with the given prototype functions.
|
|
1895
|
+
*
|
|
1896
|
+
* @param {string} [name] - Optional class name.
|
|
1897
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1898
|
+
* @returns {typeof InlineMacroProcessor}
|
|
1899
|
+
*/
|
|
1900
|
+
createInlineMacroProcessor(name, functions) {
|
|
1901
|
+
if (arguments.length === 1) {
|
|
1902
|
+
functions = name
|
|
1903
|
+
name = null
|
|
1904
|
+
}
|
|
1905
|
+
return this._buildProcessorClass(InlineMacroProcessor, name, functions)
|
|
1906
|
+
},
|
|
1907
|
+
|
|
1908
|
+
/**
|
|
1909
|
+
* Create and return a new InlineMacroProcessor instance with the given prototype functions.
|
|
1910
|
+
*
|
|
1911
|
+
* @param {string} [name] - Optional class name.
|
|
1912
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1913
|
+
* @returns {InlineMacroProcessor}
|
|
1914
|
+
*/
|
|
1915
|
+
newInlineMacroProcessor(name, functions) {
|
|
1916
|
+
if (arguments.length === 1) {
|
|
1917
|
+
functions = name
|
|
1918
|
+
name = null
|
|
1919
|
+
}
|
|
1920
|
+
return new (this.createInlineMacroProcessor(name, functions))()
|
|
1921
|
+
},
|
|
1922
|
+
|
|
1923
|
+
/**
|
|
1924
|
+
* Create a BlockMacroProcessor subclass with the given prototype functions.
|
|
1925
|
+
*
|
|
1926
|
+
* @param {string} [name] - Optional class name.
|
|
1927
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1928
|
+
* @returns {typeof BlockMacroProcessor}
|
|
1929
|
+
*/
|
|
1930
|
+
createBlockMacroProcessor(name, functions) {
|
|
1931
|
+
if (arguments.length === 1) {
|
|
1932
|
+
functions = name
|
|
1933
|
+
name = null
|
|
1934
|
+
}
|
|
1935
|
+
return this._buildProcessorClass(BlockMacroProcessor, name, functions)
|
|
1936
|
+
},
|
|
1937
|
+
|
|
1938
|
+
/**
|
|
1939
|
+
* Create and return a new BlockMacroProcessor instance with the given prototype functions.
|
|
1940
|
+
*
|
|
1941
|
+
* @param {string} [name] - Optional class name.
|
|
1942
|
+
* @param {object} [functions] - Methods to mix into the prototype.
|
|
1943
|
+
* @returns {BlockMacroProcessor}
|
|
1944
|
+
*/
|
|
1945
|
+
newBlockMacroProcessor(name, functions) {
|
|
1946
|
+
if (arguments.length === 1) {
|
|
1947
|
+
functions = name
|
|
1948
|
+
name = null
|
|
1949
|
+
}
|
|
1950
|
+
return new (this.createBlockMacroProcessor(name, functions))()
|
|
1951
|
+
},
|
|
1952
|
+
}
|