@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.
Files changed (91) hide show
  1. package/README.md +42 -10
  2. package/build/browser/index.js +24154 -0
  3. package/build/node/index.cjs +24735 -0
  4. package/{dist/css/asciidoctor.css → data/asciidoctor-default.css} +54 -53
  5. package/package.json +53 -100
  6. package/src/abstract_block.js +849 -0
  7. package/src/abstract_node.js +954 -0
  8. package/src/attribute_entry.js +12 -0
  9. package/src/attribute_list.js +380 -0
  10. package/src/block.js +168 -0
  11. package/src/browser/asset.js +22 -0
  12. package/src/browser/reader.js +138 -0
  13. package/src/browser.js +121 -0
  14. package/src/callouts.js +85 -0
  15. package/src/compliance.js +54 -0
  16. package/src/constants.js +665 -0
  17. package/src/convert.js +370 -0
  18. package/src/converter/composite.js +83 -0
  19. package/src/converter/docbook5.js +1031 -0
  20. package/src/converter/html5.js +1899 -0
  21. package/src/converter/manpage.js +935 -0
  22. package/src/converter/template.js +459 -0
  23. package/src/converter.js +478 -0
  24. package/src/data/stylesheet-data.js +2 -0
  25. package/src/document.js +2134 -0
  26. package/src/extensions.js +1952 -0
  27. package/src/footnote.js +28 -0
  28. package/src/helpers.js +355 -0
  29. package/src/index.js +138 -0
  30. package/src/inline.js +158 -0
  31. package/src/list.js +240 -0
  32. package/src/load.js +276 -0
  33. package/src/logging.js +526 -0
  34. package/src/parser.js +3661 -0
  35. package/src/path_resolver.js +472 -0
  36. package/src/reader.js +1755 -0
  37. package/src/rx.js +829 -0
  38. package/src/section.js +354 -0
  39. package/src/stylesheets.js +30 -0
  40. package/src/substitutors.js +2241 -0
  41. package/src/syntaxHighlighter/highlightjs.js +90 -0
  42. package/src/syntaxHighlighter/html_pipeline.js +33 -0
  43. package/src/syntax_highlighter.js +304 -0
  44. package/src/table.js +952 -0
  45. package/src/timings.js +78 -0
  46. package/types/abstract_block.d.ts +343 -0
  47. package/types/abstract_node.d.ts +471 -0
  48. package/types/attribute_entry.d.ts +7 -0
  49. package/types/attribute_list.d.ts +52 -0
  50. package/types/block.d.ts +55 -0
  51. package/types/browser/asset.d.ts +7 -0
  52. package/types/browser/reader.d.ts +29 -0
  53. package/types/callouts.d.ts +36 -0
  54. package/types/compliance.d.ts +23 -0
  55. package/types/constants.d.ts +268 -0
  56. package/types/convert.d.ts +34 -0
  57. package/types/converter/composite.d.ts +20 -0
  58. package/types/converter/docbook5.d.ts +41 -0
  59. package/types/converter/html5.d.ts +51 -0
  60. package/types/converter/manpage.d.ts +59 -0
  61. package/types/converter/template.d.ts +83 -0
  62. package/types/converter.d.ts +150 -0
  63. package/types/data/stylesheet-data.d.ts +2 -0
  64. package/types/document.d.ts +495 -0
  65. package/types/extensions.d.ts +876 -0
  66. package/types/footnote.d.ts +18 -0
  67. package/types/helpers.d.ts +146 -0
  68. package/types/index.d.ts +73 -3731
  69. package/types/inline.d.ts +69 -0
  70. package/types/list.d.ts +114 -0
  71. package/types/load.d.ts +39 -0
  72. package/types/logging.d.ts +187 -0
  73. package/types/parser.d.ts +114 -0
  74. package/types/path_resolver.d.ts +103 -0
  75. package/types/reader.d.ts +184 -0
  76. package/types/rx.d.ts +513 -0
  77. package/types/section.d.ts +122 -0
  78. package/types/stylesheets.d.ts +10 -0
  79. package/types/substitutors.d.ts +208 -0
  80. package/types/syntaxHighlighter/highlightjs.d.ts +33 -0
  81. package/types/syntaxHighlighter/html_pipeline.d.ts +16 -0
  82. package/types/syntax_highlighter.d.ts +167 -0
  83. package/types/table.d.ts +231 -0
  84. package/types/timings.d.ts +25 -0
  85. package/types/tsconfig.json +9 -0
  86. package/LICENSE +0 -21
  87. package/dist/browser/asciidoctor.js +0 -47654
  88. package/dist/browser/asciidoctor.min.js +0 -1452
  89. package/dist/graalvm/asciidoctor.js +0 -47402
  90. package/dist/node/asciidoctor.cjs +0 -21567
  91. package/dist/node/asciidoctor.js +0 -23037
package/src/rx.js ADDED
@@ -0,0 +1,829 @@
1
+ // ESM conversion of rx.rb
2
+ // A collection of regular expression constants used by the parser.
3
+ //
4
+ // Ruby → JavaScript regex engine differences handled here:
5
+ //
6
+ // Ruby \p{Alpha} → JS \p{Alphabetic} (Unicode Binary Property, requires 'u' flag)
7
+ // Ruby \p{Alnum} → JS \p{Alphabetic}\p{N} (inside […]) or [\p{Alphabetic}\p{N}]
8
+ // Ruby \p{Word} → JS \p{Alphabetic}\p{N}\p{Pc} (Letter + Number + Connector Punct)
9
+ // Ruby \p{Blank} → JS \p{Zs}\t (Unicode Space_Separator + tab)
10
+ // Ruby CC_ALL (. with /m) → [\s\S] (no 's' flag needed)
11
+ // Ruby CC_ANY (.) → .
12
+ // Ruby ^ / $ → always line anchors in Ruby; in JS only with 'm' flag
13
+ // Ruby \A / \Z → ^ / $ in JS (string anchors, no 'm' flag)
14
+ //
15
+ // IMPORTANT – 'u' flag and unset back-references:
16
+ // Without 'u': \n to an unset group matches the empty string (Ruby-compatible).
17
+ // With 'u': \n to an unset group fails (stricter).
18
+ // → InlineLinkRx is intentionally kept WITHOUT the 'u' flag because it relies on
19
+ // the (?!\2) trick (negative lookahead of an unset back-reference) to guard the
20
+ // angle-bracket branch. All other patterns use 'u'.
21
+
22
+ // ── Character class string constants ─────────────────────────────────────────
23
+ // CC_* → raw content for insertion INSIDE a character class: [${CC_WORD}]
24
+ // CG_* → complete character class GROUP for standalone use: ${CG_WORD}
25
+ //
26
+ // These are runtime strings whose value contains real regex syntax (single
27
+ // backslashes) so that String.raw`…${CC_WORD}…` produces correct regex source.
28
+
29
+ export const CC_ALL = '[\\s\\S]' // any char including newlines (Ruby . with /m flag)
30
+ export const CC_ANY = '.' // any char except newlines
31
+ export const CC_EOL = '$' // end of line / string
32
+
33
+ // \p{Alphabetic} ≈ Ruby \p{Alpha} – all Unicode alphabetic characters
34
+ export const CC_ALPHA = '\\p{Alphabetic}' // inside [...]
35
+ export const CG_ALPHA = '\\p{Alphabetic}' // standalone (unary property, no brackets needed)
36
+
37
+ // \p{Alphabetic}\p{N} ≈ Ruby \p{Alnum} – alphabetics + all Unicode numbers
38
+ export const CC_ALNUM = '\\p{Alphabetic}\\p{N}' // inside [...]
39
+ export const CG_ALNUM = '[\\p{Alphabetic}\\p{N}]' // standalone group
40
+
41
+ // \p{Alphabetic}\p{N}\p{Pc} ≈ Ruby \p{Word}
42
+ // Letter + Number + Connector Punctuation (underscore, undertie, …)
43
+ export const CC_WORD = '\\p{Alphabetic}\\p{N}\\p{Pc}' // inside [...]
44
+ export const CG_WORD = '[\\p{Alphabetic}\\p{N}\\p{Pc}]' // standalone group
45
+
46
+ // \p{Zs}\t ≈ Ruby \p{Blank} – Unicode Space_Separator category + tab
47
+ export const CG_BLANK = '[\\p{Zs}\\t]' // standalone group
48
+
49
+ // Attribute list pattern fragment: \[([^\[\]]+)\]
50
+ // Ruby: QuoteAttributeListRxt = %(\\[([^\\[\\]]+)\\])
51
+ export const QuoteAttributeListRxt = '\\[([^\\[\\]]+)\\]'
52
+
53
+ // ── Helpers ───────────────────────────────────────────────────────────────────
54
+
55
+ /**
56
+ * Build a regex with the Unicode flag ('u'), enabling \p{…} property escapes.
57
+ * @param {string} src - Regex source string (use String.raw for easy authoring).
58
+ * @param {string} extraFlags - Additional flags, e.g. 'm' for multiline ^ / $
59
+ */
60
+ const ru = (src, extraFlags = '') => new RegExp(src, `u${extraFlags}`)
61
+
62
+ /**
63
+ * Escape all regex metacharacters in str (equivalent to Regexp.escape in Ruby).
64
+ */
65
+ function escapeRegex(str) {
66
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
67
+ }
68
+
69
+ /**
70
+ * Build a lazy-initialised regex map, mirroring Ruby's Hash.new { |h,k| h[k] = … }.
71
+ * Accessing map[key] creates and caches the regex for that key.
72
+ */
73
+ function makeLazyRxMap(buildFn) {
74
+ const cache = new Map()
75
+ return new Proxy(Object.create(null), {
76
+ get(_target, key) {
77
+ if (typeof key !== 'string') return undefined
78
+ if (!cache.has(key)) cache.set(key, buildFn(key))
79
+ return cache.get(key)
80
+ },
81
+ })
82
+ }
83
+
84
+ // ── Document header ───────────────────────────────────────────────────────────
85
+
86
+ /**
87
+ * Matches the author info line immediately following the document title.
88
+ * @example
89
+ * Doc Writer <doc@example.com>
90
+ * Mary_Sue Brontë
91
+ */
92
+ export const AuthorInfoLineRx = ru(
93
+ String.raw`^(${CG_WORD}[${CC_WORD}\-'.]*)(?: +(${CG_WORD}[${CC_WORD}\-'.]*))?` +
94
+ String.raw`(?: +(${CG_WORD}[${CC_WORD}\-'.]*))?(?:[ ]+<([^>]+)>)?$`
95
+ )
96
+
97
+ /**
98
+ * Matches the delimiter that separates multiple authors.
99
+ * @example
100
+ * Doc Writer; Junior Writer
101
+ */
102
+ export const AuthorDelimiterRx = /;(?: |$)/
103
+
104
+ /**
105
+ * Matches the revision info line immediately following the author info line.
106
+ * @example
107
+ * v1.0
108
+ * 2013-01-01
109
+ * v1.0, 2013-01-01: Ring in the new year release
110
+ */
111
+ export const RevisionInfoLineRx =
112
+ /^(?:[^\d{]*(.*?),)? *(?!:)(.*?)(?: *(?!^),?: *(.*))?$/
113
+
114
+ /**
115
+ * Matches the title and volnum in the manpage doctype.
116
+ * @example
117
+ * = asciidoctor(1)
118
+ * = asciidoctor ( 1 )
119
+ */
120
+ export const ManpageTitleVolnumRx = /^(.+?) *\( *(.+?) *\)$/
121
+
122
+ /**
123
+ * Matches the name and purpose in the manpage doctype.
124
+ * @example
125
+ * asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
126
+ */
127
+ export const ManpageNamePurposeRx = /^(.+?) +- +(.+)$/
128
+
129
+ // ── Preprocessor directives ───────────────────────────────────────────────────
130
+
131
+ /**
132
+ * Matches a conditional preprocessor directive (ifdef, ifndef, ifeval, endif).
133
+ * @example
134
+ * ifdef::basebackend-html[]
135
+ * ifeval::["{asciidoctor-version}" >= "0.1.0"]
136
+ * endif::[]
137
+ */
138
+ export const ConditionalDirectiveRx =
139
+ /^(\\)?(ifdef|ifndef|ifeval|endif)::(\S*?(?:([,+])\S*?)?)\[(.+)?\]$/
140
+
141
+ /**
142
+ * Matches a restricted (safe) eval expression.
143
+ * @example
144
+ * "{asciidoctor-version}" >= "0.1.0"
145
+ */
146
+ export const EvalExpressionRx = /^(.+?) *([=!><]=|[><]) *(.+)$/
147
+
148
+ /**
149
+ * Matches an include preprocessor directive.
150
+ * @example
151
+ * include::chapter1.ad[]
152
+ * include::example.txt[lines=1;2;5..10]
153
+ */
154
+ export const IncludeDirectiveRx =
155
+ /^(\\)?include::([^\s[](?:[^[]*[^\s[])?)\[(.+)?\]$/
156
+
157
+ /**
158
+ * Matches a trailing tag directive in an include file.
159
+ *
160
+ * NOTE: 'm' flag required so that $ matches end-of-line (not only end-of-string) in JS.
161
+ * NOTE: accounts for \r in Windows line endings.
162
+ * @example
163
+ * // tag::try-catch[]
164
+ * // end::try-catch[]
165
+ */
166
+ export const TagDirectiveRx = /\b(?:tag|(e)nd)::(\S+?)\[\](?=$|[ \r])/m
167
+
168
+ // ── Attribute entries and references ─────────────────────────────────────────
169
+
170
+ /**
171
+ * Matches a document attribute entry.
172
+ * @example
173
+ * :foo: bar
174
+ * :First Name: Dan
175
+ * :sectnums!:
176
+ */
177
+ export const AttributeEntryRx = ru(
178
+ String.raw`^:(!?${CG_WORD}[^:]*):(?:[ \t]+(.*))?$`
179
+ )
180
+
181
+ /** Matches invalid characters in an attribute name. */
182
+ export const InvalidAttributeNameCharsRx = ru(String.raw`[^${CC_WORD}\-]`)
183
+
184
+ /**
185
+ * Matches a pass inline macro surrounding an attribute entry value.
186
+ *
187
+ * NOTE: ^ / $ are string anchors here (no 'm' flag). [\s\S]* allows multi-line values.
188
+ * @example
189
+ * pass:[text]
190
+ * pass:a[{a} {b} {c}]
191
+ */
192
+ export const AttributeEntryPassMacroRx =
193
+ /^pass:([a-z]+(?:,[a-z-]+)*)?\[([\s\S]*)\]$/
194
+
195
+ /**
196
+ * Matches an inline attribute reference.
197
+ * @example
198
+ * {foobar}
199
+ * {counter:sequence-name:1}
200
+ * {set:foo:bar}
201
+ */
202
+ export const AttributeReferenceRx = ru(
203
+ String.raw`(\\)?\{(${CG_WORD}[${CC_WORD}\-]*|(set|counter2?):.*?)(\\)?\}`
204
+ )
205
+
206
+ // ── Paragraphs and delimited blocks ──────────────────────────────────────────
207
+
208
+ /**
209
+ * Matches an anchor (id + optional reference text) on a line above a block.
210
+ * @example
211
+ * [[idname]]
212
+ * [[idname,Reference Text]]
213
+ */
214
+ export const BlockAnchorRx = ru(
215
+ String.raw`^\[\[(?:|([${CC_ALPHA}_:][${CC_WORD}\-:.]*)(?:, *(.+))?)\]\]$`
216
+ )
217
+
218
+ /**
219
+ * Matches an attribute list above a block element.
220
+ * @example
221
+ * [quote, Adam Smith, Wealth of Nations]
222
+ * [{lead}]
223
+ */
224
+ export const BlockAttributeListRx = ru(
225
+ String.raw`^\[(|[${CC_WORD}.#%{,"'].*)\]$`
226
+ )
227
+
228
+ /** Combined pattern matching either a block anchor or a block attribute list. */
229
+ export const BlockAttributeLineRx = ru(
230
+ String.raw`^\[(?:|[${CC_WORD}.#%{,"'].*|\[(?:|[${CC_ALPHA}_:][${CC_WORD}\-:.]*(?:, *.+)?)\])\]$`
231
+ )
232
+
233
+ /**
234
+ * Matches a title above a block.
235
+ * @example
236
+ * .Title goes here
237
+ */
238
+ export const BlockTitleRx = /^\.(\.?[^ \t.].*)$/
239
+
240
+ /**
241
+ * Matches an admonition label at the start of a paragraph.
242
+ * @example
243
+ * NOTE: Just a little note.
244
+ * TIP: Don't forget!
245
+ */
246
+ export const AdmonitionParagraphRx =
247
+ /^(NOTE|TIP|IMPORTANT|WARNING|CAUTION):[ \t]+/
248
+
249
+ /**
250
+ * Matches a literal paragraph (line preceded by at least one space or tab).
251
+ * @example
252
+ * <SPACE>Foo
253
+ * <TAB>Foo
254
+ */
255
+ export const LiteralParagraphRx = /^([ \t]+.*)$/
256
+
257
+ // ── Section titles ────────────────────────────────────────────────────────────
258
+
259
+ /**
260
+ * Matches an Atx (single-line) section title.
261
+ * @example
262
+ * == Foo
263
+ * == Foo ==
264
+ */
265
+ export const AtxSectionTitleRx = /^(=={0,5})[ \t]+(.+?)(?:[ \t]+\1)?$/
266
+
267
+ /** Extended Atx section title supporting the Markdown variant (#). */
268
+ export const ExtAtxSectionTitleRx =
269
+ /^(=={0,5}|##{0,5})[ \t]+(.+?)(?:[ \t]+\1)?$/
270
+
271
+ /**
272
+ * Matches the first line of a Setext (two-line) section title.
273
+ * Must not start with '.' and must contain at least one alphanumeric character.
274
+ */
275
+ export const SetextSectionTitleRx = ru(String.raw`^((?!\.).*?${CG_ALNUM}.*)$`)
276
+
277
+ /**
278
+ * Matches an anchor inside a section title.
279
+ * @example
280
+ * Section Title [[idname]]
281
+ * Section Title [[idname,Reference Text]]
282
+ */
283
+ export const InlineSectionAnchorRx = ru(
284
+ String.raw` (\\)?\[\[([${CC_ALPHA}_:][${CC_WORD}\-:.]*)(?:, *(.+?))?\]\]$`
285
+ )
286
+
287
+ /**
288
+ * Matches invalid ID characters in a section title.
289
+ * NOTE: Uppercase excluded; expression is run only on a lowercase string.
290
+ */
291
+ export const InvalidSectionIdCharsRx = ru(
292
+ String.raw`<[^>]+>|&(?:[a-z][a-z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-f][\da-f][\da-f]{0,3});|[^ ${CC_WORD}\-.]+?`
293
+ )
294
+
295
+ /** Matches an explicit section level style like sect1. */
296
+ export const SectionLevelStyleRx = /^sect\d$/
297
+
298
+ // ── Lists ─────────────────────────────────────────────────────────────────────
299
+
300
+ /**
301
+ * Detects the start of any list item.
302
+ *
303
+ * NOTE: Check only up to the blank character since non-whitespace follows.
304
+ * IMPORTANT: Must agree with the per-list-type regexps or the parser will hang.
305
+ */
306
+ export const AnyListRx =
307
+ /^(?:[ \t]*(?:-|\*\**|\.\.*|\u2022|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|(?!\/\/[^/])[ \t]*[^ \t].*?(?::::{0,2}|;;)(?:$|[ \t])|<(?:\d+|\.)>[ \t])/
308
+
309
+ /**
310
+ * Matches an unordered list item.
311
+ * @example
312
+ * * Foo
313
+ * - Foo
314
+ */
315
+ export const UnorderedListRx = /^[ \t]*(-|\*\**|\u2022)[ \t]+([\s\S]*)$/
316
+
317
+ /**
318
+ * Matches an ordered list item.
319
+ * @example
320
+ * . Foo 1. Foo a. Foo I. Foo
321
+ */
322
+ export const OrderedListRx =
323
+ /^[ \t]*(\.\.*|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]+([\s\S]*)$/
324
+
325
+ /** Ordinal pattern for each ordered list type. */
326
+ export const OrderedListMarkerRxMap = {
327
+ arabic: /\d+\./,
328
+ loweralpha: /[a-z]\./,
329
+ lowerroman: /[ivx]+\)/,
330
+ upperalpha: /[A-Z]\./,
331
+ upperroman: /[IVX]+\)/,
332
+ }
333
+
334
+ /**
335
+ * Matches a description list entry.
336
+ * @example
337
+ * foo::
338
+ * foo:: The metasyntactic variable …
339
+ */
340
+ export const DescriptionListRx =
341
+ /^(?!\/\/[^/])[ \t]*([^ \t].*?)(:::{0,2}|;;)(?:$|[ \t]+([\s\S]*)$)/
342
+
343
+ /** Matches a sibling description list item (excluding the delimiter given by key). */
344
+ export const DescriptionListSiblingRx = {
345
+ '::': /^(?!\/\/[^/])[ \t]*([^ \t].*?[^:]|[^ \t:])(::)(?:$|[ \t]+([\s\S]*)$)/,
346
+ ':::':
347
+ /^(?!\/\/[^/])[ \t]*([^ \t].*?[^:]|[^ \t:])(:::)(?:$|[ \t]+([\s\S]*)$)/,
348
+ '::::':
349
+ /^(?!\/\/[^/])[ \t]*([^ \t].*?[^:]|[^ \t:])(::::)(?:$|[ \t]+([\s\S]*)$)/,
350
+ ';;': /^(?!\/\/[^/])[ \t]*([^ \t].*?)(;;)(?:$|[ \t]+([\s\S]*)$)/,
351
+ }
352
+
353
+ /**
354
+ * Matches a callout list item.
355
+ * @example
356
+ * <1> Explanation
357
+ * <.> Explanation with automatic number
358
+ */
359
+ export const CalloutListRx = /^<(\d+|\.)>[ \t]+([\s\S]*)$/
360
+
361
+ /**
362
+ * Matches a callout reference inside literal text (applied line-by-line).
363
+ *
364
+ * Group layout:
365
+ * 1 – optional line-comment prefix (// # -- ;;)
366
+ * 2 – backslash escape
367
+ * 3 – optional XML comment delimiter (--)
368
+ * 4 – callout number or dot
369
+ */
370
+ export const CalloutExtractRx =
371
+ /((?:\/\/|#|--|;;) ?)?(\\)?<!?(|--)(\d+|\.)\3>(?=(?: ?\\?<!?\3(?:\d+|\.)\3>)*$)/m
372
+
373
+ /**
374
+ * Template string for CalloutExtractRxMap entries.
375
+ * Runtime value: (\\)?<()(\d+|\.)>(?=(?: ?\\?<(?:\d+|\.)>)*$)
376
+ * Note: 'm' flag added so $ matches end-of-line (Ruby regex default behaviour).
377
+ */
378
+ export const CalloutExtractRxt =
379
+ '(\\\\)?<()([\\d]+|\\.)>(?=(?: ?\\\\?<(?:\\d+|\\.)>)*$)'
380
+
381
+ /**
382
+ * Lazy map: line-comment string → callout-extract regex.
383
+ * Mirrors Ruby: Hash.new { |h,k| h[k] = /(prefix)?#{CalloutExtractRxt}/ }
384
+ */
385
+ export const CalloutExtractRxMap = makeLazyRxMap((key) => {
386
+ const prefix = key ? `(${escapeRegex(key)} ?)?` : '()?'
387
+ return new RegExp(`${prefix}${CalloutExtractRxt}`, 'm')
388
+ })
389
+
390
+ /** Matches a callout reference when scanning source (special chars NOT yet replaced). */
391
+ export const CalloutScanRx =
392
+ /\\?<!?(|--)(\d+|\.)\1>(?=(?: ?\\?<!?\1(?:\d+|\.)\1>)*$)/m
393
+
394
+ /**
395
+ * Matches a callout reference in HTML output (special chars already replaced).
396
+ *
397
+ * Group layout mirrors CalloutExtractRx.
398
+ * Note: 'm' flag so $ matches end-of-line, matching Ruby regex semantics.
399
+ */
400
+ export const CalloutSourceRx =
401
+ /((?:\/\/|#|--|;;) ?)?(\\)?&lt;!?(|--)(\d+|\.)\3&gt;(?=(?: ?\\?&lt;!?\3(?:\d+|\.)\3&gt;)*$)/m
402
+
403
+ /**
404
+ * Template string for CalloutSourceRxMap entries.
405
+ * Runtime value: (\\)?&lt;()(\d+|\.)&gt;(?=(?: ?\\?&lt;(?:\d+|\.)&gt;)*$)
406
+ */
407
+ export const CalloutSourceRxt =
408
+ '(\\\\)?&lt;()([\\d]+|\\.)&gt;(?=(?: ?\\\\?&lt;(?:\\d+|\\.)&gt;)*$)'
409
+
410
+ /** Lazy map: line-comment string → callout-source regex. */
411
+ export const CalloutSourceRxMap = makeLazyRxMap((key) => {
412
+ const prefix = key ? `(${escapeRegex(key)} ?)?` : '()?'
413
+ return new RegExp(`${prefix}${CalloutSourceRxt}`, 'm')
414
+ })
415
+
416
+ /** Dynamic map from list context to its regex. */
417
+ export const ListRxMap = {
418
+ ulist: UnorderedListRx,
419
+ olist: OrderedListRx,
420
+ dlist: DescriptionListRx,
421
+ colist: CalloutListRx,
422
+ }
423
+
424
+ // ── Tables ────────────────────────────────────────────────────────────────────
425
+
426
+ /**
427
+ * Parses the column test (colspec) for a table.
428
+ * @example
429
+ * 1*h,2*,^3e
430
+ */
431
+ export const ColumnSpecRx =
432
+ /^(?:(\d+)\*)?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?(\d+%?|~)?([a-z])?$/
433
+
434
+ /**
435
+ * Parses the start of a cell test.
436
+ * @example
437
+ * 2.3+<.>m
438
+ */
439
+ export const CellSpecStartRx =
440
+ /^[ \t]*(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
441
+
442
+ /** Parses the end of a cell test. */
443
+ export const CellSpecEndRx =
444
+ /[ \t]+(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
445
+
446
+ // ── Block macros ──────────────────────────────────────────────────────────────
447
+
448
+ /**
449
+ * Matches the custom block macro pattern.
450
+ * @example
451
+ * gist::123456[]
452
+ */
453
+ export const CustomBlockMacroRx = ru(
454
+ String.raw`^(${CG_WORD}[${CC_WORD}\-]*)::(|\S|\S.*?\S)\[(.+)?\]$`
455
+ )
456
+
457
+ /**
458
+ * Matches an image, video or audio block macro.
459
+ * @example
460
+ * image::filename.png[Caption]
461
+ * video::http://youtube.com/12345[Cats vs Dogs]
462
+ */
463
+ export const BlockMediaMacroRx = /^(image|video|audio)::(\S|\S.*?\S)\[(.+)?\]$/
464
+
465
+ /**
466
+ * Matches the TOC block macro.
467
+ * @example
468
+ * toc::[]
469
+ * toc::[levels=2]
470
+ */
471
+ export const BlockTocMacroRx = /^toc::\[(.+)?\]$/
472
+
473
+ // ── Inline macros ─────────────────────────────────────────────────────────────
474
+
475
+ /**
476
+ * Matches an anchor (id + optional reference text) in the flow of text.
477
+ *
478
+ * Group layout:
479
+ * 1 – backslash escape
480
+ * 2 – id (double-bracket form)
481
+ * 3 – reftext (double-bracket form)
482
+ * 4 – id (anchor: macro form)
483
+ * 5 – reftext (anchor: macro form)
484
+ * @example
485
+ * [[idname]]
486
+ * [[idname,Reference Text]]
487
+ * anchor:idname[]
488
+ * anchor:idname[Reference Text]
489
+ */
490
+ export const InlineAnchorRx = ru(
491
+ String.raw`(\\)?(?:\[\[([${CC_ALPHA}_:][${CC_WORD}\-:.]*)(?:, *(.+?))? ?\]\]` +
492
+ String.raw`|anchor:([${CC_ALPHA}_:][${CC_WORD}\-:.]*)\[(?:\]|([\s\S]*?[^\\])\]))`
493
+ )
494
+
495
+ /** Scans for a non-escaped anchor in the flow of text. */
496
+ export const InlineAnchorScanRx = ru(
497
+ String.raw`(?:^|[^\\\[])\[\[([${CC_ALPHA}_:][${CC_WORD}\-:.]*)(?:, *(.+?))? ?\]\]` +
498
+ String.raw`|(?:^|[^\\])anchor:([${CC_ALPHA}_:][${CC_WORD}\-:.]*)\[(?:\]|(.*?[^\\])\])`
499
+ )
500
+
501
+ /** Scans for a leading, non-escaped anchor. */
502
+ export const LeadingInlineAnchorRx = ru(
503
+ String.raw`^\[\[([${CC_ALPHA}_:][${CC_WORD}\-:.]*)(?:, *(.+?))?\]\]`
504
+ )
505
+
506
+ /**
507
+ * Matches a bibliography anchor at the start of a list item.
508
+ * @example
509
+ * [[[Fowler_1997]]] Fowler M. ...
510
+ */
511
+ export const InlineBiblioAnchorRx = ru(
512
+ String.raw`^\[\[\[([${CC_ALPHA}_:][${CC_WORD}\-:.]*)(?:, *(.+?))?\]\]\]`
513
+ )
514
+
515
+ /**
516
+ * Matches an inline e-mail address.
517
+ * @example
518
+ * doc.writer@example.com
519
+ */
520
+ export const InlineEmailRx = ru(
521
+ String.raw`([\\>:/])?${CG_WORD}(?:&amp;|[${CC_WORD}\-.%+])*` +
522
+ String.raw`@${CG_ALNUM}[${CC_ALNUM}_\-.]*\.[a-zA-Z]{2,5}\b`
523
+ )
524
+
525
+ /**
526
+ * Matches an inline footnote macro (may span multiple lines).
527
+ *
528
+ * NOTE: [\s\S]*? allows multiline content (Ruby /m + CC_ALL).
529
+ * NOTE: (?!</a>) avoids matching inside an anchor tag.
530
+ * @example
531
+ * footnote:[text]
532
+ * footnote:id[text]
533
+ * footnoteref:[id,text] (legacy)
534
+ */
535
+ export const InlineFootnoteMacroRx = ru(
536
+ String.raw`\\?footnote(?:(ref):|:([${CC_WORD}\-]+)?)\[(?:|([\s\S]*?[^\\]))\](?!</a>)`
537
+ )
538
+
539
+ /**
540
+ * Matches an image or icon inline macro (may span multiple lines).
541
+ * @example
542
+ * image:filename.png[Alt Text]
543
+ * icon:github[large]
544
+ */
545
+ export const InlineImageMacroRx =
546
+ /\\?i(?:mage|con):([^:\s[](?:[^\n[]*[^\s[])?)\[(|[\s\S]*?[^\\])\]/
547
+
548
+ /**
549
+ * Matches an indexterm inline macro (may span multiple lines).
550
+ * @example
551
+ * indexterm:[Tigers,Big cats]
552
+ * (((Tigers,Big cats)))
553
+ * ((Tigers))
554
+ */
555
+ export const InlineIndextermMacroRx =
556
+ /\\?(?:(indexterm2?):\[([\s\S]*?[^\\])\]|\(\(([\s\S]+?)\)\)(?!\)))/
557
+
558
+ /**
559
+ * Matches either the kbd or btn inline macro (may span multiple lines).
560
+ * @example
561
+ * kbd:[F3] kbd:[Ctrl+Shift+T] btn:[Save]
562
+ */
563
+ export const InlineKbdBtnMacroRx = /(\\)?(kbd|btn):\[([\s\S]*?[^\\])\]/
564
+
565
+ /**
566
+ * Matches an implicit link and the link inline macro.
567
+ *
568
+ * NOTE: This is the Opal/JS variant of the pattern.
569
+ * Group 2 captures ':' inside a lookahead from the &lt;<protocol> branch.
570
+ * (?!\2) then guards the &gt;-terminated branch: when group 2 IS ':',
571
+ * the guard prevents matching '://' at the start of the path; when group 2
572
+ * is UNSET (other prefix branches), (?!\2) expands to (?!"") which ALWAYS
573
+ * FAILS – correctly preventing the &gt; branch for non-&lt; prefixes.
574
+ *
575
+ * *** NO 'u' FLAG: the (?!\2) guard relies on unset back-references matching
576
+ * the empty string, which only holds in non-Unicode mode. ***
577
+ *
578
+ * Group layout:
579
+ * 1 – prefix (^, link:, blank, \\?&lt; or punctuation)
580
+ * 2 – ':' captured by lookahead (only when prefix is \\?&lt;)
581
+ * 3 – URL scheme + ://
582
+ * 4 – target before [ (formal macro)
583
+ * 5 – attrlist (formal macro, may be empty)
584
+ * 6 – target before &gt; (angle-bracket autolink, requires &lt; prefix)
585
+ * 7 – target (bare autolink)
586
+ * 8 – last non-terminating char of bare target
587
+ * @example
588
+ * https://github.com
589
+ * https://github.com[GitHub]
590
+ * <https://github.com>
591
+ * link:https://github.com[]
592
+ */
593
+ export const InlineLinkRx =
594
+ /(^|link:|[ \t\u00a0]|\\?&lt;(?=\\?(?:https?|file|ftp|irc)(:))|[>()[\];"'])(\\?(?:https?|file|ftp|irc):\/\/)(?:([^\s[\]]+)\[(|[\s\S]*?[^\\])\]|(?!\2)([^\s]+?)&gt;|([^\s[\]<]*([^\s,.?![\]<)])))/m
595
+
596
+ /**
597
+ * Matches a link or e-mail inline macro (may span multiple lines).
598
+ * @example
599
+ * link:path[label]
600
+ * mailto:doc.writer@example.com[]
601
+ */
602
+ export const InlineLinkMacroRx =
603
+ /\\?(?:link|(mailto)):(|[^:\s[][^\s[]*)\[(|[\s\S]*?[^\\])\]/
604
+
605
+ /** Matches the name of a macro. */
606
+ export const MacroNameRx = ru(String.raw`^${CG_WORD}[${CC_WORD}\-]*$`)
607
+
608
+ /**
609
+ * Matches a stem (and alternatives) inline macro (may span multiple lines).
610
+ * @example
611
+ * stem:[x != 0]
612
+ * latexmath:[\sqrt{4} = 2]
613
+ */
614
+ export const InlineStemMacroRx =
615
+ /\\?(stem|(?:latex|ascii)math):([a-z]+(?:,[a-z-]+)*)?\[([\s\S]*?[^\\])\]/
616
+
617
+ /**
618
+ * Matches a menu inline macro (may span multiple lines).
619
+ * @example
620
+ * menu:File[Save As...]
621
+ * menu:View[Page Style > No Style]
622
+ */
623
+ export const InlineMenuMacroRx = ru(
624
+ String.raw`\\?menu:(${CG_WORD}|[${CC_WORD}&][^\n\[]*[^\s\[])` +
625
+ String.raw`\[ *(?:|([\s\S]*?[^\\]))\]`
626
+ )
627
+
628
+ /**
629
+ * Matches an implicit menu inline macro.
630
+ * @example
631
+ * "File > New..."
632
+ */
633
+ export const InlineMenuRx = ru(
634
+ String.raw`\\?"([${CC_WORD}&][^"]*?[ \n]+&gt;[ \n]+[^"]*)"`
635
+ )
636
+
637
+ /**
638
+ * Matches an inline passthrough (may span multiple lines).
639
+ *
640
+ * Group layout (false / non-compat):
641
+ * 1 – preceding context or escape boundary
642
+ * 2 – '[' captured by lookahead (back-reference trick for attribute list detection)
643
+ * 3 – x- / 'attrlist x-' content
644
+ * 4 – QuoteAttributeListRxt content
645
+ * 5 – optional backslash before opening delimiter
646
+ * 6 – full quoted span (including delimiters)
647
+ * 7 – opening/closing delimiter (+ or `)
648
+ * 8 – span content
649
+ *
650
+ * Group layout (true / compat):
651
+ * 1 – preceding char or start-of-line
652
+ * 2 – ($) end-of-string sentinel (never matches in inline text, preserves group count)
653
+ * 3 – empty group paired with sentinel
654
+ * 4 – QuoteAttributeListRxt content
655
+ * 5 – optional backslash before opening delimiter
656
+ * 6 – full quoted span
657
+ * 7 – opening/closing delimiter (`)
658
+ * 8 – span content
659
+ *
660
+ * NOTE: 'u' flag used, but the 'm' flag is also set so that ^ is a line anchor.
661
+ * Unset optional back-references (\5?) with 'u' flag: the '?' quantifier
662
+ * allows 0 occurrences, so the match continues even when the group is unset.
663
+ * @example
664
+ * +text+
665
+ * [x-]+text+
666
+ * `text` (compat only)
667
+ */
668
+ export const InlinePassRx = {
669
+ false: [
670
+ '+',
671
+ '-]',
672
+ ru(
673
+ String.raw`((?:^|[^${CC_WORD};:\\])(?=(\[)|\+)|\\(?=\[)|(?=\\\+))` +
674
+ String.raw`(?:\2(x-|[^\[\]]+ x-)\]|(?:` +
675
+ QuoteAttributeListRxt +
676
+ String.raw`)?(?=(\\)?\+))` +
677
+ String.raw`(\5?(\+|` +
678
+ '`' +
679
+ String.raw`)(\S|\S` +
680
+ CC_ALL +
681
+ String.raw`*?\S)\7)(?!${CG_WORD})`,
682
+ 'm'
683
+ ),
684
+ ],
685
+ true: [
686
+ '`',
687
+ null,
688
+ ru(
689
+ String.raw`(^|[^` +
690
+ '`' +
691
+ String.raw`${CC_WORD}])(?:($)()|(?:` +
692
+ QuoteAttributeListRxt +
693
+ String.raw`)(?=(\\?)))?` +
694
+ String.raw`(\5?(` +
695
+ '`' +
696
+ String.raw`)([^` +
697
+ '`' +
698
+ String.raw`\s]|[^` +
699
+ '`' +
700
+ String.raw`\s]` +
701
+ CC_ALL +
702
+ String.raw`*?\S)\7)(?![` +
703
+ '`' +
704
+ String.raw`${CC_WORD}])`,
705
+ 'm'
706
+ ),
707
+ ],
708
+ }
709
+
710
+ /**
711
+ * Matches several variants of the passthrough inline macro (may span multiple lines).
712
+ *
713
+ * Group layout:
714
+ * 1 – optional backslash before attribute list
715
+ * 2 – attribute list content (QuoteAttributeListRxt)
716
+ * 3 – backslash(es) before delimiter (0–2)
717
+ * 4 – delimiter: +++, ++, or $$
718
+ * 5 – content between delimiters (\4 closes)
719
+ * 6 – backslash before pass: macro
720
+ * 7 – subs list after pass:
721
+ * 8 – content inside pass:[…]
722
+ * @example
723
+ * +++text+++
724
+ * $$text$$
725
+ * pass:quotes[text]
726
+ * pass:[]
727
+ */
728
+ export const InlinePassMacroRx = new RegExp(
729
+ `(?:(?:(\\\\?)${QuoteAttributeListRxt})?(\\\\{0,2})(\\+\\+\\+?|\\$\\$)([\\s\\S]*?)\\4|(\\\\?)pass:([a-z]+(?:,[a-z-]+)*)?\\[(|[\\s\\S]*?[^\\\\])\\])`
730
+ )
731
+
732
+ /**
733
+ * Matches an xref (cross-reference) inline macro (may span multiple lines).
734
+ *
735
+ * NOTE: { included to support targets beginning with an attribute reference.
736
+ * NOTE: Special characters are already entity-encoded in the matched text.
737
+ *
738
+ * Group layout:
739
+ * 1 – target of <<…>> form
740
+ * 2 – target of xref:…[] form
741
+ * 3 – link text inside xref:…[…]
742
+ * @example
743
+ * <<id,reftext>>
744
+ * xref:id[reftext]
745
+ */
746
+ export const InlineXrefMacroRx = ru(
747
+ String.raw`\\?(?:&lt;&lt;([${CC_WORD}#/.:{]` +
748
+ CC_ALL +
749
+ String.raw`*?)&gt;&gt;` +
750
+ String.raw`|xref:([${CC_WORD}#/.:{]` +
751
+ CC_ALL +
752
+ String.raw`*?)\[(?:\]|(` +
753
+ CC_ALL +
754
+ String.raw`*?[^\\])\]))`
755
+ )
756
+
757
+ // ── Layout ────────────────────────────────────────────────────────────────────
758
+
759
+ /**
760
+ * Matches a trailing + preceded by at least one space, forcing a hard line break.
761
+ *
762
+ * NOTE: 'm' flag required so that ^ / $ are line anchors (not string anchors) in JS.
763
+ * @example
764
+ * Humpty Dumpty sat on a wall, +
765
+ * Humpty Dumpty had a great fall.
766
+ */
767
+ export const HardLineBreakRx = /^(.*) \+$/m
768
+
769
+ /**
770
+ * Matches a Markdown horizontal rule.
771
+ * @example
772
+ * --- or - - -
773
+ * *** or * * *
774
+ * ___ or _ _ _
775
+ */
776
+ export const MarkdownThematicBreakRx = /^ {0,3}([-*_])( *)\1\2\1$/
777
+
778
+ /**
779
+ * Matches an AsciiDoc or Markdown horizontal rule, or an AsciiDoc page break.
780
+ * @example
781
+ * ''' <<< --- *** ___
782
+ */
783
+ export const ExtLayoutBreakRx = /^(?:'{3,}|<{3,}|([-*_])( *)\1\2\1)$/
784
+
785
+ // ── General ───────────────────────────────────────────────────────────────────
786
+
787
+ /** Matches consecutive blank lines. */
788
+ export const BlankLineRx = /\n{2,}/
789
+
790
+ /**
791
+ * Matches whitespace escaped by a backslash.
792
+ * @example
793
+ * three\ blind\ mice
794
+ */
795
+ export const EscapedSpaceRx = /\\([ \t\n])/g
796
+
797
+ /** Detects text that may contain replaceable characters. */
798
+ export const ReplaceableTextRx = /[&']|--|\.\.\.|\([CRT]M?\)/
799
+
800
+ /**
801
+ * Matches a whitespace delimiter (space, tab, newline).
802
+ * Replicates the parsing rules of Ruby %w strings.
803
+ *
804
+ * TODO: Replace with /(?<!\\)[ \t\n]+/ when lookbehind is universally available.
805
+ */
806
+ export const SpaceDelimiterRx = /([^\\])[ \t\n]+/g
807
+
808
+ /** Matches a + or - modifier in a subs list. */
809
+ export const SubModifierSniffRx = /[+-]/
810
+
811
+ /**
812
+ * Matches one or more consecutive digits at the end of a line.
813
+ * @example
814
+ * docbook5 html5
815
+ */
816
+ export const TrailingDigitsRx = /\d+$/
817
+
818
+ /**
819
+ * Detects strings that resemble URIs.
820
+ *
821
+ * NOTE: ^ is used as a string-start anchor (no 'm' flag), equivalent to Ruby \A.
822
+ * NOTE: Does NOT match Windows paths like c:/sample.adoc or c:\sample.adoc.
823
+ * @example
824
+ * http://domain https://domain file:///path data:info
825
+ */
826
+ export const UriSniffRx = ru(String.raw`^${CG_ALPHA}[${CC_ALNUM}.+\-]+:\/{0,2}`)
827
+
828
+ /** Detects XML tags. */
829
+ export const XmlSanitizeRx = /<[^>]+>/