@wdprlib/parser 2.2.0 → 3.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.
package/dist/index.cjs CHANGED
@@ -36,6 +36,7 @@ __export(exports_src, {
36
36
  resolveListUsers: () => resolveListUsers,
37
37
  resolveIncludesAsync: () => resolveIncludesAsync,
38
38
  resolveIncludes: () => resolveIncludes,
39
+ preprocessIftags: () => preprocessIftags,
39
40
  parseTags: () => parseTags,
40
41
  parseParent: () => parseParent,
41
42
  parseOrder: () => parseOrder,
@@ -83,6 +84,7 @@ class Lexer {
83
84
  state;
84
85
  options;
85
86
  splitBlockClosePositions = new Set;
87
+ blockOpenerDepth = 0;
86
88
  constructor(source, options = {}) {
87
89
  this.options = {
88
90
  trackPositions: options.trackPositions ?? true
@@ -182,6 +184,11 @@ class Lexer {
182
184
  const position = this.options.trackPositions ? import_ast.createPosition(startPos, endPos) : import_ast.createPosition(import_ast.createPoint(0, 0, 0), import_ast.createPoint(0, 0, 0));
183
185
  const lineStart = this.state.tokens.length === 0 || this.state.tokens[this.state.tokens.length - 1]?.type === "NEWLINE";
184
186
  this.state.tokens.push(createToken(type, value, position, lineStart));
187
+ if (type === "BLOCK_OPEN" || type === "BLOCK_END_OPEN") {
188
+ this.blockOpenerDepth++;
189
+ } else if (type === "BLOCK_CLOSE" && this.blockOpenerDepth > 0) {
190
+ this.blockOpenerDepth--;
191
+ }
185
192
  }
186
193
  scanToken() {
187
194
  const char = this.current();
@@ -446,7 +453,7 @@ class Lexer {
446
453
  }
447
454
  if (char === '"') {
448
455
  const lastNonWs = this.lastNonWhitespaceTokenType();
449
- if (lastNonWs === "EQUALS") {
456
+ if (this.blockOpenerDepth > 0 && lastNonWs === "EQUALS") {
450
457
  let quoted = this.advance();
451
458
  while (!this.isAtEnd() && this.current() !== '"' && this.current() !== `
452
459
  `) {
@@ -691,6 +698,28 @@ var KNOWN_BLOCK_NAMES = new Set([
691
698
  "gallery",
692
699
  "file"
693
700
  ]);
701
+ var INDENT_ACCEPTING_BLOCK_NAMES = new Set([
702
+ "bibliography",
703
+ "ul",
704
+ "ol",
705
+ "li",
706
+ "code",
707
+ "collapsible",
708
+ "div",
709
+ "div_",
710
+ "embed",
711
+ "embedvideo",
712
+ "embedaudio",
713
+ "html",
714
+ "iframe",
715
+ "iftags",
716
+ "math",
717
+ "module",
718
+ "module654",
719
+ "table",
720
+ "tabview",
721
+ "tabs"
722
+ ]);
694
723
 
695
724
  // packages/parser/src/parser/rules/utils.ts
696
725
  var SAFE_ATTRIBUTES = new Set([
@@ -817,13 +846,14 @@ function parseBlockName(ctx, startPos) {
817
846
 
818
847
  // packages/parser/src/parser/rules/inline/utils.ts
819
848
  function isExcludedBlockToken(ctx, tokenPos) {
820
- if (!ctx.excludedBlockNames?.size)
849
+ const excluded = ctx.scope.excludedBlockNames;
850
+ if (!excluded?.size)
821
851
  return false;
822
852
  const token = ctx.tokens[tokenPos];
823
853
  if (token?.type !== "BLOCK_OPEN" && token?.type !== "BLOCK_END_OPEN")
824
854
  return false;
825
855
  const nameResult = parseBlockName(ctx, tokenPos + 1);
826
- return nameResult !== null && ctx.excludedBlockNames.has(nameResult.name);
856
+ return nameResult !== null && excluded.has(nameResult.name);
827
857
  }
828
858
  function isUnknownBlockToken(ctx, tokenPos) {
829
859
  const token = ctx.tokens[tokenPos];
@@ -838,6 +868,15 @@ function isUnknownBlockToken(ctx, tokenPos) {
838
868
  }
839
869
  return !KNOWN_BLOCK_NAMES.has(nameResult.name);
840
870
  }
871
+ function isIndentAcceptingBlock(ctx, tokenPos) {
872
+ const token = ctx.tokens[tokenPos];
873
+ if (token?.type !== "BLOCK_OPEN" && token?.type !== "BLOCK_END_OPEN")
874
+ return false;
875
+ const nameResult = parseBlockName(ctx, tokenPos + 1);
876
+ if (nameResult === null)
877
+ return false;
878
+ return INDENT_ACCEPTING_BLOCK_NAMES.has(nameResult.name);
879
+ }
841
880
  function canApplyInlineRule(rule, token) {
842
881
  if (rule.startTokens.length === 0) {
843
882
  return true;
@@ -855,9 +894,9 @@ function parseInlineUntil(ctx, endType) {
855
894
  if (!token || token.type === "EOF") {
856
895
  break;
857
896
  }
858
- if (paragraphMode && ctx.blockCloseCondition) {
897
+ if (paragraphMode && ctx.scope.blockCloseCondition) {
859
898
  const checkCtx = { ...ctx, pos };
860
- if (ctx.blockCloseCondition(checkCtx)) {
899
+ if (ctx.scope.blockCloseCondition(checkCtx)) {
861
900
  break;
862
901
  }
863
902
  }
@@ -909,7 +948,7 @@ function parseInlineUntil(ctx, endType) {
909
948
  skipWhitespace++;
910
949
  }
911
950
  const blockNameToken = ctx.tokens[afterOpen + skipWhitespace];
912
- if (blockNameToken && (blockNameToken.type === "TEXT" || blockNameToken.type === "IDENTIFIER") && blockNameToken.value.toLowerCase() === "footnoteblock" && ctx.footnoteBlockParsed) {
951
+ if (blockNameToken && (blockNameToken.type === "TEXT" || blockNameToken.type === "IDENTIFIER") && blockNameToken.value.toLowerCase() === "footnoteblock" && ctx.scope.footnoteBlockParsed) {
913
952
  isInvalidBlockOpen = true;
914
953
  }
915
954
  }
@@ -931,7 +970,8 @@ function parseInlineUntil(ctx, endType) {
931
970
  }
932
971
  const isExcludedBlock = (nextMeaningfulToken?.type === "BLOCK_OPEN" || nextMeaningfulToken?.type === "BLOCK_END_OPEN") && isExcludedBlockToken(ctx, pos + lookAhead);
933
972
  const isUnknownBlock = (nextMeaningfulToken?.type === "BLOCK_OPEN" || nextMeaningfulToken?.type === "BLOCK_END_OPEN") && isUnknownBlockToken(ctx, pos + lookAhead);
934
- const isBlockStart = nextMeaningfulToken && BLOCK_START_TOKENS.includes(nextMeaningfulToken.type) && nextMeaningfulToken.lineStart && !isOrphanCloseSpan && !isAnchorName && !isInvalidBlockOpen && !isInvalidHeading && !isExcludedBlock && !isUnknownBlock;
973
+ const isIndentedBlockOpener = nextMeaningfulToken && (nextMeaningfulToken.type === "BLOCK_OPEN" || nextMeaningfulToken.type === "BLOCK_END_OPEN") && isIndentAcceptingBlock(ctx, pos + lookAhead);
974
+ const isBlockStart = nextMeaningfulToken && BLOCK_START_TOKENS.includes(nextMeaningfulToken.type) && (nextMeaningfulToken.lineStart || isIndentedBlockOpener) && !isOrphanCloseSpan && !isAnchorName && !isInvalidBlockOpen && !isInvalidHeading && !isExcludedBlock && !isUnknownBlock;
935
975
  if (!nextMeaningfulToken || nextMeaningfulToken.type === "NEWLINE" || nextMeaningfulToken.type === "EOF" || isBlockStart) {
936
976
  if (isBlockStart && nodes.length > 0) {
937
977
  const nextPos = pos + lookAhead;
@@ -1309,8 +1349,11 @@ function parseBlocksUntil(ctx, closeCondition, options) {
1309
1349
  ...ctx,
1310
1350
  pos,
1311
1351
  blockRules,
1312
- blockCloseCondition: closeCondition,
1313
- excludedBlockNames: excluded
1352
+ scope: {
1353
+ ...ctx.scope,
1354
+ blockCloseCondition: closeCondition,
1355
+ excludedBlockNames: excluded
1356
+ }
1314
1357
  };
1315
1358
  for (const rule of blockRules) {
1316
1359
  if (canApplyBlockRule(rule, token)) {
@@ -2298,15 +2341,15 @@ var divRule = {
2298
2341
  if (ctx.tokens[pos]?.type !== "NEWLINE") {
2299
2342
  return consumeFailedDiv(ctx);
2300
2343
  }
2301
- if (ctx.divClosesBudget === 0) {
2344
+ if (ctx.scope.divClosesBudget === 0) {
2302
2345
  return { success: false };
2303
2346
  }
2304
2347
  pos++;
2305
2348
  consumed++;
2306
2349
  const openPosition = openToken.position;
2307
2350
  let bodyBudget;
2308
- if (ctx.divClosesBudget !== undefined) {
2309
- bodyBudget = ctx.divClosesBudget - 1;
2351
+ if (ctx.scope.divClosesBudget !== undefined) {
2352
+ bodyBudget = ctx.scope.divClosesBudget - 1;
2310
2353
  } else {
2311
2354
  const closesInScope = countDivCloses(ctx, pos);
2312
2355
  bodyBudget = closesInScope > 0 ? closesInScope - 1 : 0;
@@ -2321,7 +2364,11 @@ var divRule = {
2321
2364
  }
2322
2365
  return false;
2323
2366
  };
2324
- const bodyCtx = { ...ctx, pos, divClosesBudget: bodyBudget };
2367
+ const bodyCtx = {
2368
+ ...ctx,
2369
+ pos,
2370
+ scope: { ...ctx.scope, divClosesBudget: bodyBudget }
2371
+ };
2325
2372
  let children;
2326
2373
  if (paragraphStrip) {
2327
2374
  const bodyResult = parseBlocksUntil(bodyCtx, closeCondition);
@@ -3964,10 +4011,10 @@ var footnoteBlockRule = {
3964
4011
  }
3965
4012
  pos++;
3966
4013
  consumed++;
3967
- if (ctx.footnoteBlockParsed) {
4014
+ if (ctx.scope.footnoteBlockParsed) {
3968
4015
  return { success: false };
3969
4016
  }
3970
- ctx.footnoteBlockParsed = true;
4017
+ ctx.scope = { ...ctx.scope, footnoteBlockParsed: true };
3971
4018
  const title = attrs.title !== undefined ? attrs.title : null;
3972
4019
  const hide = attrs.hide === "true" || attrs.hide === "yes";
3973
4020
  return {
@@ -4723,6 +4770,24 @@ var mathBlockRule = {
4723
4770
  };
4724
4771
 
4725
4772
  // packages/parser/src/parser/rules/block/html.ts
4773
+ function lookaheadHasHtmlClose(ctx, from) {
4774
+ for (let i = from;i < ctx.tokens.length; i++) {
4775
+ const t = ctx.tokens[i];
4776
+ if (!t || t.type === "EOF")
4777
+ return false;
4778
+ if (t.type !== "BLOCK_END_OPEN")
4779
+ continue;
4780
+ const closeName = parseBlockName(ctx, i + 1);
4781
+ if (closeName?.name.toLowerCase() !== "html")
4782
+ continue;
4783
+ let cp = i + 1 + closeName.consumed;
4784
+ while (ctx.tokens[cp]?.type === "WHITESPACE")
4785
+ cp++;
4786
+ if (ctx.tokens[cp]?.type === "BLOCK_CLOSE")
4787
+ return true;
4788
+ }
4789
+ return false;
4790
+ }
4726
4791
  var htmlBlockRule = {
4727
4792
  name: "html",
4728
4793
  startTokens: ["BLOCK_OPEN"],
@@ -4749,20 +4814,32 @@ var htmlBlockRule = {
4749
4814
  }
4750
4815
  pos++;
4751
4816
  consumed++;
4817
+ const disabled = ctx.settings.allowHtmlBlocks === false;
4818
+ const hasCloseAhead = disabled && lookaheadHasHtmlClose(ctx, pos);
4752
4819
  let contents = "";
4753
4820
  let foundClose = false;
4754
4821
  while (pos < ctx.tokens.length) {
4755
4822
  const token = ctx.tokens[pos];
4756
4823
  if (!token || token.type === "EOF")
4757
4824
  break;
4825
+ if (disabled && !hasCloseAhead && token.type === "NEWLINE" && ctx.tokens[pos + 1]?.type === "NEWLINE") {
4826
+ break;
4827
+ }
4758
4828
  if (token.type === "BLOCK_END_OPEN") {
4759
4829
  const closeNameResult = parseBlockName(ctx, pos + 1);
4760
4830
  if (closeNameResult?.name.toLowerCase() === "html") {
4761
- foundClose = true;
4762
- break;
4831
+ let checkPos = pos + 1 + closeNameResult.consumed;
4832
+ while (ctx.tokens[checkPos]?.type === "WHITESPACE")
4833
+ checkPos++;
4834
+ if (ctx.tokens[checkPos]?.type === "BLOCK_CLOSE") {
4835
+ foundClose = true;
4836
+ break;
4837
+ }
4763
4838
  }
4764
4839
  }
4765
- contents += token.value;
4840
+ if (!disabled) {
4841
+ contents += token.value;
4842
+ }
4766
4843
  pos++;
4767
4844
  consumed++;
4768
4845
  }
@@ -4773,7 +4850,16 @@ var htmlBlockRule = {
4773
4850
  message: "Missing closing tag [[/html]] for [[html]]",
4774
4851
  position: openToken.position
4775
4852
  });
4776
- return { success: false };
4853
+ if (!disabled) {
4854
+ return { success: false };
4855
+ }
4856
+ ctx.diagnostics.push({
4857
+ severity: "info",
4858
+ code: "html-block-disabled",
4859
+ message: "[[html]] block ignored: disabled by settings",
4860
+ position: openToken.position
4861
+ });
4862
+ return { success: true, elements: [], consumed };
4777
4863
  }
4778
4864
  if (ctx.tokens[pos]?.type === "BLOCK_END_OPEN") {
4779
4865
  pos++;
@@ -4783,6 +4869,10 @@ var htmlBlockRule = {
4783
4869
  pos += closeNameResult.consumed;
4784
4870
  consumed += closeNameResult.consumed;
4785
4871
  }
4872
+ while (ctx.tokens[pos]?.type === "WHITESPACE") {
4873
+ pos++;
4874
+ consumed++;
4875
+ }
4786
4876
  if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
4787
4877
  pos++;
4788
4878
  consumed++;
@@ -4792,6 +4882,15 @@ var htmlBlockRule = {
4792
4882
  consumed++;
4793
4883
  }
4794
4884
  }
4885
+ if (disabled) {
4886
+ ctx.diagnostics.push({
4887
+ severity: "info",
4888
+ code: "html-block-disabled",
4889
+ message: "[[html]] block ignored: disabled by settings",
4890
+ position: openToken.position
4891
+ });
4892
+ return { success: true, elements: [], consumed };
4893
+ }
4795
4894
  contents = contents.trim();
4796
4895
  ctx.htmlBlocks.push(contents);
4797
4896
  return {
@@ -6477,6 +6576,82 @@ var commentRule = {
6477
6576
  }
6478
6577
  };
6479
6578
 
6579
+ // packages/parser/src/parser/rules/inline/html.ts
6580
+ var htmlInlineRule = {
6581
+ name: "html",
6582
+ startTokens: ["BLOCK_OPEN"],
6583
+ parse(ctx) {
6584
+ const openToken = currentToken(ctx);
6585
+ if (openToken.type !== "BLOCK_OPEN") {
6586
+ return { success: false };
6587
+ }
6588
+ let pos = ctx.pos + 1;
6589
+ let consumed = 1;
6590
+ const nameResult = parseBlockName(ctx, pos);
6591
+ if (!nameResult || nameResult.name.toLowerCase() !== "html") {
6592
+ return { success: false };
6593
+ }
6594
+ pos += nameResult.consumed;
6595
+ consumed += nameResult.consumed;
6596
+ const attrResult = parseAttributesRaw(ctx, pos);
6597
+ pos += attrResult.consumed;
6598
+ consumed += attrResult.consumed;
6599
+ if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
6600
+ return { success: false };
6601
+ }
6602
+ pos++;
6603
+ consumed++;
6604
+ if (ctx.settings.allowHtmlBlocks !== false) {
6605
+ return { success: false };
6606
+ }
6607
+ const hasCloseAhead = lookaheadHasHtmlClose(ctx, pos);
6608
+ let foundClose = false;
6609
+ while (pos < ctx.tokens.length) {
6610
+ const token = ctx.tokens[pos];
6611
+ if (!token || token.type === "EOF")
6612
+ break;
6613
+ if (!hasCloseAhead && token.type === "NEWLINE" && ctx.tokens[pos + 1]?.type === "NEWLINE") {
6614
+ break;
6615
+ }
6616
+ if (token.type === "BLOCK_END_OPEN") {
6617
+ const closeNameResult = parseBlockName(ctx, pos + 1);
6618
+ if (closeNameResult?.name.toLowerCase() === "html") {
6619
+ let checkPos = pos + 1 + closeNameResult.consumed;
6620
+ while (ctx.tokens[checkPos]?.type === "WHITESPACE")
6621
+ checkPos++;
6622
+ if (ctx.tokens[checkPos]?.type === "BLOCK_CLOSE") {
6623
+ foundClose = true;
6624
+ consumed += checkPos - pos + 1;
6625
+ pos = checkPos + 1;
6626
+ if (ctx.tokens[pos]?.type === "NEWLINE") {
6627
+ pos++;
6628
+ consumed++;
6629
+ }
6630
+ break;
6631
+ }
6632
+ }
6633
+ }
6634
+ pos++;
6635
+ consumed++;
6636
+ }
6637
+ if (!foundClose) {
6638
+ ctx.diagnostics.push({
6639
+ severity: "warning",
6640
+ code: "unclosed-block",
6641
+ message: "Missing closing tag [[/html]] for [[html]]",
6642
+ position: openToken.position
6643
+ });
6644
+ }
6645
+ ctx.diagnostics.push({
6646
+ severity: "info",
6647
+ code: "html-block-disabled",
6648
+ message: "[[html]] block ignored: disabled by settings",
6649
+ position: openToken.position
6650
+ });
6651
+ return { success: true, elements: [], consumed };
6652
+ }
6653
+ };
6654
+
6480
6655
  // packages/parser/src/parser/rules/inline/raw.ts
6481
6656
  var rawRule = {
6482
6657
  name: "raw",
@@ -8187,6 +8362,7 @@ var inlineRules = [
8187
8362
  underscoreLineBreakRule,
8188
8363
  newlineLineBreakRule,
8189
8364
  commentRule,
8365
+ htmlInlineRule,
8190
8366
  rawRule,
8191
8367
  imageRule,
8192
8368
  sizeRule,
@@ -8704,83 +8880,333 @@ function buildTableOfContents(entries) {
8704
8880
  return trees.map((tree) => buildTocList(indexer, tree.list));
8705
8881
  }
8706
8882
 
8707
- // packages/parser/src/parser/parse.ts
8708
- class Parser {
8709
- ctx;
8710
- constructor(tokens, options = {}) {
8711
- this.ctx = {
8712
- tokens,
8713
- pos: 0,
8714
- version: options.version ?? "wikidot",
8715
- trackPositions: options.trackPositions ?? true,
8716
- settings: options.settings ?? import_ast2.DEFAULT_SETTINGS,
8717
- footnotes: [],
8718
- tocEntries: [],
8719
- codeBlocks: [],
8720
- htmlBlocks: [],
8721
- footnoteBlockParsed: false,
8722
- bibcites: [],
8723
- diagnostics: [],
8724
- blockRules,
8725
- blockFallbackRule: paragraphRule,
8726
- inlineRules
8727
- };
8728
- }
8729
- parse() {
8730
- const children = [];
8731
- while (!this.isAtEnd()) {
8732
- const blocks = this.parseBlock();
8733
- children.push(...blocks);
8734
- }
8735
- const mergedChildren = mergeSpanStripParagraphs(children);
8736
- const divProcessed = suppressDivAdjacentParagraphs(mergedChildren);
8737
- const cleanedChildren = cleanInternalFlags(divProcessed);
8738
- const hasFootnoteBlock = cleanedChildren.some((el) => el.element === "footnote-block");
8739
- if (!hasFootnoteBlock) {
8740
- cleanedChildren.push({
8741
- element: "footnote-block",
8742
- data: { title: null, hide: false }
8743
- });
8883
+ // packages/parser/src/parser/rules/block/module/walk.ts
8884
+ function walkElements(elements, callback) {
8885
+ for (const element of elements) {
8886
+ callback(element);
8887
+ if (element.element === "list") {
8888
+ const listData = element.data;
8889
+ for (const item of listData.items) {
8890
+ if (item["item-type"] === "elements") {
8891
+ walkElements(item.elements, callback);
8892
+ } else if (item["item-type"] === "sub-list") {
8893
+ walkElements([{ element: "list", data: item.data }], callback);
8894
+ }
8895
+ }
8896
+ continue;
8744
8897
  }
8745
- const tableOfContents = buildTableOfContents(this.ctx.tocEntries);
8746
- const result = {
8747
- elements: cleanedChildren
8748
- };
8749
- if (tableOfContents.length > 0) {
8750
- result["table-of-contents"] = tableOfContents;
8898
+ if (element.element === "table") {
8899
+ const tableData = element.data;
8900
+ for (const row of tableData.rows) {
8901
+ for (const cell of row.cells) {
8902
+ walkElements(cell.elements, callback);
8903
+ }
8904
+ }
8905
+ continue;
8751
8906
  }
8752
- if (this.ctx.footnotes.length > 0) {
8753
- result.footnotes = this.ctx.footnotes;
8907
+ if (element.element === "definition-list") {
8908
+ const defListData = element.data;
8909
+ for (const item of defListData) {
8910
+ walkElements(item.key, callback);
8911
+ walkElements(item.value, callback);
8912
+ }
8913
+ continue;
8754
8914
  }
8755
- if (this.ctx.codeBlocks.length > 0) {
8756
- result["code-blocks"] = this.ctx.codeBlocks;
8915
+ if (element.element === "tab-view") {
8916
+ const tabData = element.data;
8917
+ for (const tab of tabData) {
8918
+ walkElements(tab.elements, callback);
8919
+ }
8920
+ continue;
8757
8921
  }
8758
- if (this.ctx.htmlBlocks.length > 0) {
8759
- result["html-blocks"] = this.ctx.htmlBlocks;
8922
+ if ("data" in element && element.data && typeof element.data === "object") {
8923
+ const data = element.data;
8924
+ if ("elements" in data && Array.isArray(data.elements)) {
8925
+ walkElements(data.elements, callback);
8926
+ }
8760
8927
  }
8761
- return { ast: result, diagnostics: this.ctx.diagnostics };
8762
- }
8763
- isAtEnd() {
8764
- return this.ctx.pos >= this.ctx.tokens.length || this.currentToken().type === "EOF";
8765
- }
8766
- currentToken() {
8767
- return this.ctx.tokens[this.ctx.pos] ?? this.eofToken();
8768
8928
  }
8769
- eofToken() {
8929
+ }
8930
+ function mapElementChildren(element, transform) {
8931
+ if (element.element === "list") {
8932
+ const listData = element.data;
8933
+ const newItems = [];
8934
+ for (const item of listData.items) {
8935
+ if (item["item-type"] === "elements") {
8936
+ newItems.push({
8937
+ "item-type": "elements",
8938
+ attributes: item.attributes,
8939
+ elements: transform(item.elements)
8940
+ });
8941
+ } else if (item["item-type"] === "sub-list") {
8942
+ const subListResult = transform([{ element: "list", data: item.data }]);
8943
+ const resolvedList = subListResult[0];
8944
+ if (resolvedList?.element === "list") {
8945
+ newItems.push({
8946
+ "item-type": "sub-list",
8947
+ element: "list",
8948
+ data: resolvedList.data
8949
+ });
8950
+ } else {
8951
+ newItems.push(item);
8952
+ }
8953
+ }
8954
+ }
8770
8955
  return {
8771
- type: "EOF",
8772
- value: "",
8773
- position: {
8774
- start: { line: 0, column: 0, offset: 0 },
8775
- end: { line: 0, column: 0, offset: 0 }
8776
- },
8777
- lineStart: false
8956
+ element: "list",
8957
+ data: { ...listData, items: newItems }
8778
8958
  };
8779
8959
  }
8780
- skipWhitespace() {
8781
- while (this.currentToken().type === "WHITESPACE") {
8782
- this.ctx.pos++;
8783
- }
8960
+ if (element.element === "table") {
8961
+ const tableData = element.data;
8962
+ const newRows = [];
8963
+ for (const row of tableData.rows) {
8964
+ const newCells = [];
8965
+ for (const cell of row.cells) {
8966
+ newCells.push({ ...cell, elements: transform(cell.elements) });
8967
+ }
8968
+ newRows.push({ ...row, cells: newCells });
8969
+ }
8970
+ return {
8971
+ element: "table",
8972
+ data: { ...tableData, rows: newRows }
8973
+ };
8974
+ }
8975
+ if (element.element === "definition-list") {
8976
+ const defListData = element.data;
8977
+ const newItems = [];
8978
+ for (const item of defListData) {
8979
+ newItems.push({
8980
+ key_string: item.key_string,
8981
+ key: transform(item.key),
8982
+ value: transform(item.value)
8983
+ });
8984
+ }
8985
+ return {
8986
+ element: "definition-list",
8987
+ data: newItems
8988
+ };
8989
+ }
8990
+ if (element.element === "tab-view") {
8991
+ const tabData = element.data;
8992
+ const newTabs = [];
8993
+ for (const tab of tabData) {
8994
+ newTabs.push({ ...tab, elements: transform(tab.elements) });
8995
+ }
8996
+ return {
8997
+ element: "tab-view",
8998
+ data: newTabs
8999
+ };
9000
+ }
9001
+ if ("data" in element && element.data && typeof element.data === "object") {
9002
+ const data = element.data;
9003
+ if ("elements" in data && Array.isArray(data.elements)) {
9004
+ return {
9005
+ ...element,
9006
+ data: {
9007
+ ...data,
9008
+ elements: transform(data.elements)
9009
+ }
9010
+ };
9011
+ }
9012
+ }
9013
+ return element;
9014
+ }
9015
+ function mapElementChildrenWithState(element, state, transform) {
9016
+ if (element.element === "list") {
9017
+ const listData = element.data;
9018
+ const newItems = [];
9019
+ let currentState = state;
9020
+ for (const item of listData.items) {
9021
+ if (item["item-type"] === "elements") {
9022
+ const result = transform(item.elements, currentState);
9023
+ newItems.push({
9024
+ "item-type": "elements",
9025
+ attributes: item.attributes,
9026
+ elements: result.elements
9027
+ });
9028
+ currentState = result.state;
9029
+ } else if (item["item-type"] === "sub-list") {
9030
+ const result = transform([{ element: "list", data: item.data }], currentState);
9031
+ const resolvedList = result.elements[0];
9032
+ if (resolvedList?.element === "list") {
9033
+ newItems.push({
9034
+ "item-type": "sub-list",
9035
+ element: "list",
9036
+ data: resolvedList.data
9037
+ });
9038
+ } else {
9039
+ newItems.push(item);
9040
+ }
9041
+ currentState = result.state;
9042
+ }
9043
+ }
9044
+ return {
9045
+ element: {
9046
+ element: "list",
9047
+ data: { ...listData, items: newItems }
9048
+ },
9049
+ state: currentState
9050
+ };
9051
+ }
9052
+ if (element.element === "table") {
9053
+ const tableData = element.data;
9054
+ const newRows = [];
9055
+ let currentState = state;
9056
+ for (const row of tableData.rows) {
9057
+ const newCells = [];
9058
+ for (const cell of row.cells) {
9059
+ const result = transform(cell.elements, currentState);
9060
+ newCells.push({ ...cell, elements: result.elements });
9061
+ currentState = result.state;
9062
+ }
9063
+ newRows.push({ ...row, cells: newCells });
9064
+ }
9065
+ return {
9066
+ element: {
9067
+ element: "table",
9068
+ data: { ...tableData, rows: newRows }
9069
+ },
9070
+ state: currentState
9071
+ };
9072
+ }
9073
+ if (element.element === "definition-list") {
9074
+ const defListData = element.data;
9075
+ const newItems = [];
9076
+ let currentState = state;
9077
+ for (const item of defListData) {
9078
+ const keyResult = transform(item.key, currentState);
9079
+ currentState = keyResult.state;
9080
+ const valueResult = transform(item.value, currentState);
9081
+ currentState = valueResult.state;
9082
+ newItems.push({
9083
+ key_string: item.key_string,
9084
+ key: keyResult.elements,
9085
+ value: valueResult.elements
9086
+ });
9087
+ }
9088
+ return {
9089
+ element: {
9090
+ element: "definition-list",
9091
+ data: newItems
9092
+ },
9093
+ state: currentState
9094
+ };
9095
+ }
9096
+ if (element.element === "tab-view") {
9097
+ const tabData = element.data;
9098
+ const newTabs = [];
9099
+ let currentState = state;
9100
+ for (const tab of tabData) {
9101
+ const result = transform(tab.elements, currentState);
9102
+ newTabs.push({ ...tab, elements: result.elements });
9103
+ currentState = result.state;
9104
+ }
9105
+ return {
9106
+ element: {
9107
+ element: "tab-view",
9108
+ data: newTabs
9109
+ },
9110
+ state: currentState
9111
+ };
9112
+ }
9113
+ if ("data" in element && element.data && typeof element.data === "object") {
9114
+ const data = element.data;
9115
+ if ("elements" in data && Array.isArray(data.elements)) {
9116
+ const result = transform(data.elements, state);
9117
+ return {
9118
+ element: {
9119
+ ...element,
9120
+ data: {
9121
+ ...data,
9122
+ elements: result.elements
9123
+ }
9124
+ },
9125
+ state: result.state
9126
+ };
9127
+ }
9128
+ }
9129
+ return { element, state };
9130
+ }
9131
+
9132
+ // packages/parser/src/parser/parse.ts
9133
+ class Parser {
9134
+ ctx;
9135
+ constructor(tokens, options = {}) {
9136
+ this.ctx = {
9137
+ tokens,
9138
+ pos: 0,
9139
+ version: options.version ?? "wikidot",
9140
+ trackPositions: options.trackPositions ?? true,
9141
+ settings: options.settings ?? import_ast2.DEFAULT_SETTINGS,
9142
+ footnotes: [],
9143
+ tocEntries: [],
9144
+ codeBlocks: [],
9145
+ htmlBlocks: [],
9146
+ bibcites: [],
9147
+ diagnostics: [],
9148
+ blockRules,
9149
+ blockFallbackRule: paragraphRule,
9150
+ inlineRules,
9151
+ scope: {
9152
+ footnoteBlockParsed: false
9153
+ }
9154
+ };
9155
+ }
9156
+ parse() {
9157
+ const children = [];
9158
+ while (!this.isAtEnd()) {
9159
+ const blocks = this.parseBlock();
9160
+ children.push(...blocks);
9161
+ }
9162
+ const mergedChildren = mergeSpanStripParagraphs(children);
9163
+ const divProcessed = suppressDivAdjacentParagraphs(mergedChildren);
9164
+ const cleanedChildren = cleanInternalFlags(divProcessed);
9165
+ if (!containsFootnoteBlock(cleanedChildren)) {
9166
+ cleanedChildren.push({
9167
+ element: "footnote-block",
9168
+ data: { title: null, hide: false }
9169
+ });
9170
+ }
9171
+ const tableOfContents = buildTableOfContents(this.ctx.tocEntries);
9172
+ const result = {
9173
+ elements: cleanedChildren
9174
+ };
9175
+ if (tableOfContents.length > 0) {
9176
+ result["table-of-contents"] = tableOfContents;
9177
+ }
9178
+ if (this.ctx.footnotes.length > 0) {
9179
+ result.footnotes = this.ctx.footnotes;
9180
+ }
9181
+ if (this.ctx.codeBlocks.length > 0) {
9182
+ result["code-blocks"] = this.ctx.codeBlocks;
9183
+ }
9184
+ if (this.ctx.htmlBlocks.length > 0) {
9185
+ result["html-blocks"] = this.ctx.htmlBlocks;
9186
+ }
9187
+ return { ast: result, diagnostics: this.ctx.diagnostics };
9188
+ }
9189
+ isAtEnd() {
9190
+ return this.ctx.pos >= this.ctx.tokens.length || this.currentToken().type === "EOF";
9191
+ }
9192
+ currentToken() {
9193
+ return this.ctx.tokens[this.ctx.pos] ?? this.eofToken();
9194
+ }
9195
+ eofToken() {
9196
+ return {
9197
+ type: "EOF",
9198
+ value: "",
9199
+ position: {
9200
+ start: { line: 0, column: 0, offset: 0 },
9201
+ end: { line: 0, column: 0, offset: 0 }
9202
+ },
9203
+ lineStart: false
9204
+ };
9205
+ }
9206
+ skipWhitespace() {
9207
+ while (this.currentToken().type === "WHITESPACE") {
9208
+ this.ctx.pos++;
9209
+ }
8784
9210
  }
8785
9211
  parseBlock() {
8786
9212
  this.skipWhitespace();
@@ -8815,6 +9241,14 @@ function parse(source, options) {
8815
9241
  const tokens = tokenize(preprocessed, { trackPositions: options?.trackPositions });
8816
9242
  return new Parser(tokens, options).parse();
8817
9243
  }
9244
+ function containsFootnoteBlock(elements) {
9245
+ let found = false;
9246
+ walkElements(elements, (el) => {
9247
+ if (el.element === "footnote-block")
9248
+ found = true;
9249
+ });
9250
+ return found;
9251
+ }
8818
9252
  // packages/parser/src/parser/rules/block/module/listpages/compiler.ts
8819
9253
  var DEFAULT_PREVIEW_LENGTH = 200;
8820
9254
  var VARIABLE_REGEX = /%%([a-z_]+)(?:\{([^}]+)\})?(?:\((\d+)\))?(?:\|([^%]*(?:%(?!%)[^%]*)*))?%%/gi;
@@ -9022,348 +9456,99 @@ function strftime(date, format) {
9022
9456
  return String(date.getDate());
9023
9457
  case "H":
9024
9458
  return pad(date.getHours());
9025
- case "I":
9026
- return pad(date.getHours() % 12 || 12);
9027
- case "M":
9028
- return pad(date.getMinutes());
9029
- case "S":
9030
- return pad(date.getSeconds());
9031
- case "p":
9032
- return date.getHours() < 12 ? "AM" : "PM";
9033
- case "b":
9034
- return MONTHS_SHORT[date.getMonth()] ?? "";
9035
- case "B":
9036
- return MONTHS[date.getMonth()] ?? "";
9037
- case "a":
9038
- return DAYS_SHORT[date.getDay()] ?? "";
9039
- case "A":
9040
- return DAYS[date.getDay()] ?? "";
9041
- case "w":
9042
- return String(date.getDay());
9043
- case "j":
9044
- return pad(getDayOfYear(date), 3);
9045
- case "Z":
9046
- return "UTC";
9047
- case "z":
9048
- return "+0000";
9049
- case "%":
9050
- return "%";
9051
- default:
9052
- return `%${token}`;
9053
- }
9054
- });
9055
- }
9056
- function getDayOfYear(date) {
9057
- const start = new Date(date.getFullYear(), 0, 0);
9058
- const diff = date.getTime() - start.getTime();
9059
- const oneDay = 1000 * 60 * 60 * 24;
9060
- return Math.floor(diff / oneDay);
9061
- }
9062
-
9063
- // packages/parser/src/parser/rules/block/module/listusers/compiler.ts
9064
- var VARIABLE_REGEX2 = /%%([a-z_]+)%%/gi;
9065
- function compileListUsersTemplate(template) {
9066
- const parts = [];
9067
- let lastIndex = 0;
9068
- for (const match of template.matchAll(VARIABLE_REGEX2)) {
9069
- if (match.index !== undefined && match.index > lastIndex) {
9070
- parts.push(template.slice(lastIndex, match.index));
9071
- }
9072
- const [, varName] = match;
9073
- if (!varName)
9074
- continue;
9075
- const getter = createVariableGetter2(varName.toLowerCase());
9076
- parts.push(getter);
9077
- lastIndex = match.index !== undefined ? match.index + match[0].length : lastIndex;
9078
- }
9079
- if (lastIndex < template.length) {
9080
- parts.push(template.slice(lastIndex));
9081
- }
9082
- return (ctx) => {
9083
- let result = "";
9084
- for (const part of parts) {
9085
- result += typeof part === "string" ? part : part(ctx);
9086
- }
9087
- return result;
9088
- };
9089
- }
9090
- function createVariableGetter2(name) {
9091
- switch (name) {
9092
- case "number":
9093
- return (ctx) => String(ctx.user.number);
9094
- case "title":
9095
- return (ctx) => ctx.user.title;
9096
- case "name":
9097
- return (ctx) => ctx.user.name;
9098
- default:
9099
- return () => "";
9100
- }
9101
- }
9102
-
9103
- // packages/parser/src/parser/rules/block/module/listusers/extract.ts
9104
- var VARIABLE_REGEX3 = /%%([a-z_]+)%%/gi;
9105
- var KNOWN_VARIABLES = ["number", "title", "name"];
9106
- function extractListUsersVariables(template) {
9107
- const variables = new Set;
9108
- for (const match of template.matchAll(VARIABLE_REGEX3)) {
9109
- const [, varName] = match;
9110
- if (!varName)
9111
- continue;
9112
- const normalized = varName.toLowerCase();
9113
- if (KNOWN_VARIABLES.includes(normalized)) {
9114
- variables.add(normalized);
9115
- }
9116
- }
9117
- return Array.from(variables);
9118
- }
9119
-
9120
- // packages/parser/src/parser/rules/block/module/walk.ts
9121
- function walkElements(elements, callback) {
9122
- for (const element of elements) {
9123
- callback(element);
9124
- if (element.element === "list") {
9125
- const listData = element.data;
9126
- for (const item of listData.items) {
9127
- if (item["item-type"] === "elements") {
9128
- walkElements(item.elements, callback);
9129
- } else if (item["item-type"] === "sub-list") {
9130
- walkElements([{ element: "list", data: item.data }], callback);
9131
- }
9132
- }
9133
- continue;
9134
- }
9135
- if (element.element === "table") {
9136
- const tableData = element.data;
9137
- for (const row of tableData.rows) {
9138
- for (const cell of row.cells) {
9139
- walkElements(cell.elements, callback);
9140
- }
9141
- }
9142
- continue;
9143
- }
9144
- if (element.element === "definition-list") {
9145
- const defListData = element.data;
9146
- for (const item of defListData) {
9147
- walkElements(item.key, callback);
9148
- walkElements(item.value, callback);
9149
- }
9150
- continue;
9151
- }
9152
- if (element.element === "tab-view") {
9153
- const tabData = element.data;
9154
- for (const tab of tabData) {
9155
- walkElements(tab.elements, callback);
9156
- }
9157
- continue;
9158
- }
9159
- if ("data" in element && element.data && typeof element.data === "object") {
9160
- const data = element.data;
9161
- if ("elements" in data && Array.isArray(data.elements)) {
9162
- walkElements(data.elements, callback);
9163
- }
9164
- }
9165
- }
9166
- }
9167
- function mapElementChildren(element, transform) {
9168
- if (element.element === "list") {
9169
- const listData = element.data;
9170
- const newItems = [];
9171
- for (const item of listData.items) {
9172
- if (item["item-type"] === "elements") {
9173
- newItems.push({
9174
- "item-type": "elements",
9175
- attributes: item.attributes,
9176
- elements: transform(item.elements)
9177
- });
9178
- } else if (item["item-type"] === "sub-list") {
9179
- const subListResult = transform([{ element: "list", data: item.data }]);
9180
- const resolvedList = subListResult[0];
9181
- if (resolvedList?.element === "list") {
9182
- newItems.push({
9183
- "item-type": "sub-list",
9184
- element: "list",
9185
- data: resolvedList.data
9186
- });
9187
- } else {
9188
- newItems.push(item);
9189
- }
9190
- }
9191
- }
9192
- return {
9193
- element: "list",
9194
- data: { ...listData, items: newItems }
9195
- };
9196
- }
9197
- if (element.element === "table") {
9198
- const tableData = element.data;
9199
- const newRows = [];
9200
- for (const row of tableData.rows) {
9201
- const newCells = [];
9202
- for (const cell of row.cells) {
9203
- newCells.push({ ...cell, elements: transform(cell.elements) });
9204
- }
9205
- newRows.push({ ...row, cells: newCells });
9206
- }
9207
- return {
9208
- element: "table",
9209
- data: { ...tableData, rows: newRows }
9210
- };
9211
- }
9212
- if (element.element === "definition-list") {
9213
- const defListData = element.data;
9214
- const newItems = [];
9215
- for (const item of defListData) {
9216
- newItems.push({
9217
- key_string: item.key_string,
9218
- key: transform(item.key),
9219
- value: transform(item.value)
9220
- });
9221
- }
9222
- return {
9223
- element: "definition-list",
9224
- data: newItems
9225
- };
9226
- }
9227
- if (element.element === "tab-view") {
9228
- const tabData = element.data;
9229
- const newTabs = [];
9230
- for (const tab of tabData) {
9231
- newTabs.push({ ...tab, elements: transform(tab.elements) });
9232
- }
9233
- return {
9234
- element: "tab-view",
9235
- data: newTabs
9236
- };
9237
- }
9238
- if ("data" in element && element.data && typeof element.data === "object") {
9239
- const data = element.data;
9240
- if ("elements" in data && Array.isArray(data.elements)) {
9241
- return {
9242
- ...element,
9243
- data: {
9244
- ...data,
9245
- elements: transform(data.elements)
9246
- }
9247
- };
9459
+ case "I":
9460
+ return pad(date.getHours() % 12 || 12);
9461
+ case "M":
9462
+ return pad(date.getMinutes());
9463
+ case "S":
9464
+ return pad(date.getSeconds());
9465
+ case "p":
9466
+ return date.getHours() < 12 ? "AM" : "PM";
9467
+ case "b":
9468
+ return MONTHS_SHORT[date.getMonth()] ?? "";
9469
+ case "B":
9470
+ return MONTHS[date.getMonth()] ?? "";
9471
+ case "a":
9472
+ return DAYS_SHORT[date.getDay()] ?? "";
9473
+ case "A":
9474
+ return DAYS[date.getDay()] ?? "";
9475
+ case "w":
9476
+ return String(date.getDay());
9477
+ case "j":
9478
+ return pad(getDayOfYear(date), 3);
9479
+ case "Z":
9480
+ return "UTC";
9481
+ case "z":
9482
+ return "+0000";
9483
+ case "%":
9484
+ return "%";
9485
+ default:
9486
+ return `%${token}`;
9248
9487
  }
9249
- }
9250
- return element;
9488
+ });
9251
9489
  }
9252
- function mapElementChildrenWithState(element, state, transform) {
9253
- if (element.element === "list") {
9254
- const listData = element.data;
9255
- const newItems = [];
9256
- let currentState = state;
9257
- for (const item of listData.items) {
9258
- if (item["item-type"] === "elements") {
9259
- const result = transform(item.elements, currentState);
9260
- newItems.push({
9261
- "item-type": "elements",
9262
- attributes: item.attributes,
9263
- elements: result.elements
9264
- });
9265
- currentState = result.state;
9266
- } else if (item["item-type"] === "sub-list") {
9267
- const result = transform([{ element: "list", data: item.data }], currentState);
9268
- const resolvedList = result.elements[0];
9269
- if (resolvedList?.element === "list") {
9270
- newItems.push({
9271
- "item-type": "sub-list",
9272
- element: "list",
9273
- data: resolvedList.data
9274
- });
9275
- } else {
9276
- newItems.push(item);
9277
- }
9278
- currentState = result.state;
9279
- }
9280
- }
9281
- return {
9282
- element: {
9283
- element: "list",
9284
- data: { ...listData, items: newItems }
9285
- },
9286
- state: currentState
9287
- };
9288
- }
9289
- if (element.element === "table") {
9290
- const tableData = element.data;
9291
- const newRows = [];
9292
- let currentState = state;
9293
- for (const row of tableData.rows) {
9294
- const newCells = [];
9295
- for (const cell of row.cells) {
9296
- const result = transform(cell.elements, currentState);
9297
- newCells.push({ ...cell, elements: result.elements });
9298
- currentState = result.state;
9299
- }
9300
- newRows.push({ ...row, cells: newCells });
9490
+ function getDayOfYear(date) {
9491
+ const start = new Date(date.getFullYear(), 0, 0);
9492
+ const diff = date.getTime() - start.getTime();
9493
+ const oneDay = 1000 * 60 * 60 * 24;
9494
+ return Math.floor(diff / oneDay);
9495
+ }
9496
+
9497
+ // packages/parser/src/parser/rules/block/module/listusers/compiler.ts
9498
+ var VARIABLE_REGEX2 = /%%([a-z_]+)%%/gi;
9499
+ function compileListUsersTemplate(template) {
9500
+ const parts = [];
9501
+ let lastIndex = 0;
9502
+ for (const match of template.matchAll(VARIABLE_REGEX2)) {
9503
+ if (match.index !== undefined && match.index > lastIndex) {
9504
+ parts.push(template.slice(lastIndex, match.index));
9301
9505
  }
9302
- return {
9303
- element: {
9304
- element: "table",
9305
- data: { ...tableData, rows: newRows }
9306
- },
9307
- state: currentState
9308
- };
9506
+ const [, varName] = match;
9507
+ if (!varName)
9508
+ continue;
9509
+ const getter = createVariableGetter2(varName.toLowerCase());
9510
+ parts.push(getter);
9511
+ lastIndex = match.index !== undefined ? match.index + match[0].length : lastIndex;
9309
9512
  }
9310
- if (element.element === "definition-list") {
9311
- const defListData = element.data;
9312
- const newItems = [];
9313
- let currentState = state;
9314
- for (const item of defListData) {
9315
- const keyResult = transform(item.key, currentState);
9316
- currentState = keyResult.state;
9317
- const valueResult = transform(item.value, currentState);
9318
- currentState = valueResult.state;
9319
- newItems.push({
9320
- key_string: item.key_string,
9321
- key: keyResult.elements,
9322
- value: valueResult.elements
9323
- });
9324
- }
9325
- return {
9326
- element: {
9327
- element: "definition-list",
9328
- data: newItems
9329
- },
9330
- state: currentState
9331
- };
9513
+ if (lastIndex < template.length) {
9514
+ parts.push(template.slice(lastIndex));
9332
9515
  }
9333
- if (element.element === "tab-view") {
9334
- const tabData = element.data;
9335
- const newTabs = [];
9336
- let currentState = state;
9337
- for (const tab of tabData) {
9338
- const result = transform(tab.elements, currentState);
9339
- newTabs.push({ ...tab, elements: result.elements });
9340
- currentState = result.state;
9516
+ return (ctx) => {
9517
+ let result = "";
9518
+ for (const part of parts) {
9519
+ result += typeof part === "string" ? part : part(ctx);
9341
9520
  }
9342
- return {
9343
- element: {
9344
- element: "tab-view",
9345
- data: newTabs
9346
- },
9347
- state: currentState
9348
- };
9521
+ return result;
9522
+ };
9523
+ }
9524
+ function createVariableGetter2(name) {
9525
+ switch (name) {
9526
+ case "number":
9527
+ return (ctx) => String(ctx.user.number);
9528
+ case "title":
9529
+ return (ctx) => ctx.user.title;
9530
+ case "name":
9531
+ return (ctx) => ctx.user.name;
9532
+ default:
9533
+ return () => "";
9349
9534
  }
9350
- if ("data" in element && element.data && typeof element.data === "object") {
9351
- const data = element.data;
9352
- if ("elements" in data && Array.isArray(data.elements)) {
9353
- const result = transform(data.elements, state);
9354
- return {
9355
- element: {
9356
- ...element,
9357
- data: {
9358
- ...data,
9359
- elements: result.elements
9360
- }
9361
- },
9362
- state: result.state
9363
- };
9535
+ }
9536
+
9537
+ // packages/parser/src/parser/rules/block/module/listusers/extract.ts
9538
+ var VARIABLE_REGEX3 = /%%([a-z_]+)%%/gi;
9539
+ var KNOWN_VARIABLES = ["number", "title", "name"];
9540
+ function extractListUsersVariables(template) {
9541
+ const variables = new Set;
9542
+ for (const match of template.matchAll(VARIABLE_REGEX3)) {
9543
+ const [, varName] = match;
9544
+ if (!varName)
9545
+ continue;
9546
+ const normalized = varName.toLowerCase();
9547
+ if (KNOWN_VARIABLES.includes(normalized)) {
9548
+ variables.add(normalized);
9364
9549
  }
9365
9550
  }
9366
- return { element, state };
9551
+ return Array.from(variables);
9367
9552
  }
9368
9553
 
9369
9554
  // packages/parser/src/parser/rules/block/module/listpages/extract.ts
@@ -10022,6 +10207,110 @@ function resolveIfTags(data, pageTags) {
10022
10207
  const matched = evaluateTagCondition(condition, pageTags);
10023
10208
  return { evaluated: true, matched };
10024
10209
  }
10210
+ // packages/parser/src/parser/rules/block/module/iftags/preprocess.ts
10211
+ var BASE_PLACEHOLDER_OPEN = "";
10212
+ var BASE_PLACEHOLDER_CLOSE = "";
10213
+ var INNERMOST_IFTAGS_PATTERN = /\[\[\s*iftags\b([^\]]*)\]\]((?:(?!\[\[\s*iftags\b|\[\[\/\s*iftags\s*\]\]).)*)\[\[\/\s*iftags\s*\]\]/gis;
10214
+ var RAW_BLOCK_OPEN_PATTERN = /\[\[\s*(code|html)\b[^\]]*\]\]/iy;
10215
+ function preprocessIftags(source, pageTags) {
10216
+ if (pageTags === null)
10217
+ return source;
10218
+ if (!source.includes("[["))
10219
+ return source;
10220
+ const sentinels = makeUniqueSentinels(source);
10221
+ const { masked, placeholders } = maskRawRegions(source, sentinels);
10222
+ const reduced = reduceIftags(masked, pageTags);
10223
+ return restorePlaceholders(reduced, placeholders, sentinels);
10224
+ }
10225
+ function makeUniqueSentinels(source) {
10226
+ let open = BASE_PLACEHOLDER_OPEN;
10227
+ let close = BASE_PLACEHOLDER_CLOSE;
10228
+ while (source.includes(open) || source.includes(close)) {
10229
+ open += BASE_PLACEHOLDER_OPEN;
10230
+ close += BASE_PLACEHOLDER_CLOSE;
10231
+ }
10232
+ return { open, close };
10233
+ }
10234
+ function reduceIftags(source, pageTags) {
10235
+ let current = source;
10236
+ const maxIterations = source.length + 1;
10237
+ for (let i = 0;i < maxIterations; i++) {
10238
+ const next = current.replace(INNERMOST_IFTAGS_PATTERN, (_, cond, body) => {
10239
+ const condition = parseTagCondition(cond);
10240
+ return evaluateTagCondition(condition, pageTags) ? body : "";
10241
+ });
10242
+ if (next === current)
10243
+ return current;
10244
+ current = next;
10245
+ }
10246
+ return current;
10247
+ }
10248
+ function maskRawRegions(source, sentinels) {
10249
+ const placeholders = [];
10250
+ let masked = "";
10251
+ let i = 0;
10252
+ while (i < source.length) {
10253
+ if (source[i] === "[" && source[i + 1] === "[") {
10254
+ RAW_BLOCK_OPEN_PATTERN.lastIndex = i;
10255
+ const openMatch = RAW_BLOCK_OPEN_PATTERN.exec(source);
10256
+ if (openMatch) {
10257
+ const name = openMatch[1].toLowerCase();
10258
+ const openLen = openMatch[0].length;
10259
+ const closePattern = new RegExp(`\\[\\[\\/\\s*${name}\\s*\\]\\]`, "ig");
10260
+ closePattern.lastIndex = i + openLen;
10261
+ const closeMatch = closePattern.exec(source);
10262
+ if (closeMatch) {
10263
+ const regionEnd = closeMatch.index + closeMatch[0].length;
10264
+ masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
10265
+ i = regionEnd;
10266
+ continue;
10267
+ }
10268
+ if (name === "code") {
10269
+ masked += pushPlaceholder(placeholders, source.slice(i), sentinels);
10270
+ i = source.length;
10271
+ continue;
10272
+ }
10273
+ }
10274
+ }
10275
+ if (source[i] === "@" && source[i + 1] === "<") {
10276
+ const close = source.indexOf(">@", i + 2);
10277
+ const newline = source.indexOf(`
10278
+ `, i + 2);
10279
+ if (close !== -1 && (newline === -1 || close < newline)) {
10280
+ const regionEnd = close + 2;
10281
+ masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
10282
+ i = regionEnd;
10283
+ continue;
10284
+ }
10285
+ }
10286
+ if (source[i] === "@" && source[i + 1] === "@") {
10287
+ const close = source.indexOf("@@", i + 2);
10288
+ const newline = source.indexOf(`
10289
+ `, i + 2);
10290
+ if (close !== -1 && (newline === -1 || close < newline)) {
10291
+ const regionEnd = close + 2;
10292
+ masked += pushPlaceholder(placeholders, source.slice(i, regionEnd), sentinels);
10293
+ i = regionEnd;
10294
+ continue;
10295
+ }
10296
+ }
10297
+ masked += source[i];
10298
+ i++;
10299
+ }
10300
+ return { masked, placeholders };
10301
+ }
10302
+ function pushPlaceholder(placeholders, text, sentinels) {
10303
+ const idx = placeholders.length;
10304
+ placeholders.push(text);
10305
+ return `${sentinels.open}${idx}${sentinels.close}`;
10306
+ }
10307
+ function escapeRegex(str) {
10308
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10309
+ }
10310
+ function restorePlaceholders(source, placeholders, sentinels) {
10311
+ const pattern = new RegExp(`${escapeRegex(sentinels.open)}(\\d+)${escapeRegex(sentinels.close)}`, "g");
10312
+ return source.replace(pattern, (_, idx) => placeholders[Number(idx)] ?? "");
10313
+ }
10025
10314
  // packages/parser/src/parser/rules/block/module/include/resolve.ts
10026
10315
  function resolveIncludes(source, fetcher, options) {
10027
10316
  if (options?.settings && !options.settings.enablePageSyntax) {
@@ -10067,7 +10356,67 @@ async function resolveIncludesAsync(source, fetcher, options) {
10067
10356
  };
10068
10357
  return expandIterativeAsync(source, cachedFetcher, maxIterations);
10069
10358
  }
10070
- var INCLUDE_PATTERN = /^\[\[include\s([^\]]*(?:\](?!\])[^\]]*)*)\]\]/gim;
10359
+ var INCLUDE_OPEN_PATTERN = /^\[\[include\s/gim;
10360
+ function isRestOfLineBlank(source, pos) {
10361
+ for (let i = pos;i < source.length; i++) {
10362
+ const ch = source[i];
10363
+ if (ch === `
10364
+ `)
10365
+ return true;
10366
+ if (ch !== " " && ch !== "\t" && ch !== "\r")
10367
+ return false;
10368
+ }
10369
+ return true;
10370
+ }
10371
+ function scanIncludeDirectives(source) {
10372
+ const matches = [];
10373
+ const opener = new RegExp(INCLUDE_OPEN_PATTERN.source, INCLUDE_OPEN_PATTERN.flags);
10374
+ let m;
10375
+ while ((m = opener.exec(source)) !== null) {
10376
+ const start = m.index;
10377
+ const contentStart = start + m[0].length;
10378
+ const firstNewline = source.indexOf(`
10379
+ `, start);
10380
+ let depth = 0;
10381
+ let linkDepth = 0;
10382
+ let i = start;
10383
+ let closeEnd = -1;
10384
+ while (i < source.length) {
10385
+ if (source.startsWith("[[[", i)) {
10386
+ linkDepth++;
10387
+ i += 3;
10388
+ } else if (linkDepth > 0 && source.startsWith("]]]", i)) {
10389
+ linkDepth--;
10390
+ i += 3;
10391
+ } else if (linkDepth > 0) {
10392
+ i++;
10393
+ } else if (source.startsWith("[[", i)) {
10394
+ depth++;
10395
+ i += 2;
10396
+ } else if (source.startsWith("]]", i)) {
10397
+ const closeStart = i;
10398
+ depth--;
10399
+ i += 2;
10400
+ if (depth <= 0) {
10401
+ const onOpenerLine = firstNewline === -1 || closeStart < firstNewline;
10402
+ if (onOpenerLine || isRestOfLineBlank(source, i)) {
10403
+ closeEnd = i;
10404
+ break;
10405
+ }
10406
+ }
10407
+ } else {
10408
+ i++;
10409
+ }
10410
+ }
10411
+ if (closeEnd === -1) {
10412
+ opener.lastIndex = start + 2;
10413
+ continue;
10414
+ }
10415
+ matches.push({ start, end: closeEnd, inner: source.slice(contentStart, closeEnd - 2) });
10416
+ opener.lastIndex = closeEnd;
10417
+ }
10418
+ return matches;
10419
+ }
10071
10420
  function parseIncludeDirective(inner) {
10072
10421
  const normalized = inner.replace(/\n/g, " ");
10073
10422
  const parts = normalized.split("|");
@@ -10091,14 +10440,24 @@ function parseIncludeDirective(inner) {
10091
10440
  }
10092
10441
  }
10093
10442
  const variables = {};
10443
+ const hasConcrete = new Set;
10094
10444
  for (const segment of varSegments) {
10095
10445
  const eqIndex = segment.indexOf("=");
10096
- if (eqIndex !== -1) {
10097
- const key = segment.slice(0, eqIndex).trim();
10098
- const value = segment.slice(eqIndex + 1).trim();
10099
- if (key) {
10446
+ if (eqIndex === -1)
10447
+ continue;
10448
+ const key = segment.slice(0, eqIndex).trim();
10449
+ if (!key)
10450
+ continue;
10451
+ const value = segment.slice(eqIndex + 1).trim();
10452
+ const isPlaceholder = /^\{\$[^}]*\}$/.test(value);
10453
+ const isConcrete = value !== "" && !isPlaceholder;
10454
+ if (isConcrete) {
10455
+ if (!hasConcrete.has(key)) {
10100
10456
  variables[key] = value;
10457
+ hasConcrete.add(key);
10101
10458
  }
10459
+ } else if (!Object.hasOwn(variables, key)) {
10460
+ variables[key] = value;
10102
10461
  }
10103
10462
  }
10104
10463
  let location;
@@ -10115,7 +10474,7 @@ function parseIncludeDirective(inner) {
10115
10474
  }
10116
10475
  return { location, variables };
10117
10476
  }
10118
- function replaceOneInclude(_match, inner, fetcher) {
10477
+ function replaceOneInclude(inner, fetcher) {
10119
10478
  const { location, variables } = parseIncludeDirective(inner);
10120
10479
  const content = fetcher(location);
10121
10480
  if (content === null) {
@@ -10128,25 +10487,33 @@ Page to be included "${location.page}" cannot be found!
10128
10487
  function expandIterative(source, fetcher, maxIterations) {
10129
10488
  let current = source;
10130
10489
  for (let i = 0;i < maxIterations; i++) {
10131
- const previous = current;
10132
- current = current.replace(INCLUDE_PATTERN, (_match, inner) => replaceOneInclude(_match, inner, fetcher));
10133
- if (current === previous)
10490
+ const directives = scanIncludeDirectives(current);
10491
+ if (directives.length === 0)
10492
+ break;
10493
+ let result = "";
10494
+ let lastPos = 0;
10495
+ for (const { start, end, inner } of directives) {
10496
+ result += current.slice(lastPos, start);
10497
+ result += replaceOneInclude(inner, fetcher);
10498
+ lastPos = end;
10499
+ }
10500
+ result += current.slice(lastPos);
10501
+ if (result === current)
10134
10502
  break;
10503
+ current = result;
10135
10504
  }
10136
10505
  return current;
10137
10506
  }
10138
10507
  async function expandIterativeAsync(source, fetcher, maxIterations) {
10139
10508
  let current = source;
10140
10509
  for (let i = 0;i < maxIterations; i++) {
10141
- const previous = current;
10142
- const pattern = new RegExp(INCLUDE_PATTERN.source, INCLUDE_PATTERN.flags);
10510
+ const directives = scanIncludeDirectives(current);
10511
+ if (directives.length === 0)
10512
+ break;
10143
10513
  let result = "";
10144
10514
  let lastPos = 0;
10145
- let match;
10146
- while ((match = pattern.exec(current)) !== null) {
10147
- const fullMatch = match[0];
10148
- const inner = match[1];
10149
- result += current.slice(lastPos, match.index);
10515
+ for (const { start, end, inner } of directives) {
10516
+ result += current.slice(lastPos, start);
10150
10517
  const { location, variables } = parseIncludeDirective(inner);
10151
10518
  const content = await fetcher(location);
10152
10519
  if (content === null) {
@@ -10156,12 +10523,12 @@ Page to be included "${location.page}" cannot be found!
10156
10523
  } else {
10157
10524
  result += substituteVariables(content, variables);
10158
10525
  }
10159
- lastPos = match.index + fullMatch.length;
10526
+ lastPos = end;
10160
10527
  }
10161
10528
  result += current.slice(lastPos);
10162
- current = result;
10163
- if (current === previous)
10529
+ if (result === current)
10164
10530
  break;
10531
+ current = result;
10165
10532
  }
10166
10533
  return current;
10167
10534
  }