@wdprlib/parser 4.3.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.
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) {
@@ -6744,16 +6749,31 @@ function expandIterativeWithTrace(source, fetcher, maxIterations) {
6744
6749
  reachedMaxIterations: iterations.length === maxIterations && scanIncludeDirectives(current2).length > 0
6745
6750
  };
6746
6751
  }
6747
- async function expandIterativeAsync(source, fetcher, maxIterations) {
6752
+ async function expandIterativeAsyncWithTrace(source, fetcher, maxIterations) {
6748
6753
  let current2 = source;
6749
6754
  const replacementCache = new Map;
6755
+ const dependencies = [];
6756
+ const iterations = [];
6750
6757
  for (let i = 0;i < maxIterations; i++) {
6751
- const expanded = await expandOneIterationAsync(current2, fetcher, replacementCache);
6752
- if (expanded === null || expanded === current2)
6758
+ const expanded = await expandOneIterationAsyncWithTrace(current2, fetcher, replacementCache, i);
6759
+ if (expanded === null)
6753
6760
  break;
6754
- current2 = expanded;
6761
+ dependencies.push(...expanded.dependencies);
6762
+ iterations.push({
6763
+ iteration: i,
6764
+ directives: expanded.references,
6765
+ changed: expanded.source !== current2
6766
+ });
6767
+ if (expanded.source === current2)
6768
+ break;
6769
+ current2 = expanded.source;
6755
6770
  }
6756
- return current2;
6771
+ return {
6772
+ source: current2,
6773
+ dependencies,
6774
+ iterations,
6775
+ reachedMaxIterations: iterations.length === maxIterations && scanIncludeDirectives(current2).length > 0
6776
+ };
6757
6777
  }
6758
6778
  function expandOneIteration(source, fetcher, replacementCache) {
6759
6779
  if (!MAYBE_INCLUDE_PATTERN.test(source))
@@ -6791,12 +6811,13 @@ function expandOneIterationWithTrace(source, fetcher, replacementCache, iteratio
6791
6811
  dependencies
6792
6812
  };
6793
6813
  }
6794
- async function expandOneIterationAsync(source, fetcher, replacementCache) {
6814
+ async function expandOneIterationAsyncWithTrace(source, fetcher, replacementCache, iteration) {
6795
6815
  if (!MAYBE_INCLUDE_PATTERN.test(source))
6796
6816
  return null;
6797
6817
  const directives = scanIncludeDirectives(source);
6798
6818
  if (directives.length === 0)
6799
6819
  return null;
6820
+ const references = directives.map(createIncludeReference);
6800
6821
  const replacements = await Promise.all(directives.map(({ inner }) => replaceCachedAsync(inner, fetcher, replacementCache)));
6801
6822
  const parts = [];
6802
6823
  let lastPos = 0;
@@ -6806,7 +6827,11 @@ async function expandOneIterationAsync(source, fetcher, replacementCache) {
6806
6827
  lastPos = end;
6807
6828
  }
6808
6829
  parts.push(source.slice(lastPos));
6809
- return parts.join("");
6830
+ return {
6831
+ source: parts.join(""),
6832
+ references,
6833
+ dependencies: references.map((reference) => ({ ...reference, iteration }))
6834
+ };
6810
6835
  }
6811
6836
  function replaceCached(inner, fetcher, cache) {
6812
6837
  const cached = cache.get(inner);
@@ -6848,12 +6873,20 @@ function resolveIncludesWithTrace(source, fetcher, options) {
6848
6873
  return expandIterativeWithTrace(source, cachedFetcher, maxIterations);
6849
6874
  }
6850
6875
  async function resolveIncludesAsync(source, fetcher, options) {
6876
+ return (await resolveIncludesAsyncWithTrace(source, fetcher, options)).source;
6877
+ }
6878
+ async function resolveIncludesAsyncWithTrace(source, fetcher, options) {
6851
6879
  if (options?.settings && !options.settings.enablePageSyntax) {
6852
- return source;
6880
+ return {
6881
+ source,
6882
+ dependencies: [],
6883
+ iterations: [],
6884
+ reachedMaxIterations: false
6885
+ };
6853
6886
  }
6854
6887
  const maxIterations = options?.maxIterations ?? 10;
6855
6888
  const cachedFetcher = createCachedAsyncIncludeFetcher(fetcher, normalizePageKey);
6856
- return expandIterativeAsync(source, cachedFetcher, maxIterations);
6889
+ return expandIterativeAsyncWithTrace(source, cachedFetcher, maxIterations);
6857
6890
  }
6858
6891
  function normalizePageKey(location) {
6859
6892
  const site = location.site ?? "";
@@ -6867,7 +6900,7 @@ function isListUsersModule2(module) {
6867
6900
  function resolveListUsers(_module, data, compiledTemplate, parse) {
6868
6901
  const ctx = { user: data.user };
6869
6902
  const substituted = compiledTemplate(ctx);
6870
- const itemAst = parse(substituted);
6903
+ const itemAst = getModuleParseAst(parse(substituted));
6871
6904
  return itemAst.elements;
6872
6905
  }
6873
6906
  // packages/parser/src/parser/rules/block/module/resolution/data-maps.ts
@@ -7020,6 +7053,117 @@ function countDynamicModules(elements) {
7020
7053
  return counts;
7021
7054
  }
7022
7055
 
7056
+ // packages/parser/src/parser/rules/block/module/resolution/styles.ts
7057
+ import { STYLE_ANCHOR_PREFIX, STYLE_SLOT_PREFIX } from "@wdprlib/ast";
7058
+ function collectStyles(elements, ignoredAnchors = new WeakSet) {
7059
+ const styles = [];
7060
+ const anchoredStyles = [];
7061
+ const anchors = [];
7062
+ const usedSlots = collectExistingStyleSlots(elements);
7063
+ const ctx = { nextSlotId: 0, usedSlots };
7064
+ const filtered = collectStylesFromElements(elements, styles, anchoredStyles, anchors, ignoredAnchors, ctx);
7065
+ return { elements: filtered, styles, anchoredStyles, anchors };
7066
+ }
7067
+ function mergeCollectedStyles(existing, collected, resolvedSlots = new Map, anchoredStyles = []) {
7068
+ const unanchoredPrevious = [...existing ?? []];
7069
+ for (const style of anchoredStyles)
7070
+ removeFirst(unanchoredPrevious, style);
7071
+ for (const style of collected) {
7072
+ if (isStyleSlotMarker(style))
7073
+ removeFirst(unanchoredPrevious, style);
7074
+ }
7075
+ const merged = unanchoredPrevious.filter((style) => !isStyleSlotMarker(style));
7076
+ for (const style of collected) {
7077
+ if (isStyleSlotMarker(style))
7078
+ appendStyleSlot(merged, style, resolvedSlots);
7079
+ else
7080
+ merged.push(style);
7081
+ }
7082
+ return merged;
7083
+ }
7084
+ function isStyleSlotMarker(style) {
7085
+ return style.startsWith(STYLE_SLOT_PREFIX);
7086
+ }
7087
+ function createStyleSlotMarker(slotId) {
7088
+ return `${STYLE_SLOT_PREFIX}${slotId}`;
7089
+ }
7090
+ function getStyleSlotId(data) {
7091
+ const slotId = data._styleSlot;
7092
+ return Number.isSafeInteger(slotId) && slotId >= 0 ? slotId : undefined;
7093
+ }
7094
+ function removeFirst(values, value) {
7095
+ const index = values.indexOf(value);
7096
+ if (index >= 0)
7097
+ values.splice(index, 1);
7098
+ }
7099
+ function appendStyleSlot(target, marker, resolvedSlots) {
7100
+ const slotId = Number(marker.slice(STYLE_SLOT_PREFIX.length));
7101
+ const replacement = resolvedSlots.get(slotId);
7102
+ if (replacement)
7103
+ target.push(...replacement);
7104
+ else
7105
+ target.push(marker);
7106
+ }
7107
+ function collectExistingStyleSlots(elements) {
7108
+ const slots = new Set;
7109
+ walkElements(elements, (element) => {
7110
+ if (element.element !== "if-tags")
7111
+ return;
7112
+ const slotId = getStyleSlotId(element.data);
7113
+ if (slotId !== undefined)
7114
+ slots.add(slotId);
7115
+ });
7116
+ return slots;
7117
+ }
7118
+ function allocateStyleSlot(ctx) {
7119
+ while (ctx.usedSlots.has(ctx.nextSlotId))
7120
+ ctx.nextSlotId++;
7121
+ const slotId = ctx.nextSlotId++;
7122
+ ctx.usedSlots.add(slotId);
7123
+ return slotId;
7124
+ }
7125
+ function collectStylesFromElements(elements, styles, anchoredStyles, anchors, ignoredAnchors, ctx) {
7126
+ const result = [];
7127
+ for (const element of elements) {
7128
+ if (element.element === "style") {
7129
+ const css = element.data;
7130
+ if (css.startsWith(STYLE_SLOT_PREFIX)) {
7131
+ styles.push(css);
7132
+ continue;
7133
+ }
7134
+ if (css.startsWith(STYLE_ANCHOR_PREFIX)) {
7135
+ anchors.push(element);
7136
+ if (ignoredAnchors.has(element)) {
7137
+ result.push(element);
7138
+ continue;
7139
+ }
7140
+ const anchoredCss = css.slice(STYLE_ANCHOR_PREFIX.length);
7141
+ styles.push(anchoredCss);
7142
+ anchoredStyles.push(anchoredCss);
7143
+ result.push(element);
7144
+ continue;
7145
+ }
7146
+ styles.push(css);
7147
+ const anchor = { element: "style", data: `${STYLE_ANCHOR_PREFIX}${css}` };
7148
+ anchors.push(anchor);
7149
+ result.push(anchor);
7150
+ continue;
7151
+ }
7152
+ if (element.element === "if-tags") {
7153
+ const slotId = getStyleSlotId(element.data) ?? allocateStyleSlot(ctx);
7154
+ styles.push(createStyleSlotMarker(slotId));
7155
+ const data = { ...element.data, _styleSlot: slotId };
7156
+ result.push({
7157
+ element: "if-tags",
7158
+ data
7159
+ });
7160
+ continue;
7161
+ }
7162
+ result.push(mapElementChildren(element, (children) => collectStylesFromElements(children, styles, anchoredStyles, anchors, ignoredAnchors, ctx)));
7163
+ }
7164
+ return result;
7165
+ }
7166
+
7023
7167
  // packages/parser/src/parser/rules/block/module/resolution/walk-resolve.ts
7024
7168
  function walkAndResolve(elements, ctx) {
7025
7169
  const result = [];
@@ -7042,6 +7186,7 @@ function walkAndResolve(elements, ctx) {
7042
7186
  if (isIfTagsElement(element)) {
7043
7187
  const ifTagsData = element.data;
7044
7188
  const resolveResult = resolveIfTags(ifTagsData, ctx.pageTags);
7189
+ const styleSlotId = getStyleSlotId(ifTagsData);
7045
7190
  if (resolveResult.evaluated) {
7046
7191
  if (resolveResult.matched) {
7047
7192
  const childResult = walkAndResolve(ifTagsData.elements, {
@@ -7050,11 +7195,23 @@ function walkAndResolve(elements, ctx) {
7050
7195
  listUsersIdCounter: listUsersId,
7051
7196
  tagCloudIdCounter: tagCloudId
7052
7197
  });
7053
- result.push(...childResult.elements);
7198
+ if (styleSlotId === undefined) {
7199
+ result.push(...childResult.elements);
7200
+ } else {
7201
+ const collected = collectStyles(childResult.elements);
7202
+ ctx.resolvedStyleSlots.set(styleSlotId, collected.styles);
7203
+ for (const anchor of collected.anchors)
7204
+ ctx.routedStyleAnchors.add(anchor);
7205
+ result.push({ element: "style", data: createStyleSlotMarker(styleSlotId) }, ...collected.elements);
7206
+ }
7054
7207
  listPagesId = childResult.nextListPagesId;
7055
7208
  listUsersId = childResult.nextListUsersId;
7056
7209
  tagCloudId = childResult.nextTagCloudId;
7057
7210
  } else {
7211
+ if (styleSlotId !== undefined) {
7212
+ ctx.resolvedStyleSlots.set(styleSlotId, []);
7213
+ result.push({ element: "style", data: createStyleSlotMarker(styleSlotId) });
7214
+ }
7058
7215
  const counts = countDynamicModules(ifTagsData.elements);
7059
7216
  listPagesId += counts.listPagesId;
7060
7217
  listUsersId += counts.listUsersId;
@@ -7109,44 +7266,361 @@ function walkAndResolve(elements, ctx) {
7109
7266
  };
7110
7267
  }
7111
7268
 
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 };
7269
+ // packages/parser/src/parser/rules/block/module/resolution/document.ts
7270
+ import {
7271
+ evaluateExpression,
7272
+ isTruthy
7273
+ } from "@wdprlib/ast";
7274
+
7275
+ // packages/parser/src/parser/toc.ts
7276
+ class TocIndexer {
7277
+ index = 0;
7278
+ next() {
7279
+ return this.index++;
7280
+ }
7119
7281
  }
7120
- function collectStylesFromElements(elements, styles, ctx) {
7282
+ function buildTocList(indexer, items) {
7283
+ const listItems = items.map((item) => buildTocListItem(indexer, item));
7284
+ return {
7285
+ element: "list",
7286
+ data: {
7287
+ type: "bullet",
7288
+ attributes: {},
7289
+ items: listItems
7290
+ }
7291
+ };
7292
+ }
7293
+ function buildTocListItem(indexer, item) {
7294
+ if (item.kind === "list") {
7295
+ return {
7296
+ "item-type": "sub-list",
7297
+ element: "list",
7298
+ data: {
7299
+ type: "bullet",
7300
+ attributes: {},
7301
+ items: item.children.map((child) => buildTocListItem(indexer, child))
7302
+ }
7303
+ };
7304
+ }
7305
+ const anchor = `#toc${indexer.next()}`;
7306
+ const linkElement = {
7307
+ element: "link",
7308
+ data: {
7309
+ type: "table-of-contents",
7310
+ link: anchor,
7311
+ extra: null,
7312
+ label: { text: item.value },
7313
+ target: null
7314
+ }
7315
+ };
7316
+ return {
7317
+ "item-type": "elements",
7318
+ attributes: {},
7319
+ elements: [linkElement]
7320
+ };
7321
+ }
7322
+ function buildTableOfContents(entries) {
7323
+ if (entries.length === 0) {
7324
+ return [];
7325
+ }
7326
+ const depthItems = entries.map((entry) => ({
7327
+ depth: entry.level - 1,
7328
+ ltype: null,
7329
+ value: entry.text
7330
+ }));
7331
+ const trees = processDepths(null, depthItems);
7332
+ const indexer = new TocIndexer;
7333
+ return trees.map((tree) => buildTocList(indexer, tree.list));
7334
+ }
7335
+
7336
+ // packages/parser/src/parser/rules/block/module/resolution/document.ts
7337
+ class ModuleDocumentRegistry {
7338
+ footnotesByElement = new WeakMap;
7339
+ diagnostics = [];
7340
+ register(result, options = {}) {
7341
+ const parseResult = isParseResult(result) ? result : null;
7342
+ const ast = parseResult ? parseResult.ast : result;
7343
+ const elements = options.stripLegacyImplicitFootnoteBlock ? stripTrailingDefaultFootnoteBlock(ast.elements) : ast.elements;
7344
+ const registeredAst = elements === ast.elements ? ast : { ...ast, elements };
7345
+ if (parseResult) {
7346
+ this.diagnostics.push(...parseResult.diagnostics);
7347
+ }
7348
+ this.registerFootnotes(registeredAst);
7349
+ return registeredAst;
7350
+ }
7351
+ finalize(ast, elements, pageTags, ensureFootnoteBlock = true) {
7352
+ const normalizedElements = normalizeFootnoteBlocks(elements, ensureFootnoteBlock);
7353
+ const footnotes = [];
7354
+ const htmlBlocks = [];
7355
+ const codeBlocks = [];
7356
+ const tocEntries = [];
7357
+ const collectElement = (element) => {
7358
+ if (element.element === "footnote") {
7359
+ const content = this.footnotesByElement.get(element);
7360
+ if (content)
7361
+ footnotes.push(content);
7362
+ } else if (element.element === "html") {
7363
+ htmlBlocks.push(element.data.contents);
7364
+ } else if (element.element === "code") {
7365
+ codeBlocks.push(element.data);
7366
+ } else if (isTocHeading(element)) {
7367
+ tocEntries.push({
7368
+ level: element.data.type.header.level,
7369
+ text: extractHeadingText(element.data.elements)
7370
+ });
7371
+ }
7372
+ };
7373
+ walkRenderOrder(normalizedElements, pageTags, collectElement);
7374
+ const result = { ...ast, elements: normalizedElements };
7375
+ setOptionalArray(result, "footnotes", footnotes);
7376
+ setOptionalArray(result, "html-blocks", htmlBlocks);
7377
+ setOptionalArray(result, "code-blocks", codeBlocks);
7378
+ setOptionalArray(result, "table-of-contents", buildTableOfContents(tocEntries));
7379
+ return result;
7380
+ }
7381
+ registerFootnotes(ast) {
7382
+ const footnoteElements = [];
7383
+ walkSyntaxOrder(ast.elements, (element) => {
7384
+ if (element.element === "footnote")
7385
+ footnoteElements.push(element);
7386
+ });
7387
+ for (let i = 0;i < footnoteElements.length; i++) {
7388
+ const content = ast.footnotes?.[i];
7389
+ if (content)
7390
+ this.footnotesByElement.set(footnoteElements[i], content);
7391
+ }
7392
+ }
7393
+ }
7394
+ function isParseResult(result) {
7395
+ return "ast" in result && "diagnostics" in result;
7396
+ }
7397
+ function setOptionalArray(tree, key, value) {
7398
+ if (value.length > 0) {
7399
+ tree[key] = value;
7400
+ } else {
7401
+ delete tree[key];
7402
+ }
7403
+ }
7404
+ function stripTrailingDefaultFootnoteBlock(elements) {
7405
+ const last = elements.at(-1);
7406
+ if (last?.element !== "footnote-block" || last.data.title !== null || last.data.hide === true) {
7407
+ return elements;
7408
+ }
7409
+ return elements.slice(0, -1);
7410
+ }
7411
+ function normalizeFootnoteBlocks(elements, ensureFootnoteBlock) {
7412
+ const state = { found: false };
7413
+ const normalized = mapSyntaxElements(elements, (element) => {
7414
+ if (element.element !== "footnote-block")
7415
+ return element;
7416
+ if (state.found)
7417
+ return null;
7418
+ state.found = true;
7419
+ return element;
7420
+ });
7421
+ if (!state.found && ensureFootnoteBlock) {
7422
+ normalized.push({
7423
+ element: "footnote-block",
7424
+ data: { title: null, hide: false }
7425
+ });
7426
+ }
7427
+ return normalized;
7428
+ }
7429
+ function containsSyntaxFootnoteBlock(elements) {
7430
+ let found = false;
7431
+ walkSyntaxOrder(elements, (element) => {
7432
+ if (element.element === "footnote-block")
7433
+ found = true;
7434
+ });
7435
+ return found;
7436
+ }
7437
+ function mapSyntaxElements(elements, transform) {
7121
7438
  const result = [];
7439
+ for (const original of elements) {
7440
+ const element = transform(original);
7441
+ if (!element)
7442
+ continue;
7443
+ result.push(mapSyntaxChildren(element, (children) => mapSyntaxElements(children, transform)));
7444
+ }
7445
+ return result;
7446
+ }
7447
+ function mapSyntaxChildren(element, transform) {
7448
+ if (element.element === "if") {
7449
+ return {
7450
+ ...element,
7451
+ data: {
7452
+ ...element.data,
7453
+ then: transform(element.data.then),
7454
+ else: transform(element.data.else)
7455
+ }
7456
+ };
7457
+ }
7458
+ if (element.element === "ifexpr") {
7459
+ return {
7460
+ ...element,
7461
+ data: {
7462
+ ...element.data,
7463
+ then: transform(element.data.then),
7464
+ else: transform(element.data.else)
7465
+ }
7466
+ };
7467
+ }
7468
+ if (element.element === "bibliography-block") {
7469
+ return {
7470
+ ...element,
7471
+ data: {
7472
+ ...element.data,
7473
+ entries: element.data.entries.map((entry) => ({
7474
+ ...entry,
7475
+ key: transform(entry.key),
7476
+ value: transform(entry.value)
7477
+ }))
7478
+ }
7479
+ };
7480
+ }
7481
+ if (element.element === "list") {
7482
+ return {
7483
+ ...element,
7484
+ data: {
7485
+ ...element.data,
7486
+ items: element.data.items.map((item) => item["item-type"] === "elements" ? { ...item, elements: transform(item.elements) } : mapSubList(item, transform))
7487
+ }
7488
+ };
7489
+ }
7490
+ if (element.element === "table") {
7491
+ return {
7492
+ ...element,
7493
+ data: {
7494
+ ...element.data,
7495
+ rows: element.data.rows.map((row) => ({
7496
+ ...row,
7497
+ cells: row.cells.map((cell) => ({ ...cell, elements: transform(cell.elements) }))
7498
+ }))
7499
+ }
7500
+ };
7501
+ }
7502
+ if (element.element === "definition-list") {
7503
+ return {
7504
+ ...element,
7505
+ data: element.data.map((entry) => ({
7506
+ ...entry,
7507
+ key: transform(entry.key),
7508
+ value: transform(entry.value)
7509
+ }))
7510
+ };
7511
+ }
7512
+ if (element.element === "tab-view") {
7513
+ return {
7514
+ ...element,
7515
+ data: element.data.map((tab) => ({ ...tab, elements: transform(tab.elements) }))
7516
+ };
7517
+ }
7518
+ const children = getGenericElementChildren(element);
7519
+ return children === null ? element : withGenericElementChildren(element, transform(children));
7520
+ }
7521
+ function mapSubList(item, transform) {
7522
+ const transformed = transform([listElement(item.data)])[0];
7523
+ return transformed?.element === "list" ? { ...item, data: transformed.data } : item;
7524
+ }
7525
+ function walkSyntaxOrder(elements, callback) {
7122
7526
  for (const element of elements) {
7123
- if (element.element === "style") {
7124
- styles.push(element.data);
7527
+ callback(element);
7528
+ walkAllSyntaxChildren(element, (children) => walkSyntaxOrder(children, callback));
7529
+ }
7530
+ }
7531
+ function walkRenderOrder(elements, pageTags, callback) {
7532
+ for (const element of elements) {
7533
+ callback(element);
7534
+ if (element.element === "if") {
7535
+ walkRenderOrder(isTruthy(element.data.condition) ? element.data.then : element.data.else, pageTags, callback);
7536
+ continue;
7537
+ }
7538
+ if (element.element === "ifexpr") {
7539
+ const evaluated = evaluateExpression(element.data.expression);
7540
+ if (evaluated.success) {
7541
+ walkRenderOrder(evaluated.value !== 0 ? element.data.then : element.data.else, pageTags, callback);
7542
+ }
7125
7543
  continue;
7126
7544
  }
7127
7545
  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
- });
7546
+ const resolved = resolveIfTags(element.data, pageTags);
7547
+ if (!resolved.evaluated || resolved.matched) {
7548
+ walkRenderOrder(element.data.elements, pageTags, callback);
7549
+ }
7135
7550
  continue;
7136
7551
  }
7137
- result.push(mapElementChildren(element, (children) => collectStylesFromElements(children, styles, ctx)));
7552
+ if (element.element === "bibliography-block") {
7553
+ if (!element.data.hide) {
7554
+ for (const entry of element.data.entries) {
7555
+ walkRenderOrder(entry.value, pageTags, callback);
7556
+ }
7557
+ }
7558
+ continue;
7559
+ }
7560
+ walkBasicChildren(element, (children) => walkRenderOrder(children, pageTags, callback));
7138
7561
  }
7139
- return result;
7562
+ }
7563
+ function walkAllSyntaxChildren(element, callback) {
7564
+ if (element.element === "if" || element.element === "ifexpr") {
7565
+ callback(element.data.then);
7566
+ callback(element.data.else);
7567
+ return;
7568
+ }
7569
+ if (element.element === "bibliography-block") {
7570
+ for (const entry of element.data.entries) {
7571
+ callback(entry.key);
7572
+ callback(entry.value);
7573
+ }
7574
+ return;
7575
+ }
7576
+ walkBasicChildren(element, callback);
7577
+ }
7578
+ function walkBasicChildren(element, callback) {
7579
+ if (element.element === "list") {
7580
+ for (const item of element.data.items) {
7581
+ callback(item["item-type"] === "elements" ? item.elements : [listElement(item.data)]);
7582
+ }
7583
+ return;
7584
+ }
7585
+ if (element.element === "table") {
7586
+ for (const row of element.data.rows) {
7587
+ for (const cell of row.cells)
7588
+ callback(cell.elements);
7589
+ }
7590
+ return;
7591
+ }
7592
+ if (element.element === "definition-list") {
7593
+ for (const entry of element.data) {
7594
+ callback(entry.key);
7595
+ callback(entry.value);
7596
+ }
7597
+ return;
7598
+ }
7599
+ if (element.element === "tab-view") {
7600
+ for (const tab of element.data)
7601
+ callback(tab.elements);
7602
+ return;
7603
+ }
7604
+ const children = getGenericElementChildren(element);
7605
+ if (children !== null)
7606
+ callback(children);
7607
+ }
7608
+ function isTocHeading(element) {
7609
+ return element.element === "container" && typeof element.data.type === "object" && "header" in element.data.type && element.data.type.header["has-toc"];
7140
7610
  }
7141
7611
 
7142
7612
  // packages/parser/src/parser/rules/block/module/resolve.ts
7143
7613
  var MODULE_SECONDARY_INCLUDE_MAX_ITERATIONS = 5;
7144
7614
  async function resolveModules(ast, dataProvider, options) {
7145
- const parse = createModuleParseFunction(options, dataProvider);
7615
+ const registry = new ModuleDocumentRegistry;
7616
+ registry.register(ast);
7617
+ const parse = createModuleParseFunction(options, dataProvider, registry);
7146
7618
  const listPagesCtx = await buildListPagesContext(dataProvider, options.requirements.listPages ?? [], options.compiledListPagesTemplates, parse, options.urlPath);
7147
7619
  const listUsersCtx = await buildListUsersContext(dataProvider, options.requirements.listUsers ?? [], options.compiledListUsersTemplates, parse);
7148
7620
  const tagCloudCtx = await buildTagCloudContext(dataProvider, options.requirements.tagCloud ?? []);
7149
7621
  const pageTags = dataProvider.getPageTags?.() ?? null;
7622
+ const resolvedStyleSlots = new Map;
7623
+ const routedStyleAnchors = new WeakSet;
7150
7624
  const resolvedElements = walkAndResolve(ast.elements, {
7151
7625
  listPages: listPagesCtx,
7152
7626
  listUsers: listUsersCtx,
@@ -7157,24 +7631,30 @@ async function resolveModules(ast, dataProvider, options) {
7157
7631
  pageTags,
7158
7632
  listPagesIdCounter: 0,
7159
7633
  listUsersIdCounter: 0,
7160
- tagCloudIdCounter: 0
7634
+ tagCloudIdCounter: 0,
7635
+ resolvedStyleSlots,
7636
+ routedStyleAnchors
7161
7637
  });
7162
- const { elements: finalElements, styles } = collectStyles(resolvedElements.elements);
7638
+ const {
7639
+ elements: finalElements,
7640
+ styles,
7641
+ anchoredStyles
7642
+ } = collectStyles(resolvedElements.elements, routedStyleAnchors);
7163
7643
  const result = {
7164
7644
  ...ast,
7165
7645
  elements: finalElements
7166
7646
  };
7167
- if (styles.length > 0) {
7168
- result.styles = styles;
7169
- }
7170
- return result;
7647
+ const mergedStyles = mergeCollectedStyles(ast.styles, styles, resolvedStyleSlots, anchoredStyles);
7648
+ if (mergedStyles.length > 0)
7649
+ result.styles = mergedStyles;
7650
+ options.onDiagnostics?.(registry.diagnostics);
7651
+ return registry.finalize(result, finalElements, pageTags, containsSyntaxFootnoteBlock(ast.elements));
7171
7652
  }
7172
- function createModuleParseFunction(options, dataProvider) {
7653
+ function createModuleParseFunction(options, dataProvider, registry) {
7173
7654
  const transform = createModuleSourceTransform(options, dataProvider);
7174
- if (!transform) {
7175
- return options.parse;
7176
- }
7177
- return (source) => options.parse(transform(source));
7655
+ return (source) => registry.register(options.parse(transform ? transform(source) : source), {
7656
+ stripLegacyImplicitFootnoteBlock: true
7657
+ });
7178
7658
  }
7179
7659
  function createModuleSourceTransform(options, dataProvider) {
7180
7660
  if (options.transformModuleSource) {
@@ -12309,6 +12789,7 @@ function createParseContext(tokens, options = {}) {
12309
12789
  version: options.version ?? "wikidot",
12310
12790
  trackPositions: options.trackPositions ?? true,
12311
12791
  settings: options.settings ?? DEFAULT_SETTINGS,
12792
+ appendImplicitFootnoteBlock: options.appendImplicitFootnoteBlock ?? true,
12312
12793
  footnotes: [],
12313
12794
  tocEntries: [],
12314
12795
  codeBlocks: [],
@@ -12826,67 +13307,6 @@ function suppressAtLevel(elements) {
12826
13307
  function suppressDivAdjacentParagraphs(elements) {
12827
13308
  return suppressAtLevel(elements);
12828
13309
  }
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
13310
  // packages/parser/src/parser/parse/footnotes.ts
12891
13311
  function containsFootnoteBlock(elements) {
12892
13312
  let found = false;
@@ -12900,7 +13320,9 @@ function containsFootnoteBlock(elements) {
12900
13320
  // packages/parser/src/parser/parse/result.ts
12901
13321
  function finalizeParseResult(ctx, children) {
12902
13322
  const cleanedChildren = postprocessChildren(children);
12903
- appendImplicitFootnoteBlock(cleanedChildren);
13323
+ if (ctx.appendImplicitFootnoteBlock) {
13324
+ appendImplicitFootnoteBlock(cleanedChildren);
13325
+ }
12904
13326
  return {
12905
13327
  ast: buildSyntaxTree(ctx, cleanedChildren),
12906
13328
  diagnostics: ctx.diagnostics
@@ -13273,10 +13695,10 @@ function preprocess(text) {
13273
13695
  }
13274
13696
 
13275
13697
  // packages/parser/src/parser/preprocess/expr/evaluate.ts
13276
- import { evaluateExpression, formatExprValue, isTruthy } from "@wdprlib/ast";
13698
+ import { evaluateExpression as evaluateExpression2, formatExprValue, isTruthy as isTruthy2 } from "@wdprlib/ast";
13277
13699
  function evaluateDirective(kind, match) {
13278
13700
  if (kind === "expr") {
13279
- const result2 = evaluateExpression(match.head);
13701
+ const result2 = evaluateExpression2(match.head);
13280
13702
  if (result2.success)
13281
13703
  return formatExprValue(result2.value);
13282
13704
  if (result2.error === "empty expression")
@@ -13286,11 +13708,11 @@ function evaluateDirective(kind, match) {
13286
13708
  if (kind === "if") {
13287
13709
  if (!match.hasPipe)
13288
13710
  return "";
13289
- return isTruthy(match.head) ? match.thenText : match.elseText;
13711
+ return isTruthy2(match.head) ? match.thenText : match.elseText;
13290
13712
  }
13291
13713
  if (!match.hasPipe)
13292
13714
  return "";
13293
- const result = evaluateExpression(match.head);
13715
+ const result = evaluateExpression2(match.head);
13294
13716
  if (!result.success)
13295
13717
  return "ERROR";
13296
13718
  return result.value !== 0 && !Number.isNaN(result.value) ? match.thenText : match.elseText;
@@ -13470,6 +13892,252 @@ function parse(source, options) {
13470
13892
  });
13471
13893
  return new Parser(tokens, options).parse();
13472
13894
  }
13895
+ // packages/parser/src/pipeline/process.ts
13896
+ import { DEFAULT_SETTINGS as DEFAULT_SETTINGS2 } from "@wdprlib/ast";
13897
+
13898
+ // packages/parser/src/parser/rules/block/module/resolution/resolve-async.ts
13899
+ async function resolveModulesWithAsyncParse(ast, dataProvider, options) {
13900
+ const registry = new ModuleDocumentRegistry;
13901
+ registry.register(ast);
13902
+ const parse2 = async (source) => registry.register(await options.parse(source));
13903
+ const [listPagesData, listUsersData, tagCloudData] = await Promise.all([
13904
+ buildListPagesDataMap(dataProvider, options.requirements.listPages ?? [], options.urlPath),
13905
+ buildListUsersDataMap(dataProvider, options.requirements.listUsers ?? []),
13906
+ buildTagCloudDataMap(dataProvider, options.requirements.tagCloud ?? [])
13907
+ ]);
13908
+ const context = {
13909
+ dataProvider,
13910
+ listPagesData,
13911
+ listUsersData,
13912
+ tagCloudData,
13913
+ options,
13914
+ parse: parse2
13915
+ };
13916
+ const state = { listPagesId: 0, listUsersId: 0, tagCloudId: 0 };
13917
+ const resolvedElements = await resolveElements(ast.elements, context, state);
13918
+ const { elements, styles, anchoredStyles } = collectStyles(resolvedElements);
13919
+ const intermediate = { ...ast, elements };
13920
+ const mergedStyles = mergeCollectedStyles(ast.styles, styles, new Map, anchoredStyles);
13921
+ if (mergedStyles.length > 0)
13922
+ intermediate.styles = mergedStyles;
13923
+ else
13924
+ delete intermediate.styles;
13925
+ return {
13926
+ ast: registry.finalize(intermediate, elements, options.pageTags),
13927
+ diagnostics: registry.diagnostics
13928
+ };
13929
+ }
13930
+ async function resolveElements(elements, context, state) {
13931
+ const result = [];
13932
+ for (const element of elements) {
13933
+ if (element.element === "module") {
13934
+ const resolved = await resolveModuleElement(element, context, state);
13935
+ if (resolved !== null) {
13936
+ result.push(...resolved);
13937
+ continue;
13938
+ }
13939
+ }
13940
+ if (element.element === "if-tags") {
13941
+ const resolution = resolveIfTags(element.data, context.options.pageTags);
13942
+ if (resolution.matched) {
13943
+ result.push(...await resolveElements(element.data.elements, context, state));
13944
+ }
13945
+ continue;
13946
+ }
13947
+ result.push(await resolveElementChildren(element, context, state));
13948
+ }
13949
+ return result;
13950
+ }
13951
+ async function resolveModuleElement(element, context, state) {
13952
+ if (isListPagesModule2(element.data)) {
13953
+ const id = state.listPagesId++;
13954
+ const data = context.listPagesData.get(id);
13955
+ const template = context.options.compiledListPagesTemplates.get(id);
13956
+ if (data && template)
13957
+ return resolveListPagesAsync(element.data, data, template, context.parse);
13958
+ return context.dataProvider.fetchListPages ? [] : [element];
13959
+ }
13960
+ if (isListUsersModule2(element.data)) {
13961
+ const id = state.listUsersId++;
13962
+ const data = context.listUsersData.get(id);
13963
+ const template = context.options.compiledListUsersTemplates?.get(id);
13964
+ if (data && template) {
13965
+ const variableContext = { user: data.user };
13966
+ return (await context.parse(template(variableContext))).elements;
13967
+ }
13968
+ return context.dataProvider.fetchListUsers ? [] : [element];
13969
+ }
13970
+ if (isTagCloudModule(element.data)) {
13971
+ const id = state.tagCloudId++;
13972
+ const data = context.tagCloudData.get(id);
13973
+ if (data)
13974
+ return resolveTagCloud(element.data, data);
13975
+ return context.dataProvider.fetchTagCloud ? [] : [element];
13976
+ }
13977
+ return null;
13978
+ }
13979
+ async function resolveListPagesAsync(module, data, template, parse2) {
13980
+ if (data.pages.length === 0)
13981
+ return [];
13982
+ const result = [];
13983
+ if (module["prepend-line"] && !module.separate) {
13984
+ result.push(...(await parse2(module["prepend-line"])).elements);
13985
+ }
13986
+ for (let i = 0;i < data.pages.length; i++) {
13987
+ const page = data.pages[i];
13988
+ if (!page)
13989
+ continue;
13990
+ const variableContext = {
13991
+ page,
13992
+ index: i + 1,
13993
+ total: data.totalCount,
13994
+ limit: module.limit,
13995
+ site: data.site
13996
+ };
13997
+ const parsed = await parse2(template(variableContext));
13998
+ if (module.separate) {
13999
+ result.push({
14000
+ element: "container",
14001
+ data: {
14002
+ type: "div",
14003
+ attributes: { class: "list-pages-item" },
14004
+ elements: parsed.elements
14005
+ }
14006
+ });
14007
+ } else {
14008
+ result.push(...parsed.elements);
14009
+ }
14010
+ }
14011
+ if (module["append-line"] && !module.separate) {
14012
+ result.push(...(await parse2(module["append-line"])).elements);
14013
+ }
14014
+ return module.wrapper ? [
14015
+ {
14016
+ element: "container",
14017
+ data: {
14018
+ type: "div",
14019
+ attributes: { class: "list-pages-box" },
14020
+ elements: result
14021
+ }
14022
+ }
14023
+ ] : result;
14024
+ }
14025
+ async function resolveElementChildren(element, context, state) {
14026
+ if (element.element === "list") {
14027
+ const items = [];
14028
+ for (const item of element.data.items) {
14029
+ if (item["item-type"] === "elements") {
14030
+ items.push({ ...item, elements: await resolveElements(item.elements, context, state) });
14031
+ } else {
14032
+ const resolved = (await resolveElements([listElement(item.data)], context, state))[0];
14033
+ items.push(resolved?.element === "list" ? { ...item, data: resolved.data } : item);
14034
+ }
14035
+ }
14036
+ return { ...element, data: { ...element.data, items } };
14037
+ }
14038
+ if (element.element === "table") {
14039
+ const rows = [];
14040
+ for (const row of element.data.rows) {
14041
+ const cells = [];
14042
+ for (const cell of row.cells) {
14043
+ cells.push({ ...cell, elements: await resolveElements(cell.elements, context, state) });
14044
+ }
14045
+ rows.push({ ...row, cells });
14046
+ }
14047
+ return { ...element, data: { ...element.data, rows } };
14048
+ }
14049
+ if (element.element === "definition-list") {
14050
+ const entries = [];
14051
+ for (const entry of element.data) {
14052
+ entries.push({
14053
+ ...entry,
14054
+ key: await resolveElements(entry.key, context, state),
14055
+ value: await resolveElements(entry.value, context, state)
14056
+ });
14057
+ }
14058
+ return { ...element, data: entries };
14059
+ }
14060
+ if (element.element === "tab-view") {
14061
+ const tabs = [];
14062
+ for (const tab of element.data) {
14063
+ tabs.push({ ...tab, elements: await resolveElements(tab.elements, context, state) });
14064
+ }
14065
+ return { ...element, data: tabs };
14066
+ }
14067
+ const children = getGenericElementChildren(element);
14068
+ return children === null ? element : withGenericElementChildren(element, await resolveElements(children, context, state));
14069
+ }
14070
+
14071
+ // packages/parser/src/pipeline/process.ts
14072
+ async function processWikitext(source, options) {
14073
+ const settings = options.settings ?? DEFAULT_SETTINGS2;
14074
+ const callbackContext = {
14075
+ page: options.page,
14076
+ settings
14077
+ };
14078
+ const dependencies = [];
14079
+ const fetchInclude = createRequestIncludeFetcher(options.dataProvider?.fetchInclude ? (pageRef) => options.dataProvider.fetchInclude(pageRef, callbackContext) : undefined);
14080
+ const resolveSource = async (input) => {
14081
+ if (!fetchInclude)
14082
+ return input;
14083
+ const resolution = await resolveIncludesAsyncWithTrace(input, fetchInclude, {
14084
+ maxIterations: options.includeMaxIterations,
14085
+ settings
14086
+ });
14087
+ dependencies.push(...resolution.dependencies);
14088
+ return resolution.source;
14089
+ };
14090
+ const expandedSource = await resolveSource(source);
14091
+ const initial = parse(expandedSource, {
14092
+ settings,
14093
+ pageTags: options.page.tags,
14094
+ appendImplicitFootnoteBlock: false
14095
+ });
14096
+ const extraction = extractDataRequirements(initial.ast);
14097
+ const dataProvider = createModuleDataProvider(options, callbackContext);
14098
+ const resolved = await resolveModulesWithAsyncParse(initial.ast, dataProvider, {
14099
+ parse: async (fragmentSource) => parse(await resolveSource(fragmentSource), {
14100
+ settings,
14101
+ pageTags: options.page.tags,
14102
+ appendImplicitFootnoteBlock: false
14103
+ }),
14104
+ compiledListPagesTemplates: extraction.compiledListPagesTemplates,
14105
+ compiledListUsersTemplates: extraction.compiledListUsersTemplates,
14106
+ requirements: extraction.requirements,
14107
+ urlPath: options.page.urlPath,
14108
+ pageTags: options.page.tags
14109
+ });
14110
+ return {
14111
+ ast: resolved.ast,
14112
+ page: options.page,
14113
+ settings,
14114
+ diagnostics: [...initial.diagnostics, ...resolved.diagnostics],
14115
+ dependencies
14116
+ };
14117
+ }
14118
+ function createModuleDataProvider(options, context) {
14119
+ const provider = options.dataProvider;
14120
+ return {
14121
+ fetchListPages: provider?.fetchListPages ? (query, requirement) => provider.fetchListPages(query, requirement, context) : undefined,
14122
+ fetchListUsers: provider?.fetchListUsers ? (requirement) => provider.fetchListUsers(requirement, context) : undefined,
14123
+ fetchTagCloud: provider?.fetchTagCloud ? (requirement) => provider.fetchTagCloud(requirement, context) : undefined,
14124
+ getPageTags: () => options.page.tags
14125
+ };
14126
+ }
14127
+ function createRequestIncludeFetcher(fetcher) {
14128
+ if (!fetcher)
14129
+ return;
14130
+ const cache = new Map;
14131
+ return (pageRef) => {
14132
+ const key = `${pageRef.site ?? ""}:${pageRef.page.toLowerCase()}`;
14133
+ const cached = cache.get(key);
14134
+ if (cached)
14135
+ return cached;
14136
+ const result = fetcher(pageRef).catch(() => null);
14137
+ cache.set(key, result);
14138
+ return result;
14139
+ };
14140
+ }
13473
14141
  export {
13474
14142
  tokenize,
13475
14143
  text,
@@ -13477,8 +14145,10 @@ export {
13477
14145
  resolveModules,
13478
14146
  resolveListUsers,
13479
14147
  resolveIncludesWithTrace,
14148
+ resolveIncludesAsyncWithTrace,
13480
14149
  resolveIncludesAsync,
13481
14150
  resolveIncludes,
14151
+ processWikitext,
13482
14152
  preprocessIftags,
13483
14153
  parseTags,
13484
14154
  parseParent,
@@ -13513,5 +14183,5 @@ export {
13513
14183
  STYLE_SLOT_PREFIX2 as STYLE_SLOT_PREFIX,
13514
14184
  Parser,
13515
14185
  Lexer,
13516
- DEFAULT_SETTINGS2 as DEFAULT_SETTINGS
14186
+ DEFAULT_SETTINGS3 as DEFAULT_SETTINGS
13517
14187
  };