@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.
- package/dist/index.cjs +295 -118
- package/dist/index.js +272 -95
- package/package.json +5 -3
- package/src/index.ts +163 -0
- package/src/lexer/index.ts +20 -0
- package/src/lexer/lexer.ts +687 -0
- package/src/lexer/tokens.ts +141 -0
- package/src/parser/constants.ts +173 -0
- package/src/parser/depth.ts +251 -0
- package/src/parser/index.ts +18 -0
- package/src/parser/parse.ts +315 -0
- package/src/parser/postprocess/divAdjacentParagraph.ts +76 -0
- package/src/parser/postprocess/index.ts +15 -0
- package/src/parser/postprocess/spanStrip.ts +697 -0
- package/src/parser/preprocess/expr.ts +265 -0
- package/src/parser/preprocess/index.ts +38 -0
- package/src/parser/preprocess/typography.ts +67 -0
- package/src/parser/preprocess/utils.ts +250 -0
- package/src/parser/preprocess/whitespace.ts +111 -0
- package/src/parser/rules/block/align.ts +282 -0
- package/src/parser/rules/block/bibliography.ts +359 -0
- package/src/parser/rules/block/block-list.ts +689 -0
- package/src/parser/rules/block/blockquote.ts +238 -0
- package/src/parser/rules/block/center.ts +87 -0
- package/src/parser/rules/block/clear-float.ts +75 -0
- package/src/parser/rules/block/code.ts +187 -0
- package/src/parser/rules/block/collapsible.ts +337 -0
- package/src/parser/rules/block/comment.ts +73 -0
- package/src/parser/rules/block/content-separator.ts +79 -0
- package/src/parser/rules/block/definition-list.ts +270 -0
- package/src/parser/rules/block/div.ts +400 -0
- package/src/parser/rules/block/embed-block.ts +153 -0
- package/src/parser/rules/block/footnoteblock.ts +200 -0
- package/src/parser/rules/block/heading.ts +142 -0
- package/src/parser/rules/block/horizontal-rule.ts +61 -0
- package/src/parser/rules/block/html.ts +222 -0
- package/src/parser/rules/block/iframe.ts +239 -0
- package/src/parser/rules/block/iftags.ts +150 -0
- package/src/parser/rules/block/include.ts +179 -0
- package/src/parser/rules/block/index.ts +127 -0
- package/src/parser/rules/block/list.ts +244 -0
- package/src/parser/rules/block/math.ts +183 -0
- package/src/parser/rules/block/module/backlinks/index.ts +31 -0
- package/src/parser/rules/block/module/backlinks/types.ts +21 -0
- package/src/parser/rules/block/module/categories/index.ts +34 -0
- package/src/parser/rules/block/module/categories/types.ts +21 -0
- package/src/parser/rules/block/module/css/index.ts +37 -0
- package/src/parser/rules/block/module/iftags/condition.ts +109 -0
- package/src/parser/rules/block/module/iftags/index.ts +26 -0
- package/src/parser/rules/block/module/iftags/preprocess.ts +140 -0
- package/src/parser/rules/block/module/iftags/resolve.ts +73 -0
- package/src/parser/rules/block/module/iftags/types.ts +63 -0
- package/src/parser/rules/block/module/include/index.ts +20 -0
- package/src/parser/rules/block/module/include/resolve.ts +556 -0
- package/src/parser/rules/block/module/index.ts +122 -0
- package/src/parser/rules/block/module/join/index.ts +34 -0
- package/src/parser/rules/block/module/join/types.ts +23 -0
- package/src/parser/rules/block/module/listpages/compiler.ts +453 -0
- package/src/parser/rules/block/module/listpages/extract.ts +410 -0
- package/src/parser/rules/block/module/listpages/index.ts +83 -0
- package/src/parser/rules/block/module/listpages/normalize.ts +390 -0
- package/src/parser/rules/block/module/listpages/parser.ts +106 -0
- package/src/parser/rules/block/module/listpages/resolve.ts +130 -0
- package/src/parser/rules/block/module/listpages/types.ts +513 -0
- package/src/parser/rules/block/module/listpages/url-resolver.ts +186 -0
- package/src/parser/rules/block/module/listusers/compiler.ts +77 -0
- package/src/parser/rules/block/module/listusers/extract.ts +45 -0
- package/src/parser/rules/block/module/listusers/index.ts +36 -0
- package/src/parser/rules/block/module/listusers/parser.ts +54 -0
- package/src/parser/rules/block/module/listusers/resolve.ts +58 -0
- package/src/parser/rules/block/module/listusers/types.ts +93 -0
- package/src/parser/rules/block/module/mapping.ts +61 -0
- package/src/parser/rules/block/module/page-tree/index.ts +38 -0
- package/src/parser/rules/block/module/page-tree/types.ts +29 -0
- package/src/parser/rules/block/module/rate/index.ts +28 -0
- package/src/parser/rules/block/module/rate/types.ts +19 -0
- package/src/parser/rules/block/module/resolve.ts +411 -0
- package/src/parser/rules/block/module/types-common.ts +59 -0
- package/src/parser/rules/block/module/types.ts +61 -0
- package/src/parser/rules/block/module/utils.ts +43 -0
- package/src/parser/rules/block/module/walk.ts +380 -0
- package/src/parser/rules/block/module.ts +164 -0
- package/src/parser/rules/block/orphan-li.ts +177 -0
- package/src/parser/rules/block/paragraph.ts +157 -0
- package/src/parser/rules/block/table-block.ts +726 -0
- package/src/parser/rules/block/table.ts +441 -0
- package/src/parser/rules/block/tabview.ts +331 -0
- package/src/parser/rules/block/toc.ts +129 -0
- package/src/parser/rules/block/utils.ts +615 -0
- package/src/parser/rules/index.ts +49 -0
- package/src/parser/rules/inline/anchor-name.ts +154 -0
- package/src/parser/rules/inline/anchor.ts +327 -0
- package/src/parser/rules/inline/bibcite.ts +153 -0
- package/src/parser/rules/inline/bold.ts +86 -0
- package/src/parser/rules/inline/color.ts +140 -0
- package/src/parser/rules/inline/comment.ts +90 -0
- package/src/parser/rules/inline/equation-ref.ts +115 -0
- package/src/parser/rules/inline/expr.ts +526 -0
- package/src/parser/rules/inline/footnote.ts +223 -0
- package/src/parser/rules/inline/guillemet.ts +64 -0
- package/src/parser/rules/inline/html.ts +132 -0
- package/src/parser/rules/inline/image.ts +328 -0
- package/src/parser/rules/inline/index.ts +150 -0
- package/src/parser/rules/inline/italic.ts +74 -0
- package/src/parser/rules/inline/line-break.ts +326 -0
- package/src/parser/rules/inline/link-anchor.ts +147 -0
- package/src/parser/rules/inline/link-single.ts +164 -0
- package/src/parser/rules/inline/link-star.ts +134 -0
- package/src/parser/rules/inline/link-triple.ts +267 -0
- package/src/parser/rules/inline/math-inline.ts +126 -0
- package/src/parser/rules/inline/monospace.ts +78 -0
- package/src/parser/rules/inline/raw.ts +262 -0
- package/src/parser/rules/inline/size.ts +244 -0
- package/src/parser/rules/inline/span.ts +424 -0
- package/src/parser/rules/inline/strikethrough.ts +115 -0
- package/src/parser/rules/inline/subscript.ts +84 -0
- package/src/parser/rules/inline/superscript.ts +84 -0
- package/src/parser/rules/inline/text.ts +84 -0
- package/src/parser/rules/inline/underline.ts +127 -0
- package/src/parser/rules/inline/user.ts +147 -0
- package/src/parser/rules/inline/utils.ts +344 -0
- package/src/parser/rules/types.ts +252 -0
- package/src/parser/rules/utils.ts +155 -0
- package/src/parser/toc.ts +130 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* AST element traversal and transformation utilities.
|
|
4
|
+
*
|
|
5
|
+
* Provides shared logic for recursively visiting and transforming child elements
|
|
6
|
+
* across all AST node types that contain nested elements. The AST has several
|
|
7
|
+
* "special" structures (list, table, definition-list, tab-view) that store
|
|
8
|
+
* children in type-specific locations, plus a generic pattern where elements
|
|
9
|
+
* are stored in `data.elements`. These utilities abstract over those differences
|
|
10
|
+
* so callers can focus on their transformation logic.
|
|
11
|
+
*
|
|
12
|
+
* Three main functions are provided:
|
|
13
|
+
* - `walkElements` - Read-only traversal (visitor pattern)
|
|
14
|
+
* - `mapElementChildren` - Stateless transformation of child arrays
|
|
15
|
+
* - `mapElementChildrenWithState` - Stateful transformation with threaded state
|
|
16
|
+
*
|
|
17
|
+
* Used by the ListPages extraction, module resolution, and include resolution systems.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type {
|
|
23
|
+
Element,
|
|
24
|
+
ListData,
|
|
25
|
+
ListItem,
|
|
26
|
+
TableData,
|
|
27
|
+
TableRow,
|
|
28
|
+
TableCell,
|
|
29
|
+
DefinitionListItem,
|
|
30
|
+
TabData,
|
|
31
|
+
} from "@wdprlib/ast";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Walk all elements recursively in depth-first order, calling a callback for each.
|
|
35
|
+
*
|
|
36
|
+
* The callback is invoked for every element in the tree, including elements nested
|
|
37
|
+
* inside lists, tables, definition lists, tab views, and any element with a
|
|
38
|
+
* `data.elements` array. The callback is called before descending into children
|
|
39
|
+
* (pre-order traversal).
|
|
40
|
+
*
|
|
41
|
+
* This is a read-only traversal; the callback cannot modify the tree structure.
|
|
42
|
+
* Use `mapElementChildren` or `mapElementChildrenWithState` for transformations.
|
|
43
|
+
*
|
|
44
|
+
* @param elements - Array of elements to traverse
|
|
45
|
+
* @param callback - Function called for each element encountered
|
|
46
|
+
*/
|
|
47
|
+
export function walkElements(elements: Element[], callback: (element: Element) => void): void {
|
|
48
|
+
for (const element of elements) {
|
|
49
|
+
callback(element);
|
|
50
|
+
|
|
51
|
+
// List
|
|
52
|
+
if (element.element === "list") {
|
|
53
|
+
const listData = element.data as ListData;
|
|
54
|
+
for (const item of listData.items) {
|
|
55
|
+
if (item["item-type"] === "elements") {
|
|
56
|
+
walkElements(item.elements, callback);
|
|
57
|
+
} else if (item["item-type"] === "sub-list") {
|
|
58
|
+
walkElements([{ element: "list", data: item.data } as Element], callback);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Table
|
|
65
|
+
if (element.element === "table") {
|
|
66
|
+
const tableData = element.data as TableData;
|
|
67
|
+
for (const row of tableData.rows) {
|
|
68
|
+
for (const cell of row.cells) {
|
|
69
|
+
walkElements(cell.elements, callback);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Definition list
|
|
76
|
+
if (element.element === "definition-list") {
|
|
77
|
+
const defListData = element.data as DefinitionListItem[];
|
|
78
|
+
for (const item of defListData) {
|
|
79
|
+
walkElements(item.key, callback);
|
|
80
|
+
walkElements(item.value, callback);
|
|
81
|
+
}
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Tab view
|
|
86
|
+
if (element.element === "tab-view") {
|
|
87
|
+
const tabData = element.data as TabData[];
|
|
88
|
+
for (const tab of tabData) {
|
|
89
|
+
walkElements(tab.elements, callback);
|
|
90
|
+
}
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Generic elements with data.elements
|
|
95
|
+
if ("data" in element && element.data && typeof element.data === "object") {
|
|
96
|
+
const data = element.data as Record<string, unknown>;
|
|
97
|
+
if ("elements" in data && Array.isArray(data.elements)) {
|
|
98
|
+
walkElements(data.elements as Element[], callback);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Create a new element with all child element arrays transformed by a function.
|
|
106
|
+
*
|
|
107
|
+
* This is a structural mapper that knows how to find child element arrays in all
|
|
108
|
+
* AST node types (list items, table cells, definition list keys/values, tab panels,
|
|
109
|
+
* and generic `data.elements`). The transform function receives each child array
|
|
110
|
+
* and returns a new array; the original element is not mutated.
|
|
111
|
+
*
|
|
112
|
+
* If the element has no children, it is returned unchanged (same reference).
|
|
113
|
+
*
|
|
114
|
+
* @param element - The element whose children should be transformed
|
|
115
|
+
* @param transform - Function that receives a child element array and returns a transformed array
|
|
116
|
+
* @returns A new element with transformed children, or the original element if it has no children
|
|
117
|
+
*/
|
|
118
|
+
export function mapElementChildren(
|
|
119
|
+
element: Element,
|
|
120
|
+
transform: (elements: Element[]) => Element[],
|
|
121
|
+
): Element {
|
|
122
|
+
// List
|
|
123
|
+
if (element.element === "list") {
|
|
124
|
+
const listData = element.data as ListData;
|
|
125
|
+
const newItems: ListItem[] = [];
|
|
126
|
+
|
|
127
|
+
for (const item of listData.items) {
|
|
128
|
+
if (item["item-type"] === "elements") {
|
|
129
|
+
newItems.push({
|
|
130
|
+
"item-type": "elements",
|
|
131
|
+
attributes: item.attributes,
|
|
132
|
+
elements: transform(item.elements),
|
|
133
|
+
});
|
|
134
|
+
} else if (item["item-type"] === "sub-list") {
|
|
135
|
+
const subListResult = transform([{ element: "list", data: item.data } as Element]);
|
|
136
|
+
const resolvedList = subListResult[0];
|
|
137
|
+
if (resolvedList?.element === "list") {
|
|
138
|
+
newItems.push({
|
|
139
|
+
"item-type": "sub-list",
|
|
140
|
+
element: "list",
|
|
141
|
+
data: resolvedList.data as ListData,
|
|
142
|
+
});
|
|
143
|
+
} else {
|
|
144
|
+
newItems.push(item);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
element: "list",
|
|
151
|
+
data: { ...listData, items: newItems },
|
|
152
|
+
} as Element;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Table
|
|
156
|
+
if (element.element === "table") {
|
|
157
|
+
const tableData = element.data as TableData;
|
|
158
|
+
const newRows: TableRow[] = [];
|
|
159
|
+
|
|
160
|
+
for (const row of tableData.rows) {
|
|
161
|
+
const newCells: TableCell[] = [];
|
|
162
|
+
for (const cell of row.cells) {
|
|
163
|
+
newCells.push({ ...cell, elements: transform(cell.elements) });
|
|
164
|
+
}
|
|
165
|
+
newRows.push({ ...row, cells: newCells });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
element: "table",
|
|
170
|
+
data: { ...tableData, rows: newRows },
|
|
171
|
+
} as Element;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Definition list
|
|
175
|
+
if (element.element === "definition-list") {
|
|
176
|
+
const defListData = element.data as DefinitionListItem[];
|
|
177
|
+
const newItems: DefinitionListItem[] = [];
|
|
178
|
+
|
|
179
|
+
for (const item of defListData) {
|
|
180
|
+
newItems.push({
|
|
181
|
+
key_string: item.key_string,
|
|
182
|
+
key: transform(item.key),
|
|
183
|
+
value: transform(item.value),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
element: "definition-list",
|
|
189
|
+
data: newItems,
|
|
190
|
+
} as Element;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Tab view
|
|
194
|
+
if (element.element === "tab-view") {
|
|
195
|
+
const tabData = element.data as TabData[];
|
|
196
|
+
const newTabs: TabData[] = [];
|
|
197
|
+
|
|
198
|
+
for (const tab of tabData) {
|
|
199
|
+
newTabs.push({ ...tab, elements: transform(tab.elements) });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
element: "tab-view",
|
|
204
|
+
data: newTabs,
|
|
205
|
+
} as Element;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Generic elements with data.elements
|
|
209
|
+
if ("data" in element && element.data && typeof element.data === "object") {
|
|
210
|
+
const data = element.data as Record<string, unknown>;
|
|
211
|
+
if ("elements" in data && Array.isArray(data.elements)) {
|
|
212
|
+
return {
|
|
213
|
+
...element,
|
|
214
|
+
data: {
|
|
215
|
+
...data,
|
|
216
|
+
elements: transform(data.elements as Element[]),
|
|
217
|
+
},
|
|
218
|
+
} as Element;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// No children
|
|
223
|
+
return element;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Create a new element with all child arrays transformed by a stateful function.
|
|
228
|
+
*
|
|
229
|
+
* Like `mapElementChildren`, but the transform function also receives and returns
|
|
230
|
+
* a state value. State is threaded sequentially through each child group: the output
|
|
231
|
+
* state from one group becomes the input state for the next. This is useful when
|
|
232
|
+
* the transformation needs to track information across sibling groups, such as
|
|
233
|
+
* maintaining a monotonically increasing ID counter.
|
|
234
|
+
*
|
|
235
|
+
* @typeParam S - The type of the threaded state
|
|
236
|
+
* @param element - The element whose children should be transformed
|
|
237
|
+
* @param state - Initial state value
|
|
238
|
+
* @param transform - Function that receives a child array and current state,
|
|
239
|
+
* returning transformed elements and updated state
|
|
240
|
+
* @returns Object with the new element and final state value
|
|
241
|
+
*/
|
|
242
|
+
export function mapElementChildrenWithState<S>(
|
|
243
|
+
element: Element,
|
|
244
|
+
state: S,
|
|
245
|
+
transform: (elements: Element[], state: S) => { elements: Element[]; state: S },
|
|
246
|
+
): { element: Element; state: S } {
|
|
247
|
+
// List
|
|
248
|
+
if (element.element === "list") {
|
|
249
|
+
const listData = element.data as ListData;
|
|
250
|
+
const newItems: ListItem[] = [];
|
|
251
|
+
let currentState = state;
|
|
252
|
+
|
|
253
|
+
for (const item of listData.items) {
|
|
254
|
+
if (item["item-type"] === "elements") {
|
|
255
|
+
const result = transform(item.elements, currentState);
|
|
256
|
+
newItems.push({
|
|
257
|
+
"item-type": "elements",
|
|
258
|
+
attributes: item.attributes,
|
|
259
|
+
elements: result.elements,
|
|
260
|
+
});
|
|
261
|
+
currentState = result.state;
|
|
262
|
+
} else if (item["item-type"] === "sub-list") {
|
|
263
|
+
const result = transform([{ element: "list", data: item.data } as Element], currentState);
|
|
264
|
+
const resolvedList = result.elements[0];
|
|
265
|
+
if (resolvedList?.element === "list") {
|
|
266
|
+
newItems.push({
|
|
267
|
+
"item-type": "sub-list",
|
|
268
|
+
element: "list",
|
|
269
|
+
data: resolvedList.data as ListData,
|
|
270
|
+
});
|
|
271
|
+
} else {
|
|
272
|
+
newItems.push(item);
|
|
273
|
+
}
|
|
274
|
+
currentState = result.state;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
element: {
|
|
280
|
+
element: "list",
|
|
281
|
+
data: { ...listData, items: newItems },
|
|
282
|
+
} as Element,
|
|
283
|
+
state: currentState,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Table
|
|
288
|
+
if (element.element === "table") {
|
|
289
|
+
const tableData = element.data as TableData;
|
|
290
|
+
const newRows: TableRow[] = [];
|
|
291
|
+
let currentState = state;
|
|
292
|
+
|
|
293
|
+
for (const row of tableData.rows) {
|
|
294
|
+
const newCells: TableCell[] = [];
|
|
295
|
+
for (const cell of row.cells) {
|
|
296
|
+
const result = transform(cell.elements, currentState);
|
|
297
|
+
newCells.push({ ...cell, elements: result.elements });
|
|
298
|
+
currentState = result.state;
|
|
299
|
+
}
|
|
300
|
+
newRows.push({ ...row, cells: newCells });
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
element: {
|
|
305
|
+
element: "table",
|
|
306
|
+
data: { ...tableData, rows: newRows },
|
|
307
|
+
} as Element,
|
|
308
|
+
state: currentState,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Definition list
|
|
313
|
+
if (element.element === "definition-list") {
|
|
314
|
+
const defListData = element.data as DefinitionListItem[];
|
|
315
|
+
const newItems: DefinitionListItem[] = [];
|
|
316
|
+
let currentState = state;
|
|
317
|
+
|
|
318
|
+
for (const item of defListData) {
|
|
319
|
+
const keyResult = transform(item.key, currentState);
|
|
320
|
+
currentState = keyResult.state;
|
|
321
|
+
const valueResult = transform(item.value, currentState);
|
|
322
|
+
currentState = valueResult.state;
|
|
323
|
+
newItems.push({
|
|
324
|
+
key_string: item.key_string,
|
|
325
|
+
key: keyResult.elements,
|
|
326
|
+
value: valueResult.elements,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
element: {
|
|
332
|
+
element: "definition-list",
|
|
333
|
+
data: newItems,
|
|
334
|
+
} as Element,
|
|
335
|
+
state: currentState,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Tab view
|
|
340
|
+
if (element.element === "tab-view") {
|
|
341
|
+
const tabData = element.data as TabData[];
|
|
342
|
+
const newTabs: TabData[] = [];
|
|
343
|
+
let currentState = state;
|
|
344
|
+
|
|
345
|
+
for (const tab of tabData) {
|
|
346
|
+
const result = transform(tab.elements, currentState);
|
|
347
|
+
newTabs.push({ ...tab, elements: result.elements });
|
|
348
|
+
currentState = result.state;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
element: {
|
|
353
|
+
element: "tab-view",
|
|
354
|
+
data: newTabs,
|
|
355
|
+
} as Element,
|
|
356
|
+
state: currentState,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Generic elements with data.elements
|
|
361
|
+
if ("data" in element && element.data && typeof element.data === "object") {
|
|
362
|
+
const data = element.data as Record<string, unknown>;
|
|
363
|
+
if ("elements" in data && Array.isArray(data.elements)) {
|
|
364
|
+
const result = transform(data.elements as Element[], state);
|
|
365
|
+
return {
|
|
366
|
+
element: {
|
|
367
|
+
...element,
|
|
368
|
+
data: {
|
|
369
|
+
...data,
|
|
370
|
+
elements: result.elements,
|
|
371
|
+
},
|
|
372
|
+
} as Element,
|
|
373
|
+
state: result.state,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// No children
|
|
379
|
+
return { element, state };
|
|
380
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { Element } from "@wdprlib/ast";
|
|
2
|
+
import type { BlockRule, ParseContext, RuleResult } from "../types";
|
|
3
|
+
import { currentToken } from "../types";
|
|
4
|
+
import { parseBlockName } from "../utils";
|
|
5
|
+
import { parseAttributesRaw } from "./utils";
|
|
6
|
+
import { getModuleRuleByName } from "./module/mapping";
|
|
7
|
+
|
|
8
|
+
export const moduleRule: BlockRule = {
|
|
9
|
+
name: "module",
|
|
10
|
+
startTokens: ["BLOCK_OPEN"],
|
|
11
|
+
requiresLineStart: false,
|
|
12
|
+
|
|
13
|
+
parse(ctx: ParseContext): RuleResult<Element> {
|
|
14
|
+
const openToken = currentToken(ctx);
|
|
15
|
+
if (openToken.type !== "BLOCK_OPEN") {
|
|
16
|
+
return { success: false };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let pos = ctx.pos + 1;
|
|
20
|
+
let consumed = 1;
|
|
21
|
+
|
|
22
|
+
const nameResult = parseBlockName(ctx, pos);
|
|
23
|
+
if (!nameResult || (nameResult.name !== "module" && nameResult.name !== "module654")) {
|
|
24
|
+
return { success: false };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Page syntax disabled (e.g., forum-post mode)
|
|
28
|
+
if (!ctx.settings.enablePageSyntax) {
|
|
29
|
+
return { success: false };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
pos += nameResult.consumed;
|
|
33
|
+
consumed += nameResult.consumed;
|
|
34
|
+
|
|
35
|
+
// Skip whitespace
|
|
36
|
+
while (ctx.tokens[pos]?.type === "WHITESPACE") {
|
|
37
|
+
pos++;
|
|
38
|
+
consumed++;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Get module name (next TEXT or IDENTIFIER token)
|
|
42
|
+
let moduleName = "";
|
|
43
|
+
const nameToken = ctx.tokens[pos];
|
|
44
|
+
if (nameToken?.type === "TEXT" || nameToken?.type === "IDENTIFIER") {
|
|
45
|
+
moduleName = nameToken.value;
|
|
46
|
+
pos++;
|
|
47
|
+
consumed++;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Parse remaining attributes
|
|
51
|
+
const attrResult = parseAttributesRaw(ctx, pos);
|
|
52
|
+
pos += attrResult.consumed;
|
|
53
|
+
consumed += attrResult.consumed;
|
|
54
|
+
|
|
55
|
+
// Expect ]]
|
|
56
|
+
if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
|
|
57
|
+
return { success: false };
|
|
58
|
+
}
|
|
59
|
+
pos++;
|
|
60
|
+
consumed++;
|
|
61
|
+
|
|
62
|
+
// Dispatch to specific module parser
|
|
63
|
+
const moduleParseRule = getModuleRuleByName(moduleName);
|
|
64
|
+
|
|
65
|
+
// Check for body based on module rule's hasBody flag
|
|
66
|
+
// For unknown modules, default to no body (they will be parsed as bodyless)
|
|
67
|
+
let body: string | undefined;
|
|
68
|
+
const moduleHasBody = moduleParseRule?.hasBody ?? false;
|
|
69
|
+
|
|
70
|
+
if (moduleHasBody && ctx.tokens[pos]?.type === "NEWLINE") {
|
|
71
|
+
pos++;
|
|
72
|
+
consumed++;
|
|
73
|
+
|
|
74
|
+
let bodyContent = "";
|
|
75
|
+
let foundClose = false;
|
|
76
|
+
while (pos < ctx.tokens.length) {
|
|
77
|
+
const token = ctx.tokens[pos];
|
|
78
|
+
if (!token || token.type === "EOF") {
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (token.type === "BLOCK_END_OPEN") {
|
|
83
|
+
const closeNameResult = parseBlockName(ctx, pos + 1);
|
|
84
|
+
if (
|
|
85
|
+
closeNameResult &&
|
|
86
|
+
(closeNameResult.name === "module" || closeNameResult.name === "module654")
|
|
87
|
+
) {
|
|
88
|
+
foundClose = true;
|
|
89
|
+
pos++;
|
|
90
|
+
consumed++;
|
|
91
|
+
pos += closeNameResult.consumed;
|
|
92
|
+
consumed += closeNameResult.consumed;
|
|
93
|
+
if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
|
|
94
|
+
pos++;
|
|
95
|
+
consumed++;
|
|
96
|
+
}
|
|
97
|
+
if (ctx.tokens[pos]?.type === "NEWLINE") {
|
|
98
|
+
pos++;
|
|
99
|
+
consumed++;
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
bodyContent += token.value;
|
|
106
|
+
pos++;
|
|
107
|
+
consumed++;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!foundClose) {
|
|
111
|
+
ctx.diagnostics.push({
|
|
112
|
+
severity: "warning",
|
|
113
|
+
code: "unclosed-block",
|
|
114
|
+
message: "Missing closing tag [[/module]] for [[module]]",
|
|
115
|
+
position: openToken.position,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (bodyContent.trim()) {
|
|
120
|
+
body = bodyContent.trim();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (moduleParseRule) {
|
|
125
|
+
const result = moduleParseRule.parse(ctx, pos, attrResult.attrs, body);
|
|
126
|
+
|
|
127
|
+
// Element を直接返すモジュール(CSS等)
|
|
128
|
+
if ("element" in result) {
|
|
129
|
+
return {
|
|
130
|
+
success: true,
|
|
131
|
+
elements: [result as Element],
|
|
132
|
+
consumed,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
success: true,
|
|
138
|
+
elements: [
|
|
139
|
+
{
|
|
140
|
+
element: "module",
|
|
141
|
+
data: result,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
consumed,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
success: true,
|
|
150
|
+
elements: [
|
|
151
|
+
{
|
|
152
|
+
element: "module",
|
|
153
|
+
data: {
|
|
154
|
+
module: "unknown",
|
|
155
|
+
name: moduleName,
|
|
156
|
+
arguments: attrResult.attrs,
|
|
157
|
+
body,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
consumed,
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
};
|