@wdprlib/parser 4.3.0 → 5.0.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 (32) hide show
  1. package/README.md +26 -38
  2. package/dist/index.cjs +1035 -249
  3. package/dist/index.d.cts +203 -158
  4. package/dist/index.d.ts +203 -158
  5. package/dist/index.js +930 -139
  6. package/package.json +2 -2
  7. package/src/index.ts +13 -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/preprocess/expr/index.ts +26 -0
  12. package/src/parser/preprocess/utils/raw-regions.ts +16 -7
  13. package/src/parser/rules/block/module/include/index.ts +6 -1
  14. package/src/parser/rules/block/module/include/resolve/index.ts +23 -3
  15. package/src/parser/rules/block/module/include/resolve/iterate.ts +71 -0
  16. package/src/parser/rules/block/module/index.ts +4 -1
  17. package/src/parser/rules/block/module/listpages/index.ts +2 -0
  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/listpages/selectors.ts +64 -0
  21. package/src/parser/rules/block/module/listpages/types/external-data.ts +22 -0
  22. package/src/parser/rules/block/module/listusers/resolve.ts +2 -2
  23. package/src/parser/rules/block/module/resolution/document.ts +357 -0
  24. package/src/parser/rules/block/module/resolution/resolve-async.ts +269 -0
  25. package/src/parser/rules/block/module/resolution/styles.ts +134 -12
  26. package/src/parser/rules/block/module/resolution/walk-resolve.ts +19 -1
  27. package/src/parser/rules/block/module/resolve.ts +32 -13
  28. package/src/parser/rules/block/module/types.ts +7 -2
  29. package/src/parser/rules/contracts/parse-context.ts +1 -0
  30. package/src/pipeline/index.ts +7 -0
  31. package/src/pipeline/process.ts +172 -0
  32. package/src/pipeline/types.ts +63 -0
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  listItemElements,
16
16
  listItemSubList
17
17
  } from "@wdprlib/ast";
18
- import { createSettings, DEFAULT_SETTINGS as DEFAULT_SETTINGS2 } from "@wdprlib/ast";
18
+ import { createSettings, DEFAULT_SETTINGS as DEFAULT_SETTINGS3 } from "@wdprlib/ast";
19
19
 
20
20
  // packages/parser/src/lexer/tokens.ts
21
21
  function createToken(type, value, position, lineStart = false) {
@@ -5782,6 +5782,11 @@ function extractDataRequirements(ast) {
5782
5782
  });
5783
5783
  return result;
5784
5784
  }
5785
+ // packages/parser/src/parser/rules/block/module/types.ts
5786
+ function getModuleParseAst(result) {
5787
+ return "ast" in result ? result.ast : result;
5788
+ }
5789
+
5785
5790
  // packages/parser/src/parser/rules/block/module/listpages/resolution/items.ts
5786
5791
  function renderListPagesItems(module, data, compiledTemplate, parse) {
5787
5792
  const items = [];
@@ -5796,7 +5801,7 @@ function renderListPagesItems(module, data, compiledTemplate, parse) {
5796
5801
  limit: module.limit,
5797
5802
  site: data.site
5798
5803
  };
5799
- const itemAst = parse(compiledTemplate(ctx));
5804
+ const itemAst = getModuleParseAst(parse(compiledTemplate(ctx)));
5800
5805
  if (module.separate) {
5801
5806
  items.push({
5802
5807
  element: "container",
@@ -5820,12 +5825,12 @@ function wrapListPagesResult(module, items, parse) {
5820
5825
  }
5821
5826
  const result = [];
5822
5827
  if (module["prepend-line"] && !module.separate) {
5823
- const prependAst = parse(module["prepend-line"]);
5828
+ const prependAst = getModuleParseAst(parse(module["prepend-line"]));
5824
5829
  result.push(...prependAst.elements);
5825
5830
  }
5826
5831
  result.push(...items);
5827
5832
  if (module["append-line"] && !module.separate) {
5828
- const appendAst = parse(module["append-line"]);
5833
+ const appendAst = getModuleParseAst(parse(module["append-line"]));
5829
5834
  result.push(...appendAst.elements);
5830
5835
  }
5831
5836
  if (module.wrapper) {
@@ -5851,6 +5856,60 @@ function resolveListPages(module, data, compiledTemplate, parse) {
5851
5856
  const items = renderListPagesItems(module, data, compiledTemplate, parse);
5852
5857
  return wrapListPagesResult(module, items, parse);
5853
5858
  }
5859
+ // packages/parser/src/parser/rules/block/module/listpages/types/external-data.ts
5860
+ function definePageData(input) {
5861
+ const separator = input.fullname.indexOf(":");
5862
+ const category = separator === -1 ? "_default" : input.fullname.slice(0, separator);
5863
+ const name = separator === -1 ? input.fullname : input.fullname.slice(separator + 1);
5864
+ return {
5865
+ ...input,
5866
+ name: input.name ?? name,
5867
+ category: input.category ?? category,
5868
+ hiddenTags: input.hiddenTags ?? [],
5869
+ children: input.children ?? 0,
5870
+ comments: input.comments ?? 0,
5871
+ size: input.size ?? 0,
5872
+ rating: input.rating ?? 0,
5873
+ ratingVotes: input.ratingVotes ?? 0,
5874
+ revisions: input.revisions ?? 0
5875
+ };
5876
+ }
5877
+ // packages/parser/src/parser/rules/block/module/listpages/selectors.ts
5878
+ function matchesListPagesSelectors(page, query, currentPage) {
5879
+ return matchesCategory(page.category, query.category, currentPage.category) && matchesTags(page, query.tags, currentPage);
5880
+ }
5881
+ function matchesCategory(category, selector, currentCategory) {
5882
+ if (!selector)
5883
+ return category === currentCategory;
5884
+ if (selector.exclude.includes(category))
5885
+ return false;
5886
+ const hasPositiveSelector = selector.all || selector.current || selector.include.length > 0;
5887
+ return !hasPositiveSelector || selector.all || selector.current && category === currentCategory || selector.include.includes(category);
5888
+ }
5889
+ function matchesTags(page, selector, currentPage) {
5890
+ if (!selector)
5891
+ return true;
5892
+ const allTags = new Set([...page.tags, ...page.hiddenTags]);
5893
+ if (selector.all.some((tag) => !allTags.has(tag)))
5894
+ return false;
5895
+ if (selector.any.length > 0 && !selector.any.some((tag) => allTags.has(tag)))
5896
+ return false;
5897
+ if (selector.none.some((tag) => allTags.has(tag)))
5898
+ return false;
5899
+ if (selector.special === "none")
5900
+ return allTags.size === 0;
5901
+ const currentTags = new Set(currentPage.tags.filter((tag) => !tag.startsWith("_")));
5902
+ if (selector.special === "same-visible") {
5903
+ return page.tags.some((tag) => currentTags.has(tag));
5904
+ }
5905
+ if (selector.special === "same-all") {
5906
+ return setsEqual(new Set(page.tags), currentTags);
5907
+ }
5908
+ return true;
5909
+ }
5910
+ function setsEqual(left, right) {
5911
+ return left.size === right.size && [...left].every((value) => right.has(value));
5912
+ }
5854
5913
  // packages/parser/src/parser/rules/block/module/listpages/url-resolution/fields.ts
5855
5914
  var URL_RESOLVABLE_FIELDS = [
5856
5915
  { attr: "offset", queryKey: "offset", type: "number" },
@@ -6280,13 +6339,20 @@ var BASE_PLACEHOLDER_OPEN = "";
6280
6339
  var BASE_PLACEHOLDER_CLOSE = "";
6281
6340
  var RAW_BLOCK_OPEN_PATTERN = /\[\[\s*(code|html)\b[^\]]*\]\]/iy;
6282
6341
  function makeUniqueSentinels(source) {
6283
- let open = BASE_PLACEHOLDER_OPEN;
6284
- let close = BASE_PLACEHOLDER_CLOSE;
6285
- while (source.includes(open) || source.includes(close)) {
6286
- open += BASE_PLACEHOLDER_OPEN;
6287
- close += BASE_PLACEHOLDER_CLOSE;
6342
+ let openRun = 0;
6343
+ let closeRun = 0;
6344
+ let longestOpenRun = 0;
6345
+ let longestCloseRun = 0;
6346
+ for (const char of source) {
6347
+ openRun = char === BASE_PLACEHOLDER_OPEN ? openRun + 1 : 0;
6348
+ closeRun = char === BASE_PLACEHOLDER_CLOSE ? closeRun + 1 : 0;
6349
+ longestOpenRun = Math.max(longestOpenRun, openRun);
6350
+ longestCloseRun = Math.max(longestCloseRun, closeRun);
6288
6351
  }
6289
- return { open, close };
6352
+ return {
6353
+ open: BASE_PLACEHOLDER_OPEN.repeat(longestOpenRun + 1),
6354
+ close: BASE_PLACEHOLDER_CLOSE.repeat(longestCloseRun + 1)
6355
+ };
6290
6356
  }
6291
6357
  function maskRawRegions(source, sentinels) {
6292
6358
  const placeholders = [];
@@ -6744,16 +6810,31 @@ function expandIterativeWithTrace(source, fetcher, maxIterations) {
6744
6810
  reachedMaxIterations: iterations.length === maxIterations && scanIncludeDirectives(current2).length > 0
6745
6811
  };
6746
6812
  }
6747
- async function expandIterativeAsync(source, fetcher, maxIterations) {
6813
+ async function expandIterativeAsyncWithTrace(source, fetcher, maxIterations) {
6748
6814
  let current2 = source;
6749
6815
  const replacementCache = new Map;
6816
+ const dependencies = [];
6817
+ const iterations = [];
6750
6818
  for (let i = 0;i < maxIterations; i++) {
6751
- const expanded = await expandOneIterationAsync(current2, fetcher, replacementCache);
6752
- if (expanded === null || expanded === current2)
6819
+ const expanded = await expandOneIterationAsyncWithTrace(current2, fetcher, replacementCache, i);
6820
+ if (expanded === null)
6753
6821
  break;
6754
- current2 = expanded;
6822
+ dependencies.push(...expanded.dependencies);
6823
+ iterations.push({
6824
+ iteration: i,
6825
+ directives: expanded.references,
6826
+ changed: expanded.source !== current2
6827
+ });
6828
+ if (expanded.source === current2)
6829
+ break;
6830
+ current2 = expanded.source;
6755
6831
  }
6756
- return current2;
6832
+ return {
6833
+ source: current2,
6834
+ dependencies,
6835
+ iterations,
6836
+ reachedMaxIterations: iterations.length === maxIterations && scanIncludeDirectives(current2).length > 0
6837
+ };
6757
6838
  }
6758
6839
  function expandOneIteration(source, fetcher, replacementCache) {
6759
6840
  if (!MAYBE_INCLUDE_PATTERN.test(source))
@@ -6791,12 +6872,13 @@ function expandOneIterationWithTrace(source, fetcher, replacementCache, iteratio
6791
6872
  dependencies
6792
6873
  };
6793
6874
  }
6794
- async function expandOneIterationAsync(source, fetcher, replacementCache) {
6875
+ async function expandOneIterationAsyncWithTrace(source, fetcher, replacementCache, iteration) {
6795
6876
  if (!MAYBE_INCLUDE_PATTERN.test(source))
6796
6877
  return null;
6797
6878
  const directives = scanIncludeDirectives(source);
6798
6879
  if (directives.length === 0)
6799
6880
  return null;
6881
+ const references = directives.map(createIncludeReference);
6800
6882
  const replacements = await Promise.all(directives.map(({ inner }) => replaceCachedAsync(inner, fetcher, replacementCache)));
6801
6883
  const parts = [];
6802
6884
  let lastPos = 0;
@@ -6806,7 +6888,11 @@ async function expandOneIterationAsync(source, fetcher, replacementCache) {
6806
6888
  lastPos = end;
6807
6889
  }
6808
6890
  parts.push(source.slice(lastPos));
6809
- return parts.join("");
6891
+ return {
6892
+ source: parts.join(""),
6893
+ references,
6894
+ dependencies: references.map((reference) => ({ ...reference, iteration }))
6895
+ };
6810
6896
  }
6811
6897
  function replaceCached(inner, fetcher, cache) {
6812
6898
  const cached = cache.get(inner);
@@ -6848,12 +6934,20 @@ function resolveIncludesWithTrace(source, fetcher, options) {
6848
6934
  return expandIterativeWithTrace(source, cachedFetcher, maxIterations);
6849
6935
  }
6850
6936
  async function resolveIncludesAsync(source, fetcher, options) {
6937
+ return (await resolveIncludesAsyncWithTrace(source, fetcher, options)).source;
6938
+ }
6939
+ async function resolveIncludesAsyncWithTrace(source, fetcher, options) {
6851
6940
  if (options?.settings && !options.settings.enablePageSyntax) {
6852
- return source;
6941
+ return {
6942
+ source,
6943
+ dependencies: [],
6944
+ iterations: [],
6945
+ reachedMaxIterations: false
6946
+ };
6853
6947
  }
6854
6948
  const maxIterations = options?.maxIterations ?? 10;
6855
6949
  const cachedFetcher = createCachedAsyncIncludeFetcher(fetcher, normalizePageKey);
6856
- return expandIterativeAsync(source, cachedFetcher, maxIterations);
6950
+ return expandIterativeAsyncWithTrace(source, cachedFetcher, maxIterations);
6857
6951
  }
6858
6952
  function normalizePageKey(location) {
6859
6953
  const site = location.site ?? "";
@@ -6867,7 +6961,7 @@ function isListUsersModule2(module) {
6867
6961
  function resolveListUsers(_module, data, compiledTemplate, parse) {
6868
6962
  const ctx = { user: data.user };
6869
6963
  const substituted = compiledTemplate(ctx);
6870
- const itemAst = parse(substituted);
6964
+ const itemAst = getModuleParseAst(parse(substituted));
6871
6965
  return itemAst.elements;
6872
6966
  }
6873
6967
  // packages/parser/src/parser/rules/block/module/resolution/data-maps.ts
@@ -7020,6 +7114,117 @@ function countDynamicModules(elements) {
7020
7114
  return counts;
7021
7115
  }
7022
7116
 
7117
+ // packages/parser/src/parser/rules/block/module/resolution/styles.ts
7118
+ import { STYLE_ANCHOR_PREFIX, STYLE_SLOT_PREFIX } from "@wdprlib/ast";
7119
+ function collectStyles(elements, ignoredAnchors = new WeakSet) {
7120
+ const styles = [];
7121
+ const anchoredStyles = [];
7122
+ const anchors = [];
7123
+ const usedSlots = collectExistingStyleSlots(elements);
7124
+ const ctx = { nextSlotId: 0, usedSlots };
7125
+ const filtered = collectStylesFromElements(elements, styles, anchoredStyles, anchors, ignoredAnchors, ctx);
7126
+ return { elements: filtered, styles, anchoredStyles, anchors };
7127
+ }
7128
+ function mergeCollectedStyles(existing, collected, resolvedSlots = new Map, anchoredStyles = []) {
7129
+ const unanchoredPrevious = [...existing ?? []];
7130
+ for (const style of anchoredStyles)
7131
+ removeFirst(unanchoredPrevious, style);
7132
+ for (const style of collected) {
7133
+ if (isStyleSlotMarker(style))
7134
+ removeFirst(unanchoredPrevious, style);
7135
+ }
7136
+ const merged = unanchoredPrevious.filter((style) => !isStyleSlotMarker(style));
7137
+ for (const style of collected) {
7138
+ if (isStyleSlotMarker(style))
7139
+ appendStyleSlot(merged, style, resolvedSlots);
7140
+ else
7141
+ merged.push(style);
7142
+ }
7143
+ return merged;
7144
+ }
7145
+ function isStyleSlotMarker(style) {
7146
+ return style.startsWith(STYLE_SLOT_PREFIX);
7147
+ }
7148
+ function createStyleSlotMarker(slotId) {
7149
+ return `${STYLE_SLOT_PREFIX}${slotId}`;
7150
+ }
7151
+ function getStyleSlotId(data) {
7152
+ const slotId = data._styleSlot;
7153
+ return Number.isSafeInteger(slotId) && slotId >= 0 ? slotId : undefined;
7154
+ }
7155
+ function removeFirst(values, value) {
7156
+ const index = values.indexOf(value);
7157
+ if (index >= 0)
7158
+ values.splice(index, 1);
7159
+ }
7160
+ function appendStyleSlot(target, marker, resolvedSlots) {
7161
+ const slotId = Number(marker.slice(STYLE_SLOT_PREFIX.length));
7162
+ const replacement = resolvedSlots.get(slotId);
7163
+ if (replacement)
7164
+ target.push(...replacement);
7165
+ else
7166
+ target.push(marker);
7167
+ }
7168
+ function collectExistingStyleSlots(elements) {
7169
+ const slots = new Set;
7170
+ walkElements(elements, (element) => {
7171
+ if (element.element !== "if-tags")
7172
+ return;
7173
+ const slotId = getStyleSlotId(element.data);
7174
+ if (slotId !== undefined)
7175
+ slots.add(slotId);
7176
+ });
7177
+ return slots;
7178
+ }
7179
+ function allocateStyleSlot(ctx) {
7180
+ while (ctx.usedSlots.has(ctx.nextSlotId))
7181
+ ctx.nextSlotId++;
7182
+ const slotId = ctx.nextSlotId++;
7183
+ ctx.usedSlots.add(slotId);
7184
+ return slotId;
7185
+ }
7186
+ function collectStylesFromElements(elements, styles, anchoredStyles, anchors, ignoredAnchors, ctx) {
7187
+ const result = [];
7188
+ for (const element of elements) {
7189
+ if (element.element === "style") {
7190
+ const css = element.data;
7191
+ if (css.startsWith(STYLE_SLOT_PREFIX)) {
7192
+ styles.push(css);
7193
+ continue;
7194
+ }
7195
+ if (css.startsWith(STYLE_ANCHOR_PREFIX)) {
7196
+ anchors.push(element);
7197
+ if (ignoredAnchors.has(element)) {
7198
+ result.push(element);
7199
+ continue;
7200
+ }
7201
+ const anchoredCss = css.slice(STYLE_ANCHOR_PREFIX.length);
7202
+ styles.push(anchoredCss);
7203
+ anchoredStyles.push(anchoredCss);
7204
+ result.push(element);
7205
+ continue;
7206
+ }
7207
+ styles.push(css);
7208
+ const anchor = { element: "style", data: `${STYLE_ANCHOR_PREFIX}${css}` };
7209
+ anchors.push(anchor);
7210
+ result.push(anchor);
7211
+ continue;
7212
+ }
7213
+ if (element.element === "if-tags") {
7214
+ const slotId = getStyleSlotId(element.data) ?? allocateStyleSlot(ctx);
7215
+ styles.push(createStyleSlotMarker(slotId));
7216
+ const data = { ...element.data, _styleSlot: slotId };
7217
+ result.push({
7218
+ element: "if-tags",
7219
+ data
7220
+ });
7221
+ continue;
7222
+ }
7223
+ result.push(mapElementChildren(element, (children) => collectStylesFromElements(children, styles, anchoredStyles, anchors, ignoredAnchors, ctx)));
7224
+ }
7225
+ return result;
7226
+ }
7227
+
7023
7228
  // packages/parser/src/parser/rules/block/module/resolution/walk-resolve.ts
7024
7229
  function walkAndResolve(elements, ctx) {
7025
7230
  const result = [];
@@ -7042,6 +7247,7 @@ function walkAndResolve(elements, ctx) {
7042
7247
  if (isIfTagsElement(element)) {
7043
7248
  const ifTagsData = element.data;
7044
7249
  const resolveResult = resolveIfTags(ifTagsData, ctx.pageTags);
7250
+ const styleSlotId = getStyleSlotId(ifTagsData);
7045
7251
  if (resolveResult.evaluated) {
7046
7252
  if (resolveResult.matched) {
7047
7253
  const childResult = walkAndResolve(ifTagsData.elements, {
@@ -7050,11 +7256,23 @@ function walkAndResolve(elements, ctx) {
7050
7256
  listUsersIdCounter: listUsersId,
7051
7257
  tagCloudIdCounter: tagCloudId
7052
7258
  });
7053
- result.push(...childResult.elements);
7259
+ if (styleSlotId === undefined) {
7260
+ result.push(...childResult.elements);
7261
+ } else {
7262
+ const collected = collectStyles(childResult.elements);
7263
+ ctx.resolvedStyleSlots.set(styleSlotId, collected.styles);
7264
+ for (const anchor of collected.anchors)
7265
+ ctx.routedStyleAnchors.add(anchor);
7266
+ result.push({ element: "style", data: createStyleSlotMarker(styleSlotId) }, ...collected.elements);
7267
+ }
7054
7268
  listPagesId = childResult.nextListPagesId;
7055
7269
  listUsersId = childResult.nextListUsersId;
7056
7270
  tagCloudId = childResult.nextTagCloudId;
7057
7271
  } else {
7272
+ if (styleSlotId !== undefined) {
7273
+ ctx.resolvedStyleSlots.set(styleSlotId, []);
7274
+ result.push({ element: "style", data: createStyleSlotMarker(styleSlotId) });
7275
+ }
7058
7276
  const counts = countDynamicModules(ifTagsData.elements);
7059
7277
  listPagesId += counts.listPagesId;
7060
7278
  listUsersId += counts.listUsersId;
@@ -7109,44 +7327,361 @@ function walkAndResolve(elements, ctx) {
7109
7327
  };
7110
7328
  }
7111
7329
 
7112
- // packages/parser/src/parser/rules/block/module/resolution/styles.ts
7113
- import { STYLE_SLOT_PREFIX } from "@wdprlib/ast";
7114
- function collectStyles(elements) {
7115
- const styles = [];
7116
- const ctx = { nextSlotId: 0 };
7117
- const filtered = collectStylesFromElements(elements, styles, ctx);
7118
- return { elements: filtered, styles };
7330
+ // packages/parser/src/parser/rules/block/module/resolution/document.ts
7331
+ import {
7332
+ evaluateExpression,
7333
+ isTruthy
7334
+ } from "@wdprlib/ast";
7335
+
7336
+ // packages/parser/src/parser/toc.ts
7337
+ class TocIndexer {
7338
+ index = 0;
7339
+ next() {
7340
+ return this.index++;
7341
+ }
7342
+ }
7343
+ function buildTocList(indexer, items) {
7344
+ const listItems = items.map((item) => buildTocListItem(indexer, item));
7345
+ return {
7346
+ element: "list",
7347
+ data: {
7348
+ type: "bullet",
7349
+ attributes: {},
7350
+ items: listItems
7351
+ }
7352
+ };
7353
+ }
7354
+ function buildTocListItem(indexer, item) {
7355
+ if (item.kind === "list") {
7356
+ return {
7357
+ "item-type": "sub-list",
7358
+ element: "list",
7359
+ data: {
7360
+ type: "bullet",
7361
+ attributes: {},
7362
+ items: item.children.map((child) => buildTocListItem(indexer, child))
7363
+ }
7364
+ };
7365
+ }
7366
+ const anchor = `#toc${indexer.next()}`;
7367
+ const linkElement = {
7368
+ element: "link",
7369
+ data: {
7370
+ type: "table-of-contents",
7371
+ link: anchor,
7372
+ extra: null,
7373
+ label: { text: item.value },
7374
+ target: null
7375
+ }
7376
+ };
7377
+ return {
7378
+ "item-type": "elements",
7379
+ attributes: {},
7380
+ elements: [linkElement]
7381
+ };
7382
+ }
7383
+ function buildTableOfContents(entries) {
7384
+ if (entries.length === 0) {
7385
+ return [];
7386
+ }
7387
+ const depthItems = entries.map((entry) => ({
7388
+ depth: entry.level - 1,
7389
+ ltype: null,
7390
+ value: entry.text
7391
+ }));
7392
+ const trees = processDepths(null, depthItems);
7393
+ const indexer = new TocIndexer;
7394
+ return trees.map((tree) => buildTocList(indexer, tree.list));
7395
+ }
7396
+
7397
+ // packages/parser/src/parser/rules/block/module/resolution/document.ts
7398
+ class ModuleDocumentRegistry {
7399
+ footnotesByElement = new WeakMap;
7400
+ diagnostics = [];
7401
+ register(result, options = {}) {
7402
+ const parseResult = isParseResult(result) ? result : null;
7403
+ const ast = parseResult ? parseResult.ast : result;
7404
+ const elements = options.stripLegacyImplicitFootnoteBlock ? stripTrailingDefaultFootnoteBlock(ast.elements) : ast.elements;
7405
+ const registeredAst = elements === ast.elements ? ast : { ...ast, elements };
7406
+ if (parseResult) {
7407
+ this.diagnostics.push(...parseResult.diagnostics);
7408
+ }
7409
+ this.registerFootnotes(registeredAst);
7410
+ return registeredAst;
7411
+ }
7412
+ finalize(ast, elements, pageTags, ensureFootnoteBlock = true) {
7413
+ const normalizedElements = normalizeFootnoteBlocks(elements, ensureFootnoteBlock);
7414
+ const footnotes = [];
7415
+ const htmlBlocks = [];
7416
+ const codeBlocks = [];
7417
+ const tocEntries = [];
7418
+ const collectElement = (element) => {
7419
+ if (element.element === "footnote") {
7420
+ const content = this.footnotesByElement.get(element);
7421
+ if (content)
7422
+ footnotes.push(content);
7423
+ } else if (element.element === "html") {
7424
+ htmlBlocks.push(element.data.contents);
7425
+ } else if (element.element === "code") {
7426
+ codeBlocks.push(element.data);
7427
+ } else if (isTocHeading(element)) {
7428
+ tocEntries.push({
7429
+ level: element.data.type.header.level,
7430
+ text: extractHeadingText(element.data.elements)
7431
+ });
7432
+ }
7433
+ };
7434
+ walkRenderOrder(normalizedElements, pageTags, collectElement);
7435
+ const result = { ...ast, elements: normalizedElements };
7436
+ setOptionalArray(result, "footnotes", footnotes);
7437
+ setOptionalArray(result, "html-blocks", htmlBlocks);
7438
+ setOptionalArray(result, "code-blocks", codeBlocks);
7439
+ setOptionalArray(result, "table-of-contents", buildTableOfContents(tocEntries));
7440
+ return result;
7441
+ }
7442
+ registerFootnotes(ast) {
7443
+ const footnoteElements = [];
7444
+ walkSyntaxOrder(ast.elements, (element) => {
7445
+ if (element.element === "footnote")
7446
+ footnoteElements.push(element);
7447
+ });
7448
+ for (let i = 0;i < footnoteElements.length; i++) {
7449
+ const content = ast.footnotes?.[i];
7450
+ if (content)
7451
+ this.footnotesByElement.set(footnoteElements[i], content);
7452
+ }
7453
+ }
7454
+ }
7455
+ function isParseResult(result) {
7456
+ return "ast" in result && "diagnostics" in result;
7457
+ }
7458
+ function setOptionalArray(tree, key, value) {
7459
+ if (value.length > 0) {
7460
+ tree[key] = value;
7461
+ } else {
7462
+ delete tree[key];
7463
+ }
7464
+ }
7465
+ function stripTrailingDefaultFootnoteBlock(elements) {
7466
+ const last = elements.at(-1);
7467
+ if (last?.element !== "footnote-block" || last.data.title !== null || last.data.hide === true) {
7468
+ return elements;
7469
+ }
7470
+ return elements.slice(0, -1);
7471
+ }
7472
+ function normalizeFootnoteBlocks(elements, ensureFootnoteBlock) {
7473
+ const state = { found: false };
7474
+ const normalized = mapSyntaxElements(elements, (element) => {
7475
+ if (element.element !== "footnote-block")
7476
+ return element;
7477
+ if (state.found)
7478
+ return null;
7479
+ state.found = true;
7480
+ return element;
7481
+ });
7482
+ if (!state.found && ensureFootnoteBlock) {
7483
+ normalized.push({
7484
+ element: "footnote-block",
7485
+ data: { title: null, hide: false }
7486
+ });
7487
+ }
7488
+ return normalized;
7489
+ }
7490
+ function containsSyntaxFootnoteBlock(elements) {
7491
+ let found = false;
7492
+ walkSyntaxOrder(elements, (element) => {
7493
+ if (element.element === "footnote-block")
7494
+ found = true;
7495
+ });
7496
+ return found;
7119
7497
  }
7120
- function collectStylesFromElements(elements, styles, ctx) {
7498
+ function mapSyntaxElements(elements, transform) {
7121
7499
  const result = [];
7500
+ for (const original of elements) {
7501
+ const element = transform(original);
7502
+ if (!element)
7503
+ continue;
7504
+ result.push(mapSyntaxChildren(element, (children) => mapSyntaxElements(children, transform)));
7505
+ }
7506
+ return result;
7507
+ }
7508
+ function mapSyntaxChildren(element, transform) {
7509
+ if (element.element === "if") {
7510
+ return {
7511
+ ...element,
7512
+ data: {
7513
+ ...element.data,
7514
+ then: transform(element.data.then),
7515
+ else: transform(element.data.else)
7516
+ }
7517
+ };
7518
+ }
7519
+ if (element.element === "ifexpr") {
7520
+ return {
7521
+ ...element,
7522
+ data: {
7523
+ ...element.data,
7524
+ then: transform(element.data.then),
7525
+ else: transform(element.data.else)
7526
+ }
7527
+ };
7528
+ }
7529
+ if (element.element === "bibliography-block") {
7530
+ return {
7531
+ ...element,
7532
+ data: {
7533
+ ...element.data,
7534
+ entries: element.data.entries.map((entry) => ({
7535
+ ...entry,
7536
+ key: transform(entry.key),
7537
+ value: transform(entry.value)
7538
+ }))
7539
+ }
7540
+ };
7541
+ }
7542
+ if (element.element === "list") {
7543
+ return {
7544
+ ...element,
7545
+ data: {
7546
+ ...element.data,
7547
+ items: element.data.items.map((item) => item["item-type"] === "elements" ? { ...item, elements: transform(item.elements) } : mapSubList(item, transform))
7548
+ }
7549
+ };
7550
+ }
7551
+ if (element.element === "table") {
7552
+ return {
7553
+ ...element,
7554
+ data: {
7555
+ ...element.data,
7556
+ rows: element.data.rows.map((row) => ({
7557
+ ...row,
7558
+ cells: row.cells.map((cell) => ({ ...cell, elements: transform(cell.elements) }))
7559
+ }))
7560
+ }
7561
+ };
7562
+ }
7563
+ if (element.element === "definition-list") {
7564
+ return {
7565
+ ...element,
7566
+ data: element.data.map((entry) => ({
7567
+ ...entry,
7568
+ key: transform(entry.key),
7569
+ value: transform(entry.value)
7570
+ }))
7571
+ };
7572
+ }
7573
+ if (element.element === "tab-view") {
7574
+ return {
7575
+ ...element,
7576
+ data: element.data.map((tab) => ({ ...tab, elements: transform(tab.elements) }))
7577
+ };
7578
+ }
7579
+ const children = getGenericElementChildren(element);
7580
+ return children === null ? element : withGenericElementChildren(element, transform(children));
7581
+ }
7582
+ function mapSubList(item, transform) {
7583
+ const transformed = transform([listElement(item.data)])[0];
7584
+ return transformed?.element === "list" ? { ...item, data: transformed.data } : item;
7585
+ }
7586
+ function walkSyntaxOrder(elements, callback) {
7122
7587
  for (const element of elements) {
7123
- if (element.element === "style") {
7124
- styles.push(element.data);
7588
+ callback(element);
7589
+ walkAllSyntaxChildren(element, (children) => walkSyntaxOrder(children, callback));
7590
+ }
7591
+ }
7592
+ function walkRenderOrder(elements, pageTags, callback) {
7593
+ for (const element of elements) {
7594
+ callback(element);
7595
+ if (element.element === "if") {
7596
+ walkRenderOrder(isTruthy(element.data.condition) ? element.data.then : element.data.else, pageTags, callback);
7597
+ continue;
7598
+ }
7599
+ if (element.element === "ifexpr") {
7600
+ const evaluated = evaluateExpression(element.data.expression);
7601
+ if (evaluated.success) {
7602
+ walkRenderOrder(evaluated.value !== 0 ? element.data.then : element.data.else, pageTags, callback);
7603
+ }
7125
7604
  continue;
7126
7605
  }
7127
7606
  if (element.element === "if-tags") {
7128
- const slotId = ctx.nextSlotId++;
7129
- styles.push(`${STYLE_SLOT_PREFIX}${slotId}`);
7130
- const data = { ...element.data, _styleSlot: slotId };
7131
- result.push({
7132
- element: "if-tags",
7133
- data
7134
- });
7607
+ const resolved = resolveIfTags(element.data, pageTags);
7608
+ if (!resolved.evaluated || resolved.matched) {
7609
+ walkRenderOrder(element.data.elements, pageTags, callback);
7610
+ }
7611
+ continue;
7612
+ }
7613
+ if (element.element === "bibliography-block") {
7614
+ if (!element.data.hide) {
7615
+ for (const entry of element.data.entries) {
7616
+ walkRenderOrder(entry.value, pageTags, callback);
7617
+ }
7618
+ }
7135
7619
  continue;
7136
7620
  }
7137
- result.push(mapElementChildren(element, (children) => collectStylesFromElements(children, styles, ctx)));
7621
+ walkBasicChildren(element, (children) => walkRenderOrder(children, pageTags, callback));
7138
7622
  }
7139
- return result;
7623
+ }
7624
+ function walkAllSyntaxChildren(element, callback) {
7625
+ if (element.element === "if" || element.element === "ifexpr") {
7626
+ callback(element.data.then);
7627
+ callback(element.data.else);
7628
+ return;
7629
+ }
7630
+ if (element.element === "bibliography-block") {
7631
+ for (const entry of element.data.entries) {
7632
+ callback(entry.key);
7633
+ callback(entry.value);
7634
+ }
7635
+ return;
7636
+ }
7637
+ walkBasicChildren(element, callback);
7638
+ }
7639
+ function walkBasicChildren(element, callback) {
7640
+ if (element.element === "list") {
7641
+ for (const item of element.data.items) {
7642
+ callback(item["item-type"] === "elements" ? item.elements : [listElement(item.data)]);
7643
+ }
7644
+ return;
7645
+ }
7646
+ if (element.element === "table") {
7647
+ for (const row of element.data.rows) {
7648
+ for (const cell of row.cells)
7649
+ callback(cell.elements);
7650
+ }
7651
+ return;
7652
+ }
7653
+ if (element.element === "definition-list") {
7654
+ for (const entry of element.data) {
7655
+ callback(entry.key);
7656
+ callback(entry.value);
7657
+ }
7658
+ return;
7659
+ }
7660
+ if (element.element === "tab-view") {
7661
+ for (const tab of element.data)
7662
+ callback(tab.elements);
7663
+ return;
7664
+ }
7665
+ const children = getGenericElementChildren(element);
7666
+ if (children !== null)
7667
+ callback(children);
7668
+ }
7669
+ function isTocHeading(element) {
7670
+ return element.element === "container" && typeof element.data.type === "object" && "header" in element.data.type && element.data.type.header["has-toc"];
7140
7671
  }
7141
7672
 
7142
7673
  // packages/parser/src/parser/rules/block/module/resolve.ts
7143
7674
  var MODULE_SECONDARY_INCLUDE_MAX_ITERATIONS = 5;
7144
7675
  async function resolveModules(ast, dataProvider, options) {
7145
- const parse = createModuleParseFunction(options, dataProvider);
7676
+ const registry = new ModuleDocumentRegistry;
7677
+ registry.register(ast);
7678
+ const parse = createModuleParseFunction(options, dataProvider, registry);
7146
7679
  const listPagesCtx = await buildListPagesContext(dataProvider, options.requirements.listPages ?? [], options.compiledListPagesTemplates, parse, options.urlPath);
7147
7680
  const listUsersCtx = await buildListUsersContext(dataProvider, options.requirements.listUsers ?? [], options.compiledListUsersTemplates, parse);
7148
7681
  const tagCloudCtx = await buildTagCloudContext(dataProvider, options.requirements.tagCloud ?? []);
7149
7682
  const pageTags = dataProvider.getPageTags?.() ?? null;
7683
+ const resolvedStyleSlots = new Map;
7684
+ const routedStyleAnchors = new WeakSet;
7150
7685
  const resolvedElements = walkAndResolve(ast.elements, {
7151
7686
  listPages: listPagesCtx,
7152
7687
  listUsers: listUsersCtx,
@@ -7157,24 +7692,30 @@ async function resolveModules(ast, dataProvider, options) {
7157
7692
  pageTags,
7158
7693
  listPagesIdCounter: 0,
7159
7694
  listUsersIdCounter: 0,
7160
- tagCloudIdCounter: 0
7695
+ tagCloudIdCounter: 0,
7696
+ resolvedStyleSlots,
7697
+ routedStyleAnchors
7161
7698
  });
7162
- const { elements: finalElements, styles } = collectStyles(resolvedElements.elements);
7699
+ const {
7700
+ elements: finalElements,
7701
+ styles,
7702
+ anchoredStyles
7703
+ } = collectStyles(resolvedElements.elements, routedStyleAnchors);
7163
7704
  const result = {
7164
7705
  ...ast,
7165
7706
  elements: finalElements
7166
7707
  };
7167
- if (styles.length > 0) {
7168
- result.styles = styles;
7169
- }
7170
- return result;
7708
+ const mergedStyles = mergeCollectedStyles(ast.styles, styles, resolvedStyleSlots, anchoredStyles);
7709
+ if (mergedStyles.length > 0)
7710
+ result.styles = mergedStyles;
7711
+ options.onDiagnostics?.(registry.diagnostics);
7712
+ return registry.finalize(result, finalElements, pageTags, containsSyntaxFootnoteBlock(ast.elements));
7171
7713
  }
7172
- function createModuleParseFunction(options, dataProvider) {
7714
+ function createModuleParseFunction(options, dataProvider, registry) {
7173
7715
  const transform = createModuleSourceTransform(options, dataProvider);
7174
- if (!transform) {
7175
- return options.parse;
7176
- }
7177
- return (source) => options.parse(transform(source));
7716
+ return (source) => registry.register(options.parse(transform ? transform(source) : source), {
7717
+ stripLegacyImplicitFootnoteBlock: true
7718
+ });
7178
7719
  }
7179
7720
  function createModuleSourceTransform(options, dataProvider) {
7180
7721
  if (options.transformModuleSource) {
@@ -12309,6 +12850,7 @@ function createParseContext(tokens, options = {}) {
12309
12850
  version: options.version ?? "wikidot",
12310
12851
  trackPositions: options.trackPositions ?? true,
12311
12852
  settings: options.settings ?? DEFAULT_SETTINGS,
12853
+ appendImplicitFootnoteBlock: options.appendImplicitFootnoteBlock ?? true,
12312
12854
  footnotes: [],
12313
12855
  tocEntries: [],
12314
12856
  codeBlocks: [],
@@ -12826,67 +13368,6 @@ function suppressAtLevel(elements) {
12826
13368
  function suppressDivAdjacentParagraphs(elements) {
12827
13369
  return suppressAtLevel(elements);
12828
13370
  }
12829
- // packages/parser/src/parser/toc.ts
12830
- class TocIndexer {
12831
- index = 0;
12832
- next() {
12833
- return this.index++;
12834
- }
12835
- }
12836
- function buildTocList(indexer, items) {
12837
- const listItems = items.map((item) => buildTocListItem(indexer, item));
12838
- return {
12839
- element: "list",
12840
- data: {
12841
- type: "bullet",
12842
- attributes: {},
12843
- items: listItems
12844
- }
12845
- };
12846
- }
12847
- function buildTocListItem(indexer, item) {
12848
- if (item.kind === "list") {
12849
- return {
12850
- "item-type": "sub-list",
12851
- element: "list",
12852
- data: {
12853
- type: "bullet",
12854
- attributes: {},
12855
- items: item.children.map((child) => buildTocListItem(indexer, child))
12856
- }
12857
- };
12858
- }
12859
- const anchor = `#toc${indexer.next()}`;
12860
- const linkElement = {
12861
- element: "link",
12862
- data: {
12863
- type: "table-of-contents",
12864
- link: anchor,
12865
- extra: null,
12866
- label: { text: item.value },
12867
- target: null
12868
- }
12869
- };
12870
- return {
12871
- "item-type": "elements",
12872
- attributes: {},
12873
- elements: [linkElement]
12874
- };
12875
- }
12876
- function buildTableOfContents(entries) {
12877
- if (entries.length === 0) {
12878
- return [];
12879
- }
12880
- const depthItems = entries.map((entry) => ({
12881
- depth: entry.level - 1,
12882
- ltype: null,
12883
- value: entry.text
12884
- }));
12885
- const trees = processDepths(null, depthItems);
12886
- const indexer = new TocIndexer;
12887
- return trees.map((tree) => buildTocList(indexer, tree.list));
12888
- }
12889
-
12890
13371
  // packages/parser/src/parser/parse/footnotes.ts
12891
13372
  function containsFootnoteBlock(elements) {
12892
13373
  let found = false;
@@ -12900,7 +13381,9 @@ function containsFootnoteBlock(elements) {
12900
13381
  // packages/parser/src/parser/parse/result.ts
12901
13382
  function finalizeParseResult(ctx, children) {
12902
13383
  const cleanedChildren = postprocessChildren(children);
12903
- appendImplicitFootnoteBlock(cleanedChildren);
13384
+ if (ctx.appendImplicitFootnoteBlock) {
13385
+ appendImplicitFootnoteBlock(cleanedChildren);
13386
+ }
12904
13387
  return {
12905
13388
  ast: buildSyntaxTree(ctx, cleanedChildren),
12906
13389
  diagnostics: ctx.diagnostics
@@ -13272,30 +13755,6 @@ function preprocess(text) {
13272
13755
  return result;
13273
13756
  }
13274
13757
 
13275
- // packages/parser/src/parser/preprocess/expr/evaluate.ts
13276
- import { evaluateExpression, formatExprValue, isTruthy } from "@wdprlib/ast";
13277
- function evaluateDirective(kind, match) {
13278
- if (kind === "expr") {
13279
- const result2 = evaluateExpression(match.head);
13280
- if (result2.success)
13281
- return formatExprValue(result2.value);
13282
- if (result2.error === "empty expression")
13283
- return "";
13284
- return "ERROR";
13285
- }
13286
- if (kind === "if") {
13287
- if (!match.hasPipe)
13288
- return "";
13289
- return isTruthy(match.head) ? match.thenText : match.elseText;
13290
- }
13291
- if (!match.hasPipe)
13292
- return "";
13293
- const result = evaluateExpression(match.head);
13294
- if (!result.success)
13295
- return "ERROR";
13296
- return result.value !== 0 && !Number.isNaN(result.value) ? match.thenText : match.elseText;
13297
- }
13298
-
13299
13758
  // packages/parser/src/parser/preprocess/expr/chars.ts
13300
13759
  function isWhitespace(ch) {
13301
13760
  return ch === " " || ch === "\t" || ch === `
@@ -13324,6 +13783,30 @@ function matchDirectiveKind(source, i) {
13324
13783
  return null;
13325
13784
  }
13326
13785
 
13786
+ // packages/parser/src/parser/preprocess/expr/evaluate.ts
13787
+ import { evaluateExpression as evaluateExpression2, formatExprValue, isTruthy as isTruthy2 } from "@wdprlib/ast";
13788
+ function evaluateDirective(kind, match) {
13789
+ if (kind === "expr") {
13790
+ const result2 = evaluateExpression2(match.head);
13791
+ if (result2.success)
13792
+ return formatExprValue(result2.value);
13793
+ if (result2.error === "empty expression")
13794
+ return "";
13795
+ return "ERROR";
13796
+ }
13797
+ if (kind === "if") {
13798
+ if (!match.hasPipe)
13799
+ return "";
13800
+ return isTruthy2(match.head) ? match.thenText : match.elseText;
13801
+ }
13802
+ if (!match.hasPipe)
13803
+ return "";
13804
+ const result = evaluateExpression2(match.head);
13805
+ if (!result.success)
13806
+ return "ERROR";
13807
+ return result.value !== 0 && !Number.isNaN(result.value) ? match.thenText : match.elseText;
13808
+ }
13809
+
13327
13810
  // packages/parser/src/parser/preprocess/expr/parse.ts
13328
13811
  function tryParseInnermostDirective(source, start, kind) {
13329
13812
  const keywordLen = kind === "ifexpr" ? 6 : kind === "expr" ? 4 : 2;
@@ -13425,11 +13908,14 @@ function expandInnermost(source) {
13425
13908
  }
13426
13909
 
13427
13910
  // packages/parser/src/parser/preprocess/expr/index.ts
13911
+ var MAX_EXPR_NESTING = 64;
13428
13912
  function preprocessExpr(source) {
13429
13913
  if (!source.includes("[[#"))
13430
13914
  return source;
13431
13915
  const sentinels = makeUniqueSentinels(source);
13432
13916
  const { masked, placeholders } = maskRawRegions(source, sentinels);
13917
+ if (exceedsExprNestingLimit(masked))
13918
+ return source;
13433
13919
  const reduced = reduceExpr(masked);
13434
13920
  return restorePlaceholders(reduced, placeholders, sentinels);
13435
13921
  }
@@ -13444,6 +13930,26 @@ function reduceExpr(source) {
13444
13930
  }
13445
13931
  return current2;
13446
13932
  }
13933
+ function exceedsExprNestingLimit(source) {
13934
+ const expressionStack = [];
13935
+ let expressionDepth = 0;
13936
+ for (let i = 0;i < source.length; i++) {
13937
+ if (source.startsWith("[[", i)) {
13938
+ const isExpression = matchDirectiveKind(source, i) !== null;
13939
+ expressionStack.push(isExpression);
13940
+ if (isExpression && ++expressionDepth > MAX_EXPR_NESTING)
13941
+ return true;
13942
+ i++;
13943
+ continue;
13944
+ }
13945
+ if (source.startsWith("]]", i)) {
13946
+ if (expressionStack.pop())
13947
+ expressionDepth--;
13948
+ i++;
13949
+ }
13950
+ }
13951
+ return false;
13952
+ }
13447
13953
 
13448
13954
  // packages/parser/src/parser/parse/source.ts
13449
13955
  function prepareSourceForParse(source, options) {
@@ -13470,6 +13976,287 @@ function parse(source, options) {
13470
13976
  });
13471
13977
  return new Parser(tokens, options).parse();
13472
13978
  }
13979
+ // packages/parser/src/pipeline/process.ts
13980
+ import {
13981
+ DEFAULT_SETTINGS as DEFAULT_SETTINGS2
13982
+ } from "@wdprlib/ast";
13983
+
13984
+ // packages/parser/src/parser/rules/block/module/resolution/resolve-async.ts
13985
+ async function resolveModulesWithAsyncParse(ast, dataProvider, options) {
13986
+ const registry = new ModuleDocumentRegistry;
13987
+ registry.register(ast);
13988
+ const parse2 = async (source) => registry.register(await options.parse(source));
13989
+ const [listPagesData, listUsersData, tagCloudData] = await Promise.all([
13990
+ buildListPagesDataMap(dataProvider, options.requirements.listPages ?? [], options.urlPath),
13991
+ buildListUsersDataMap(dataProvider, options.requirements.listUsers ?? []),
13992
+ buildTagCloudDataMap(dataProvider, options.requirements.tagCloud ?? [])
13993
+ ]);
13994
+ const context = {
13995
+ dataProvider,
13996
+ listPagesData,
13997
+ listUsersData,
13998
+ tagCloudData,
13999
+ options,
14000
+ parse: parse2
14001
+ };
14002
+ const state = { listPagesId: 0, listUsersId: 0, tagCloudId: 0 };
14003
+ const resolvedElements = await resolveElements(ast.elements, context, state);
14004
+ const { elements, styles, anchoredStyles } = collectStyles(resolvedElements);
14005
+ const intermediate = { ...ast, elements };
14006
+ const mergedStyles = mergeCollectedStyles(ast.styles, styles, new Map, anchoredStyles);
14007
+ if (mergedStyles.length > 0)
14008
+ intermediate.styles = mergedStyles;
14009
+ else
14010
+ delete intermediate.styles;
14011
+ return {
14012
+ ast: registry.finalize(intermediate, elements, options.pageTags),
14013
+ diagnostics: registry.diagnostics
14014
+ };
14015
+ }
14016
+ async function resolveElements(elements, context, state) {
14017
+ const result = [];
14018
+ for (const element of elements) {
14019
+ if (element.element === "module") {
14020
+ const resolved = await resolveModuleElement(element, context, state);
14021
+ if (resolved !== null) {
14022
+ result.push(...resolved);
14023
+ continue;
14024
+ }
14025
+ }
14026
+ if (element.element === "if-tags") {
14027
+ const resolution = resolveIfTags(element.data, context.options.pageTags);
14028
+ if (resolution.matched) {
14029
+ result.push(...await resolveElements(element.data.elements, context, state));
14030
+ }
14031
+ continue;
14032
+ }
14033
+ result.push(await resolveElementChildren(element, context, state));
14034
+ }
14035
+ return result;
14036
+ }
14037
+ async function resolveModuleElement(element, context, state) {
14038
+ if (isListPagesModule2(element.data)) {
14039
+ const id = state.listPagesId++;
14040
+ const data = context.listPagesData.get(id);
14041
+ const template = context.options.compiledListPagesTemplates.get(id);
14042
+ if (data && template)
14043
+ return resolveListPagesAsync(element.data, data, template, context.parse);
14044
+ return context.dataProvider.fetchListPages ? [] : [element];
14045
+ }
14046
+ if (isListUsersModule2(element.data)) {
14047
+ const id = state.listUsersId++;
14048
+ const data = context.listUsersData.get(id);
14049
+ const template = context.options.compiledListUsersTemplates?.get(id);
14050
+ if (data && template) {
14051
+ const variableContext = { user: data.user };
14052
+ return (await context.parse(template(variableContext))).elements;
14053
+ }
14054
+ return context.dataProvider.fetchListUsers ? [] : [element];
14055
+ }
14056
+ if (isTagCloudModule(element.data)) {
14057
+ const id = state.tagCloudId++;
14058
+ const data = context.tagCloudData.get(id);
14059
+ if (data)
14060
+ return resolveTagCloud(element.data, data);
14061
+ return context.dataProvider.fetchTagCloud ? [] : [element];
14062
+ }
14063
+ return null;
14064
+ }
14065
+ async function resolveListPagesAsync(module, data, template, parse2) {
14066
+ if (data.pages.length === 0)
14067
+ return [];
14068
+ const result = [];
14069
+ if (module["prepend-line"] && !module.separate) {
14070
+ result.push(...(await parse2(module["prepend-line"])).elements);
14071
+ }
14072
+ for (let i = 0;i < data.pages.length; i++) {
14073
+ const page = data.pages[i];
14074
+ if (!page)
14075
+ continue;
14076
+ const variableContext = {
14077
+ page,
14078
+ index: i + 1,
14079
+ total: data.totalCount,
14080
+ limit: module.limit,
14081
+ site: data.site
14082
+ };
14083
+ const parsed = await parse2(template(variableContext));
14084
+ if (module.separate) {
14085
+ result.push({
14086
+ element: "container",
14087
+ data: {
14088
+ type: "div",
14089
+ attributes: { class: "list-pages-item" },
14090
+ elements: parsed.elements
14091
+ }
14092
+ });
14093
+ } else {
14094
+ result.push(...parsed.elements);
14095
+ }
14096
+ }
14097
+ if (module["append-line"] && !module.separate) {
14098
+ result.push(...(await parse2(module["append-line"])).elements);
14099
+ }
14100
+ return module.wrapper ? [
14101
+ {
14102
+ element: "container",
14103
+ data: {
14104
+ type: "div",
14105
+ attributes: { class: "list-pages-box" },
14106
+ elements: result
14107
+ }
14108
+ }
14109
+ ] : result;
14110
+ }
14111
+ async function resolveElementChildren(element, context, state) {
14112
+ if (element.element === "list") {
14113
+ const items = [];
14114
+ for (const item of element.data.items) {
14115
+ if (item["item-type"] === "elements") {
14116
+ items.push({ ...item, elements: await resolveElements(item.elements, context, state) });
14117
+ } else {
14118
+ const resolved = (await resolveElements([listElement(item.data)], context, state))[0];
14119
+ items.push(resolved?.element === "list" ? { ...item, data: resolved.data } : item);
14120
+ }
14121
+ }
14122
+ return { ...element, data: { ...element.data, items } };
14123
+ }
14124
+ if (element.element === "table") {
14125
+ const rows = [];
14126
+ for (const row of element.data.rows) {
14127
+ const cells = [];
14128
+ for (const cell of row.cells) {
14129
+ cells.push({ ...cell, elements: await resolveElements(cell.elements, context, state) });
14130
+ }
14131
+ rows.push({ ...row, cells });
14132
+ }
14133
+ return { ...element, data: { ...element.data, rows } };
14134
+ }
14135
+ if (element.element === "definition-list") {
14136
+ const entries = [];
14137
+ for (const entry of element.data) {
14138
+ entries.push({
14139
+ ...entry,
14140
+ key: await resolveElements(entry.key, context, state),
14141
+ value: await resolveElements(entry.value, context, state)
14142
+ });
14143
+ }
14144
+ return { ...element, data: entries };
14145
+ }
14146
+ if (element.element === "tab-view") {
14147
+ const tabs = [];
14148
+ for (const tab of element.data) {
14149
+ tabs.push({ ...tab, elements: await resolveElements(tab.elements, context, state) });
14150
+ }
14151
+ return { ...element, data: tabs };
14152
+ }
14153
+ const children = getGenericElementChildren(element);
14154
+ return children === null ? element : withGenericElementChildren(element, await resolveElements(children, context, state));
14155
+ }
14156
+
14157
+ // packages/parser/src/pipeline/process.ts
14158
+ var DEFAULT_MODULE_MAX_PASSES = 5;
14159
+ var PIPELINE_DIAGNOSTIC_POSITION = {
14160
+ start: { line: 1, column: 1, offset: 0 },
14161
+ end: { line: 1, column: 1, offset: 0 }
14162
+ };
14163
+ async function processWikitext(source, options) {
14164
+ const settings = options.settings ?? DEFAULT_SETTINGS2;
14165
+ const callbackContext = {
14166
+ page: options.page,
14167
+ settings
14168
+ };
14169
+ const dependencies = [];
14170
+ const diagnostics = [];
14171
+ const fetchInclude = createRequestIncludeFetcher(options.dataProvider?.fetchInclude ? (pageRef) => options.dataProvider.fetchInclude(pageRef, callbackContext) : undefined);
14172
+ const resolveSource = async (input) => {
14173
+ if (!fetchInclude)
14174
+ return input;
14175
+ const resolution = await resolveIncludesAsyncWithTrace(input, fetchInclude, {
14176
+ maxIterations: options.includeMaxIterations,
14177
+ settings
14178
+ });
14179
+ dependencies.push(...resolution.dependencies);
14180
+ if (resolution.reachedMaxIterations) {
14181
+ diagnostics.push(createLimitDiagnostic("include-resolution-limit", `Include expansion stopped after ${options.includeMaxIterations ?? 10} iterations.`));
14182
+ }
14183
+ return resolution.source;
14184
+ };
14185
+ const expandedSource = await resolveSource(source);
14186
+ const initial = parse(expandedSource, {
14187
+ settings,
14188
+ pageTags: options.page.tags,
14189
+ appendImplicitFootnoteBlock: false
14190
+ });
14191
+ diagnostics.push(...initial.diagnostics);
14192
+ const dataProvider = createModuleDataProvider(options, callbackContext);
14193
+ const parseFragment = async (fragmentSource) => parse(await resolveSource(fragmentSource), {
14194
+ settings,
14195
+ pageTags: options.page.tags,
14196
+ appendImplicitFootnoteBlock: false
14197
+ });
14198
+ let ast = initial.ast;
14199
+ for (let pass = 0;pass < DEFAULT_MODULE_MAX_PASSES; pass++) {
14200
+ const extraction = extractDataRequirements(ast);
14201
+ if (pass > 0 && !hasResolvableRequirements(extraction.requirements, options.dataProvider)) {
14202
+ break;
14203
+ }
14204
+ const resolved = await resolveModulesWithAsyncParse(ast, dataProvider, {
14205
+ parse: parseFragment,
14206
+ compiledListPagesTemplates: extraction.compiledListPagesTemplates,
14207
+ compiledListUsersTemplates: extraction.compiledListUsersTemplates,
14208
+ requirements: extraction.requirements,
14209
+ urlPath: options.page.urlPath,
14210
+ pageTags: options.page.tags
14211
+ });
14212
+ ast = resolved.ast;
14213
+ diagnostics.push(...resolved.diagnostics);
14214
+ }
14215
+ if (hasResolvableRequirements(extractDataRequirements(ast).requirements, options.dataProvider)) {
14216
+ diagnostics.push(createLimitDiagnostic("module-resolution-limit", `Module resolution stopped after ${DEFAULT_MODULE_MAX_PASSES} passes.`));
14217
+ }
14218
+ return {
14219
+ ast,
14220
+ page: options.page,
14221
+ settings,
14222
+ diagnostics,
14223
+ dependencies
14224
+ };
14225
+ }
14226
+ function hasResolvableRequirements(requirements, provider) {
14227
+ return Boolean(provider?.fetchListPages && requirements.listPages.length > 0 || provider?.fetchListUsers && requirements.listUsers.length > 0 || provider?.fetchTagCloud && requirements.tagCloud.length > 0);
14228
+ }
14229
+ function createLimitDiagnostic(code, message) {
14230
+ return {
14231
+ severity: "warning",
14232
+ code,
14233
+ message,
14234
+ position: PIPELINE_DIAGNOSTIC_POSITION
14235
+ };
14236
+ }
14237
+ function createModuleDataProvider(options, context) {
14238
+ const provider = options.dataProvider;
14239
+ return {
14240
+ fetchListPages: provider?.fetchListPages ? (query, requirement) => provider.fetchListPages(query, requirement, context) : undefined,
14241
+ fetchListUsers: provider?.fetchListUsers ? (requirement) => provider.fetchListUsers(requirement, context) : undefined,
14242
+ fetchTagCloud: provider?.fetchTagCloud ? (requirement) => provider.fetchTagCloud(requirement, context) : undefined,
14243
+ getPageTags: () => options.page.tags
14244
+ };
14245
+ }
14246
+ function createRequestIncludeFetcher(fetcher) {
14247
+ if (!fetcher)
14248
+ return;
14249
+ const cache = new Map;
14250
+ return (pageRef) => {
14251
+ const key = `${pageRef.site ?? ""}:${pageRef.page.toLowerCase()}`;
14252
+ const cached = cache.get(key);
14253
+ if (cached)
14254
+ return cached;
14255
+ const result = fetcher(pageRef).catch(() => null);
14256
+ cache.set(key, result);
14257
+ return result;
14258
+ };
14259
+ }
13473
14260
  export {
13474
14261
  tokenize,
13475
14262
  text,
@@ -13477,8 +14264,10 @@ export {
13477
14264
  resolveModules,
13478
14265
  resolveListUsers,
13479
14266
  resolveIncludesWithTrace,
14267
+ resolveIncludesAsyncWithTrace,
13480
14268
  resolveIncludesAsync,
13481
14269
  resolveIncludes,
14270
+ processWikitext,
13482
14271
  preprocessIftags,
13483
14272
  parseTags,
13484
14273
  parseParent,
@@ -13489,6 +14278,7 @@ export {
13489
14278
  parse,
13490
14279
  paragraph,
13491
14280
  normalizeQuery,
14281
+ matchesListPagesSelectors,
13492
14282
  listItemSubList,
13493
14283
  listItemElements,
13494
14284
  list,
@@ -13502,6 +14292,7 @@ export {
13502
14292
  extractListUsersVariables,
13503
14293
  extractIncludeReferences,
13504
14294
  extractDataRequirements,
14295
+ definePageData,
13505
14296
  createToken,
13506
14297
  createSettings,
13507
14298
  createPosition,
@@ -13513,5 +14304,5 @@ export {
13513
14304
  STYLE_SLOT_PREFIX2 as STYLE_SLOT_PREFIX,
13514
14305
  Parser,
13515
14306
  Lexer,
13516
- DEFAULT_SETTINGS2 as DEFAULT_SETTINGS
14307
+ DEFAULT_SETTINGS3 as DEFAULT_SETTINGS
13517
14308
  };