@wdprlib/parser 4.2.0 → 4.4.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 (30) hide show
  1. package/README.md +28 -38
  2. package/dist/index.cjs +973 -134
  3. package/dist/index.d.cts +195 -158
  4. package/dist/index.d.ts +195 -158
  5. package/dist/index.js +955 -113
  6. package/package.json +2 -2
  7. package/src/index.ts +17 -0
  8. package/src/parser/parse/context.ts +1 -0
  9. package/src/parser/parse/options.ts +6 -0
  10. package/src/parser/parse/result.ts +3 -1
  11. package/src/parser/rules/block/gallery/index.ts +215 -0
  12. package/src/parser/rules/block/gallery/items.ts +62 -0
  13. package/src/parser/rules/block/index.ts +3 -0
  14. package/src/parser/rules/block/module/include/index.ts +6 -1
  15. package/src/parser/rules/block/module/include/resolve/index.ts +23 -3
  16. package/src/parser/rules/block/module/include/resolve/iterate.ts +71 -0
  17. package/src/parser/rules/block/module/index.ts +2 -1
  18. package/src/parser/rules/block/module/listpages/resolution/items.ts +2 -2
  19. package/src/parser/rules/block/module/listpages/resolution/wrapper.ts +3 -3
  20. package/src/parser/rules/block/module/listusers/resolve.ts +2 -2
  21. package/src/parser/rules/block/module/resolution/document.ts +357 -0
  22. package/src/parser/rules/block/module/resolution/resolve-async.ts +269 -0
  23. package/src/parser/rules/block/module/resolution/styles.ts +134 -12
  24. package/src/parser/rules/block/module/resolution/walk-resolve.ts +19 -1
  25. package/src/parser/rules/block/module/resolve.ts +32 -13
  26. package/src/parser/rules/block/module/types.ts +7 -2
  27. package/src/parser/rules/contracts/parse-context.ts +1 -0
  28. package/src/pipeline/index.ts +7 -0
  29. package/src/pipeline/process.ts +105 -0
  30. package/src/pipeline/types.ts +63 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wdprlib/parser",
3
- "version": "4.2.0",
3
+ "version": "4.4.0",
4
4
  "description": "Parser for Wikidot markup",
5
5
  "keywords": [
6
6
  "ast",
@@ -41,6 +41,6 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@braintree/sanitize-url": "^7.1.1",
44
- "@wdprlib/ast": "2.2.0"
44
+ "@wdprlib/ast": "2.4.0"
45
45
  }
46
46
  }
package/src/index.ts CHANGED
@@ -58,6 +58,12 @@ export type {
58
58
  DateItem,
59
59
  Embed,
60
60
  TocEntry,
61
+ // Gallery
62
+ GallerySize,
63
+ GalleryOrder,
64
+ GalleryItem,
65
+ GalleryContent,
66
+ GalleryData,
61
67
  // Diagnostics
62
68
  Diagnostic,
63
69
  DiagnosticSeverity,
@@ -92,6 +98,15 @@ export { Lexer, tokenize, createToken } from "./lexer";
92
98
  export type { ParserOptions } from "./parser";
93
99
  export { Parser, parse } from "./parser";
94
100
 
101
+ // High-level parser pipeline
102
+ export { processWikitext } from "./pipeline";
103
+ export type {
104
+ ProcessedWikitextDocument,
105
+ ProcessWikitextCallbackContext,
106
+ ProcessWikitextDataProvider,
107
+ ProcessWikitextOptions,
108
+ } from "./pipeline";
109
+
95
110
  // Modules (ListPages, ListUsers, IfTags, Include, etc.)
96
111
  export type {
97
112
  // ListPages query types
@@ -115,6 +130,7 @@ export type {
115
130
  ExtractionResult,
116
131
  // Resolution types
117
132
  ParseFunction,
133
+ ModuleParseResult,
118
134
  ModuleSourceTransform,
119
135
  ResolveOptions,
120
136
  // Include resolution
@@ -157,6 +173,7 @@ export {
157
173
  extractIncludeReferences,
158
174
  resolveIncludes,
159
175
  resolveIncludesAsync,
176
+ resolveIncludesAsyncWithTrace,
160
177
  resolveIncludesWithTrace,
161
178
  // IfTags source-level preprocessing (run between include expansion and parse)
162
179
  preprocessIftags,
@@ -10,6 +10,7 @@ export function createParseContext(tokens: Token[], options: ParserOptions = {})
10
10
  version: options.version ?? "wikidot",
11
11
  trackPositions: options.trackPositions ?? true,
12
12
  settings: options.settings ?? DEFAULT_SETTINGS,
13
+ appendImplicitFootnoteBlock: options.appendImplicitFootnoteBlock ?? true,
13
14
  footnotes: [],
14
15
  tocEntries: [],
15
16
  codeBlocks: [],
@@ -31,4 +31,10 @@ export interface ParserOptions {
31
31
  * - `string[]`: every iftags block is evaluated against the given tags eagerly.
32
32
  */
33
33
  pageTags?: string[] | null;
34
+ /**
35
+ * Append the implicit document-level footnote block when no explicit block
36
+ * exists. Defaults to true. Fragment pipelines can disable this and add one
37
+ * block after all fragments have been merged.
38
+ */
39
+ appendImplicitFootnoteBlock?: boolean;
34
40
  }
@@ -10,7 +10,9 @@ import { containsFootnoteBlock } from "./footnotes";
10
10
 
11
11
  export function finalizeParseResult(ctx: ParseContext, children: Element[]): ParseResult {
12
12
  const cleanedChildren = postprocessChildren(children);
13
- appendImplicitFootnoteBlock(cleanedChildren);
13
+ if (ctx.appendImplicitFootnoteBlock) {
14
+ appendImplicitFootnoteBlock(cleanedChildren);
15
+ }
14
16
 
15
17
  return {
16
18
  ast: buildSyntaxTree(ctx, cleanedChildren),
@@ -0,0 +1,215 @@
1
+ /**
2
+ *
3
+ * Block rule for the Wikidot `[[gallery]]` image gallery.
4
+ *
5
+ * ```
6
+ * [[gallery size="thumbnail" order="name" viewer="no"]]
7
+ * : first-image.jpg
8
+ * : *page/other-image.jpg link="some-page" alt="Alt text"
9
+ * [[/gallery]]
10
+ * ```
11
+ *
12
+ * The content form requires the `]]` to be followed immediately by one or
13
+ * more `: source` lines and a `[[/gallery]]` close tag (Wikidot regex
14
+ * `\[\[gallery(\s[^\]]*?)?\]\](?:((?:\n: [^\n]+)+)\n\[\[\/gallery\]\])?`).
15
+ * Anything else falls back to the standalone content-less form, which shows
16
+ * the current page's image attachments after data resolution
17
+ * (`content: { type: "auto", files: null }`); the following text then
18
+ * parses normally.
19
+ *
20
+ * Honored opening-tag attributes are `size` (small / medium / thumbnail /
21
+ * square / original, fallback thumbnail), `viewer` (`"no"`/`"false"`
22
+ * disable the lightbox) and `order` (auto-collection sort order, with
23
+ * Wikidot's deprecated aliases normalized).
24
+ *
25
+ * Deliberate differences from the Wikidot parser: the opening tag must
26
+ * fit on one line and attribute names are lowercased with unquoted values
27
+ * accepted (both shared with wdpr's other block rules), and `flickr:`
28
+ * sources get no special treatment — they resolve like any other
29
+ * filename (wdpr does not call the Flickr API).
30
+ *
31
+ * @module
32
+ */
33
+ import type { Element, GalleryItem, GalleryOrder, GallerySize } from "@wdprlib/ast";
34
+ import type { BlockRule, ParseContext, RuleResult } from "../../types";
35
+ import { parseAttributesRaw, parseBlockName } from "../utils";
36
+ import { parseGalleryItemLine } from "./items";
37
+
38
+ export { parseGalleryItemLine } from "./items";
39
+
40
+ const GALLERY_SIZES: readonly string[] = ["small", "medium", "thumbnail", "square", "original"];
41
+
42
+ /** Validate a size keyword, falling back to thumbnail like Wikidot. */
43
+ function normalizeSize(value: string | undefined): GallerySize {
44
+ return value !== undefined && GALLERY_SIZES.includes(value)
45
+ ? (value as GallerySize)
46
+ : "thumbnail";
47
+ }
48
+
49
+ /** `viewer="no"` / `viewer="false"` disable the lightbox; anything else enables it. */
50
+ function normalizeViewer(value: string | undefined): boolean {
51
+ return value !== "no" && value !== "false";
52
+ }
53
+
54
+ /**
55
+ * Normalize the `order` attribute, folding Wikidot's deprecated aliases
56
+ * (`nameDesc`, `dateAdded`, `dateAddedDesc`) and the documented
57
+ * ListPages-compatibility forms (`"name desc desc"` / `"created_at desc
58
+ * desc"`, which mean the same as without the `desc desc`).
59
+ */
60
+ function normalizeOrder(value: string | undefined): GalleryOrder {
61
+ switch (value) {
62
+ case "name":
63
+ case "name desc":
64
+ case "created_at":
65
+ case "created_at desc":
66
+ return value;
67
+ case "nameDesc":
68
+ return "name desc";
69
+ case "dateAdded":
70
+ return "created_at";
71
+ case "dateAddedDesc":
72
+ return "created_at desc";
73
+ case "name desc desc":
74
+ return "name";
75
+ case "created_at desc desc":
76
+ return "created_at";
77
+ default:
78
+ return "name";
79
+ }
80
+ }
81
+
82
+ interface GalleryContentResult {
83
+ /** Trimmed line contents (after the leading `: `) */
84
+ lines: string[];
85
+ /** Token count from the content start (the NEWLINE after `]]`) through `[[/gallery]]` */
86
+ consumed: number;
87
+ }
88
+
89
+ /**
90
+ * Try to match the content form starting at `pos` (the token right after the
91
+ * opening tag's `]]`): one NEWLINE, then consecutive `: source` lines, then a
92
+ * NEWLINE directly followed by `[[/gallery]]`. Returns null when the token
93
+ * stream deviates from the Wikidot regex, which makes the gallery standalone.
94
+ */
95
+ function tryParseGalleryContent(ctx: ParseContext, pos: number): GalleryContentResult | null {
96
+ const start = pos;
97
+ if (ctx.tokens[pos]?.type !== "NEWLINE") {
98
+ return null;
99
+ }
100
+
101
+ const lines: string[] = [];
102
+ let p = pos + 1;
103
+
104
+ for (;;) {
105
+ const colon = ctx.tokens[p];
106
+ if (colon?.type !== "COLON" || !colon.lineStart) {
107
+ break;
108
+ }
109
+ // Wikidot requires a literal space after the colon (`\n: `)
110
+ const space = ctx.tokens[p + 1];
111
+ if (space?.type !== "WHITESPACE" || !space.value.startsWith(" ")) {
112
+ break;
113
+ }
114
+
115
+ let content = "";
116
+ let q = p + 1;
117
+ while (q < ctx.tokens.length) {
118
+ const token = ctx.tokens[q];
119
+ if (!token || token.type === "NEWLINE" || token.type === "EOF") {
120
+ break;
121
+ }
122
+ content += token.value;
123
+ q++;
124
+ }
125
+ // `: [^\n]+` needs at least one character after the space
126
+ if (content === " ") {
127
+ return null;
128
+ }
129
+ if (ctx.tokens[q]?.type !== "NEWLINE") {
130
+ // line hit EOF: the close tag can no longer follow on its own line
131
+ return null;
132
+ }
133
+
134
+ lines.push(content.trim());
135
+ p = q + 1;
136
+ }
137
+
138
+ if (lines.length === 0) {
139
+ return null;
140
+ }
141
+
142
+ // The last consumed NEWLINE must be directly followed by [[/gallery]]
143
+ if (ctx.tokens[p]?.type !== "BLOCK_END_OPEN") {
144
+ return null;
145
+ }
146
+ const nameResult = parseBlockName(ctx, p + 1);
147
+ if (!nameResult || nameResult.name !== "gallery") {
148
+ return null;
149
+ }
150
+ const closePos = p + 1 + nameResult.consumed;
151
+ if (ctx.tokens[closePos]?.type !== "BLOCK_CLOSE") {
152
+ return null;
153
+ }
154
+
155
+ return { lines, consumed: closePos + 1 - start };
156
+ }
157
+
158
+ export const galleryRule: BlockRule = {
159
+ name: "gallery",
160
+ startTokens: ["BLOCK_OPEN"],
161
+ requiresLineStart: true,
162
+
163
+ parse(ctx: ParseContext): RuleResult<Element> {
164
+ if (ctx.tokens[ctx.pos]?.type !== "BLOCK_OPEN") {
165
+ return { success: false };
166
+ }
167
+
168
+ let pos = ctx.pos + 1;
169
+ const nameResult = parseBlockName(ctx, pos);
170
+ if (!nameResult || nameResult.name !== "gallery") {
171
+ return { success: false };
172
+ }
173
+ pos += nameResult.consumed;
174
+
175
+ const attrResult = parseAttributesRaw(ctx, pos);
176
+ pos += attrResult.consumed;
177
+
178
+ if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
179
+ return { success: false };
180
+ }
181
+ pos++;
182
+
183
+ const size = normalizeSize(attrResult.attrs.size);
184
+ const viewer = normalizeViewer(attrResult.attrs.viewer);
185
+ const order = normalizeOrder(attrResult.attrs.order);
186
+ const openConsumed = pos - ctx.pos;
187
+
188
+ const content = tryParseGalleryContent(ctx, pos);
189
+ if (!content) {
190
+ return {
191
+ success: true,
192
+ elements: [
193
+ {
194
+ element: "gallery",
195
+ data: { size, order, viewer, content: { type: "auto", files: null } },
196
+ },
197
+ ],
198
+ consumed: openConsumed,
199
+ };
200
+ }
201
+
202
+ const items: GalleryItem[] = content.lines.map(parseGalleryItemLine);
203
+
204
+ return {
205
+ success: true,
206
+ elements: [
207
+ {
208
+ element: "gallery",
209
+ data: { size, order, viewer, content: { type: "items", items } },
210
+ },
211
+ ],
212
+ consumed: openConsumed + content.consumed,
213
+ };
214
+ },
215
+ };
@@ -0,0 +1,62 @@
1
+ import type { GalleryItem } from "@wdprlib/ast";
2
+
3
+ /**
4
+ * Parse one gallery content line (the text after the leading `: `,
5
+ * already trimmed) into a {@link GalleryItem}.
6
+ *
7
+ * The text before the first space is the source; the rest is parsed as
8
+ * `key="value"` attributes of which `link` and `alt` are honored. A
9
+ * leading `*` on the source or on the link value requests a new window.
10
+ */
11
+ export function parseGalleryItemLine(content: string): GalleryItem {
12
+ const spacePos = content.indexOf(" ");
13
+ let source = spacePos < 0 ? content : content.slice(0, spacePos);
14
+ const attrText = spacePos < 0 ? "" : content.slice(spacePos + 1);
15
+
16
+ let newWindow = false;
17
+ if (source.startsWith("*")) {
18
+ source = source.slice(1);
19
+ newWindow = true;
20
+ }
21
+
22
+ const attrs = parseItemAttrs(attrText);
23
+ let link = attrs.get("link") ?? null;
24
+ if (link !== null && link.startsWith("*")) {
25
+ newWindow = true;
26
+ link = link.slice(1);
27
+ }
28
+ const alt = attrs.get("alt") ?? null;
29
+
30
+ return { source, link, alt, newWindow };
31
+ }
32
+
33
+ /**
34
+ * Parse `key="value"` attribute pairs from a gallery item line, following
35
+ * the splitting behavior of Wikidot's gallery `getAttrs()`: fragments are
36
+ * separated by `="`, values run to the last `"` in each fragment and are
37
+ * backslash-unescaped, and keys are not lowercased.
38
+ */
39
+ function parseItemAttrs(text: string): Map<string, string> {
40
+ const attrs = new Map<string, string>();
41
+ const parts = text.trim().split('="');
42
+ let key = parts[0]?.trim() ?? "";
43
+
44
+ for (let i = 1; i < parts.length; i++) {
45
+ const val = parts[i] ?? "";
46
+ const quotePos = val.lastIndexOf('"');
47
+ if (quotePos < 0) {
48
+ attrs.set(key, "");
49
+ key = val.slice(1).trim();
50
+ } else {
51
+ attrs.set(key, stripslashes(val.slice(0, quotePos)));
52
+ key = val.slice(quotePos + 1).trim();
53
+ }
54
+ }
55
+
56
+ return attrs;
57
+ }
58
+
59
+ /** PHP-style `stripslashes()`: drop each escaping backslash, and a trailing lone one. */
60
+ function stripslashes(value: string): string {
61
+ return value.replace(/\\(.)/gs, "$1").replace(/\\$/, "");
62
+ }
@@ -49,6 +49,7 @@ import { iftagsRule } from "./iftags";
49
49
  import { tocRule } from "./toc";
50
50
  import { orphanLiRule } from "./orphan-li";
51
51
  import { bibliographyRule } from "./bibliography";
52
+ import { galleryRule } from "./gallery";
52
53
 
53
54
  export { headingRule } from "./heading";
54
55
  export { horizontalRuleRule } from "./horizontal-rule";
@@ -79,6 +80,7 @@ export { iftagsRule } from "./iftags";
79
80
  export { tocRule } from "./toc";
80
81
  export { orphanLiRule } from "./orphan-li";
81
82
  export { bibliographyRule } from "./bibliography";
83
+ export { galleryRule } from "./gallery";
82
84
 
83
85
  /**
84
86
  * All block rules in priority order.
@@ -120,6 +122,7 @@ export const blockRules: BlockRule[] = [
120
122
  iframeRule,
121
123
  iftagsRule,
122
124
  bibliographyRule,
125
+ galleryRule,
123
126
  divRule,
124
127
  // paragraphRule is not included - used as fallback
125
128
  ];
@@ -16,7 +16,12 @@
16
16
  * @module
17
17
  */
18
18
 
19
- export { resolveIncludes, resolveIncludesAsync, resolveIncludesWithTrace } from "./resolve";
19
+ export {
20
+ resolveIncludes,
21
+ resolveIncludesAsync,
22
+ resolveIncludesAsyncWithTrace,
23
+ resolveIncludesWithTrace,
24
+ } from "./resolve";
20
25
  export { extractIncludeReferences } from "./references";
21
26
  export type {
22
27
  IncludeFetcher,
@@ -7,7 +7,11 @@
7
7
 
8
8
  import type { PageRef } from "@wdprlib/ast";
9
9
  import { createCachedAsyncIncludeFetcher, createCachedIncludeFetcher } from "./cache";
10
- import { expandIterative, expandIterativeAsync, expandIterativeWithTrace } from "./iterate";
10
+ import {
11
+ expandIterative,
12
+ expandIterativeAsyncWithTrace,
13
+ expandIterativeWithTrace,
14
+ } from "./iterate";
11
15
  import type {
12
16
  AsyncIncludeFetcher,
13
17
  IncludeFetcher,
@@ -84,13 +88,29 @@ export async function resolveIncludesAsync(
84
88
  fetcher: AsyncIncludeFetcher,
85
89
  options?: ResolveIncludesOptions,
86
90
  ): Promise<string> {
91
+ return (await resolveIncludesAsyncWithTrace(source, fetcher, options)).source;
92
+ }
93
+
94
+ /**
95
+ * Async include expansion with dependency and iteration trace data.
96
+ */
97
+ export async function resolveIncludesAsyncWithTrace(
98
+ source: string,
99
+ fetcher: AsyncIncludeFetcher,
100
+ options?: ResolveIncludesOptions,
101
+ ): Promise<ResolveIncludesTraceResult> {
87
102
  if (options?.settings && !options.settings.enablePageSyntax) {
88
- return source;
103
+ return {
104
+ source,
105
+ dependencies: [],
106
+ iterations: [],
107
+ reachedMaxIterations: false,
108
+ };
89
109
  }
90
110
 
91
111
  const maxIterations = options?.maxIterations ?? 10;
92
112
  const cachedFetcher = createCachedAsyncIncludeFetcher(fetcher, normalizePageKey);
93
- return expandIterativeAsync(source, cachedFetcher, maxIterations);
113
+ return expandIterativeAsyncWithTrace(source, cachedFetcher, maxIterations);
94
114
  }
95
115
 
96
116
  /**
@@ -102,6 +102,41 @@ export async function expandIterativeAsync(
102
102
  return current;
103
103
  }
104
104
 
105
+ /** Async counterpart of {@link expandIterativeWithTrace}. */
106
+ export async function expandIterativeAsyncWithTrace(
107
+ source: string,
108
+ fetcher: AsyncIncludeFetcher,
109
+ maxIterations: number,
110
+ ): Promise<ResolveIncludesTraceResult> {
111
+ let current = source;
112
+ const replacementCache = new Map<string, Promise<string>>();
113
+ const dependencies: IncludeDependency[] = [];
114
+ const iterations: ResolveIncludesTraceResult["iterations"] = [];
115
+
116
+ for (let i = 0; i < maxIterations; i++) {
117
+ const expanded = await expandOneIterationAsyncWithTrace(current, fetcher, replacementCache, i);
118
+ if (expanded === null) break;
119
+
120
+ dependencies.push(...expanded.dependencies);
121
+ iterations.push({
122
+ iteration: i,
123
+ directives: expanded.references,
124
+ changed: expanded.source !== current,
125
+ });
126
+
127
+ if (expanded.source === current) break;
128
+ current = expanded.source;
129
+ }
130
+
131
+ return {
132
+ source: current,
133
+ dependencies,
134
+ iterations,
135
+ reachedMaxIterations:
136
+ iterations.length === maxIterations && scanIncludeDirectives(current).length > 0,
137
+ };
138
+ }
139
+
105
140
  function expandOneIteration(
106
141
  source: string,
107
142
  fetcher: IncludeFetcher,
@@ -179,6 +214,42 @@ async function expandOneIterationAsync(
179
214
  return parts.join("");
180
215
  }
181
216
 
217
+ async function expandOneIterationAsyncWithTrace(
218
+ source: string,
219
+ fetcher: AsyncIncludeFetcher,
220
+ replacementCache: Map<string, Promise<string>>,
221
+ iteration: number,
222
+ ): Promise<{
223
+ source: string;
224
+ references: IncludeReference[];
225
+ dependencies: IncludeDependency[];
226
+ } | null> {
227
+ if (!MAYBE_INCLUDE_PATTERN.test(source)) return null;
228
+
229
+ const directives = scanIncludeDirectives(source);
230
+ if (directives.length === 0) return null;
231
+
232
+ const references = directives.map(createIncludeReference);
233
+ const replacements = await Promise.all(
234
+ directives.map(({ inner }) => replaceCachedAsync(inner, fetcher, replacementCache)),
235
+ );
236
+ const parts: string[] = [];
237
+ let lastPos = 0;
238
+
239
+ for (let i = 0; i < directives.length; i++) {
240
+ const { start, end } = directives[i]!;
241
+ parts.push(source.slice(lastPos, start), replacements[i]!);
242
+ lastPos = end;
243
+ }
244
+ parts.push(source.slice(lastPos));
245
+
246
+ return {
247
+ source: parts.join(""),
248
+ references,
249
+ dependencies: references.map((reference) => ({ ...reference, iteration })),
250
+ };
251
+ }
252
+
182
253
  function replaceCached(inner: string, fetcher: IncludeFetcher, cache: Map<string, string>): string {
183
254
  const cached = cache.get(inner);
184
255
  if (cached !== undefined) return cached;
@@ -20,7 +20,7 @@
20
20
  */
21
21
 
22
22
  // Module rule types and registry
23
- export type { ModuleRule } from "./types";
23
+ export type { ModuleParseResult, ModuleRule } from "./types";
24
24
  export { MODULE_RULES, getModuleRuleByName } from "./mapping";
25
25
  export { moduleRule } from "./rule";
26
26
 
@@ -106,6 +106,7 @@ export {
106
106
  extractIncludeReferences,
107
107
  resolveIncludes,
108
108
  resolveIncludesAsync,
109
+ resolveIncludesAsyncWithTrace,
109
110
  resolveIncludesWithTrace,
110
111
  } from "./include";
111
112
 
@@ -1,6 +1,6 @@
1
1
  import type { Element } from "@wdprlib/ast";
2
2
  import type { CompiledTemplate, ListPagesExternalData, VariableContext } from "../types";
3
- import type { ParseFunction } from "../../types";
3
+ import { getModuleParseAst, type ParseFunction } from "../../types";
4
4
  import type { ListPagesModuleData } from "../resolve";
5
5
 
6
6
  export function renderListPagesItems(
@@ -23,7 +23,7 @@ export function renderListPagesItems(
23
23
  site: data.site,
24
24
  };
25
25
 
26
- const itemAst = parse(compiledTemplate(ctx));
26
+ const itemAst = getModuleParseAst(parse(compiledTemplate(ctx)));
27
27
 
28
28
  if (module.separate) {
29
29
  items.push({
@@ -1,5 +1,5 @@
1
1
  import type { Element } from "@wdprlib/ast";
2
- import type { ParseFunction } from "../../types";
2
+ import { getModuleParseAst, type ParseFunction } from "../../types";
3
3
  import type { ListPagesModuleData } from "../resolve";
4
4
 
5
5
  export function wrapListPagesResult(
@@ -14,14 +14,14 @@ export function wrapListPagesResult(
14
14
  const result: Element[] = [];
15
15
 
16
16
  if (module["prepend-line"] && !module.separate) {
17
- const prependAst = parse(module["prepend-line"]);
17
+ const prependAst = getModuleParseAst(parse(module["prepend-line"]));
18
18
  result.push(...prependAst.elements);
19
19
  }
20
20
 
21
21
  result.push(...items);
22
22
 
23
23
  if (module["append-line"] && !module.separate) {
24
- const appendAst = parse(module["append-line"]);
24
+ const appendAst = getModuleParseAst(parse(module["append-line"]));
25
25
  result.push(...appendAst.elements);
26
26
  }
27
27
 
@@ -15,7 +15,7 @@ import type {
15
15
  ListUsersCompiledTemplate,
16
16
  ListUsersVariableContext,
17
17
  } from "./types";
18
- import type { ParseFunction } from "../types";
18
+ import { getModuleParseAst, type ParseFunction } from "../types";
19
19
 
20
20
  /**
21
21
  * Narrowed type for the list-users variant of the Module discriminated union.
@@ -53,6 +53,6 @@ export function resolveListUsers(
53
53
  ): Element[] {
54
54
  const ctx: ListUsersVariableContext = { user: data.user };
55
55
  const substituted = compiledTemplate(ctx);
56
- const itemAst = parse(substituted);
56
+ const itemAst = getModuleParseAst(parse(substituted));
57
57
  return itemAst.elements;
58
58
  }