@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
@@ -1,41 +1,161 @@
1
1
  import type { Element } from "@wdprlib/ast";
2
- import { STYLE_SLOT_PREFIX } from "@wdprlib/ast";
3
- import { mapElementChildren } from "../walk";
2
+ import { STYLE_ANCHOR_PREFIX, STYLE_SLOT_PREFIX } from "@wdprlib/ast";
3
+ import { mapElementChildren, walkElements } from "../walk";
4
4
  import type { IfTagsData } from "../iftags/resolve";
5
5
 
6
6
  type IfTagsDataWithStyleSlot = IfTagsData & {
7
- _styleSlot: number;
7
+ _styleSlot?: number;
8
8
  };
9
9
 
10
+ export type ResolvedStyleSlots = ReadonlyMap<number, readonly string[]>;
11
+
12
+ export interface StyleCollectionResult {
13
+ elements: Element[];
14
+ styles: string[];
15
+ anchoredStyles: string[];
16
+ anchors: Element[];
17
+ }
18
+
10
19
  /**
11
20
  * Collect and remove style elements from the AST.
12
21
  *
13
22
  * Unresolved `if-tags` elements receive style-slot placeholders so render-time
14
23
  * iftags evaluation can preserve style order relative to already collected CSS.
15
24
  */
16
- export function collectStyles(elements: Element[]): { elements: Element[]; styles: string[] } {
25
+ export function collectStyles(
26
+ elements: Element[],
27
+ ignoredAnchors: WeakSet<Element> = new WeakSet(),
28
+ ): StyleCollectionResult {
17
29
  const styles: string[] = [];
18
- const ctx = { nextSlotId: 0 };
19
- const filtered = collectStylesFromElements(elements, styles, ctx);
20
- return { elements: filtered, styles };
30
+ const anchoredStyles: string[] = [];
31
+ const anchors: Element[] = [];
32
+ const usedSlots = collectExistingStyleSlots(elements);
33
+ const ctx = { nextSlotId: 0, usedSlots };
34
+ const filtered = collectStylesFromElements(
35
+ elements,
36
+ styles,
37
+ anchoredStyles,
38
+ anchors,
39
+ ignoredAnchors,
40
+ ctx,
41
+ );
42
+ return { elements: filtered, styles, anchoredStyles, anchors };
43
+ }
44
+
45
+ /**
46
+ * Preserve styles collected by earlier resolution passes while keeping
47
+ * unresolved IfTags style-slot markers idempotent across repeated passes.
48
+ */
49
+ export function mergeCollectedStyles(
50
+ existing: readonly string[] | undefined,
51
+ collected: readonly string[],
52
+ resolvedSlots: ResolvedStyleSlots = new Map(),
53
+ anchoredStyles: readonly string[] = [],
54
+ ): string[] {
55
+ const unanchoredPrevious = [...(existing ?? [])];
56
+ for (const style of anchoredStyles) removeFirst(unanchoredPrevious, style);
57
+ for (const style of collected) {
58
+ if (isStyleSlotMarker(style)) removeFirst(unanchoredPrevious, style);
59
+ }
60
+
61
+ const merged = unanchoredPrevious.filter((style) => !isStyleSlotMarker(style));
62
+ for (const style of collected) {
63
+ if (isStyleSlotMarker(style)) appendStyleSlot(merged, style, resolvedSlots);
64
+ else merged.push(style);
65
+ }
66
+ return merged;
67
+ }
68
+
69
+ function isStyleSlotMarker(style: string): boolean {
70
+ return style.startsWith(STYLE_SLOT_PREFIX);
71
+ }
72
+
73
+ export function createStyleSlotMarker(slotId: number): string {
74
+ return `${STYLE_SLOT_PREFIX}${slotId}`;
75
+ }
76
+
77
+ export function getStyleSlotId(data: IfTagsData): number | undefined {
78
+ const slotId = (data as IfTagsDataWithStyleSlot)._styleSlot;
79
+ return Number.isSafeInteger(slotId) && slotId! >= 0 ? slotId : undefined;
80
+ }
81
+
82
+ function removeFirst(values: string[], value: string): void {
83
+ const index = values.indexOf(value);
84
+ if (index >= 0) values.splice(index, 1);
85
+ }
86
+
87
+ function appendStyleSlot(
88
+ target: string[],
89
+ marker: string,
90
+ resolvedSlots: ResolvedStyleSlots,
91
+ ): void {
92
+ const slotId = Number(marker.slice(STYLE_SLOT_PREFIX.length));
93
+ const replacement = resolvedSlots.get(slotId);
94
+ if (replacement) target.push(...replacement);
95
+ else target.push(marker);
96
+ }
97
+
98
+ function collectExistingStyleSlots(elements: Element[]): Set<number> {
99
+ const slots = new Set<number>();
100
+ walkElements(elements, (element) => {
101
+ if (element.element !== "if-tags") return;
102
+ const slotId = getStyleSlotId(element.data);
103
+ if (slotId !== undefined) slots.add(slotId);
104
+ });
105
+ return slots;
106
+ }
107
+
108
+ function allocateStyleSlot(ctx: StyleCollectionContext): number {
109
+ while (ctx.usedSlots.has(ctx.nextSlotId)) ctx.nextSlotId++;
110
+ const slotId = ctx.nextSlotId++;
111
+ ctx.usedSlots.add(slotId);
112
+ return slotId;
113
+ }
114
+
115
+ interface StyleCollectionContext {
116
+ nextSlotId: number;
117
+ usedSlots: Set<number>;
21
118
  }
22
119
 
23
120
  function collectStylesFromElements(
24
121
  elements: Element[],
25
122
  styles: string[],
26
- ctx: { nextSlotId: number },
123
+ anchoredStyles: string[],
124
+ anchors: Element[],
125
+ ignoredAnchors: WeakSet<Element>,
126
+ ctx: StyleCollectionContext,
27
127
  ): Element[] {
28
128
  const result: Element[] = [];
29
129
 
30
130
  for (const element of elements) {
31
131
  if (element.element === "style") {
32
- styles.push(element.data as string);
132
+ const css = element.data as string;
133
+ if (css.startsWith(STYLE_SLOT_PREFIX)) {
134
+ styles.push(css);
135
+ continue;
136
+ }
137
+ if (css.startsWith(STYLE_ANCHOR_PREFIX)) {
138
+ anchors.push(element);
139
+ if (ignoredAnchors.has(element)) {
140
+ result.push(element);
141
+ continue;
142
+ }
143
+ const anchoredCss = css.slice(STYLE_ANCHOR_PREFIX.length);
144
+ styles.push(anchoredCss);
145
+ anchoredStyles.push(anchoredCss);
146
+ result.push(element);
147
+ continue;
148
+ }
149
+ styles.push(css);
150
+ const anchor: Element = { element: "style", data: `${STYLE_ANCHOR_PREFIX}${css}` };
151
+ anchors.push(anchor);
152
+ result.push(anchor);
33
153
  continue;
34
154
  }
35
155
 
36
156
  if (element.element === "if-tags") {
37
- const slotId = ctx.nextSlotId++;
38
- styles.push(`${STYLE_SLOT_PREFIX}${slotId}`);
157
+ const slotId = getStyleSlotId(element.data) ?? allocateStyleSlot(ctx);
158
+ styles.push(createStyleSlotMarker(slotId));
39
159
  const data: IfTagsDataWithStyleSlot = { ...(element.data as IfTagsData), _styleSlot: slotId };
40
160
  result.push({
41
161
  element: "if-tags",
@@ -45,7 +165,9 @@ function collectStylesFromElements(
45
165
  }
46
166
 
47
167
  result.push(
48
- mapElementChildren(element, (children) => collectStylesFromElements(children, styles, ctx)),
168
+ mapElementChildren(element, (children) =>
169
+ collectStylesFromElements(children, styles, anchoredStyles, anchors, ignoredAnchors, ctx),
170
+ ),
49
171
  );
50
172
  }
51
173
 
@@ -3,6 +3,7 @@ import { mapElementChildrenWithState } from "../walk";
3
3
  import { isIfTagsElement, resolveIfTags, type IfTagsData } from "../iftags/resolve";
4
4
  import type { ListPagesContext, ListUsersContext, TagCloudContext } from "./contexts";
5
5
  import { countDynamicModules, resolveDynamicModuleElement } from "./dynamic-modules";
6
+ import { collectStyles, createStyleSlotMarker, getStyleSlotId } from "./styles";
6
7
 
7
8
  /**
8
9
  * Resolution context passed through AST traversal.
@@ -21,6 +22,8 @@ export interface WalkContext {
21
22
  listPagesIdCounter: number;
22
23
  listUsersIdCounter: number;
23
24
  tagCloudIdCounter: number;
25
+ resolvedStyleSlots: Map<number, string[]>;
26
+ routedStyleAnchors: WeakSet<Element>;
24
27
  }
25
28
 
26
29
  export interface WalkResult {
@@ -56,6 +59,7 @@ export function walkAndResolve(elements: Element[], ctx: WalkContext): WalkResul
56
59
  if (isIfTagsElement(element)) {
57
60
  const ifTagsData = element.data as IfTagsData;
58
61
  const resolveResult = resolveIfTags(ifTagsData, ctx.pageTags);
62
+ const styleSlotId = getStyleSlotId(ifTagsData);
59
63
 
60
64
  if (resolveResult.evaluated) {
61
65
  if (resolveResult.matched) {
@@ -65,11 +69,25 @@ export function walkAndResolve(elements: Element[], ctx: WalkContext): WalkResul
65
69
  listUsersIdCounter: listUsersId,
66
70
  tagCloudIdCounter: tagCloudId,
67
71
  });
68
- result.push(...childResult.elements);
72
+ if (styleSlotId === undefined) {
73
+ result.push(...childResult.elements);
74
+ } else {
75
+ const collected = collectStyles(childResult.elements);
76
+ ctx.resolvedStyleSlots.set(styleSlotId, collected.styles);
77
+ for (const anchor of collected.anchors) ctx.routedStyleAnchors.add(anchor);
78
+ result.push(
79
+ { element: "style", data: createStyleSlotMarker(styleSlotId) },
80
+ ...collected.elements,
81
+ );
82
+ }
69
83
  listPagesId = childResult.nextListPagesId;
70
84
  listUsersId = childResult.nextListUsersId;
71
85
  tagCloudId = childResult.nextTagCloudId;
72
86
  } else {
87
+ if (styleSlotId !== undefined) {
88
+ ctx.resolvedStyleSlots.set(styleSlotId, []);
89
+ result.push({ element: "style", data: createStyleSlotMarker(styleSlotId) });
90
+ }
73
91
  const counts = countDynamicModules(ifTagsData.elements);
74
92
  listPagesId += counts.listPagesId;
75
93
  listUsersId += counts.listUsersId;
@@ -16,7 +16,7 @@
16
16
  * @module
17
17
  */
18
18
 
19
- import type { SyntaxTree } from "@wdprlib/ast";
19
+ import type { Diagnostic, Element, SyntaxTree } from "@wdprlib/ast";
20
20
  import type { DataProvider } from "./types-common";
21
21
  import { resolveIncludes } from "./include";
22
22
  import type { ListPagesDataRequirement, CompiledTemplate } from "./listpages/types";
@@ -29,7 +29,8 @@ import {
29
29
  buildTagCloudContext,
30
30
  } from "./resolution/contexts";
31
31
  import { walkAndResolve } from "./resolution/walk-resolve";
32
- import { collectStyles } from "./resolution/styles";
32
+ import { collectStyles, mergeCollectedStyles } from "./resolution/styles";
33
+ import { containsSyntaxFootnoteBlock, ModuleDocumentRegistry } from "./resolution/document";
33
34
 
34
35
  const MODULE_SECONDARY_INCLUDE_MAX_ITERATIONS = 5;
35
36
 
@@ -110,6 +111,9 @@ export interface ResolveOptions {
110
111
  * state outside wdpr.
111
112
  */
112
113
  transformModuleSource?: ModuleSourceTransform;
114
+
115
+ /** Receives diagnostics emitted by ListPages/ListUsers secondary parses. */
116
+ onDiagnostics?: (diagnostics: Diagnostic[]) => void;
113
117
  }
114
118
 
115
119
  /**
@@ -131,7 +135,9 @@ export async function resolveModules(
131
135
  dataProvider: DataProvider,
132
136
  options: ResolveOptions,
133
137
  ): Promise<SyntaxTree> {
134
- const parse = createModuleParseFunction(options, dataProvider);
138
+ const registry = new ModuleDocumentRegistry();
139
+ registry.register(ast);
140
+ const parse = createModuleParseFunction(options, dataProvider, registry);
135
141
  const listPagesCtx = await buildListPagesContext(
136
142
  dataProvider,
137
143
  options.requirements.listPages ?? [],
@@ -147,6 +153,8 @@ export async function resolveModules(
147
153
  );
148
154
  const tagCloudCtx = await buildTagCloudContext(dataProvider, options.requirements.tagCloud ?? []);
149
155
  const pageTags = dataProvider.getPageTags?.() ?? null;
156
+ const resolvedStyleSlots = new Map<number, string[]>();
157
+ const routedStyleAnchors = new WeakSet<Element>();
150
158
 
151
159
  // Resolve AST
152
160
  const resolvedElements = walkAndResolve(ast.elements, {
@@ -160,33 +168,44 @@ export async function resolveModules(
160
168
  listPagesIdCounter: 0,
161
169
  listUsersIdCounter: 0,
162
170
  tagCloudIdCounter: 0,
171
+ resolvedStyleSlots,
172
+ routedStyleAnchors,
163
173
  });
164
174
 
165
175
  // Collect style elements from resolved AST
166
- const { elements: finalElements, styles } = collectStyles(resolvedElements.elements);
176
+ const {
177
+ elements: finalElements,
178
+ styles,
179
+ anchoredStyles,
180
+ } = collectStyles(resolvedElements.elements, routedStyleAnchors);
167
181
 
168
182
  const result: SyntaxTree = {
169
183
  ...ast,
170
184
  elements: finalElements,
171
185
  };
172
186
 
173
- if (styles.length > 0) {
174
- result.styles = styles;
175
- }
187
+ const mergedStyles = mergeCollectedStyles(ast.styles, styles, resolvedStyleSlots, anchoredStyles);
188
+ if (mergedStyles.length > 0) result.styles = mergedStyles;
176
189
 
177
- return result;
190
+ options.onDiagnostics?.(registry.diagnostics);
191
+ return registry.finalize(
192
+ result,
193
+ finalElements,
194
+ pageTags,
195
+ containsSyntaxFootnoteBlock(ast.elements),
196
+ );
178
197
  }
179
198
 
180
199
  function createModuleParseFunction(
181
200
  options: ResolveOptions,
182
201
  dataProvider: DataProvider,
202
+ registry: ModuleDocumentRegistry,
183
203
  ): ParseFunction {
184
204
  const transform = createModuleSourceTransform(options, dataProvider);
185
- if (!transform) {
186
- return options.parse;
187
- }
188
-
189
- return (source: string) => options.parse(transform(source));
205
+ return (source: string) =>
206
+ registry.register(options.parse(transform ? transform(source) : source), {
207
+ stripLegacyImplicitFootnoteBlock: true,
208
+ });
190
209
  }
191
210
 
192
211
  function createModuleSourceTransform(
@@ -10,7 +10,7 @@
10
10
  * @module
11
11
  */
12
12
 
13
- import type { Element, Module } from "@wdprlib/ast";
13
+ import type { Element, Module, ParseResult, SyntaxTree } from "@wdprlib/ast";
14
14
  import type { ParseContext } from "../../types";
15
15
 
16
16
  /**
@@ -23,7 +23,12 @@ import type { ParseContext } from "../../types";
23
23
  * @param input - Wikitext string to parse
24
24
  * @returns Object containing the parsed elements
25
25
  */
26
- export type ParseFunction = (input: string) => { elements: Element[] };
26
+ export type ModuleParseResult = SyntaxTree | ParseResult;
27
+ export type ParseFunction = (input: string) => ModuleParseResult;
28
+
29
+ export function getModuleParseAst(result: ModuleParseResult): SyntaxTree {
30
+ return "ast" in result ? result.ast : result;
31
+ }
27
32
 
28
33
  /**
29
34
  * Definition of a module rule that handles a specific Wikidot module type.
@@ -25,6 +25,7 @@ export interface ParseContext {
25
25
  version: Version;
26
26
  trackPositions: boolean;
27
27
  settings: WikitextSettings;
28
+ appendImplicitFootnoteBlock: boolean;
28
29
  footnotes: Element[][];
29
30
  tocEntries: TocEntry[];
30
31
  codeBlocks: CodeBlockData[];
@@ -0,0 +1,7 @@
1
+ export { processWikitext } from "./process";
2
+ export type {
3
+ ProcessedWikitextDocument,
4
+ ProcessWikitextCallbackContext,
5
+ ProcessWikitextDataProvider,
6
+ ProcessWikitextOptions,
7
+ } from "./types";
@@ -0,0 +1,105 @@
1
+ import { DEFAULT_SETTINGS, type PageRef, type WikitextPageContext } from "@wdprlib/ast";
2
+ import { parse } from "../parser";
3
+ import { extractDataRequirements } from "../parser/rules/block/module/listpages/extract";
4
+ import {
5
+ resolveIncludesAsyncWithTrace,
6
+ type AsyncIncludeFetcher,
7
+ type IncludeDependency,
8
+ } from "../parser/rules/block/module/include";
9
+ import type { DataProvider } from "../parser/rules/block/module/types-common";
10
+ import { resolveModulesWithAsyncParse } from "../parser/rules/block/module/resolution/resolve-async";
11
+ import type {
12
+ ProcessedWikitextDocument,
13
+ ProcessWikitextCallbackContext,
14
+ ProcessWikitextOptions,
15
+ } from "./types";
16
+
17
+ export async function processWikitext<TPage extends WikitextPageContext>(
18
+ source: string,
19
+ options: ProcessWikitextOptions<TPage>,
20
+ ): Promise<ProcessedWikitextDocument<TPage>> {
21
+ const settings = options.settings ?? DEFAULT_SETTINGS;
22
+ const callbackContext: ProcessWikitextCallbackContext<TPage> = {
23
+ page: options.page,
24
+ settings,
25
+ };
26
+ const dependencies: IncludeDependency[] = [];
27
+ const fetchInclude = createRequestIncludeFetcher(
28
+ options.dataProvider?.fetchInclude
29
+ ? (pageRef) => options.dataProvider!.fetchInclude!(pageRef, callbackContext)
30
+ : undefined,
31
+ );
32
+ const resolveSource = async (input: string): Promise<string> => {
33
+ if (!fetchInclude) return input;
34
+ const resolution = await resolveIncludesAsyncWithTrace(input, fetchInclude, {
35
+ maxIterations: options.includeMaxIterations,
36
+ settings,
37
+ });
38
+ dependencies.push(...resolution.dependencies);
39
+ return resolution.source;
40
+ };
41
+
42
+ const expandedSource = await resolveSource(source);
43
+ const initial = parse(expandedSource, {
44
+ settings,
45
+ pageTags: options.page.tags,
46
+ appendImplicitFootnoteBlock: false,
47
+ });
48
+ const extraction = extractDataRequirements(initial.ast);
49
+ const dataProvider = createModuleDataProvider(options, callbackContext);
50
+ const resolved = await resolveModulesWithAsyncParse(initial.ast, dataProvider, {
51
+ parse: async (fragmentSource) =>
52
+ parse(await resolveSource(fragmentSource), {
53
+ settings,
54
+ pageTags: options.page.tags,
55
+ appendImplicitFootnoteBlock: false,
56
+ }),
57
+ compiledListPagesTemplates: extraction.compiledListPagesTemplates,
58
+ compiledListUsersTemplates: extraction.compiledListUsersTemplates,
59
+ requirements: extraction.requirements,
60
+ urlPath: options.page.urlPath,
61
+ pageTags: options.page.tags,
62
+ });
63
+
64
+ return {
65
+ ast: resolved.ast,
66
+ page: options.page,
67
+ settings,
68
+ diagnostics: [...initial.diagnostics, ...resolved.diagnostics],
69
+ dependencies,
70
+ };
71
+ }
72
+
73
+ function createModuleDataProvider<TPage extends WikitextPageContext>(
74
+ options: ProcessWikitextOptions<TPage>,
75
+ context: ProcessWikitextCallbackContext<TPage>,
76
+ ): DataProvider {
77
+ const provider = options.dataProvider;
78
+ return {
79
+ fetchListPages: provider?.fetchListPages
80
+ ? (query, requirement) => provider.fetchListPages!(query, requirement, context)
81
+ : undefined,
82
+ fetchListUsers: provider?.fetchListUsers
83
+ ? (requirement) => provider.fetchListUsers!(requirement, context)
84
+ : undefined,
85
+ fetchTagCloud: provider?.fetchTagCloud
86
+ ? (requirement) => provider.fetchTagCloud!(requirement, context)
87
+ : undefined,
88
+ getPageTags: () => options.page.tags,
89
+ };
90
+ }
91
+
92
+ function createRequestIncludeFetcher(
93
+ fetcher: AsyncIncludeFetcher | undefined,
94
+ ): AsyncIncludeFetcher | undefined {
95
+ if (!fetcher) return undefined;
96
+ const cache = new Map<string, Promise<string | null>>();
97
+ return (pageRef: PageRef): Promise<string | null> => {
98
+ const key = `${pageRef.site ?? ""}:${pageRef.page.toLowerCase()}`;
99
+ const cached = cache.get(key);
100
+ if (cached) return cached;
101
+ const result = fetcher(pageRef).catch(() => null);
102
+ cache.set(key, result);
103
+ return result;
104
+ };
105
+ }
@@ -0,0 +1,63 @@
1
+ import type {
2
+ Diagnostic,
3
+ PageRef,
4
+ SyntaxTree,
5
+ WikitextPageContext,
6
+ WikitextSettings,
7
+ } from "@wdprlib/ast";
8
+ import type { IncludeDependency } from "../parser/rules/block/module/include";
9
+ import type {
10
+ ListPagesDataRequirement,
11
+ ListPagesExternalData,
12
+ NormalizedListPagesQuery,
13
+ } from "../parser/rules/block/module/listpages/types";
14
+ import type {
15
+ ListUsersDataRequirement,
16
+ ListUsersExternalData,
17
+ } from "../parser/rules/block/module/listusers/types";
18
+ import type {
19
+ TagCloudDataRequirement,
20
+ TagCloudExternalData,
21
+ } from "../parser/rules/block/module/tagcloud/types";
22
+
23
+ export interface ProcessWikitextCallbackContext<TPage extends WikitextPageContext> {
24
+ page: TPage;
25
+ settings: WikitextSettings;
26
+ }
27
+
28
+ export interface ProcessWikitextDataProvider<TPage extends WikitextPageContext> {
29
+ fetchInclude?: (
30
+ pageRef: PageRef,
31
+ context: ProcessWikitextCallbackContext<TPage>,
32
+ ) => Promise<string | null>;
33
+ fetchListPages?: (
34
+ query: NormalizedListPagesQuery,
35
+ requirement: ListPagesDataRequirement,
36
+ context: ProcessWikitextCallbackContext<TPage>,
37
+ ) => Promise<ListPagesExternalData | null | undefined>;
38
+ fetchListUsers?: (
39
+ requirement: ListUsersDataRequirement,
40
+ context: ProcessWikitextCallbackContext<TPage>,
41
+ ) => Promise<ListUsersExternalData | null | undefined>;
42
+ fetchTagCloud?: (
43
+ requirement: TagCloudDataRequirement,
44
+ context: ProcessWikitextCallbackContext<TPage>,
45
+ ) => Promise<TagCloudExternalData | null | undefined>;
46
+ }
47
+
48
+ export interface ProcessWikitextOptions<TPage extends WikitextPageContext> {
49
+ page: TPage;
50
+ settings?: WikitextSettings;
51
+ dataProvider?: ProcessWikitextDataProvider<TPage>;
52
+ includeMaxIterations?: number;
53
+ }
54
+
55
+ export interface ProcessedWikitextDocument<
56
+ TPage extends WikitextPageContext = WikitextPageContext,
57
+ > {
58
+ ast: SyntaxTree;
59
+ page: TPage;
60
+ settings: WikitextSettings;
61
+ diagnostics: Diagnostic[];
62
+ dependencies: IncludeDependency[];
63
+ }