@wdprlib/parser 3.1.2 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/index.cjs +295 -118
  2. package/dist/index.js +272 -95
  3. package/package.json +5 -3
  4. package/src/index.ts +163 -0
  5. package/src/lexer/index.ts +20 -0
  6. package/src/lexer/lexer.ts +687 -0
  7. package/src/lexer/tokens.ts +141 -0
  8. package/src/parser/constants.ts +173 -0
  9. package/src/parser/depth.ts +251 -0
  10. package/src/parser/index.ts +18 -0
  11. package/src/parser/parse.ts +315 -0
  12. package/src/parser/postprocess/divAdjacentParagraph.ts +76 -0
  13. package/src/parser/postprocess/index.ts +15 -0
  14. package/src/parser/postprocess/spanStrip.ts +697 -0
  15. package/src/parser/preprocess/expr.ts +265 -0
  16. package/src/parser/preprocess/index.ts +38 -0
  17. package/src/parser/preprocess/typography.ts +67 -0
  18. package/src/parser/preprocess/utils.ts +250 -0
  19. package/src/parser/preprocess/whitespace.ts +111 -0
  20. package/src/parser/rules/block/align.ts +282 -0
  21. package/src/parser/rules/block/bibliography.ts +359 -0
  22. package/src/parser/rules/block/block-list.ts +689 -0
  23. package/src/parser/rules/block/blockquote.ts +238 -0
  24. package/src/parser/rules/block/center.ts +87 -0
  25. package/src/parser/rules/block/clear-float.ts +75 -0
  26. package/src/parser/rules/block/code.ts +187 -0
  27. package/src/parser/rules/block/collapsible.ts +337 -0
  28. package/src/parser/rules/block/comment.ts +73 -0
  29. package/src/parser/rules/block/content-separator.ts +79 -0
  30. package/src/parser/rules/block/definition-list.ts +270 -0
  31. package/src/parser/rules/block/div.ts +400 -0
  32. package/src/parser/rules/block/embed-block.ts +153 -0
  33. package/src/parser/rules/block/footnoteblock.ts +200 -0
  34. package/src/parser/rules/block/heading.ts +142 -0
  35. package/src/parser/rules/block/horizontal-rule.ts +61 -0
  36. package/src/parser/rules/block/html.ts +222 -0
  37. package/src/parser/rules/block/iframe.ts +239 -0
  38. package/src/parser/rules/block/iftags.ts +150 -0
  39. package/src/parser/rules/block/include.ts +179 -0
  40. package/src/parser/rules/block/index.ts +127 -0
  41. package/src/parser/rules/block/list.ts +244 -0
  42. package/src/parser/rules/block/math.ts +183 -0
  43. package/src/parser/rules/block/module/backlinks/index.ts +31 -0
  44. package/src/parser/rules/block/module/backlinks/types.ts +21 -0
  45. package/src/parser/rules/block/module/categories/index.ts +34 -0
  46. package/src/parser/rules/block/module/categories/types.ts +21 -0
  47. package/src/parser/rules/block/module/css/index.ts +37 -0
  48. package/src/parser/rules/block/module/iftags/condition.ts +109 -0
  49. package/src/parser/rules/block/module/iftags/index.ts +26 -0
  50. package/src/parser/rules/block/module/iftags/preprocess.ts +140 -0
  51. package/src/parser/rules/block/module/iftags/resolve.ts +73 -0
  52. package/src/parser/rules/block/module/iftags/types.ts +63 -0
  53. package/src/parser/rules/block/module/include/index.ts +20 -0
  54. package/src/parser/rules/block/module/include/resolve.ts +556 -0
  55. package/src/parser/rules/block/module/index.ts +122 -0
  56. package/src/parser/rules/block/module/join/index.ts +34 -0
  57. package/src/parser/rules/block/module/join/types.ts +23 -0
  58. package/src/parser/rules/block/module/listpages/compiler.ts +453 -0
  59. package/src/parser/rules/block/module/listpages/extract.ts +410 -0
  60. package/src/parser/rules/block/module/listpages/index.ts +83 -0
  61. package/src/parser/rules/block/module/listpages/normalize.ts +390 -0
  62. package/src/parser/rules/block/module/listpages/parser.ts +106 -0
  63. package/src/parser/rules/block/module/listpages/resolve.ts +130 -0
  64. package/src/parser/rules/block/module/listpages/types.ts +513 -0
  65. package/src/parser/rules/block/module/listpages/url-resolver.ts +186 -0
  66. package/src/parser/rules/block/module/listusers/compiler.ts +77 -0
  67. package/src/parser/rules/block/module/listusers/extract.ts +45 -0
  68. package/src/parser/rules/block/module/listusers/index.ts +36 -0
  69. package/src/parser/rules/block/module/listusers/parser.ts +54 -0
  70. package/src/parser/rules/block/module/listusers/resolve.ts +58 -0
  71. package/src/parser/rules/block/module/listusers/types.ts +93 -0
  72. package/src/parser/rules/block/module/mapping.ts +61 -0
  73. package/src/parser/rules/block/module/page-tree/index.ts +38 -0
  74. package/src/parser/rules/block/module/page-tree/types.ts +29 -0
  75. package/src/parser/rules/block/module/rate/index.ts +28 -0
  76. package/src/parser/rules/block/module/rate/types.ts +19 -0
  77. package/src/parser/rules/block/module/resolve.ts +411 -0
  78. package/src/parser/rules/block/module/types-common.ts +59 -0
  79. package/src/parser/rules/block/module/types.ts +61 -0
  80. package/src/parser/rules/block/module/utils.ts +43 -0
  81. package/src/parser/rules/block/module/walk.ts +380 -0
  82. package/src/parser/rules/block/module.ts +164 -0
  83. package/src/parser/rules/block/orphan-li.ts +177 -0
  84. package/src/parser/rules/block/paragraph.ts +157 -0
  85. package/src/parser/rules/block/table-block.ts +726 -0
  86. package/src/parser/rules/block/table.ts +441 -0
  87. package/src/parser/rules/block/tabview.ts +331 -0
  88. package/src/parser/rules/block/toc.ts +129 -0
  89. package/src/parser/rules/block/utils.ts +615 -0
  90. package/src/parser/rules/index.ts +49 -0
  91. package/src/parser/rules/inline/anchor-name.ts +154 -0
  92. package/src/parser/rules/inline/anchor.ts +327 -0
  93. package/src/parser/rules/inline/bibcite.ts +153 -0
  94. package/src/parser/rules/inline/bold.ts +86 -0
  95. package/src/parser/rules/inline/color.ts +140 -0
  96. package/src/parser/rules/inline/comment.ts +90 -0
  97. package/src/parser/rules/inline/equation-ref.ts +115 -0
  98. package/src/parser/rules/inline/expr.ts +526 -0
  99. package/src/parser/rules/inline/footnote.ts +223 -0
  100. package/src/parser/rules/inline/guillemet.ts +64 -0
  101. package/src/parser/rules/inline/html.ts +132 -0
  102. package/src/parser/rules/inline/image.ts +328 -0
  103. package/src/parser/rules/inline/index.ts +150 -0
  104. package/src/parser/rules/inline/italic.ts +74 -0
  105. package/src/parser/rules/inline/line-break.ts +326 -0
  106. package/src/parser/rules/inline/link-anchor.ts +147 -0
  107. package/src/parser/rules/inline/link-single.ts +164 -0
  108. package/src/parser/rules/inline/link-star.ts +134 -0
  109. package/src/parser/rules/inline/link-triple.ts +267 -0
  110. package/src/parser/rules/inline/math-inline.ts +126 -0
  111. package/src/parser/rules/inline/monospace.ts +78 -0
  112. package/src/parser/rules/inline/raw.ts +262 -0
  113. package/src/parser/rules/inline/size.ts +244 -0
  114. package/src/parser/rules/inline/span.ts +424 -0
  115. package/src/parser/rules/inline/strikethrough.ts +115 -0
  116. package/src/parser/rules/inline/subscript.ts +84 -0
  117. package/src/parser/rules/inline/superscript.ts +84 -0
  118. package/src/parser/rules/inline/text.ts +84 -0
  119. package/src/parser/rules/inline/underline.ts +127 -0
  120. package/src/parser/rules/inline/user.ts +147 -0
  121. package/src/parser/rules/inline/utils.ts +344 -0
  122. package/src/parser/rules/types.ts +252 -0
  123. package/src/parser/rules/utils.ts +155 -0
  124. package/src/parser/toc.ts +130 -0
@@ -0,0 +1,223 @@
1
+ /**
2
+ *
3
+ * Parses the Wikidot footnote syntax: `[[footnote]]content[[/footnote]]`.
4
+ *
5
+ * Footnotes work in two parts: the inline `[[footnote]]` block produces
6
+ * a numbered superscript reference marker at the point of use, while the
7
+ * actual footnote content is collected separately and rendered by a
8
+ * `[[footnoteblock]]` element (typically at the bottom of the page).
9
+ *
10
+ * Footnote content supports multiple paragraphs:
11
+ * - The first paragraph is rendered as inline content (no wrapping `<p>` tag)
12
+ * - Subsequent paragraphs (separated by blank lines) are each wrapped
13
+ * in `<p>` tags, matching Wikidot's rendering behavior
14
+ * - Single newlines within a paragraph become `<br />` elements
15
+ *
16
+ * The parsed footnote content is pushed into `ctx.footnotes` (an array
17
+ * of Element arrays) so the renderer can later assign sequential numbers
18
+ * and generate the footnote block.
19
+ *
20
+ * Produces a simple `"footnote"` AST element (a marker with no data)
21
+ * at the inline reference point.
22
+ *
23
+ * @module
24
+ */
25
+ import type { Element } from "@wdprlib/ast";
26
+ import type { InlineRule, ParseContext, RuleResult } from "../types";
27
+ import { currentToken } from "../types";
28
+ import { parseBlockName } from "../utils";
29
+ import { parseInlineUntil } from "./utils";
30
+
31
+ /**
32
+ * Inline rule for parsing `[[footnote]]content[[/footnote]]`.
33
+ *
34
+ * Triggered by a `BLOCK_OPEN` (`[[`) token. Verifies the block name
35
+ * is `footnote`, then parses multiline inline content until the matching
36
+ * `[[/footnote]]` closing tag is found.
37
+ *
38
+ * Side effect: appends the parsed footnote content to `ctx.footnotes`.
39
+ */
40
+ export const footnoteRule: InlineRule = {
41
+ name: "footnote",
42
+ startTokens: ["BLOCK_OPEN"],
43
+
44
+ /**
45
+ * Attempts to parse a footnote block at the current position.
46
+ *
47
+ * @param ctx - Parse context with token stream and current position
48
+ * @returns A successful result with a `"footnote"` marker element,
49
+ * or `{ success: false }` if this is not a valid footnote
50
+ */
51
+ parse(ctx: ParseContext): RuleResult<Element> {
52
+ const openToken = currentToken(ctx);
53
+ if (openToken.type !== "BLOCK_OPEN") {
54
+ return { success: false };
55
+ }
56
+
57
+ let pos = ctx.pos + 1;
58
+ let consumed = 1;
59
+
60
+ // Parse block name
61
+ const nameResult = parseBlockName(ctx, pos);
62
+ if (!nameResult) {
63
+ return { success: false };
64
+ }
65
+
66
+ const blockName = nameResult.name;
67
+ if (blockName !== "footnote") {
68
+ return { success: false };
69
+ }
70
+
71
+ pos += nameResult.consumed;
72
+ consumed += nameResult.consumed;
73
+
74
+ // Expect ]]
75
+ while (ctx.tokens[pos]?.type === "WHITESPACE") {
76
+ pos++;
77
+ consumed++;
78
+ }
79
+ if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
80
+ return { success: false };
81
+ }
82
+ pos++;
83
+ consumed++;
84
+
85
+ // Parse content until [[/footnote]]
86
+ // Wikidot footnote behavior:
87
+ // - First paragraph: inline content (no <p> tag)
88
+ // - After blank line: content wrapped in <p> tag
89
+ const paragraphs: Element[][] = [[]];
90
+ let currentParagraph = 0;
91
+ let foundClose = false;
92
+
93
+ while (pos < ctx.tokens.length) {
94
+ const token = ctx.tokens[pos];
95
+ if (!token || token.type === "EOF") {
96
+ break;
97
+ }
98
+
99
+ // Check for [[/footnote]]
100
+ if (token.type === "BLOCK_END_OPEN") {
101
+ const closeNameResult = parseBlockName(ctx, pos + 1);
102
+ if (closeNameResult && closeNameResult.name === "footnote") {
103
+ foundClose = true;
104
+ // Skip [[/footnote]]
105
+ pos++; // [[/
106
+ consumed++;
107
+ pos += closeNameResult.consumed; // footnote
108
+ consumed += closeNameResult.consumed;
109
+ // Skip whitespace
110
+ while (ctx.tokens[pos]?.type === "WHITESPACE") {
111
+ pos++;
112
+ consumed++;
113
+ }
114
+ // Skip ]]
115
+ if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
116
+ pos++;
117
+ consumed++;
118
+ }
119
+ break;
120
+ }
121
+ }
122
+
123
+ // Handle NEWLINE - check if it's a paragraph break (blank line)
124
+ if (token.type === "NEWLINE") {
125
+ pos++;
126
+ consumed++;
127
+ // Peek ahead: skip whitespace to check for blank line (e.g. "\n \n")
128
+ let peekPos = pos;
129
+ let peekConsumed = 0;
130
+ while (ctx.tokens[peekPos]?.type === "WHITESPACE") {
131
+ peekPos++;
132
+ peekConsumed++;
133
+ }
134
+ // Look ahead for another NEWLINE (blank line = paragraph break)
135
+ if (ctx.tokens[peekPos]?.type === "NEWLINE") {
136
+ // Commit the whitespace skip
137
+ pos = peekPos;
138
+ consumed += peekConsumed;
139
+ // Skip all consecutive newlines
140
+ while (ctx.tokens[pos]?.type === "NEWLINE") {
141
+ pos++;
142
+ consumed++;
143
+ }
144
+ // Start new paragraph
145
+ currentParagraph++;
146
+ paragraphs[currentParagraph] = [];
147
+ } else {
148
+ // Single newline - just continue (becomes space or line-break)
149
+ // For Wikidot compatibility, single newlines in footnotes become <br />
150
+ paragraphs[currentParagraph]!.push({ element: "line-break" });
151
+ }
152
+ continue;
153
+ }
154
+
155
+ // Parse inline content (including newlines for multiline footnotes)
156
+ const inlineCtx: ParseContext = { ...ctx, pos };
157
+ const inlineResult = parseInlineUntil(inlineCtx, "BLOCK_END_OPEN");
158
+ if (inlineResult.elements.length > 0) {
159
+ paragraphs[currentParagraph]!.push(...inlineResult.elements);
160
+ pos += inlineResult.consumed;
161
+ consumed += inlineResult.consumed;
162
+ } else {
163
+ // Fallback: just add as text
164
+ paragraphs[currentParagraph]!.push({ element: "text", data: token.value });
165
+ pos++;
166
+ consumed++;
167
+ }
168
+ }
169
+
170
+ // Build children: first paragraph inline, subsequent paragraphs wrapped in <p>
171
+ const children: Element[] = [];
172
+ for (let i = 0; i < paragraphs.length; i++) {
173
+ const para = paragraphs[i]!;
174
+ if (para.length === 0) continue;
175
+ // Remove leading/trailing line-breaks
176
+ while (para.length > 0 && para[0]?.element === "line-break") {
177
+ para.shift();
178
+ }
179
+ while (para.length > 0 && para[para.length - 1]?.element === "line-break") {
180
+ para.pop();
181
+ }
182
+ if (para.length === 0) continue;
183
+
184
+ if (i === 0) {
185
+ // First paragraph: inline
186
+ children.push(...para);
187
+ } else {
188
+ // Subsequent paragraphs: wrapped in <p>
189
+ children.push({
190
+ element: "container",
191
+ data: {
192
+ type: "paragraph",
193
+ attributes: {},
194
+ elements: para,
195
+ },
196
+ });
197
+ }
198
+ }
199
+
200
+ if (!foundClose) {
201
+ ctx.diagnostics.push({
202
+ severity: "warning",
203
+ code: "unclosed-block",
204
+ message: "Missing closing tag [[/footnote]] for [[footnote]]",
205
+ position: openToken.position,
206
+ });
207
+ }
208
+
209
+ // Store footnote content in context
210
+ ctx.footnotes.push(children);
211
+
212
+ // Return simple footnote marker
213
+ return {
214
+ success: true,
215
+ elements: [
216
+ {
217
+ element: "footnote",
218
+ },
219
+ ],
220
+ consumed,
221
+ };
222
+ },
223
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ *
3
+ * Parses Wikidot's guillemet (angle quotation mark) syntax.
4
+ *
5
+ * Converts ASCII double-angle-bracket sequences into their Unicode
6
+ * typographic equivalents:
7
+ * - `<<` becomes `\u00AB` (LEFT-POINTING DOUBLE ANGLE QUOTATION MARK)
8
+ * - `>>` becomes `\u00BB` (RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK)
9
+ *
10
+ * These typographic characters are commonly used in European languages
11
+ * (particularly French and Russian) as quotation marks. Wikidot provides
12
+ * this shorthand so authors do not need to type the Unicode characters
13
+ * directly.
14
+ *
15
+ * Produces a `"text"` AST element containing the Unicode character.
16
+ *
17
+ * @module
18
+ */
19
+ import type { Element } from "@wdprlib/ast";
20
+ import type { InlineRule, ParseContext, RuleResult } from "../types";
21
+
22
+ /**
23
+ * Inline rule for converting `<<` and `>>` to typographic guillemets.
24
+ *
25
+ * Triggered by `LEFT_DOUBLE_ANGLE` or `RIGHT_DOUBLE_ANGLE` tokens.
26
+ * This is a simple one-to-one token replacement with no content
27
+ * parsing or nesting.
28
+ */
29
+ export const guillemetRule: InlineRule = {
30
+ name: "guillemet",
31
+ startTokens: ["LEFT_DOUBLE_ANGLE", "RIGHT_DOUBLE_ANGLE"],
32
+
33
+ /**
34
+ * Converts a double-angle-bracket token to its Unicode guillemet equivalent.
35
+ *
36
+ * @param ctx - Parse context with token stream and current position
37
+ * @returns A successful result with a `"text"` element containing the
38
+ * Unicode guillemet character, or `{ success: false }` if the
39
+ * token is neither `<<` nor `>>`
40
+ */
41
+ parse(ctx: ParseContext): RuleResult<Element> {
42
+ const token = ctx.tokens[ctx.pos];
43
+
44
+ // << → «
45
+ if (token?.type === "LEFT_DOUBLE_ANGLE") {
46
+ return {
47
+ success: true,
48
+ elements: [{ element: "text", data: "\u00AB" }],
49
+ consumed: 1,
50
+ };
51
+ }
52
+
53
+ // >> → »
54
+ if (token?.type === "RIGHT_DOUBLE_ANGLE") {
55
+ return {
56
+ success: true,
57
+ elements: [{ element: "text", data: "\u00BB" }],
58
+ consumed: 1,
59
+ };
60
+ }
61
+
62
+ return { success: false };
63
+ },
64
+ };
@@ -0,0 +1,132 @@
1
+ /**
2
+ *
3
+ * Inline-position gate for `[[html]]...[[/html]]` when the parser is
4
+ * configured with `allowHtmlBlocks: false`.
5
+ *
6
+ * The block-level {@link htmlBlockRule} already removes `[[html]]` blocks
7
+ * that sit at the start of a line, but the block dispatcher never
8
+ * reaches a `[[html]]` that appears mid-paragraph. Without this inline
9
+ * rule, the body of a disabled-but-inline-positioned `[[html]]` would
10
+ * end up parsed as paragraph text and leak into the output as escaped
11
+ * HTML.
12
+ *
13
+ * When enabled (`allowHtmlBlocks !== false`), the rule does nothing
14
+ * (returns `success: false`) so the existing paragraph behaviour is
15
+ * preserved: a stray inline `[[html]]` renders as text. The block-level
16
+ * rule handles the proper case where `[[html]]` is on its own line.
17
+ *
18
+ * When disabled (`allowHtmlBlocks === false`):
19
+ * - A well-formed `[[html ...]]...[[/html]]` is fully consumed and
20
+ * produces no AST element, emitting an `html-block-disabled` info
21
+ * diagnostic.
22
+ * - An unclosed `[[html ...]]` is consumed to the end of the token
23
+ * stream so the body cannot leak as inline text, emitting both
24
+ * `unclosed-block` (warning) and `html-block-disabled` (info).
25
+ *
26
+ * @module
27
+ */
28
+
29
+ import type { Element } from "@wdprlib/ast";
30
+ import type { InlineRule, ParseContext, RuleResult } from "../types";
31
+ import { currentToken } from "../types";
32
+ import { parseBlockName, parseAttributesRaw } from "../block/utils";
33
+ import { lookaheadHasHtmlClose } from "../block/html";
34
+
35
+ /**
36
+ * Inline rule that gates `[[html]]` when the setting disallows it.
37
+ */
38
+ export const htmlInlineRule: InlineRule = {
39
+ name: "html",
40
+ startTokens: ["BLOCK_OPEN"],
41
+
42
+ parse(ctx: ParseContext): RuleResult<Element> {
43
+ const openToken = currentToken(ctx);
44
+ if (openToken.type !== "BLOCK_OPEN") {
45
+ return { success: false };
46
+ }
47
+
48
+ let pos = ctx.pos + 1;
49
+ let consumed = 1;
50
+
51
+ const nameResult = parseBlockName(ctx, pos);
52
+ if (!nameResult || nameResult.name.toLowerCase() !== "html") {
53
+ return { success: false };
54
+ }
55
+ pos += nameResult.consumed;
56
+ consumed += nameResult.consumed;
57
+
58
+ const attrResult = parseAttributesRaw(ctx, pos);
59
+ pos += attrResult.consumed;
60
+ consumed += attrResult.consumed;
61
+
62
+ if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
63
+ return { success: false };
64
+ }
65
+ pos++;
66
+ consumed++;
67
+
68
+ // Enabled: leave inline `[[html]]` alone — it falls through to text
69
+ // rendering, matching the historical behaviour for stray block-named
70
+ // openers used inline.
71
+ if (ctx.settings.allowHtmlBlocks !== false) {
72
+ return { success: false };
73
+ }
74
+
75
+ // Disabled path: consume the body until a real `[[/html]]` (BLOCK_END_OPEN
76
+ // + name + BLOCK_CLOSE, allowing whitespace inside the close tag).
77
+ // Only allow the blank-line stop when no real close exists ahead, so
78
+ // a closed body that spans paragraphs is still consumed correctly.
79
+ const hasCloseAhead = lookaheadHasHtmlClose(ctx, pos);
80
+ let foundClose = false;
81
+ while (pos < ctx.tokens.length) {
82
+ const token = ctx.tokens[pos];
83
+ if (!token || token.type === "EOF") break;
84
+
85
+ // Stop at a blank line so an unclosed inline `[[html]]` does not
86
+ // swallow subsequent paragraphs.
87
+ if (!hasCloseAhead && token.type === "NEWLINE" && ctx.tokens[pos + 1]?.type === "NEWLINE") {
88
+ break;
89
+ }
90
+
91
+ if (token.type === "BLOCK_END_OPEN") {
92
+ const closeNameResult = parseBlockName(ctx, pos + 1);
93
+ if (closeNameResult?.name.toLowerCase() === "html") {
94
+ let checkPos = pos + 1 + closeNameResult.consumed;
95
+ while (ctx.tokens[checkPos]?.type === "WHITESPACE") checkPos++;
96
+ if (ctx.tokens[checkPos]?.type === "BLOCK_CLOSE") {
97
+ foundClose = true;
98
+ // Consume `[[/html]]` (and optional trailing newline) too.
99
+ consumed += checkPos - pos + 1;
100
+ pos = checkPos + 1;
101
+ if (ctx.tokens[pos]?.type === "NEWLINE") {
102
+ pos++;
103
+ consumed++;
104
+ }
105
+ break;
106
+ }
107
+ }
108
+ }
109
+
110
+ pos++;
111
+ consumed++;
112
+ }
113
+
114
+ if (!foundClose) {
115
+ ctx.diagnostics.push({
116
+ severity: "warning",
117
+ code: "unclosed-block",
118
+ message: "Missing closing tag [[/html]] for [[html]]",
119
+ position: openToken.position,
120
+ });
121
+ }
122
+
123
+ ctx.diagnostics.push({
124
+ severity: "info",
125
+ code: "html-block-disabled",
126
+ message: "[[html]] block ignored: disabled by settings",
127
+ position: openToken.position,
128
+ });
129
+
130
+ return { success: true, elements: [], consumed };
131
+ },
132
+ };