@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,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Data requirement extraction from parsed ASTs.
|
|
4
|
+
*
|
|
5
|
+
* After parsing, the AST may contain ListPages and ListUsers module nodes that
|
|
6
|
+
* need external data (page lists, user information) to be resolved. This module
|
|
7
|
+
* analyzes the AST to find all such modules, determines what variables their
|
|
8
|
+
* templates use (and therefore what data fields the external provider must supply),
|
|
9
|
+
* and pre-compiles their templates for efficient rendering during the resolution phase.
|
|
10
|
+
*
|
|
11
|
+
* The extraction result includes:
|
|
12
|
+
* - `DataRequirements` listing all ListPages/ListUsers queries with their needed variables
|
|
13
|
+
* - Pre-compiled template functions keyed by module ID for fast rendering
|
|
14
|
+
*
|
|
15
|
+
* This is the first phase of the three-phase ListPages lifecycle:
|
|
16
|
+
* 1. **Extract** (this module) - Analyze AST, determine data needs, compile templates
|
|
17
|
+
* 2. **Fetch** (external) - Application fetches data based on requirements
|
|
18
|
+
* 3. **Resolve** - Substitute fetched data into compiled templates and re-parse
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { SyntaxTree, Module } from "@wdprlib/ast";
|
|
24
|
+
import type {
|
|
25
|
+
DataRequirements,
|
|
26
|
+
ListPagesQuery,
|
|
27
|
+
ListPagesVariable,
|
|
28
|
+
CompiledTemplate,
|
|
29
|
+
} from "./types";
|
|
30
|
+
import type { ListUsersCompiledTemplate } from "../listusers/types";
|
|
31
|
+
import { compileTemplate } from "./compiler";
|
|
32
|
+
import { extractListUsersVariables, compileListUsersTemplate } from "../listusers/extract";
|
|
33
|
+
import { walkElements } from "../walk";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Type guard to narrow a Module union to the list-pages variant.
|
|
37
|
+
*
|
|
38
|
+
* @param module - A Module discriminated union value
|
|
39
|
+
* @returns true if the module is a list-pages module
|
|
40
|
+
*/
|
|
41
|
+
function isListPagesModule(module: Module): module is Extract<Module, { module: "list-pages" }> {
|
|
42
|
+
return module.module === "list-pages";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Regex pattern for matching ListPages template variables.
|
|
47
|
+
*
|
|
48
|
+
* Matches `%%name%%`, `%%name{param}%%`, `%%name(param)%%`, and `%%name|format%%`.
|
|
49
|
+
* The captured groups are: [1] variable name, [2] brace parameter, [3] paren parameter,
|
|
50
|
+
* [4] format/pipe parameter.
|
|
51
|
+
*/
|
|
52
|
+
const VARIABLE_REGEX = /%%([a-z_]+)(?:\{([^}]+)\})?(?:\((\d+)\))?(?:\|([^%]+))?%%/gi;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Default template used when a ListPages module has no body specified.
|
|
56
|
+
* This matches Wikidot's built-in default template that shows title, author, date, and summary.
|
|
57
|
+
*/
|
|
58
|
+
const DEFAULT_BODY_TEMPLATE = `+ %%title_linked%%
|
|
59
|
+
|
|
60
|
+
by %%created_by_linked%% %%created_at%%
|
|
61
|
+
|
|
62
|
+
%%summary%%`;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Complete result of extracting data requirements from an AST.
|
|
66
|
+
*
|
|
67
|
+
* Contains everything needed to fetch external data and then resolve modules:
|
|
68
|
+
* the data requirements tell the application what to fetch, and the pre-compiled
|
|
69
|
+
* templates are used during the resolution phase to efficiently render results.
|
|
70
|
+
*/
|
|
71
|
+
export interface ExtractionResult {
|
|
72
|
+
/** Data requirements for external fetching */
|
|
73
|
+
requirements: DataRequirements;
|
|
74
|
+
/** Pre-compiled ListPages templates keyed by module id */
|
|
75
|
+
compiledListPagesTemplates: Map<number, CompiledTemplate>;
|
|
76
|
+
/** Pre-compiled ListUsers templates keyed by module id */
|
|
77
|
+
compiledListUsersTemplates: Map<number, ListUsersCompiledTemplate>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Narrowed type for the list-pages variant of the Module union.
|
|
82
|
+
* Used internally for type-safe access to list-pages-specific fields.
|
|
83
|
+
*/
|
|
84
|
+
type ListPagesModuleData = Extract<Module, { module: "list-pages" }>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Type guard to narrow a Module union to the list-users variant.
|
|
88
|
+
*
|
|
89
|
+
* @param module - A Module discriminated union value
|
|
90
|
+
* @returns true if the module is a list-users module
|
|
91
|
+
*/
|
|
92
|
+
function isListUsersModule(module: Module): module is Extract<Module, { module: "list-users" }> {
|
|
93
|
+
return module.module === "list-users";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Extract all data requirements from a parsed AST.
|
|
98
|
+
*
|
|
99
|
+
* Walks the entire AST to find ListPages and ListUsers module elements,
|
|
100
|
+
* analyzes their templates to determine which variables are used, builds
|
|
101
|
+
* query objects from their attributes, and pre-compiles their templates.
|
|
102
|
+
*
|
|
103
|
+
* Each module is assigned a sequential ID (separate counters for ListPages
|
|
104
|
+
* and ListUsers) that is used to correlate requirements with fetched data
|
|
105
|
+
* and compiled templates during the resolution phase.
|
|
106
|
+
*
|
|
107
|
+
* @param ast - The parsed syntax tree to analyze
|
|
108
|
+
* @returns Extraction result containing requirements and compiled templates
|
|
109
|
+
*/
|
|
110
|
+
export function extractDataRequirements(ast: SyntaxTree): ExtractionResult {
|
|
111
|
+
const requirements: DataRequirements = {
|
|
112
|
+
listPages: [],
|
|
113
|
+
listUsers: [],
|
|
114
|
+
};
|
|
115
|
+
const compiledListPagesTemplates = new Map<number, CompiledTemplate>();
|
|
116
|
+
const compiledListUsersTemplates = new Map<number, ListUsersCompiledTemplate>();
|
|
117
|
+
|
|
118
|
+
let listPagesIdCounter = 0;
|
|
119
|
+
let listUsersIdCounter = 0;
|
|
120
|
+
|
|
121
|
+
walkElements(ast.elements, (element) => {
|
|
122
|
+
if (element.element !== "module") return;
|
|
123
|
+
|
|
124
|
+
if (isListPagesModule(element.data)) {
|
|
125
|
+
const listPages = element.data;
|
|
126
|
+
const id = listPagesIdCounter++;
|
|
127
|
+
|
|
128
|
+
// Get body template (use Wikidot default when not specified)
|
|
129
|
+
const body = listPages.body ?? DEFAULT_BODY_TEMPLATE;
|
|
130
|
+
|
|
131
|
+
// Extract needed variables from template
|
|
132
|
+
const extraction = extractVariablesFromTemplate(body);
|
|
133
|
+
|
|
134
|
+
// Build query from module parameters
|
|
135
|
+
const query = buildQuery(listPages);
|
|
136
|
+
|
|
137
|
+
// Get urlAttrPrefix from parsed module data (kebab-case key)
|
|
138
|
+
const urlAttrPrefix = listPages["url-attr-prefix"];
|
|
139
|
+
|
|
140
|
+
// All raw attributes (for @URL resolution by external apps)
|
|
141
|
+
const rawAttributes = listPages.attributes ?? {};
|
|
142
|
+
|
|
143
|
+
requirements.listPages.push({
|
|
144
|
+
id,
|
|
145
|
+
query,
|
|
146
|
+
neededVariables: extraction.variables,
|
|
147
|
+
contentSectionIndices: extraction.contentIndices,
|
|
148
|
+
previewLengths: extraction.previewLengths,
|
|
149
|
+
formFields: extraction.formFields,
|
|
150
|
+
tagsLinkPrefix: extraction.tagsLinkPrefix,
|
|
151
|
+
hiddenTagsLinkPrefix: extraction.hiddenTagsLinkPrefix,
|
|
152
|
+
urlAttrPrefix,
|
|
153
|
+
rawAttributes,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Compile template
|
|
157
|
+
compiledListPagesTemplates.set(id, compileTemplate(body));
|
|
158
|
+
} else if (isListUsersModule(element.data)) {
|
|
159
|
+
const listUsers = element.data;
|
|
160
|
+
const id = listUsersIdCounter++;
|
|
161
|
+
const body = listUsers.body ?? "";
|
|
162
|
+
|
|
163
|
+
const neededVariables = extractListUsersVariables(body);
|
|
164
|
+
|
|
165
|
+
requirements.listUsers.push({
|
|
166
|
+
id,
|
|
167
|
+
users: listUsers.users,
|
|
168
|
+
neededVariables,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
compiledListUsersTemplates.set(id, compileListUsersTemplate(body));
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return { requirements, compiledListPagesTemplates, compiledListUsersTemplates };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Build a ListPagesQuery from the parsed module attributes.
|
|
180
|
+
*
|
|
181
|
+
* Maps module attribute names (which use kebab-case in the AST) to the
|
|
182
|
+
* query's camelCase property names. Also extracts data form fields
|
|
183
|
+
* (attributes prefixed with `_`) into a separate record.
|
|
184
|
+
*
|
|
185
|
+
* @param module - Parsed list-pages module data from the AST
|
|
186
|
+
* @returns Query parameters ready for normalization and external fetching
|
|
187
|
+
*/
|
|
188
|
+
function buildQuery(module: ListPagesModuleData): ListPagesQuery {
|
|
189
|
+
return {
|
|
190
|
+
pagetype: module.pagetype as ListPagesQuery["pagetype"],
|
|
191
|
+
category: module.category,
|
|
192
|
+
tags: module.tags,
|
|
193
|
+
parent: module.parent,
|
|
194
|
+
linkTo: module["link-to"],
|
|
195
|
+
createdAt: module["created-at"],
|
|
196
|
+
updatedAt: module["updated-at"],
|
|
197
|
+
createdBy: module["created-by"],
|
|
198
|
+
rating: module.rating,
|
|
199
|
+
votes: module.votes,
|
|
200
|
+
name: module.name,
|
|
201
|
+
fullname: module.fullname,
|
|
202
|
+
range: module.range as ListPagesQuery["range"],
|
|
203
|
+
order: module.order,
|
|
204
|
+
offset: module.offset,
|
|
205
|
+
limit: module.limit,
|
|
206
|
+
perPage: module["per-page"],
|
|
207
|
+
reverse: module.reverse,
|
|
208
|
+
// dataFormFields is extracted from attributes prefixed with _
|
|
209
|
+
dataFormFields: extractDataFormFields(module.attributes),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Extract data form field selectors from module attributes.
|
|
215
|
+
*
|
|
216
|
+
* Wikidot's ListPages supports filtering by data form fields using attribute
|
|
217
|
+
* names prefixed with `_` (e.g., `_color="red"`). The prefix is stripped from
|
|
218
|
+
* the key in the returned record.
|
|
219
|
+
*
|
|
220
|
+
* @param attributes - Raw module attributes
|
|
221
|
+
* @returns Record of field name to value (without `_` prefix), or undefined if no fields found
|
|
222
|
+
*/
|
|
223
|
+
function extractDataFormFields(
|
|
224
|
+
attributes: Record<string, string>,
|
|
225
|
+
): Record<string, string> | undefined {
|
|
226
|
+
const fields: Record<string, string> = {};
|
|
227
|
+
let hasFields = false;
|
|
228
|
+
|
|
229
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
230
|
+
if (key.startsWith("_")) {
|
|
231
|
+
fields[key.slice(1)] = value;
|
|
232
|
+
hasFields = true;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return hasFields ? fields : undefined;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Internal result of analyzing a ListPages template string for variable usage.
|
|
241
|
+
*/
|
|
242
|
+
interface TemplateExtraction {
|
|
243
|
+
variables: ListPagesVariable[];
|
|
244
|
+
contentIndices: number[];
|
|
245
|
+
previewLengths: number[];
|
|
246
|
+
formFields: string[];
|
|
247
|
+
tagsLinkPrefix?: string;
|
|
248
|
+
hiddenTagsLinkPrefix?: string;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Extract all variable references from a ListPages template string.
|
|
253
|
+
*
|
|
254
|
+
* Scans the template for `%%variable%%` patterns and categorizes them:
|
|
255
|
+
* - Simple variables (e.g., `%%title%%`, `%%created_at%%`)
|
|
256
|
+
* - Parameterized variables (e.g., `%%content{2}%%`, `%%form_data{color}%%`)
|
|
257
|
+
* - Preview with length (e.g., `%%preview(100)%%`)
|
|
258
|
+
* - Tags with prefix (e.g., `%%tags_linked|/tag/%%`)
|
|
259
|
+
*
|
|
260
|
+
* @param template - The template string to analyze
|
|
261
|
+
* @returns Structured extraction result with categorized variable information
|
|
262
|
+
*/
|
|
263
|
+
function extractVariablesFromTemplate(template: string): TemplateExtraction {
|
|
264
|
+
const variables = new Set<ListPagesVariable>();
|
|
265
|
+
const contentIndices = new Set<number>();
|
|
266
|
+
const previewLengths = new Set<number>();
|
|
267
|
+
const formFields = new Set<string>();
|
|
268
|
+
let tagsLinkPrefix: string | undefined;
|
|
269
|
+
let hiddenTagsLinkPrefix: string | undefined;
|
|
270
|
+
|
|
271
|
+
for (const match of template.matchAll(VARIABLE_REGEX)) {
|
|
272
|
+
const [, name, braceParam, parenParam, format] = match;
|
|
273
|
+
if (!name) continue;
|
|
274
|
+
const varName = name.toLowerCase();
|
|
275
|
+
|
|
276
|
+
// Handle parameterized variables
|
|
277
|
+
if (braceParam !== undefined) {
|
|
278
|
+
switch (varName) {
|
|
279
|
+
case "content":
|
|
280
|
+
contentIndices.add(Number(braceParam));
|
|
281
|
+
variables.add("content_n");
|
|
282
|
+
continue;
|
|
283
|
+
case "form_data":
|
|
284
|
+
formFields.add(braceParam);
|
|
285
|
+
variables.add("form_data");
|
|
286
|
+
continue;
|
|
287
|
+
case "form_raw":
|
|
288
|
+
formFields.add(braceParam);
|
|
289
|
+
variables.add("form_raw");
|
|
290
|
+
continue;
|
|
291
|
+
case "form_label":
|
|
292
|
+
formFields.add(braceParam);
|
|
293
|
+
variables.add("form_label");
|
|
294
|
+
continue;
|
|
295
|
+
case "form_hint":
|
|
296
|
+
formFields.add(braceParam);
|
|
297
|
+
variables.add("form_hint");
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (parenParam !== undefined && varName === "preview") {
|
|
303
|
+
previewLengths.add(Number(parenParam));
|
|
304
|
+
variables.add("preview_n");
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Handle tags_linked with prefix
|
|
309
|
+
if (varName === "tags_linked") {
|
|
310
|
+
if (format) tagsLinkPrefix = format;
|
|
311
|
+
variables.add("tags_linked");
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (varName === "_tags_linked") {
|
|
315
|
+
if (format) hiddenTagsLinkPrefix = format;
|
|
316
|
+
variables.add("_tags_linked");
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Normalize and add variable
|
|
321
|
+
const normalized = normalizeVariableName(varName);
|
|
322
|
+
if (normalized) {
|
|
323
|
+
variables.add(normalized);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
variables: Array.from(variables),
|
|
329
|
+
contentIndices: Array.from(contentIndices).sort((a, b) => a - b),
|
|
330
|
+
previewLengths: Array.from(previewLengths).sort((a, b) => a - b),
|
|
331
|
+
formFields: Array.from(formFields).sort(),
|
|
332
|
+
tagsLinkPrefix,
|
|
333
|
+
hiddenTagsLinkPrefix,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Normalize a variable name to its canonical `ListPagesVariable` form.
|
|
339
|
+
*
|
|
340
|
+
* Only known variable names are accepted. Unknown names return null,
|
|
341
|
+
* causing them to be silently ignored (matching Wikidot's behavior of
|
|
342
|
+
* rendering unknown variables as empty strings).
|
|
343
|
+
*
|
|
344
|
+
* @param name - Lowercase variable name extracted from the template
|
|
345
|
+
* @returns The canonical variable name, or null if not recognized
|
|
346
|
+
*/
|
|
347
|
+
function normalizeVariableName(name: string): ListPagesVariable | null {
|
|
348
|
+
// Direct mapping for known variables
|
|
349
|
+
const knownVariables: ListPagesVariable[] = [
|
|
350
|
+
// Lifecycle
|
|
351
|
+
"created_at",
|
|
352
|
+
"created_by",
|
|
353
|
+
"created_by_unix",
|
|
354
|
+
"created_by_id",
|
|
355
|
+
"created_by_linked",
|
|
356
|
+
"updated_at",
|
|
357
|
+
"updated_by",
|
|
358
|
+
"updated_by_unix",
|
|
359
|
+
"updated_by_id",
|
|
360
|
+
"updated_by_linked",
|
|
361
|
+
"commented_at",
|
|
362
|
+
"commented_by",
|
|
363
|
+
"commented_by_unix",
|
|
364
|
+
"commented_by_id",
|
|
365
|
+
"commented_by_linked",
|
|
366
|
+
// Structure
|
|
367
|
+
"name",
|
|
368
|
+
"category",
|
|
369
|
+
"fullname",
|
|
370
|
+
"title",
|
|
371
|
+
"title_linked",
|
|
372
|
+
"link",
|
|
373
|
+
"parent_name",
|
|
374
|
+
"parent_category",
|
|
375
|
+
"parent_fullname",
|
|
376
|
+
"parent_title",
|
|
377
|
+
"parent_title_linked",
|
|
378
|
+
// Content
|
|
379
|
+
"content",
|
|
380
|
+
"preview",
|
|
381
|
+
"summary",
|
|
382
|
+
"first_paragraph",
|
|
383
|
+
// Tags
|
|
384
|
+
"tags",
|
|
385
|
+
"_tags",
|
|
386
|
+
// Metrics
|
|
387
|
+
"children",
|
|
388
|
+
"comments",
|
|
389
|
+
"size",
|
|
390
|
+
"rating",
|
|
391
|
+
"rating_votes",
|
|
392
|
+
"rating_percent",
|
|
393
|
+
"revisions",
|
|
394
|
+
// Pagination
|
|
395
|
+
"index",
|
|
396
|
+
"total",
|
|
397
|
+
"limit",
|
|
398
|
+
"total_or_limit",
|
|
399
|
+
// Site
|
|
400
|
+
"site_title",
|
|
401
|
+
"site_name",
|
|
402
|
+
"site_domain",
|
|
403
|
+
];
|
|
404
|
+
|
|
405
|
+
if (knownVariables.includes(name as ListPagesVariable)) {
|
|
406
|
+
return name as ListPagesVariable;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* ListPages module for Wikidot's `[[module ListPages ...]]` block.
|
|
4
|
+
*
|
|
5
|
+
* This is the most feature-rich Wikidot module, enabling dynamic page listings
|
|
6
|
+
* with filtering, sorting, pagination, and template-based rendering. The module
|
|
7
|
+
* follows a three-phase lifecycle:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Parse** - Convert `[[module ListPages ...]]` markup into an AST node
|
|
10
|
+
* 2. **Extract** - Analyze the AST to determine what data is needed (queries, template variables)
|
|
11
|
+
* 3. **Resolve** - Substitute fetched data into templates and re-parse as wikitext
|
|
12
|
+
*
|
|
13
|
+
* This barrel module re-exports all public types and functions from the sub-modules:
|
|
14
|
+
* - `parser` - Module rule for parsing ListPages markup
|
|
15
|
+
* - `types` - Query, variable, data requirement, and normalized query types
|
|
16
|
+
* - `extract` - AST analysis and data requirement extraction
|
|
17
|
+
* - `resolve` - Data substitution and template rendering
|
|
18
|
+
* - `compiler` - Template string compilation into executable functions
|
|
19
|
+
* - `url-resolver` - `@URL|default` parameter resolution for HPC support
|
|
20
|
+
* - `normalize` - Raw query string parsing into structured types
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// Parser
|
|
26
|
+
export { listPagesModuleRule } from "./parser";
|
|
27
|
+
|
|
28
|
+
// Types
|
|
29
|
+
export type {
|
|
30
|
+
ListPagesQuery,
|
|
31
|
+
ListPagesVariable,
|
|
32
|
+
ListPagesDataRequirement,
|
|
33
|
+
DataRequirements,
|
|
34
|
+
UserInfo,
|
|
35
|
+
PageData,
|
|
36
|
+
SiteContext,
|
|
37
|
+
ListPagesExternalData,
|
|
38
|
+
ListPagesDataFetcher,
|
|
39
|
+
VariableContext,
|
|
40
|
+
CompiledTemplate,
|
|
41
|
+
// Normalized types
|
|
42
|
+
NormalizedListPagesQuery,
|
|
43
|
+
NormalizedTags,
|
|
44
|
+
NormalizedCategory,
|
|
45
|
+
NormalizedOrder,
|
|
46
|
+
NormalizedParent,
|
|
47
|
+
NormalizedDateSelector,
|
|
48
|
+
NormalizedNumericSelector,
|
|
49
|
+
OrderField,
|
|
50
|
+
OrderDirection,
|
|
51
|
+
DateComparisonOp,
|
|
52
|
+
NumericComparisonOp,
|
|
53
|
+
} from "./types";
|
|
54
|
+
|
|
55
|
+
// Extraction
|
|
56
|
+
export type { ExtractionResult } from "./extract";
|
|
57
|
+
export { extractDataRequirements } from "./extract";
|
|
58
|
+
|
|
59
|
+
// Resolution
|
|
60
|
+
export type { ParseFunction, ListPagesModuleData } from "./resolve";
|
|
61
|
+
export { isListPagesModule, resolveListPages } from "./resolve";
|
|
62
|
+
|
|
63
|
+
// Compiler
|
|
64
|
+
export { compileTemplate } from "./compiler";
|
|
65
|
+
|
|
66
|
+
// URL Resolution
|
|
67
|
+
export {
|
|
68
|
+
parseUrlParams,
|
|
69
|
+
resolveUrlValue,
|
|
70
|
+
resolveQuery,
|
|
71
|
+
resolveAndNormalizeQuery,
|
|
72
|
+
} from "./url-resolver";
|
|
73
|
+
|
|
74
|
+
// Query Normalization
|
|
75
|
+
export {
|
|
76
|
+
normalizeQuery,
|
|
77
|
+
parseTags,
|
|
78
|
+
parseCategory,
|
|
79
|
+
parseOrder,
|
|
80
|
+
parseParent,
|
|
81
|
+
parseDateSelector,
|
|
82
|
+
parseNumericSelector,
|
|
83
|
+
} from "./normalize";
|