@wdprlib/parser 4.4.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -31,7 +31,7 @@ var __export = (target, all) => {
31
31
  var exports_src = {};
32
32
  __export(exports_src, {
33
33
  tokenize: () => tokenize,
34
- text: () => import_ast8.text,
34
+ text: () => import_ast9.text,
35
35
  resolveTagCloud: () => resolveTagCloud,
36
36
  resolveModules: () => resolveModules,
37
37
  resolveListUsers: () => resolveListUsers,
@@ -48,37 +48,39 @@ __export(exports_src, {
48
48
  parseDateSelector: () => parseDateSelector,
49
49
  parseCategory: () => parseCategory,
50
50
  parse: () => parse,
51
- paragraph: () => import_ast8.paragraph,
51
+ paragraph: () => import_ast9.paragraph,
52
52
  normalizeQuery: () => normalizeQuery,
53
- listItemSubList: () => import_ast8.listItemSubList,
54
- listItemElements: () => import_ast8.listItemElements,
55
- list: () => import_ast8.list,
56
- link: () => import_ast8.link,
57
- lineBreak: () => import_ast8.lineBreak,
58
- italics: () => import_ast8.italics,
53
+ matchesListPagesSelectors: () => matchesListPagesSelectors,
54
+ listItemSubList: () => import_ast9.listItemSubList,
55
+ listItemElements: () => import_ast9.listItemElements,
56
+ list: () => import_ast9.list,
57
+ link: () => import_ast9.link,
58
+ lineBreak: () => import_ast9.lineBreak,
59
+ italics: () => import_ast9.italics,
59
60
  isTagCloudModule: () => isTagCloudModule,
60
61
  isListUsersModule: () => isListUsersModule2,
61
- horizontalRule: () => import_ast8.horizontalRule,
62
- heading: () => import_ast8.heading,
62
+ horizontalRule: () => import_ast9.horizontalRule,
63
+ heading: () => import_ast9.heading,
63
64
  extractListUsersVariables: () => extractListUsersVariables,
64
65
  extractIncludeReferences: () => extractIncludeReferences,
65
66
  extractDataRequirements: () => extractDataRequirements,
67
+ definePageData: () => definePageData,
66
68
  createToken: () => createToken,
67
- createSettings: () => import_ast9.createSettings,
68
- createPosition: () => import_ast8.createPosition,
69
- createPoint: () => import_ast8.createPoint,
70
- container: () => import_ast8.container,
69
+ createSettings: () => import_ast10.createSettings,
70
+ createPosition: () => import_ast9.createPosition,
71
+ createPoint: () => import_ast9.createPoint,
72
+ container: () => import_ast9.container,
71
73
  compileTemplate: () => compileTemplate,
72
74
  compileListUsersTemplate: () => compileListUsersTemplate,
73
- bold: () => import_ast8.bold,
75
+ bold: () => import_ast9.bold,
74
76
  STYLE_SLOT_PREFIX: () => import_ast4.STYLE_SLOT_PREFIX,
75
77
  Parser: () => Parser,
76
78
  Lexer: () => Lexer,
77
- DEFAULT_SETTINGS: () => import_ast9.DEFAULT_SETTINGS
79
+ DEFAULT_SETTINGS: () => import_ast10.DEFAULT_SETTINGS
78
80
  });
79
81
  module.exports = __toCommonJS(exports_src);
80
- var import_ast8 = require("@wdprlib/ast");
81
82
  var import_ast9 = require("@wdprlib/ast");
83
+ var import_ast10 = require("@wdprlib/ast");
82
84
 
83
85
  // packages/parser/src/lexer/tokens.ts
84
86
  function createToken(type, value, position, lineStart = false) {
@@ -1445,7 +1447,7 @@ function parseNextBlock(ctx, skipWhitespace2, isAtEnd2) {
1445
1447
  }
1446
1448
 
1447
1449
  // packages/parser/src/parser/parse/context.ts
1448
- var import_ast5 = require("@wdprlib/ast");
1450
+ var import_ast6 = require("@wdprlib/ast");
1449
1451
 
1450
1452
  // packages/parser/src/parser/rules/tokens.ts
1451
1453
  function currentToken(ctx) {
@@ -5919,6 +5921,60 @@ function resolveListPages(module2, data, compiledTemplate, parse) {
5919
5921
  const items = renderListPagesItems(module2, data, compiledTemplate, parse);
5920
5922
  return wrapListPagesResult(module2, items, parse);
5921
5923
  }
5924
+ // packages/parser/src/parser/rules/block/module/listpages/types/external-data.ts
5925
+ function definePageData(input) {
5926
+ const separator = input.fullname.indexOf(":");
5927
+ const category = separator === -1 ? "_default" : input.fullname.slice(0, separator);
5928
+ const name = separator === -1 ? input.fullname : input.fullname.slice(separator + 1);
5929
+ return {
5930
+ ...input,
5931
+ name: input.name ?? name,
5932
+ category: input.category ?? category,
5933
+ hiddenTags: input.hiddenTags ?? [],
5934
+ children: input.children ?? 0,
5935
+ comments: input.comments ?? 0,
5936
+ size: input.size ?? 0,
5937
+ rating: input.rating ?? 0,
5938
+ ratingVotes: input.ratingVotes ?? 0,
5939
+ revisions: input.revisions ?? 0
5940
+ };
5941
+ }
5942
+ // packages/parser/src/parser/rules/block/module/listpages/selectors.ts
5943
+ function matchesListPagesSelectors(page, query, currentPage) {
5944
+ return matchesCategory(page.category, query.category, currentPage.category) && matchesTags(page, query.tags, currentPage);
5945
+ }
5946
+ function matchesCategory(category, selector, currentCategory) {
5947
+ if (!selector)
5948
+ return category === currentCategory;
5949
+ if (selector.exclude.includes(category))
5950
+ return false;
5951
+ const hasPositiveSelector = selector.all || selector.current || selector.include.length > 0;
5952
+ return !hasPositiveSelector || selector.all || selector.current && category === currentCategory || selector.include.includes(category);
5953
+ }
5954
+ function matchesTags(page, selector, currentPage) {
5955
+ if (!selector)
5956
+ return true;
5957
+ const allTags = new Set([...page.tags, ...page.hiddenTags]);
5958
+ if (selector.all.some((tag) => !allTags.has(tag)))
5959
+ return false;
5960
+ if (selector.any.length > 0 && !selector.any.some((tag) => allTags.has(tag)))
5961
+ return false;
5962
+ if (selector.none.some((tag) => allTags.has(tag)))
5963
+ return false;
5964
+ if (selector.special === "none")
5965
+ return allTags.size === 0;
5966
+ const currentTags = new Set(currentPage.tags.filter((tag) => !tag.startsWith("_")));
5967
+ if (selector.special === "same-visible") {
5968
+ return page.tags.some((tag) => currentTags.has(tag));
5969
+ }
5970
+ if (selector.special === "same-all") {
5971
+ return setsEqual(new Set(page.tags), currentTags);
5972
+ }
5973
+ return true;
5974
+ }
5975
+ function setsEqual(left, right) {
5976
+ return left.size === right.size && [...left].every((value) => right.has(value));
5977
+ }
5922
5978
  // packages/parser/src/parser/rules/block/module/listpages/url-resolution/fields.ts
5923
5979
  var URL_RESOLVABLE_FIELDS = [
5924
5980
  { attr: "offset", queryKey: "offset", type: "number" },
@@ -6348,13 +6404,20 @@ var BASE_PLACEHOLDER_OPEN = "";
6348
6404
  var BASE_PLACEHOLDER_CLOSE = "";
6349
6405
  var RAW_BLOCK_OPEN_PATTERN = /\[\[\s*(code|html)\b[^\]]*\]\]/iy;
6350
6406
  function makeUniqueSentinels(source) {
6351
- let open = BASE_PLACEHOLDER_OPEN;
6352
- let close = BASE_PLACEHOLDER_CLOSE;
6353
- while (source.includes(open) || source.includes(close)) {
6354
- open += BASE_PLACEHOLDER_OPEN;
6355
- close += BASE_PLACEHOLDER_CLOSE;
6407
+ let openRun = 0;
6408
+ let closeRun = 0;
6409
+ let longestOpenRun = 0;
6410
+ let longestCloseRun = 0;
6411
+ for (const char of source) {
6412
+ openRun = char === BASE_PLACEHOLDER_OPEN ? openRun + 1 : 0;
6413
+ closeRun = char === BASE_PLACEHOLDER_CLOSE ? closeRun + 1 : 0;
6414
+ longestOpenRun = Math.max(longestOpenRun, openRun);
6415
+ longestCloseRun = Math.max(longestCloseRun, closeRun);
6356
6416
  }
6357
- return { open, close };
6417
+ return {
6418
+ open: BASE_PLACEHOLDER_OPEN.repeat(longestOpenRun + 1),
6419
+ close: BASE_PLACEHOLDER_CLOSE.repeat(longestCloseRun + 1)
6420
+ };
6358
6421
  }
6359
6422
  function maskRawRegions(source, sentinels) {
6360
6423
  const placeholders = [];
@@ -6592,13 +6655,40 @@ function scanIncludeDirectives(source) {
6592
6655
  `, start);
6593
6656
  let depth = 0;
6594
6657
  let linkDepth = 0;
6658
+ const nestedQuotes = [];
6659
+ const nestedQuoteAllowed = [];
6595
6660
  let i = start;
6596
6661
  let closeEnd = -1;
6597
6662
  while (i < source.length) {
6598
6663
  const ch = source.charCodeAt(i);
6599
6664
  const next = source.charCodeAt(i + 1);
6600
6665
  const nextNext = source.charCodeAt(i + 2);
6601
- if (ch === OPEN_BRACKET && next === OPEN_BRACKET && nextNext === OPEN_BRACKET) {
6666
+ if (ch === 10 || ch === 13) {
6667
+ linkDepth = 0;
6668
+ if (depth > 1) {
6669
+ depth = 1;
6670
+ nestedQuotes.length = 0;
6671
+ nestedQuoteAllowed.length = 0;
6672
+ }
6673
+ i++;
6674
+ continue;
6675
+ }
6676
+ const nestedQuote = nestedQuotes.at(-1);
6677
+ if (nestedQuote !== undefined && nestedQuote !== null) {
6678
+ if (ch === 92) {
6679
+ i += 2;
6680
+ } else {
6681
+ if (ch === nestedQuote) {
6682
+ nestedQuotes[nestedQuotes.length - 1] = null;
6683
+ nestedQuoteAllowed[nestedQuoteAllowed.length - 1] = false;
6684
+ }
6685
+ i++;
6686
+ }
6687
+ } else if (nestedQuote === null && nestedQuoteAllowed.at(-1) === true && (ch === 34 || ch === 39)) {
6688
+ nestedQuotes[nestedQuotes.length - 1] = ch;
6689
+ nestedQuoteAllowed[nestedQuoteAllowed.length - 1] = false;
6690
+ i++;
6691
+ } else if (ch === OPEN_BRACKET && next === OPEN_BRACKET && nextNext === OPEN_BRACKET) {
6602
6692
  linkDepth++;
6603
6693
  i += 3;
6604
6694
  } else if (linkDepth > 0 && ch === CLOSE_BRACKET && next === CLOSE_BRACKET && nextNext === CLOSE_BRACKET) {
@@ -6607,10 +6697,26 @@ function scanIncludeDirectives(source) {
6607
6697
  } else if (linkDepth > 0) {
6608
6698
  i++;
6609
6699
  } else if (ch === OPEN_BRACKET && next === OPEN_BRACKET) {
6700
+ if (nestedQuoteAllowed.length > 0) {
6701
+ nestedQuoteAllowed[nestedQuoteAllowed.length - 1] = false;
6702
+ }
6703
+ if (depth > 0) {
6704
+ nestedQuotes.push(null);
6705
+ nestedQuoteAllowed.push(false);
6706
+ }
6610
6707
  depth++;
6611
6708
  i += 2;
6709
+ } else if (ch === CLOSE_BRACKET && next !== CLOSE_BRACKET && depth > 1) {
6710
+ depth--;
6711
+ nestedQuotes.pop();
6712
+ nestedQuoteAllowed.pop();
6713
+ i++;
6612
6714
  } else if (ch === CLOSE_BRACKET && next === CLOSE_BRACKET) {
6613
6715
  const closeStart = i;
6716
+ if (depth > 1) {
6717
+ nestedQuotes.pop();
6718
+ nestedQuoteAllowed.pop();
6719
+ }
6614
6720
  depth--;
6615
6721
  i += 2;
6616
6722
  if (depth <= 0) {
@@ -6627,6 +6733,13 @@ function scanIncludeDirectives(source) {
6627
6733
  }
6628
6734
  }
6629
6735
  } else {
6736
+ if (nestedQuoteAllowed.length > 0) {
6737
+ if (ch === 61) {
6738
+ nestedQuoteAllowed[nestedQuoteAllowed.length - 1] = true;
6739
+ } else if (ch !== 32 && ch !== 9) {
6740
+ nestedQuoteAllowed[nestedQuoteAllowed.length - 1] = false;
6741
+ }
6742
+ }
6630
6743
  i++;
6631
6744
  }
6632
6745
  }
@@ -6812,13 +6925,13 @@ function expandIterativeWithTrace(source, fetcher, maxIterations) {
6812
6925
  reachedMaxIterations: iterations.length === maxIterations && scanIncludeDirectives(current2).length > 0
6813
6926
  };
6814
6927
  }
6815
- async function expandIterativeAsyncWithTrace(source, fetcher, maxIterations) {
6928
+ async function expandIterativeAsyncWithTrace(source, fetcher, maxIterations, shouldDefer) {
6816
6929
  let current2 = source;
6817
6930
  const replacementCache = new Map;
6818
6931
  const dependencies = [];
6819
6932
  const iterations = [];
6820
6933
  for (let i = 0;i < maxIterations; i++) {
6821
- const expanded = await expandOneIterationAsyncWithTrace(current2, fetcher, replacementCache, i);
6934
+ const expanded = await expandOneIterationAsyncWithTrace(current2, fetcher, replacementCache, i, shouldDefer);
6822
6935
  if (expanded === null)
6823
6936
  break;
6824
6937
  dependencies.push(...expanded.dependencies);
@@ -6835,7 +6948,7 @@ async function expandIterativeAsyncWithTrace(source, fetcher, maxIterations) {
6835
6948
  source: current2,
6836
6949
  dependencies,
6837
6950
  iterations,
6838
- reachedMaxIterations: iterations.length === maxIterations && scanIncludeDirectives(current2).length > 0
6951
+ reachedMaxIterations: iterations.length === maxIterations && hasResolvableDirectives(current2, shouldDefer)
6839
6952
  };
6840
6953
  }
6841
6954
  function expandOneIteration(source, fetcher, replacementCache) {
@@ -6874,14 +6987,15 @@ function expandOneIterationWithTrace(source, fetcher, replacementCache, iteratio
6874
6987
  dependencies
6875
6988
  };
6876
6989
  }
6877
- async function expandOneIterationAsyncWithTrace(source, fetcher, replacementCache, iteration) {
6990
+ async function expandOneIterationAsyncWithTrace(source, fetcher, replacementCache, iteration, shouldDefer) {
6878
6991
  if (!MAYBE_INCLUDE_PATTERN.test(source))
6879
6992
  return null;
6880
6993
  const directives = scanIncludeDirectives(source);
6881
6994
  if (directives.length === 0)
6882
6995
  return null;
6883
6996
  const references = directives.map(createIncludeReference);
6884
- const replacements = await Promise.all(directives.map(({ inner }) => replaceCachedAsync(inner, fetcher, replacementCache)));
6997
+ const deferred = references.map((reference) => shouldDefer?.(reference) ?? false);
6998
+ const replacements = await Promise.all(directives.map((directive, index) => deferred[index] ? Promise.resolve(source.slice(directive.start, directive.end)) : replaceCachedAsync(directive.inner, fetcher, replacementCache)));
6885
6999
  const parts = [];
6886
7000
  let lastPos = 0;
6887
7001
  for (let i = 0;i < directives.length; i++) {
@@ -6893,9 +7007,15 @@ async function expandOneIterationAsyncWithTrace(source, fetcher, replacementCach
6893
7007
  return {
6894
7008
  source: parts.join(""),
6895
7009
  references,
6896
- dependencies: references.map((reference) => ({ ...reference, iteration }))
7010
+ dependencies: references.filter((_, index) => !deferred[index]).map((reference) => ({ ...reference, iteration }))
6897
7011
  };
6898
7012
  }
7013
+ function hasResolvableDirectives(source, shouldDefer) {
7014
+ const directives = scanIncludeDirectives(source);
7015
+ if (!shouldDefer)
7016
+ return directives.length > 0;
7017
+ return directives.some((directive) => !shouldDefer(createIncludeReference(directive)));
7018
+ }
6899
7019
  function replaceCached(inner, fetcher, cache) {
6900
7020
  const cached = cache.get(inner);
6901
7021
  if (cached !== undefined)
@@ -6951,6 +7071,19 @@ async function resolveIncludesAsyncWithTrace(source, fetcher, options) {
6951
7071
  const cachedFetcher = createCachedAsyncIncludeFetcher(fetcher, normalizePageKey);
6952
7072
  return expandIterativeAsyncWithTrace(source, cachedFetcher, maxIterations);
6953
7073
  }
7074
+ async function resolveIncludesAsyncWithTraceSelective(source, fetcher, shouldDefer, options) {
7075
+ if (options?.settings && !options.settings.enablePageSyntax) {
7076
+ return {
7077
+ source,
7078
+ dependencies: [],
7079
+ iterations: [],
7080
+ reachedMaxIterations: false
7081
+ };
7082
+ }
7083
+ const maxIterations = options?.maxIterations ?? 10;
7084
+ const cachedFetcher = createCachedAsyncIncludeFetcher(fetcher, normalizePageKey);
7085
+ return expandIterativeAsyncWithTrace(source, cachedFetcher, maxIterations, shouldDefer);
7086
+ }
6954
7087
  function normalizePageKey(location) {
6955
7088
  const site = location.site ?? "";
6956
7089
  const page = location.page.toLowerCase();
@@ -8418,6 +8551,9 @@ var tabviewRule = {
8418
8551
  }
8419
8552
  };
8420
8553
 
8554
+ // packages/parser/src/parser/rules/block/include/index.ts
8555
+ var import_ast5 = require("@wdprlib/ast");
8556
+
8421
8557
  // packages/parser/src/parser/rules/block/include/arguments.ts
8422
8558
  function collectIncludeArguments(ctx, startPos) {
8423
8559
  const argumentTokens = [];
@@ -8533,6 +8669,7 @@ var includeRule = {
8533
8669
  }
8534
8670
  pos++;
8535
8671
  consumed++;
8672
+ const directiveEnd = pos;
8536
8673
  if (ctx.tokens[pos]?.type === "NEWLINE") {
8537
8674
  pos++;
8538
8675
  consumed++;
@@ -8540,6 +8677,20 @@ var includeRule = {
8540
8677
  if (!args.target) {
8541
8678
  return { success: false };
8542
8679
  }
8680
+ const location = parsePageRef(args.target);
8681
+ if (ctx.deferInclude?.(location)) {
8682
+ const source = ctx.tokens.slice(ctx.pos, directiveEnd).map((token5) => token5.value).join("");
8683
+ const elements = [];
8684
+ const lines = source.split(`
8685
+ `);
8686
+ for (let index = 0;index < lines.length; index++) {
8687
+ if (index > 0)
8688
+ elements.push(import_ast5.lineBreak());
8689
+ if (lines[index] !== "")
8690
+ elements.push(import_ast5.text(lines[index]));
8691
+ }
8692
+ return { success: true, elements: [import_ast5.paragraph(elements)], consumed };
8693
+ }
8543
8694
  return {
8544
8695
  success: true,
8545
8696
  elements: [
@@ -8548,7 +8699,7 @@ var includeRule = {
8548
8699
  data: {
8549
8700
  "paragraph-safe": false,
8550
8701
  variables: parseVariables(args.argumentTokens),
8551
- location: parsePageRef(args.target),
8702
+ location,
8552
8703
  elements: []
8553
8704
  }
8554
8705
  }
@@ -9816,9 +9967,9 @@ function parseGalleryItemLine(content) {
9816
9967
  const alt = attrs.get("alt") ?? null;
9817
9968
  return { source, link, alt, newWindow };
9818
9969
  }
9819
- function parseItemAttrs(text) {
9970
+ function parseItemAttrs(text2) {
9820
9971
  const attrs = new Map;
9821
- const parts = text.trim().split('="');
9972
+ const parts = text2.trim().split('="');
9822
9973
  let key = parts[0]?.trim() ?? "";
9823
9974
  for (let i = 1;i < parts.length; i++) {
9824
9975
  const val = parts[i] ?? "";
@@ -11880,11 +12031,11 @@ var guillemetRule = {
11880
12031
  startTokens: ["LEFT_DOUBLE_ANGLE", "RIGHT_DOUBLE_ANGLE"],
11881
12032
  parse(ctx) {
11882
12033
  const token5 = ctx.tokens[ctx.pos];
11883
- const text = guillemetText(token5?.type);
11884
- if (text) {
12034
+ const text2 = guillemetText(token5?.type);
12035
+ if (text2) {
11885
12036
  return {
11886
12037
  success: true,
11887
- elements: [{ element: "text", data: text }],
12038
+ elements: [{ element: "text", data: text2 }],
11888
12039
  consumed: 1
11889
12040
  };
11890
12041
  }
@@ -12842,14 +12993,15 @@ var inlineRules = [
12842
12993
  textRule
12843
12994
  ];
12844
12995
  // packages/parser/src/parser/parse/context.ts
12845
- function createParseContext(tokens, options = {}) {
12996
+ function createParseContext(tokens, options = {}, deferInclude) {
12846
12997
  return {
12847
12998
  tokens,
12848
12999
  pos: 0,
12849
13000
  version: options.version ?? "wikidot",
12850
13001
  trackPositions: options.trackPositions ?? true,
12851
- settings: options.settings ?? import_ast5.DEFAULT_SETTINGS,
13002
+ settings: options.settings ?? import_ast6.DEFAULT_SETTINGS,
12852
13003
  appendImplicitFootnoteBlock: options.appendImplicitFootnoteBlock ?? true,
13004
+ deferInclude,
12853
13005
  footnotes: [],
12854
13006
  tocEntries: [],
12855
13007
  codeBlocks: [],
@@ -13426,39 +13578,41 @@ class Parser {
13426
13578
  this.ctx = createParseContext(tokens, options);
13427
13579
  }
13428
13580
  parse() {
13429
- const children = [];
13430
- while (!this.isAtEnd()) {
13431
- const blocks = this.parseBlock();
13432
- children.push(...blocks);
13433
- }
13434
- return finalizeParseResult(this.ctx, children);
13581
+ return parseContext(this.ctx);
13435
13582
  }
13436
- isAtEnd() {
13437
- return this.ctx.pos >= this.ctx.tokens.length || this.currentToken().type === "EOF";
13438
- }
13439
- currentToken() {
13440
- return this.ctx.tokens[this.ctx.pos] ?? this.eofToken();
13441
- }
13442
- eofToken() {
13443
- return {
13444
- type: "EOF",
13445
- value: "",
13446
- position: {
13447
- start: { line: 0, column: 0, offset: 0 },
13448
- end: { line: 0, column: 0, offset: 0 }
13449
- },
13450
- lineStart: false
13451
- };
13452
- }
13453
- skipWhitespace() {
13454
- while (this.currentToken().type === "WHITESPACE") {
13455
- this.ctx.pos++;
13456
- }
13583
+ }
13584
+ function parseTokensWithIncludeDeferral(tokens, options, deferInclude) {
13585
+ return parseContext(createParseContext(tokens, options, deferInclude));
13586
+ }
13587
+ function parseContext(ctx) {
13588
+ const children = [];
13589
+ while (!isAtEnd3(ctx)) {
13590
+ children.push(...parseBlock(ctx));
13457
13591
  }
13458
- parseBlock() {
13459
- return parseNextBlock(this.ctx, () => this.skipWhitespace(), () => this.isAtEnd());
13592
+ return finalizeParseResult(ctx, children);
13593
+ }
13594
+ function isAtEnd3(ctx) {
13595
+ return ctx.pos >= ctx.tokens.length || currentToken2(ctx).type === "EOF";
13596
+ }
13597
+ function currentToken2(ctx) {
13598
+ return ctx.tokens[ctx.pos] ?? {
13599
+ type: "EOF",
13600
+ value: "",
13601
+ position: {
13602
+ start: { line: 0, column: 0, offset: 0 },
13603
+ end: { line: 0, column: 0, offset: 0 }
13604
+ },
13605
+ lineStart: false
13606
+ };
13607
+ }
13608
+ function skipWhitespace4(ctx) {
13609
+ while (currentToken2(ctx).type === "WHITESPACE") {
13610
+ ctx.pos++;
13460
13611
  }
13461
13612
  }
13613
+ function parseBlock(ctx) {
13614
+ return parseNextBlock(ctx, () => skipWhitespace4(ctx), () => isAtEnd3(ctx));
13615
+ }
13462
13616
 
13463
13617
  // packages/parser/src/parser/parse/plain-non-ascii.ts
13464
13618
  var LARGE_PLAIN_TEXT_MIN_LENGTH = 1e5;
@@ -13466,8 +13620,8 @@ function parsePlainNonAsciiDocument(source) {
13466
13620
  if (!isPlainNonAsciiDocument(source)) {
13467
13621
  return null;
13468
13622
  }
13469
- const text = trimNewlineEdges(source);
13470
- const elements = text === "" ? [] : buildPlainParagraphs(text);
13623
+ const text2 = trimNewlineEdges(source);
13624
+ const elements = text2 === "" ? [] : buildPlainParagraphs(text2);
13471
13625
  elements.push({
13472
13626
  element: "footnote-block",
13473
13627
  data: { title: null, hide: false }
@@ -13487,8 +13641,8 @@ function parseLargePlainTextDocument(source) {
13487
13641
  if (source.length < LARGE_PLAIN_TEXT_MIN_LENGTH || hasWikitextSyntaxCandidate(source)) {
13488
13642
  return null;
13489
13643
  }
13490
- const text = trimNewlineEdges(source);
13491
- const elements = text === "" ? [] : buildPlainParagraphs(text);
13644
+ const text2 = trimNewlineEdges(source);
13645
+ const elements = text2 === "" ? [] : buildPlainParagraphs(text2);
13492
13646
  elements.push({
13493
13647
  element: "footnote-block",
13494
13648
  data: { title: null, hide: false }
@@ -13553,10 +13707,10 @@ function trimNewlineEdges(source) {
13553
13707
  end--;
13554
13708
  return start === 0 && end === source.length ? source : source.slice(start, end);
13555
13709
  }
13556
- function buildPlainParagraphs(text) {
13557
- const paragraphs = text.indexOf(`
13710
+ function buildPlainParagraphs(text2) {
13711
+ const paragraphs = text2.indexOf(`
13558
13712
 
13559
- `) === -1 ? [text] : text.split(/\n{2,}/).filter((part) => part !== "");
13713
+ `) === -1 ? [text2] : text2.split(/\n{2,}/).filter((part) => part !== "");
13560
13714
  return paragraphs.map((paragraphText) => ({
13561
13715
  element: "container",
13562
13716
  data: {
@@ -13566,8 +13720,8 @@ function buildPlainParagraphs(text) {
13566
13720
  }
13567
13721
  }));
13568
13722
  }
13569
- function buildPlainParagraphElements(text) {
13570
- const lines = text.split(`
13723
+ function buildPlainParagraphElements(text2) {
13724
+ const lines = text2.split(`
13571
13725
  `);
13572
13726
  const elements = Array.from({ length: lines.length * 2 - 1 });
13573
13727
  let out = 0;
@@ -13581,32 +13735,32 @@ function buildPlainParagraphElements(text) {
13581
13735
  }
13582
13736
 
13583
13737
  // packages/parser/src/parser/preprocess/whitespace/detection.ts
13584
- function needsWhitespaceSubstitution(text) {
13585
- if (text.length === 0)
13738
+ function needsWhitespaceSubstitution(text2) {
13739
+ if (text2.length === 0)
13586
13740
  return false;
13587
- if (text[0] === `
13588
- ` || text[0] === " " || text[text.length - 1] === `
13741
+ if (text2[0] === `
13742
+ ` || text2[0] === " " || text2[text2.length - 1] === `
13589
13743
  `) {
13590
13744
  return true;
13591
13745
  }
13592
- return text.indexOf("\r") !== -1 || text.indexOf("\t") !== -1 || text.indexOf("\x00") !== -1 || text.indexOf(" ") !== -1 || text.indexOf(" ") !== -1 || text.indexOf("\\\n") !== -1 || text.indexOf(`
13746
+ return text2.indexOf("\r") !== -1 || text2.indexOf("\t") !== -1 || text2.indexOf("\x00") !== -1 || text2.indexOf(" ") !== -1 || text2.indexOf(" ") !== -1 || text2.indexOf("\\\n") !== -1 || text2.indexOf(`
13593
13747
 
13594
13748
 
13595
- `) !== -1 || text.indexOf(`
13749
+ `) !== -1 || text2.indexOf(`
13596
13750
  `) !== -1;
13597
13751
  }
13598
- function mayContainWhitespaceOnlyLine(text) {
13599
- const first = text[0];
13752
+ function mayContainWhitespaceOnlyLine(text2) {
13753
+ const first = text2[0];
13600
13754
  if (first === " " || first === "\t" || first === `
13601
13755
  ` || first === " " || first === " ") {
13602
13756
  return true;
13603
13757
  }
13604
- return text.indexOf(`
13605
- `) !== -1 || text.indexOf(`
13758
+ return text2.indexOf(`
13759
+ `) !== -1 || text2.indexOf(`
13606
13760
 
13607
- `) !== -1 || text.indexOf(`
13608
- `) !== -1 || text.indexOf(`
13609
-  `) !== -1 || text.indexOf(`
13761
+ `) !== -1 || text2.indexOf(`
13762
+ `) !== -1 || text2.indexOf(`
13763
+  `) !== -1 || text2.indexOf(`
13610
13764
   `) !== -1;
13611
13765
  }
13612
13766
 
@@ -13619,18 +13773,18 @@ var TABS = /\t/g;
13619
13773
  var NULL_CHARS = new RegExp(String.fromCharCode(0), "g");
13620
13774
 
13621
13775
  // packages/parser/src/parser/preprocess/whitespace/leading-spaces.ts
13622
- function replaceLeadingSpaces(text) {
13623
- return text.replace(LEADING_NONSTANDARD_WHITESPACE, (match) => {
13776
+ function replaceLeadingSpaces(text2) {
13777
+ return text2.replace(LEADING_NONSTANDARD_WHITESPACE, (match) => {
13624
13778
  return " ".repeat(match.length);
13625
13779
  });
13626
13780
  }
13627
13781
 
13628
13782
  // packages/parser/src/parser/preprocess/whitespace/index.ts
13629
- function substitute(text) {
13630
- if (!needsWhitespaceSubstitution(text)) {
13631
- return text;
13783
+ function substitute(text2) {
13784
+ if (!needsWhitespaceSubstitution(text2)) {
13785
+ return text2;
13632
13786
  }
13633
- let result = text;
13787
+ let result = text2;
13634
13788
  if (result.indexOf("\r") !== -1) {
13635
13789
  result = result.replace(DOS_MAC_NEWLINES, `
13636
13790
  `);
@@ -13660,21 +13814,21 @@ function substitute(text) {
13660
13814
  }
13661
13815
  return result;
13662
13816
  }
13663
- function trimLeadingNewlines(text) {
13817
+ function trimLeadingNewlines(text2) {
13664
13818
  let index = 0;
13665
- while (text[index] === `
13819
+ while (text2[index] === `
13666
13820
  `) {
13667
13821
  index++;
13668
13822
  }
13669
- return index === 0 ? text : text.slice(index);
13823
+ return index === 0 ? text2 : text2.slice(index);
13670
13824
  }
13671
- function trimTrailingNewlines(text) {
13672
- let end = text.length;
13673
- while (end > 0 && text[end - 1] === `
13825
+ function trimTrailingNewlines(text2) {
13826
+ let end = text2.length;
13827
+ while (end > 0 && text2[end - 1] === `
13674
13828
  `) {
13675
13829
  end--;
13676
13830
  }
13677
- return end === text.length ? text : text.slice(0, end);
13831
+ return end === text2.length ? text2 : text2.slice(0, end);
13678
13832
  }
13679
13833
 
13680
13834
  // packages/parser/src/parser/preprocess/typography.ts
@@ -13684,50 +13838,50 @@ var LEFT_DOUBLE_QUOTE = "“";
13684
13838
  var RIGHT_DOUBLE_QUOTE = "”";
13685
13839
  var LOW_DOUBLE_QUOTE = "„";
13686
13840
  var ELLIPSIS = "…";
13687
- function replaceExactEllipsisPattern(text, pattern) {
13841
+ function replaceExactEllipsisPattern(text2, pattern) {
13688
13842
  let searchFrom = 0;
13689
13843
  let result = "";
13690
13844
  let lastCopied = 0;
13691
13845
  const patternLength = pattern.length;
13692
- while (searchFrom < text.length) {
13693
- const index = text.indexOf(pattern, searchFrom);
13846
+ while (searchFrom < text2.length) {
13847
+ const index = text2.indexOf(pattern, searchFrom);
13694
13848
  if (index === -1)
13695
13849
  break;
13696
- const prev = index > 0 ? text[index - 1] : "";
13697
- const next = index + patternLength < text.length ? text[index + patternLength] : "";
13850
+ const prev = index > 0 ? text2[index - 1] : "";
13851
+ const next = index + patternLength < text2.length ? text2[index + patternLength] : "";
13698
13852
  if (prev !== "." && next !== ".") {
13699
- result += text.slice(lastCopied, index) + ELLIPSIS;
13853
+ result += text2.slice(lastCopied, index) + ELLIPSIS;
13700
13854
  lastCopied = index + patternLength;
13701
13855
  searchFrom = lastCopied;
13702
13856
  } else {
13703
13857
  searchFrom = index + 1;
13704
13858
  }
13705
13859
  }
13706
- return lastCopied === 0 ? text : result + text.slice(lastCopied);
13860
+ return lastCopied === 0 ? text2 : result + text2.slice(lastCopied);
13707
13861
  }
13708
- function replaceDelimitedTypography(text, opener, closer, leftQuote, rightQuote) {
13862
+ function replaceDelimitedTypography(text2, opener, closer, leftQuote, rightQuote) {
13709
13863
  let searchFrom = 0;
13710
13864
  let result = "";
13711
13865
  let lastCopied = 0;
13712
- while (searchFrom < text.length) {
13713
- const openIndex = text.indexOf(opener, searchFrom);
13866
+ while (searchFrom < text2.length) {
13867
+ const openIndex = text2.indexOf(opener, searchFrom);
13714
13868
  if (openIndex === -1)
13715
13869
  break;
13716
13870
  const contentStart = openIndex + opener.length;
13717
- const closeIndex = text.indexOf(closer, contentStart);
13871
+ const closeIndex = text2.indexOf(closer, contentStart);
13718
13872
  if (closeIndex === -1)
13719
13873
  break;
13720
- result += text.slice(lastCopied, openIndex);
13874
+ result += text2.slice(lastCopied, openIndex);
13721
13875
  result += leftQuote;
13722
- result += text.slice(contentStart, closeIndex);
13876
+ result += text2.slice(contentStart, closeIndex);
13723
13877
  result += rightQuote;
13724
13878
  lastCopied = closeIndex + closer.length;
13725
13879
  searchFrom = lastCopied;
13726
13880
  }
13727
- return lastCopied === 0 ? text : result + text.slice(lastCopied);
13881
+ return lastCopied === 0 ? text2 : result + text2.slice(lastCopied);
13728
13882
  }
13729
- function substitute2(text) {
13730
- let result = text;
13883
+ function substitute2(text2) {
13884
+ let result = text2;
13731
13885
  if (result.includes("``") && result.includes("''")) {
13732
13886
  result = replaceDelimitedTypography(result, "``", "''", LEFT_DOUBLE_QUOTE, RIGHT_DOUBLE_QUOTE);
13733
13887
  }
@@ -13747,37 +13901,13 @@ function substitute2(text) {
13747
13901
  }
13748
13902
 
13749
13903
  // packages/parser/src/parser/preprocess/index.ts
13750
- function preprocess(text) {
13751
- let result = text;
13904
+ function preprocess(text2) {
13905
+ let result = text2;
13752
13906
  result = substitute(result);
13753
13907
  result = substitute2(result);
13754
13908
  return result;
13755
13909
  }
13756
13910
 
13757
- // packages/parser/src/parser/preprocess/expr/evaluate.ts
13758
- var import_ast6 = require("@wdprlib/ast");
13759
- function evaluateDirective(kind, match) {
13760
- if (kind === "expr") {
13761
- const result2 = import_ast6.evaluateExpression(match.head);
13762
- if (result2.success)
13763
- return import_ast6.formatExprValue(result2.value);
13764
- if (result2.error === "empty expression")
13765
- return "";
13766
- return "ERROR";
13767
- }
13768
- if (kind === "if") {
13769
- if (!match.hasPipe)
13770
- return "";
13771
- return import_ast6.isTruthy(match.head) ? match.thenText : match.elseText;
13772
- }
13773
- if (!match.hasPipe)
13774
- return "";
13775
- const result = import_ast6.evaluateExpression(match.head);
13776
- if (!result.success)
13777
- return "ERROR";
13778
- return result.value !== 0 && !Number.isNaN(result.value) ? match.thenText : match.elseText;
13779
- }
13780
-
13781
13911
  // packages/parser/src/parser/preprocess/expr/chars.ts
13782
13912
  function isWhitespace(ch) {
13783
13913
  return ch === " " || ch === "\t" || ch === `
@@ -13806,6 +13936,30 @@ function matchDirectiveKind(source, i) {
13806
13936
  return null;
13807
13937
  }
13808
13938
 
13939
+ // packages/parser/src/parser/preprocess/expr/evaluate.ts
13940
+ var import_ast7 = require("@wdprlib/ast");
13941
+ function evaluateDirective(kind, match) {
13942
+ if (kind === "expr") {
13943
+ const result2 = import_ast7.evaluateExpression(match.head);
13944
+ if (result2.success)
13945
+ return import_ast7.formatExprValue(result2.value);
13946
+ if (result2.error === "empty expression")
13947
+ return "";
13948
+ return "ERROR";
13949
+ }
13950
+ if (kind === "if") {
13951
+ if (!match.hasPipe)
13952
+ return "";
13953
+ return import_ast7.isTruthy(match.head) ? match.thenText : match.elseText;
13954
+ }
13955
+ if (!match.hasPipe)
13956
+ return "";
13957
+ const result = import_ast7.evaluateExpression(match.head);
13958
+ if (!result.success)
13959
+ return "ERROR";
13960
+ return result.value !== 0 && !Number.isNaN(result.value) ? match.thenText : match.elseText;
13961
+ }
13962
+
13809
13963
  // packages/parser/src/parser/preprocess/expr/parse.ts
13810
13964
  function tryParseInnermostDirective(source, start, kind) {
13811
13965
  const keywordLen = kind === "ifexpr" ? 6 : kind === "expr" ? 4 : 2;
@@ -13907,11 +14061,14 @@ function expandInnermost(source) {
13907
14061
  }
13908
14062
 
13909
14063
  // packages/parser/src/parser/preprocess/expr/index.ts
14064
+ var MAX_EXPR_NESTING = 64;
13910
14065
  function preprocessExpr(source) {
13911
14066
  if (!source.includes("[[#"))
13912
14067
  return source;
13913
14068
  const sentinels = makeUniqueSentinels(source);
13914
14069
  const { masked, placeholders } = maskRawRegions(source, sentinels);
14070
+ if (exceedsExprNestingLimit(masked))
14071
+ return source;
13915
14072
  const reduced = reduceExpr(masked);
13916
14073
  return restorePlaceholders(reduced, placeholders, sentinels);
13917
14074
  }
@@ -13926,6 +14083,26 @@ function reduceExpr(source) {
13926
14083
  }
13927
14084
  return current2;
13928
14085
  }
14086
+ function exceedsExprNestingLimit(source) {
14087
+ const expressionStack = [];
14088
+ let expressionDepth = 0;
14089
+ for (let i = 0;i < source.length; i++) {
14090
+ if (source.startsWith("[[", i)) {
14091
+ const isExpression = matchDirectiveKind(source, i) !== null;
14092
+ expressionStack.push(isExpression);
14093
+ if (isExpression && ++expressionDepth > MAX_EXPR_NESTING)
14094
+ return true;
14095
+ i++;
14096
+ continue;
14097
+ }
14098
+ if (source.startsWith("]]", i)) {
14099
+ if (expressionStack.pop())
14100
+ expressionDepth--;
14101
+ i++;
14102
+ }
14103
+ }
14104
+ return false;
14105
+ }
13929
14106
 
13930
14107
  // packages/parser/src/parser/parse/source.ts
13931
14108
  function prepareSourceForParse(source, options) {
@@ -13937,6 +14114,12 @@ function prepareSourceForParse(source, options) {
13937
14114
  // packages/parser/src/parser/parse/index.ts
13938
14115
  var COMPACT_TEXT_RUN_SOURCE_LENGTH = 1e5;
13939
14116
  function parse(source, options) {
14117
+ return parseSource(source, options);
14118
+ }
14119
+ function parseWithIncludeDeferral(source, options, deferInclude) {
14120
+ return parseSource(source, options, deferInclude);
14121
+ }
14122
+ function parseSource(source, options, deferInclude) {
13940
14123
  const plainResult = parsePlainNonAsciiDocument(source);
13941
14124
  if (plainResult) {
13942
14125
  return plainResult;
@@ -13950,10 +14133,10 @@ function parse(source, options) {
13950
14133
  trackPositions: options?.trackPositions,
13951
14134
  compactTextRuns: preprocessed.length >= COMPACT_TEXT_RUN_SOURCE_LENGTH
13952
14135
  });
13953
- return new Parser(tokens, options).parse();
14136
+ return deferInclude ? parseTokensWithIncludeDeferral(tokens, options ?? {}, deferInclude) : new Parser(tokens, options).parse();
13954
14137
  }
13955
14138
  // packages/parser/src/pipeline/process.ts
13956
- var import_ast7 = require("@wdprlib/ast");
14139
+ var import_ast8 = require("@wdprlib/ast");
13957
14140
 
13958
14141
  // packages/parser/src/parser/rules/block/module/resolution/resolve-async.ts
13959
14142
  async function resolveModulesWithAsyncParse(ast, dataProvider, options) {
@@ -14129,52 +14312,94 @@ async function resolveElementChildren(element, context, state) {
14129
14312
  }
14130
14313
 
14131
14314
  // packages/parser/src/pipeline/process.ts
14315
+ var DEFAULT_MODULE_MAX_PASSES = 5;
14316
+ var PIPELINE_DIAGNOSTIC_POSITION = {
14317
+ start: { line: 1, column: 1, offset: 0 },
14318
+ end: { line: 1, column: 1, offset: 0 }
14319
+ };
14132
14320
  async function processWikitext(source, options) {
14133
- const settings = options.settings ?? import_ast7.DEFAULT_SETTINGS;
14321
+ const settings = options.settings ?? import_ast8.DEFAULT_SETTINGS;
14134
14322
  const callbackContext = {
14135
14323
  page: options.page,
14136
14324
  settings
14137
14325
  };
14138
14326
  const dependencies = [];
14327
+ const diagnostics = [];
14139
14328
  const fetchInclude = createRequestIncludeFetcher(options.dataProvider?.fetchInclude ? (pageRef) => options.dataProvider.fetchInclude(pageRef, callbackContext) : undefined);
14140
14329
  const resolveSource = async (input) => {
14141
- if (!fetchInclude)
14142
- return input;
14143
- const resolution = await resolveIncludesAsyncWithTrace(input, fetchInclude, {
14144
- maxIterations: options.includeMaxIterations,
14145
- settings
14146
- });
14147
- dependencies.push(...resolution.dependencies);
14148
- return resolution.source;
14330
+ let expanded = input;
14331
+ if (fetchInclude) {
14332
+ const resolution = await resolveIncludesAsyncWithTraceSelective(input, fetchInclude, (reference) => isCurrentPageInclude(reference.location, options.page), {
14333
+ maxIterations: options.includeMaxIterations,
14334
+ settings
14335
+ });
14336
+ dependencies.push(...resolution.dependencies);
14337
+ if (resolution.reachedMaxIterations) {
14338
+ diagnostics.push(createLimitDiagnostic("include-resolution-limit", `Include expansion stopped after ${options.includeMaxIterations ?? 10} iterations.`));
14339
+ }
14340
+ expanded = resolution.source;
14341
+ }
14342
+ return expanded;
14149
14343
  };
14150
- const expandedSource = await resolveSource(source);
14151
- const initial = parse(expandedSource, {
14152
- settings,
14153
- pageTags: options.page.tags,
14154
- appendImplicitFootnoteBlock: false
14155
- });
14156
- const extraction = extractDataRequirements(initial.ast);
14157
- const dataProvider = createModuleDataProvider(options, callbackContext);
14158
- const resolved = await resolveModulesWithAsyncParse(initial.ast, dataProvider, {
14159
- parse: async (fragmentSource) => parse(await resolveSource(fragmentSource), {
14344
+ const parseSource2 = async (input) => {
14345
+ const expanded = await resolveSource(input);
14346
+ return parseWithIncludeDeferral(expanded, {
14160
14347
  settings,
14161
14348
  pageTags: options.page.tags,
14162
14349
  appendImplicitFootnoteBlock: false
14163
- }),
14164
- compiledListPagesTemplates: extraction.compiledListPagesTemplates,
14165
- compiledListUsersTemplates: extraction.compiledListUsersTemplates,
14166
- requirements: extraction.requirements,
14167
- urlPath: options.page.urlPath,
14168
- pageTags: options.page.tags
14169
- });
14350
+ }, (location) => isCurrentPageInclude(location, options.page));
14351
+ };
14352
+ const initial = await parseSource2(source);
14353
+ diagnostics.push(...initial.diagnostics);
14354
+ const dataProvider = createModuleDataProvider(options, callbackContext);
14355
+ const parseFragment = parseSource2;
14356
+ let ast = initial.ast;
14357
+ for (let pass = 0;pass < DEFAULT_MODULE_MAX_PASSES; pass++) {
14358
+ const extraction = extractDataRequirements(ast);
14359
+ if (pass > 0 && !hasResolvableRequirements(extraction.requirements, options.dataProvider)) {
14360
+ break;
14361
+ }
14362
+ const resolved = await resolveModulesWithAsyncParse(ast, dataProvider, {
14363
+ parse: parseFragment,
14364
+ compiledListPagesTemplates: extraction.compiledListPagesTemplates,
14365
+ compiledListUsersTemplates: extraction.compiledListUsersTemplates,
14366
+ requirements: extraction.requirements,
14367
+ urlPath: options.page.urlPath,
14368
+ pageTags: options.page.tags
14369
+ });
14370
+ ast = resolved.ast;
14371
+ diagnostics.push(...resolved.diagnostics);
14372
+ }
14373
+ if (hasResolvableRequirements(extractDataRequirements(ast).requirements, options.dataProvider)) {
14374
+ diagnostics.push(createLimitDiagnostic("module-resolution-limit", `Module resolution stopped after ${DEFAULT_MODULE_MAX_PASSES} passes.`));
14375
+ }
14170
14376
  return {
14171
- ast: resolved.ast,
14377
+ ast,
14172
14378
  page: options.page,
14173
14379
  settings,
14174
- diagnostics: [...initial.diagnostics, ...resolved.diagnostics],
14380
+ diagnostics,
14175
14381
  dependencies
14176
14382
  };
14177
14383
  }
14384
+ function isCurrentPageInclude(location, page) {
14385
+ if (location.site !== null) {
14386
+ if (!page.site || location.site.toLowerCase() !== page.site.toLowerCase())
14387
+ return false;
14388
+ }
14389
+ const target = location.page.toLowerCase();
14390
+ return target === page.fullName.toLowerCase() || target === page.unixName?.toLowerCase();
14391
+ }
14392
+ function hasResolvableRequirements(requirements, provider) {
14393
+ return Boolean(provider?.fetchListPages && requirements.listPages.length > 0 || provider?.fetchListUsers && requirements.listUsers.length > 0 || provider?.fetchTagCloud && requirements.tagCloud.length > 0);
14394
+ }
14395
+ function createLimitDiagnostic(code, message) {
14396
+ return {
14397
+ severity: "warning",
14398
+ code,
14399
+ message,
14400
+ position: PIPELINE_DIAGNOSTIC_POSITION
14401
+ };
14402
+ }
14178
14403
  function createModuleDataProvider(options, context) {
14179
14404
  const provider = options.dataProvider;
14180
14405
  return {