@wdprlib/parser 5.2.0 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/index.cjs +2211 -1182
  2. package/dist/index.d.cts +15 -0
  3. package/dist/index.d.ts +15 -0
  4. package/dist/index.js +2211 -1182
  5. package/package.json +3 -2
  6. package/src/build-info.generated.ts +2 -2
  7. package/src/lexer/lexer.ts +34 -5
  8. package/src/lexer/quoted-string.ts +4 -4
  9. package/src/lexer/syntax-actions.ts +6 -1
  10. package/src/lexer/text-actions.ts +15 -1
  11. package/src/lexer/url-schemes.ts +78 -0
  12. package/src/parser/constants.ts +4 -0
  13. package/src/parser/parse/block.ts +1 -1
  14. package/src/parser/postprocess/divAdjacentParagraph.ts +23 -21
  15. package/src/parser/postprocess/spanStrip/merge.ts +8 -2
  16. package/src/parser/preprocess/typography.ts +25 -3
  17. package/src/parser/preprocess/utils/raw-regions.ts +59 -14
  18. package/src/parser/preprocess/whitespace/index.ts +8 -1
  19. package/src/parser/rules/block/bibliography/entry-content.ts +1 -1
  20. package/src/parser/rules/block/block-list/bare-content.ts +3 -1
  21. package/src/parser/rules/block/block-list/bare-paragraph.ts +7 -2
  22. package/src/parser/rules/block/block-list/item-content.ts +7 -3
  23. package/src/parser/rules/block/block-list/li-content.ts +8 -3
  24. package/src/parser/rules/block/block-list/li-item.ts +1 -1
  25. package/src/parser/rules/block/blockquote/build.ts +1 -1
  26. package/src/parser/rules/block/code/boundary.ts +76 -0
  27. package/src/parser/rules/block/code/content.ts +11 -40
  28. package/src/parser/rules/block/code/index.ts +8 -31
  29. package/src/parser/rules/block/code/open.ts +46 -0
  30. package/src/parser/rules/block/definition-list/item-key.ts +1 -1
  31. package/src/parser/rules/block/definition-list/item-value.ts +1 -1
  32. package/src/parser/rules/block/div/failed.ts +2 -0
  33. package/src/parser/rules/block/index.ts +3 -0
  34. package/src/parser/rules/block/list/line.ts +6 -3
  35. package/src/parser/rules/block/math/boundary.ts +104 -0
  36. package/src/parser/rules/block/math/index.ts +17 -57
  37. package/src/parser/rules/block/module/listpages/resolution/items.ts +2 -2
  38. package/src/parser/rules/block/module/listpages/resolution/pager.ts +43 -0
  39. package/src/parser/rules/block/module/listpages/resolution/wrapper.ts +6 -1
  40. package/src/parser/rules/block/module/listpages/resolve.ts +1 -1
  41. package/src/parser/rules/block/module/listpages/types/external-data.ts +13 -0
  42. package/src/parser/rules/block/module/listpages/url-resolution/params.ts +5 -4
  43. package/src/parser/rules/block/module/resolution/data-maps.ts +75 -3
  44. package/src/parser/rules/block/module/resolution/resolve-async.ts +12 -3
  45. package/src/parser/rules/block/note/boundary.ts +88 -0
  46. package/src/parser/rules/block/note/index.ts +37 -0
  47. package/src/parser/rules/block/paragraph/index.ts +12 -4
  48. package/src/parser/rules/block/paragraph/normalize.ts +6 -1
  49. package/src/parser/rules/block/parsing/block-item.ts +1 -1
  50. package/src/parser/rules/block/parsing/content.ts +1 -1
  51. package/src/parser/rules/block/parsing/inline-content.ts +6 -3
  52. package/src/parser/rules/block/table/pipe/cell.ts +3 -1
  53. package/src/parser/rules/block/table-block/cell-content/index.ts +1 -1
  54. package/src/parser/rules/block/table-block/cell-content/segments.ts +5 -2
  55. package/src/parser/rules/block/table-block/cell-newline.ts +2 -1
  56. package/src/parser/rules/contracts/rule.ts +4 -2
  57. package/src/parser/rules/contracts/scope.ts +2 -0
  58. package/src/parser/rules/inline/anchor/child.ts +8 -2
  59. package/src/parser/rules/inline/anchor/content.ts +3 -1
  60. package/src/parser/rules/inline/anchor/index.ts +4 -1
  61. package/src/parser/rules/inline/anchor/newline.ts +2 -1
  62. package/src/parser/rules/inline/autolink.ts +153 -0
  63. package/src/parser/rules/inline/button/attributes.ts +17 -0
  64. package/src/parser/rules/inline/button/index.ts +17 -0
  65. package/src/parser/rules/inline/button/syntax.ts +56 -0
  66. package/src/parser/rules/inline/date/index.ts +17 -0
  67. package/src/parser/rules/inline/date/syntax.ts +46 -0
  68. package/src/parser/rules/inline/email/candidates.ts +134 -0
  69. package/src/parser/rules/inline/email/index.ts +36 -0
  70. package/src/parser/rules/inline/email/scan.ts +76 -0
  71. package/src/parser/rules/inline/expr/branch.ts +3 -1
  72. package/src/parser/rules/inline/footnote/boundary.ts +56 -0
  73. package/src/parser/rules/inline/footnote/content.ts +29 -41
  74. package/src/parser/rules/inline/footnote/elements.ts +9 -34
  75. package/src/parser/rules/inline/footnote/index.ts +4 -1
  76. package/src/parser/rules/inline/formatting/close.ts +12 -0
  77. package/src/parser/rules/inline/index.ts +14 -0
  78. package/src/parser/rules/inline/line-break/newline.ts +8 -1
  79. package/src/parser/rules/inline/link-bracket/direct-url.ts +11 -3
  80. package/src/parser/rules/inline/link-bracket/parsed.ts +9 -4
  81. package/src/parser/rules/inline/link-bracket/parts.ts +14 -36
  82. package/src/parser/rules/inline/link-bracket/special-target.ts +9 -0
  83. package/src/parser/rules/inline/link-single.ts +9 -7
  84. package/src/parser/rules/inline/link-triple/index.ts +1 -0
  85. package/src/parser/rules/inline/link-triple/label.ts +7 -1
  86. package/src/parser/rules/inline/parsing/automatic-line-break.ts +35 -0
  87. package/src/parser/rules/inline/parsing/block-boundary.ts +2 -0
  88. package/src/parser/rules/inline/parsing/block-start-predicates.ts +10 -0
  89. package/src/parser/rules/inline/parsing/inline-content.ts +36 -6
  90. package/src/parser/rules/inline/parsing/plain-text.ts +7 -2
  91. package/src/parser/rules/inline/parsing/preserved-line-break.ts +13 -0
  92. package/src/parser/rules/inline/parsing/raw-tag.ts +19 -0
  93. package/src/parser/rules/inline/raw/angle.ts +2 -1
  94. package/src/parser/rules/inline/raw/end.ts +21 -1
  95. package/src/parser/rules/inline/size/content.ts +1 -1
  96. package/src/parser/rules/inline/size/value.ts +11 -0
  97. package/src/parser/rules/inline/social/index.ts +17 -0
  98. package/src/parser/rules/inline/social/syntax.ts +40 -0
  99. package/src/parser/rules/inline/span/content.ts +3 -1
  100. package/src/parser/rules/inline/span/newline.ts +2 -1
  101. package/src/parser/rules/opaque-probe.ts +58 -0
  102. package/src/pipeline/process.ts +8 -1
  103. package/src/parser/rules/block/math/content.ts +0 -54
  104. package/src/parser/rules/block/math/name.ts +0 -35
  105. package/src/parser/rules/inline/footnote/child.ts +0 -22
  106. package/src/parser/rules/inline/footnote/newline.ts +0 -27
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // packages/parser/src/build-info.generated.ts
2
2
  var buildInfo = Object.freeze({
3
- version: "5.2.0",
4
- sha: "cc290b0d882de3ed9ea33ec060ddfc8a7b9da59a",
3
+ version: "5.4.0",
4
+ sha: "5e221ddeccc585e64dd1a2eb49b14817c3c1be5d",
5
5
  dirty: false
6
6
  });
7
7
 
@@ -205,13 +205,13 @@ function isValidAnchorNameChar(code) {
205
205
  }
206
206
 
207
207
  // packages/parser/src/lexer/quoted-string.ts
208
- function scanQuotedString(state) {
208
+ function scanQuotedString(state, end = state.source.length) {
209
209
  let quoted = advance(state);
210
- while (!isAtEnd(state) && current(state) !== '"' && current(state) !== `
210
+ while (state.pos < end && current(state) !== '"' && current(state) !== `
211
211
  `) {
212
212
  quoted += advance(state);
213
213
  }
214
- if (current(state) === '"') {
214
+ if (state.pos < end && current(state) === '"') {
215
215
  quoted += advance(state);
216
216
  }
217
217
  return quoted;
@@ -296,6 +296,26 @@ function isCompactPlainTextBoundary(code) {
296
296
  }
297
297
  }
298
298
 
299
+ // packages/parser/src/lexer/url-schemes.ts
300
+ var URL_SCHEME_NAMES = new Set([
301
+ "http",
302
+ "https",
303
+ "ftp",
304
+ "gopher",
305
+ "news",
306
+ "mailto",
307
+ "mms"
308
+ ]);
309
+ var TRAILING_URL_SCHEME = /(?:^|[^A-Za-z0-9])(https?|ftp|gopher|news|mailto|mms)$/;
310
+ var URL_SCHEME_PREFIX = /^(?:(?:https?|ftp|gopher|news|mms):\/\/|mailto:)/;
311
+ var URL_PATTERN = /^(?:(?:https?|ftp|gopher|news|mms):\/\/|mailto:)(?:[^ \\/"']*\/)*[^ \t\n\\/"']*[A-Za-z%0-9/?=&~_]/;
312
+ function startsWithUrlScheme(src, pos) {
313
+ return URL_SCHEME_PREFIX.test(src.slice(pos, pos + 10));
314
+ }
315
+ function startsWithUrl(src, pos) {
316
+ return URL_PATTERN.test(src.slice(pos, pos + 2048));
317
+ }
318
+
299
319
  // packages/parser/src/lexer/syntax-actions.ts
300
320
  function scanSimpleSyntaxToken(src, pos, isLineStart) {
301
321
  switch (src[pos]) {
@@ -332,7 +352,7 @@ function scanSimpleSyntaxToken(src, pos, isLineStart) {
332
352
  }
333
353
  }
334
354
  function scanStarToken(src, pos, isLineStart) {
335
- if (src[pos + 1] === "*") {
355
+ if (src[pos + 1] === "*" && !startsWithUrl(src, pos + 2)) {
336
356
  return token("BOLD_MARKER", "**");
337
357
  }
338
358
  return isLineStart ? token("LIST_BULLET", "*") : token("STAR", "*");
@@ -518,7 +538,13 @@ function scanTextToken(src, pos) {
518
538
  return token3("TEXT", char);
519
539
  }
520
540
  function scanCompactTextToken(src, pos) {
521
- const end = findCompactPlainTextRunEnd(src, pos);
541
+ let end = findCompactPlainTextRunEnd(src, pos);
542
+ if (end > pos && src[end] === ":") {
543
+ const match = TRAILING_URL_SCHEME.exec(src.slice(pos, end));
544
+ if (match) {
545
+ end -= match[1].length;
546
+ }
547
+ }
522
548
  return end > pos ? runToken3(src, pos, end, "TEXT") : null;
523
549
  }
524
550
  function token3(type, value) {
@@ -564,6 +590,8 @@ class Lexer {
564
590
  splitBlockClosePositions = new Set;
565
591
  lastNonWhitespaceType = null;
566
592
  blockOpenerDepth = 0;
593
+ rawTagBounds = null;
594
+ rawClosesExhausted = false;
567
595
  constructor(source, options = {}) {
568
596
  this.options = {
569
597
  trackPositions: options.trackPositions ?? true,
@@ -584,8 +612,8 @@ class Lexer {
584
612
  current() {
585
613
  return current(this.state);
586
614
  }
587
- findInvalidAnchorNameEnd() {
588
- return findInvalidAnchorNameEnd(this.state.source, this.state.pos);
615
+ findInvalidAnchorNameEnd(source) {
616
+ return findInvalidAnchorNameEnd(source, this.state.pos);
589
617
  }
590
618
  advance(n = 1) {
591
619
  return advance(this.state, n);
@@ -597,6 +625,18 @@ class Lexer {
597
625
  this.state.tokens.push(createLexerToken(this.state, type, value, this.options.trackPositions));
598
626
  this.lastNonWhitespaceType = updateLastNonWhitespaceType(this.lastNonWhitespaceType, type);
599
627
  this.blockOpenerDepth = nextBlockOpenerDepth(this.blockOpenerDepth, type);
628
+ if (this.rawTagBounds === null && !this.rawClosesExhausted && (value.toLowerCase() === "button" || value.toLowerCase() === "social") && this.state.tokens.at(-2)?.type === "BLOCK_OPEN" && /\s/.test(this.current())) {
629
+ const close = this.state.source.indexOf("]]", this.state.pos);
630
+ if (close < 0) {
631
+ this.rawClosesExhausted = true;
632
+ } else {
633
+ this.rawTagBounds = {
634
+ source: this.state.source.slice(0, close),
635
+ close,
636
+ outerDepth: this.blockOpenerDepth - 1
637
+ };
638
+ }
639
+ }
600
640
  }
601
641
  emitTokenAction(action) {
602
642
  advanceByToken(this.state, action.type, action.length, action.value);
@@ -614,7 +654,14 @@ class Lexer {
614
654
  scanToken() {
615
655
  const char = this.current();
616
656
  const isLineStart = isSyntaxLineStart(this.state);
617
- const src = this.state.source;
657
+ const bounds = this.rawTagBounds;
658
+ const src = bounds?.source ?? this.state.source;
659
+ if (bounds && this.state.pos === bounds.close) {
660
+ this.emitTokenAction({ type: "BLOCK_CLOSE", value: "]]", length: 2 });
661
+ this.blockOpenerDepth = bounds.outerDepth;
662
+ this.rawTagBounds = null;
663
+ return;
664
+ }
618
665
  const spacingAction = scanSpacingToken(src, this.state.pos);
619
666
  if (spacingAction) {
620
667
  this.emitTokenAction(limitBlockquotePrefixSpace(spacingAction, this.state.tokens.at(-1)));
@@ -627,7 +674,7 @@ class Lexer {
627
674
  lineStart: isLineStart,
628
675
  physicalLineStart: this.state.lineStart,
629
676
  splitBlockClose: this.splitBlockClosePositions.has(this.state.pos),
630
- findInvalidAnchorNameEnd: () => this.findInvalidAnchorNameEnd()
677
+ findInvalidAnchorNameEnd: () => this.findInvalidAnchorNameEnd(src)
631
678
  });
632
679
  if (punctuation.handled) {
633
680
  if (punctuation.clearSplitBlockCloseAt !== undefined) {
@@ -642,7 +689,7 @@ class Lexer {
642
689
  if (char === '"') {
643
690
  const lastNonWs = this.lastNonWhitespaceTokenType();
644
691
  if (this.blockOpenerDepth > 0 && lastNonWs === "EQUALS") {
645
- this.addToken("QUOTED_STRING", scanQuotedString(this.state));
692
+ this.addToken("QUOTED_STRING", scanQuotedString(this.state, src.length));
646
693
  return;
647
694
  }
648
695
  this.advance();
@@ -830,7 +877,7 @@ function parseBlockItem(ctx) {
830
877
  }
831
878
  }
832
879
  const fallback = ctx.blockFallbackRule.parse(ctx);
833
- if (fallback.success && fallback.elements.length > 0) {
880
+ if (fallback.success) {
834
881
  return { elements: fallback.elements, consumed: fallback.consumed };
835
882
  }
836
883
  return { elements: [], consumed: 1 };
@@ -878,7 +925,8 @@ function parseBlocksUntil(ctx, closeCondition, options) {
878
925
  blockCtx.pos = pos;
879
926
  blockCtx.scope = blockScope;
880
927
  const result = parseBlockItem(blockCtx);
881
- elements.push(...result.elements);
928
+ for (const element of result.elements)
929
+ elements.push(element);
882
930
  consumed += result.consumed;
883
931
  pos += result.consumed;
884
932
  }
@@ -905,6 +953,42 @@ function getExcludedBlockRules(blockRules, excluded) {
905
953
  byExcluded.set(excluded, filtered);
906
954
  return filtered;
907
955
  }
956
+ // packages/parser/src/parser/rules/inline/parsing/automatic-line-break.ts
957
+ var origins = new WeakMap;
958
+ function createAutomaticLineBreak(token5) {
959
+ const element = { element: "line-break" };
960
+ origins.set(element, token5);
961
+ return element;
962
+ }
963
+ function precedingSingleNewline(ctx) {
964
+ let pos = ctx.pos - 1;
965
+ while (ctx.tokens[pos]?.type === "WHITESPACE")
966
+ pos--;
967
+ const newline = ctx.tokens[pos];
968
+ if (newline?.type !== "NEWLINE")
969
+ return;
970
+ pos--;
971
+ while (ctx.tokens[pos]?.type === "WHITESPACE")
972
+ pos--;
973
+ if (ctx.tokens[pos]?.type === "NEWLINE")
974
+ return;
975
+ return newline;
976
+ }
977
+ function stripAutomaticLineBreak(elements, token5) {
978
+ if (!token5)
979
+ return;
980
+ let index = elements.length - 1;
981
+ while (index >= 0) {
982
+ const element2 = elements[index];
983
+ if (element2?.element !== "text" || !/^[ \t]*$/.test(element2.data))
984
+ break;
985
+ index--;
986
+ }
987
+ const element = elements[index];
988
+ if (element && origins.get(element) === token5)
989
+ elements.length = index;
990
+ }
991
+
908
992
  // packages/parser/src/parser/rules/inline/parsing/rules.ts
909
993
  function canApplyInlineRule(rule, token5) {
910
994
  if (rule.startTokens.length === 0) {
@@ -927,18 +1011,479 @@ function getCandidateInlineRules(inlineRules, tokenType) {
927
1011
  byType.set(tokenType, candidates);
928
1012
  return candidates;
929
1013
  }
1014
+ // packages/parser/src/parser/rules/inline/parsing/raw-tag.ts
1015
+ var unclosedRanges = new WeakMap;
1016
+ function findRawTagClose(tokens, start, end) {
1017
+ const unclosed = unclosedRanges.get(tokens);
1018
+ if (unclosed && start >= unclosed.start && end <= unclosed.end)
1019
+ return null;
1020
+ for (let close = start;close < end; close++) {
1021
+ if (tokens[close]?.type === "BLOCK_CLOSE")
1022
+ return close;
1023
+ if (!tokens[close] || tokens[close]?.type === "EOF")
1024
+ break;
1025
+ }
1026
+ unclosedRanges.set(tokens, { start, end });
1027
+ return null;
1028
+ }
1029
+
1030
+ // packages/parser/src/parser/rules/inline/button/attributes.ts
1031
+ function parseButtonAttributes(source) {
1032
+ const sections = source.trim().split('="');
1033
+ const attrs = {};
1034
+ let key = sections[0].trim();
1035
+ for (const section of sections.slice(1)) {
1036
+ const quote = section.lastIndexOf('"');
1037
+ if (quote < 0)
1038
+ continue;
1039
+ if (key === "text" || key === "class" || key === "style") {
1040
+ attrs[key] = section.slice(0, quote).replace(/\\([\s\S]|$)/g, (_match, char) => char === "0" ? "\x00" : char);
1041
+ }
1042
+ key = section.slice(quote + 1).trim();
1043
+ }
1044
+ return attrs;
1045
+ }
1046
+
1047
+ // packages/parser/src/parser/rules/inline/button/syntax.ts
1048
+ function parseButtonSyntax(ctx, start, end) {
1049
+ const tokens = ctx.tokens;
1050
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "button")
1051
+ return null;
1052
+ let pos = start + 2;
1053
+ const skipSpace = () => {
1054
+ const before = pos;
1055
+ while (pos < end && /^\s+$/.test(tokens[pos]?.value ?? ""))
1056
+ pos++;
1057
+ return pos > before;
1058
+ };
1059
+ if (!skipSpace())
1060
+ return null;
1061
+ let action = "";
1062
+ while (pos < end && tokens[pos]?.type !== "BLOCK_CLOSE" && !/^\s+$/.test(tokens[pos]?.value ?? "")) {
1063
+ const part = tokens[pos]?.value ?? "";
1064
+ if (!/^[a-z0-9_-]+$/i.test(part))
1065
+ return null;
1066
+ action += part;
1067
+ pos++;
1068
+ }
1069
+ if (!action)
1070
+ return null;
1071
+ action = action.replaceAll("_", "-");
1072
+ skipSpace();
1073
+ const close = findRawTagClose(tokens, pos, end);
1074
+ if (close === null)
1075
+ return null;
1076
+ const attrs = parseButtonAttributes(tokens.slice(pos, close).map((token5) => token5.value).join(""));
1077
+ const value = (key) => attrs[key] && attrs[key] !== "0" ? attrs[key] : null;
1078
+ const attributes = {};
1079
+ for (const name of ["class", "style"]) {
1080
+ const attr = value(name);
1081
+ if (attr !== null)
1082
+ attributes[name] = attr;
1083
+ }
1084
+ return { data: { action, text: value("text"), attributes }, end: close + 1 };
1085
+ }
1086
+
1087
+ // packages/parser/src/parser/rules/inline/social/syntax.ts
1088
+ var invalidBodies = new WeakMap;
1089
+ function parseSocialSyntax(ctx, start, end) {
1090
+ const tokens = ctx.tokens;
1091
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "social")
1092
+ return null;
1093
+ const bodyStart = start + 2;
1094
+ if (tokens[bodyStart]?.type !== "BLOCK_CLOSE" && !/^\s+$/.test(tokens[bodyStart]?.value ?? ""))
1095
+ return null;
1096
+ const invalid = invalidBodies.get(tokens);
1097
+ if (invalid && bodyStart >= invalid.start && bodyStart <= invalid.bracket)
1098
+ return null;
1099
+ const close = findRawTagClose(tokens, bodyStart, end);
1100
+ if (close === null)
1101
+ return null;
1102
+ for (let pos = bodyStart;pos < close; pos++) {
1103
+ if (tokens[pos].value.includes("]")) {
1104
+ invalidBodies.set(tokens, { start: bodyStart, bracket: pos });
1105
+ return null;
1106
+ }
1107
+ }
1108
+ const body = tokens.slice(bodyStart, close).map((token5) => token5.value).join("");
1109
+ const sites = body.trim() ? body.split(",").map((site) => site.trim().toLowerCase()).filter(Boolean) : null;
1110
+ return { data: { sites }, end: close + 1 };
1111
+ }
1112
+
1113
+ // packages/parser/src/parser/rules/block/parsing/attributes/names.ts
1114
+ function consumeAttributeName(ctx, startPos, startConsumed, startName, options) {
1115
+ if (startName === "_" && isAttributeWordToken(ctx.tokens[startPos])) {
1116
+ startName += ctx.tokens[startPos]?.value ?? "";
1117
+ startPos++;
1118
+ startConsumed++;
1119
+ }
1120
+ return options.strikeHyphens ? consumeRawNameSuffix(ctx, startPos, startConsumed, startName, options.hyphenatedNames) : consumeSafeNameSuffix(ctx, startPos, startConsumed, startName);
1121
+ }
1122
+ function isAttributeNameToken(token5) {
1123
+ return isAttributeWordToken(token5) || token5?.type === "UNDERSCORE";
1124
+ }
1125
+ function consumeSafeNameSuffix(ctx, startPos, startConsumed, startName) {
1126
+ let name = startName;
1127
+ let pos = startPos;
1128
+ let consumed = startConsumed;
1129
+ while (isAttributeNameSeparator(ctx.tokens[pos]) && isAttributeWordToken(ctx.tokens[pos + 1])) {
1130
+ name += ctx.tokens[pos]?.value ?? "";
1131
+ pos++;
1132
+ consumed++;
1133
+ name += ctx.tokens[pos]?.value ?? "";
1134
+ pos++;
1135
+ consumed++;
1136
+ }
1137
+ return { name, pos, consumed };
1138
+ }
1139
+ function consumeRawNameSuffix(ctx, startPos, startConsumed, startName, hyphenatedNames) {
1140
+ let name = startName;
1141
+ let pos = startPos;
1142
+ let consumed = startConsumed;
1143
+ while (isAttributeNameSeparator(ctx.tokens[pos])) {
1144
+ while (isAttributeNameSeparator(ctx.tokens[pos])) {
1145
+ if (hyphenatedNames || ctx.tokens[pos]?.type === "UNDERSCORE") {
1146
+ name += ctx.tokens[pos]?.value ?? "-";
1147
+ }
1148
+ pos++;
1149
+ consumed++;
1150
+ }
1151
+ if (!isAttributeWordToken(ctx.tokens[pos])) {
1152
+ break;
1153
+ }
1154
+ if (hyphenatedNames || name.endsWith("_")) {
1155
+ name += ctx.tokens[pos]?.value ?? "";
1156
+ }
1157
+ pos++;
1158
+ consumed++;
1159
+ }
1160
+ return { name, pos, consumed };
1161
+ }
1162
+ function isAttributeWordToken(token5) {
1163
+ return token5?.type === "TEXT" || token5?.type === "IDENTIFIER";
1164
+ }
1165
+ function isAttributeNameSeparator(token5) {
1166
+ return token5?.type === "TEXT" && token5.value === "-" || token5?.type === "STRIKE_MARKER" || token5?.type === "UNDERSCORE";
1167
+ }
1168
+
1169
+ // packages/parser/src/parser/rules/block/parsing/attributes/values.ts
1170
+ function consumeAttributeValue(token5) {
1171
+ if (!token5) {
1172
+ return null;
1173
+ }
1174
+ if (token5.type === "QUOTED_STRING") {
1175
+ return { value: stripQuotes(token5.value) };
1176
+ }
1177
+ if (isAttributeNameToken(token5)) {
1178
+ return { value: token5.value };
1179
+ }
1180
+ return null;
1181
+ }
1182
+ function stripQuotes(value) {
1183
+ return value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
1184
+ }
1185
+
1186
+ // packages/parser/src/parser/rules/block/parsing/attributes/scanner.ts
1187
+ function scanAttributes(ctx, startPos, options) {
1188
+ const attrs = {};
1189
+ let pos = startPos;
1190
+ let consumed = 0;
1191
+ while (pos < ctx.tokens.length) {
1192
+ const token5 = ctx.tokens[pos];
1193
+ if (!token5 || isAttributeTerminator(token5)) {
1194
+ break;
1195
+ }
1196
+ if (token5.type === "WHITESPACE") {
1197
+ pos++;
1198
+ consumed++;
1199
+ continue;
1200
+ }
1201
+ if (!isAttributeNameToken(token5)) {
1202
+ pos++;
1203
+ consumed++;
1204
+ continue;
1205
+ }
1206
+ let name = token5.value;
1207
+ pos++;
1208
+ consumed++;
1209
+ const nameResult = consumeAttributeName(ctx, pos, consumed, name, options);
1210
+ pos = nameResult.pos;
1211
+ consumed = nameResult.consumed;
1212
+ name = nameResult.name.toLowerCase();
1213
+ if (ctx.tokens[pos]?.type !== "EQUALS") {
1214
+ attrs[name] = "true";
1215
+ continue;
1216
+ }
1217
+ pos++;
1218
+ consumed++;
1219
+ const valueResult = consumeAttributeValue(ctx.tokens[pos]);
1220
+ if (!valueResult) {
1221
+ continue;
1222
+ }
1223
+ attrs[name] = valueResult.value;
1224
+ pos++;
1225
+ consumed++;
1226
+ }
1227
+ return { attrs, consumed };
1228
+ }
1229
+ function isAttributeTerminator(token5) {
1230
+ return token5.type === "BLOCK_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF";
1231
+ }
1232
+
1233
+ // packages/parser/src/parser/rules/block/parsing/attributes/index.ts
1234
+ function parseAttributes(ctx, startPos) {
1235
+ const result = scanAttributes(ctx, startPos, { hyphenatedNames: true, strikeHyphens: false });
1236
+ return { attrs: filterUnsafeAttributes(result.attrs), consumed: result.consumed };
1237
+ }
1238
+ function parseAttributesRaw(ctx, startPos, hyphenatedNames = true) {
1239
+ return scanAttributes(ctx, startPos, { hyphenatedNames, strikeHyphens: true });
1240
+ }
1241
+
1242
+ // packages/parser/src/parser/rules/inline/date/syntax.ts
1243
+ function parseDateSyntax(ctx, start, end) {
1244
+ const tokens = ctx.tokens;
1245
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value !== "date")
1246
+ return null;
1247
+ let pos = start + 2;
1248
+ const skipSpace = () => {
1249
+ const before = pos;
1250
+ while (pos < end && /^(?:\s+)$/.test(tokens[pos]?.value ?? ""))
1251
+ pos++;
1252
+ return pos > before;
1253
+ };
1254
+ if (!skipSpace())
1255
+ return null;
1256
+ const value = tokens[pos]?.value ?? "";
1257
+ if (pos >= end || !/^\d+$/.test(value))
1258
+ return null;
1259
+ const timestamp = Number(value);
1260
+ if (!Number.isSafeInteger(timestamp) || timestamp > 8640000000000)
1261
+ return null;
1262
+ pos++;
1263
+ const separator = skipSpace();
1264
+ if (tokens[pos]?.type !== "BLOCK_CLOSE" && !separator)
1265
+ return null;
1266
+ let close = pos;
1267
+ for (;close < end; close++) {
1268
+ const token5 = tokens[close];
1269
+ if (!token5 || token5.type === "EOF" || token5.type === "BLOCK_OPEN" || /[\r\n]/.test(token5.value))
1270
+ return null;
1271
+ if (token5.type === "BLOCK_CLOSE")
1272
+ break;
1273
+ }
1274
+ if (close >= end)
1275
+ return null;
1276
+ const attributes = parseAttributesRaw(ctx, pos).attrs;
1277
+ const format = attributes.format || null;
1278
+ return {
1279
+ data: {
1280
+ value: { timestamp, timezone: "UTC" },
1281
+ format,
1282
+ hover: format?.split("|").slice(1).includes("agohover") ?? false
1283
+ },
1284
+ end: close + 1
1285
+ };
1286
+ }
1287
+
1288
+ // packages/parser/src/parser/rules/inline/email/scan.ts
1289
+ function isDomainChar(code) {
1290
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 45;
1291
+ }
1292
+ function isLocalChar(code) {
1293
+ return isDomainChar(code) || code === 95;
1294
+ }
1295
+ function scanEmail(source, start, commentEnd) {
1296
+ const comments = [];
1297
+ let pos = start;
1298
+ const skipComments = () => {
1299
+ let end2 = commentEnd(pos);
1300
+ while (end2 > pos) {
1301
+ comments.push({ start: pos, end: end2 });
1302
+ pos = end2;
1303
+ end2 = commentEnd(pos);
1304
+ }
1305
+ };
1306
+ let localPart = false;
1307
+ while (pos < source.length) {
1308
+ skipComments();
1309
+ if (isLocalChar(source.charCodeAt(pos))) {
1310
+ localPart = true;
1311
+ pos++;
1312
+ } else if (source[pos] === "." && localPart) {
1313
+ localPart = false;
1314
+ pos++;
1315
+ } else
1316
+ break;
1317
+ }
1318
+ const localEnd = pos;
1319
+ if (!localPart || source[pos] !== "@")
1320
+ return { localEnd, comments };
1321
+ pos++;
1322
+ let domainPart = false;
1323
+ let dots = 0;
1324
+ let end = -1;
1325
+ while (pos < source.length) {
1326
+ skipComments();
1327
+ if (isDomainChar(source.charCodeAt(pos))) {
1328
+ domainPart = true;
1329
+ pos++;
1330
+ if (dots > 0)
1331
+ end = pos;
1332
+ } else if (source[pos] === "." && domainPart) {
1333
+ domainPart = false;
1334
+ dots++;
1335
+ pos++;
1336
+ } else
1337
+ break;
1338
+ }
1339
+ if (end === -1)
1340
+ return { localEnd, comments };
1341
+ const parts = [];
1342
+ let copied = start;
1343
+ for (const comment of comments) {
1344
+ if (comment.start >= end)
1345
+ break;
1346
+ parts.push(source.slice(copied, comment.start));
1347
+ copied = comment.end;
1348
+ }
1349
+ parts.push(source.slice(copied, end));
1350
+ return { end, address: parts.join(""), localEnd, comments };
1351
+ }
1352
+
1353
+ // packages/parser/src/parser/rules/inline/email/candidates.ts
1354
+ var caches = new WeakMap;
1355
+ var EMAIL_START_TOKENS = [
1356
+ "TEXT",
1357
+ "IDENTIFIER",
1358
+ "UNDERSCORE",
1359
+ "UNDERLINE_MARKER",
1360
+ "STRIKE_MARKER"
1361
+ ];
1362
+ function getCache(tokens) {
1363
+ let cache = caches.get(tokens);
1364
+ if (cache)
1365
+ return cache;
1366
+ const offsets = [0];
1367
+ const values = [];
1368
+ for (const token5 of tokens) {
1369
+ values.push(token5.value);
1370
+ offsets.push(offsets.at(-1) + token5.value.length);
1371
+ }
1372
+ cache = {
1373
+ source: values.join(""),
1374
+ offsets,
1375
+ candidates: new Map,
1376
+ commentEnds: new Map,
1377
+ unclosedComment: Infinity
1378
+ };
1379
+ caches.set(tokens, cache);
1380
+ return cache;
1381
+ }
1382
+ function commentEnd(cache, pos) {
1383
+ const opener = cache.source[pos] === `
1384
+ ` ? pos + 1 : pos;
1385
+ if (!cache.source.startsWith("[!--", opener))
1386
+ return pos;
1387
+ const cached = cache.commentEnds.get(pos);
1388
+ if (cached !== undefined)
1389
+ return cached;
1390
+ const close = opener >= cache.unclosedComment ? -1 : cache.source.indexOf("--]", opener + 4);
1391
+ if (close === -1)
1392
+ cache.unclosedComment = Math.min(cache.unclosedComment, opener);
1393
+ const end = close === -1 ? pos : close + 3;
1394
+ cache.commentEnds.set(pos, end);
1395
+ return end;
1396
+ }
1397
+ function getEmailCandidate(tokens, index) {
1398
+ const token5 = tokens[index];
1399
+ if (!token5 || !EMAIL_START_TOKENS.includes(token5.type))
1400
+ return null;
1401
+ const cache = getCache(tokens);
1402
+ if (cache.candidates.has(index))
1403
+ return cache.candidates.get(index);
1404
+ const value = tokens[index]?.value ?? "";
1405
+ let suffix = value.length;
1406
+ while (suffix > 0 && (isLocalChar(value.charCodeAt(suffix - 1)) || value[suffix - 1] === "."))
1407
+ suffix--;
1408
+ const doubled = value.lastIndexOf("..");
1409
+ if (doubled >= suffix)
1410
+ suffix = doubled + 2;
1411
+ while (value[suffix] === ".")
1412
+ suffix++;
1413
+ if (suffix >= value.length || !isLocalChar(value.charCodeAt(suffix))) {
1414
+ cache.candidates.set(index, null);
1415
+ return null;
1416
+ }
1417
+ const start = cache.offsets[index] + suffix;
1418
+ const scanned = scanEmail(cache.source, start, (pos) => commentEnd(cache, pos));
1419
+ if (scanned.end === undefined) {
1420
+ cache.candidates.set(index, null);
1421
+ let comment = 0;
1422
+ for (let i = index + 1;i < tokens.length && cache.offsets[i + 1] <= scanned.localEnd; i++) {
1423
+ const offset = cache.offsets[i];
1424
+ while (scanned.comments[comment] && scanned.comments[comment].end <= offset)
1425
+ comment++;
1426
+ const region = scanned.comments[comment];
1427
+ if (!region || offset < region.start)
1428
+ cache.candidates.set(i, null);
1429
+ }
1430
+ return null;
1431
+ }
1432
+ let endToken = index;
1433
+ while (cache.offsets[endToken + 1] < scanned.end)
1434
+ endToken++;
1435
+ const candidate = { start, end: scanned.end, endToken, address: scanned.address };
1436
+ cache.candidates.set(index, candidate);
1437
+ return candidate;
1438
+ }
1439
+ function getEmailGroup(tokens, index, limit) {
1440
+ const first = getEmailCandidate(tokens, index);
1441
+ if (!first || first.endToken >= limit)
1442
+ return null;
1443
+ const candidates = [first];
1444
+ let last = first;
1445
+ while (last.endToken > index) {
1446
+ const next = getEmailCandidate(tokens, last.endToken);
1447
+ if (!next || next.start < last.end || next.endToken >= limit)
1448
+ break;
1449
+ candidates.push(next);
1450
+ last = next;
1451
+ }
1452
+ const cache = getCache(tokens);
1453
+ return {
1454
+ candidates,
1455
+ source: cache.source,
1456
+ start: cache.offsets[index],
1457
+ end: cache.offsets[last.endToken + 1],
1458
+ endToken: last.endToken + 1
1459
+ };
1460
+ }
1461
+ function emailRegionEnd(tokens, index, limit) {
1462
+ return getEmailGroup(tokens, index, limit)?.endToken ?? index;
1463
+ }
1464
+
930
1465
  // packages/parser/src/parser/rules/inline/raw/end.ts
1466
+ var unclosedAngleRanges = new WeakMap;
931
1467
  function rawRegionEnd(tokens, start, end) {
932
1468
  const type = tokens[start]?.type;
933
1469
  const close = type === "RAW_OPEN" ? "RAW_OPEN" : type === "RAW_BLOCK_OPEN" ? "RAW_BLOCK_CLOSE" : null;
934
1470
  if (!close)
935
1471
  return start;
1472
+ const cached = type === "RAW_BLOCK_OPEN" ? unclosedAngleRanges.get(tokens) : undefined;
1473
+ if (cached && start >= cached.start && start < cached.end && (cached.lineEnd || end <= cached.end)) {
1474
+ return start;
1475
+ }
936
1476
  for (let pos = start + 1;pos < end; pos++) {
937
- if (tokens[pos]?.type === "NEWLINE" || tokens[pos]?.type === "EOF")
938
- break;
1477
+ if (tokens[pos]?.type === "NEWLINE" || tokens[pos]?.type === "EOF") {
1478
+ if (type === "RAW_BLOCK_OPEN")
1479
+ unclosedAngleRanges.set(tokens, { start, end: pos, lineEnd: true });
1480
+ return start;
1481
+ }
939
1482
  if (tokens[pos]?.type === close)
940
1483
  return pos + 1;
941
1484
  }
1485
+ if (type === "RAW_BLOCK_OPEN")
1486
+ unclosedAngleRanges.set(tokens, { start, end, lineEnd: false });
942
1487
  return start;
943
1488
  }
944
1489
  function protectedInlineRegionEnd(tokens, start, end) {
@@ -957,7 +1502,7 @@ var MIN_INLINE_TEXT_RUN_LENGTH = 32;
957
1502
  var MIN_INLINE_TEXT_RUN_DOCUMENT_TOKENS = 1e5;
958
1503
  function collectLongPlainTextRun(ctx, startPos, endType) {
959
1504
  const firstToken = ctx.tokens[startPos];
960
- if (firstToken?.type === "TEXT" && firstToken.value.length >= MIN_INLINE_TEXT_RUN_LENGTH && firstToken.value !== "(") {
1505
+ if (!getEmailCandidate(ctx.tokens, startPos) && firstToken?.type === "TEXT" && firstToken.value.length >= MIN_INLINE_TEXT_RUN_LENGTH && firstToken.value !== "(") {
961
1506
  return { value: firstToken.value, consumed: 1 };
962
1507
  }
963
1508
  let pos = startPos;
@@ -979,10 +1524,10 @@ function collectLongPlainTextRun(ctx, startPos, endType) {
979
1524
  }
980
1525
  function isPlainTextRunToken(ctx, pos) {
981
1526
  const token5 = ctx.tokens[pos];
982
- if (!token5)
1527
+ if (!token5 || getEmailCandidate(ctx.tokens, pos))
983
1528
  return false;
984
1529
  if (token5.type === "IDENTIFIER") {
985
- return true;
1530
+ return !(URL_SCHEME_NAMES.has(token5.value) && ctx.tokens[pos + 1]?.type === "COLON");
986
1531
  }
987
1532
  if (token5.type === "WHITESPACE") {
988
1533
  const next = ctx.tokens[pos + 1];
@@ -1131,6 +1676,10 @@ var BLOCK_START_TOKENS = [
1131
1676
  "HEADING_MARKER",
1132
1677
  "HR_MARKER",
1133
1678
  "TABLE_MARKER",
1679
+ "TABLE_HEADER",
1680
+ "TABLE_LEFT",
1681
+ "TABLE_CENTER",
1682
+ "TABLE_RIGHT",
1134
1683
  "COLON",
1135
1684
  "BLOCK_OPEN",
1136
1685
  "BLOCK_END_OPEN",
@@ -1208,14 +1757,727 @@ var INDENT_ACCEPTING_BLOCK_NAMES = new Set([
1208
1757
  "tabs"
1209
1758
  ]);
1210
1759
 
1211
- // packages/parser/src/parser/rules/inline/parsing/block-boundary.ts
1212
- function isExcludedBlockToken(ctx, tokenPos) {
1213
- const excluded = ctx.scope.excludedBlockNames;
1214
- if (!excluded?.size)
1215
- return false;
1216
- const token5 = ctx.tokens[tokenPos];
1217
- if (token5?.type !== "BLOCK_OPEN" && token5?.type !== "BLOCK_END_OPEN")
1218
- return false;
1760
+ // packages/parser/src/parser/rules/block/code/open.ts
1761
+ var attributeRanges = new WeakMap;
1762
+ function findAttributesEnd(tokens, start) {
1763
+ const cached = attributeRanges.get(tokens);
1764
+ if (cached && start >= cached.start && start <= cached.end)
1765
+ return cached.end;
1766
+ let end = start;
1767
+ while (end < tokens.length) {
1768
+ const type = tokens[end]?.type;
1769
+ if (type === "BLOCK_CLOSE" || type === "NEWLINE" || type === "EOF")
1770
+ break;
1771
+ end++;
1772
+ }
1773
+ attributeRanges.set(tokens, { start, end });
1774
+ return end;
1775
+ }
1776
+ function findCodeOpen(tokens, start) {
1777
+ const name = tokens[start + 1];
1778
+ if (tokens[start]?.type !== "BLOCK_OPEN" || name?.type !== "IDENTIFIER" && name?.type !== "TEXT" || name.value.toLowerCase() !== "code" || tokens[start + 2]?.type === "UNDERSCORE")
1779
+ return null;
1780
+ const attributesEnd = findAttributesEnd(tokens, start + 2);
1781
+ const repaired = tokens[attributesEnd]?.type !== "BLOCK_CLOSE";
1782
+ const last = tokens[attributesEnd - 1];
1783
+ if (repaired && (last?.type !== "QUOTED_STRING" || !last.value.includes("]]")))
1784
+ return null;
1785
+ let bodyStart = attributesEnd + (repaired ? 0 : 1);
1786
+ if (tokens[bodyStart]?.type === "NEWLINE")
1787
+ bodyStart++;
1788
+ return {
1789
+ attributesEnd,
1790
+ bodyStart,
1791
+ repaired,
1792
+ closingSwallowed: repaired && last.value.includes("[[/code]]")
1793
+ };
1794
+ }
1795
+
1796
+ // packages/parser/src/parser/rules/block/code/boundary.ts
1797
+ var caches2 = new WeakMap;
1798
+ function codeCloseEnd(tokens, start) {
1799
+ if (tokens[start]?.type !== "BLOCK_END_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "code")
1800
+ return start;
1801
+ let end = start + 2;
1802
+ while (tokens[end]?.type === "WHITESPACE")
1803
+ end++;
1804
+ return tokens[end]?.type === "BLOCK_CLOSE" ? end + 1 : start;
1805
+ }
1806
+ function nestedCodeBodyStart(tokens, start) {
1807
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "code")
1808
+ return start;
1809
+ for (let pos = start + 2;pos < tokens.length; pos++) {
1810
+ const type = tokens[pos]?.type;
1811
+ if (type === "NEWLINE" || type === "EOF" || type === "BLOCK_OPEN")
1812
+ return start;
1813
+ if (type === "BLOCK_CLOSE") {
1814
+ const open = findCodeOpen(tokens, start);
1815
+ return open && !open.repaired ? open.bodyStart : start;
1816
+ }
1817
+ }
1818
+ return start;
1819
+ }
1820
+ function findCodeBodyBounds(tokens, start) {
1821
+ let cache = caches2.get(tokens);
1822
+ if (!cache) {
1823
+ cache = new Map;
1824
+ caches2.set(tokens, cache);
1825
+ }
1826
+ const cached = cache.get(start);
1827
+ if (cached)
1828
+ return cached;
1829
+ const stack = [start];
1830
+ let pos = start;
1831
+ while (pos < tokens.length && tokens[pos]?.type !== "EOF") {
1832
+ const rawEnd = rawRegionEnd(tokens, pos, tokens.length);
1833
+ if (rawEnd > pos) {
1834
+ pos = rawEnd;
1835
+ continue;
1836
+ }
1837
+ const end = codeCloseEnd(tokens, pos);
1838
+ if (end > pos) {
1839
+ const result2 = { closeStart: pos, end, foundClose: true };
1840
+ cache.set(stack.pop(), result2);
1841
+ if (stack.length === 0)
1842
+ return result2;
1843
+ pos = end;
1844
+ continue;
1845
+ }
1846
+ const bodyStart = nestedCodeBodyStart(tokens, pos);
1847
+ if (bodyStart > pos) {
1848
+ const child = cache.get(bodyStart);
1849
+ if (child) {
1850
+ pos = child.end;
1851
+ if (!child.foundClose)
1852
+ break;
1853
+ } else {
1854
+ stack.push(bodyStart);
1855
+ pos = bodyStart;
1856
+ }
1857
+ continue;
1858
+ }
1859
+ pos++;
1860
+ }
1861
+ const result = { closeStart: pos, end: pos, foundClose: false };
1862
+ for (const bodyStart of stack)
1863
+ cache.set(bodyStart, result);
1864
+ return result;
1865
+ }
1866
+
1867
+ // packages/parser/src/parser/rules/block/math/boundary.ts
1868
+ var nameRanges = new WeakMap;
1869
+ var bodyCaches = new WeakMap;
1870
+ function findNameEnd(tokens, start) {
1871
+ const cached = nameRanges.get(tokens);
1872
+ if (cached && start >= cached.start && start <= cached.end)
1873
+ return cached.end;
1874
+ let end = start;
1875
+ while (end < tokens.length) {
1876
+ const type = tokens[end]?.type;
1877
+ if (type === "BLOCK_CLOSE" || type === "WHITESPACE" || type === "NEWLINE" || type === "EOF")
1878
+ break;
1879
+ end++;
1880
+ }
1881
+ nameRanges.set(tokens, { start, end });
1882
+ return end;
1883
+ }
1884
+ function findMathOpen(tokens, start) {
1885
+ const name = tokens[start + 1];
1886
+ if (tokens[start]?.type !== "BLOCK_OPEN" || name?.type !== "IDENTIFIER" && name?.type !== "TEXT" || name.value.toLowerCase() !== "math" || tokens[start + 2]?.type === "UNDERSCORE")
1887
+ return null;
1888
+ let pos = start + 2;
1889
+ while (tokens[pos]?.type === "WHITESPACE")
1890
+ pos++;
1891
+ const nameStart = pos;
1892
+ if (tokens[pos]?.type === "IDENTIFIER" || tokens[pos]?.type === "TEXT") {
1893
+ pos = findNameEnd(tokens, pos);
1894
+ }
1895
+ const nameEnd = pos;
1896
+ while (tokens[pos]?.type === "WHITESPACE")
1897
+ pos++;
1898
+ if (tokens[pos]?.type !== "BLOCK_CLOSE")
1899
+ return null;
1900
+ pos++;
1901
+ if (tokens[pos]?.type === "NEWLINE")
1902
+ pos++;
1903
+ return { nameStart, nameEnd, bodyStart: pos };
1904
+ }
1905
+ function mathCloseEnd(tokens, start) {
1906
+ if (tokens[start]?.type !== "BLOCK_END_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "math" || tokens[start + 2]?.type !== "BLOCK_CLOSE")
1907
+ return start;
1908
+ const end = start + 3;
1909
+ return tokens[end]?.type === "NEWLINE" ? end + 1 : end;
1910
+ }
1911
+ function findMathBodyBounds(tokens, start) {
1912
+ let cache = bodyCaches.get(tokens);
1913
+ if (!cache) {
1914
+ cache = new Map;
1915
+ bodyCaches.set(tokens, cache);
1916
+ }
1917
+ const visited = [];
1918
+ let result = {
1919
+ closeStart: tokens.length,
1920
+ end: tokens.length,
1921
+ foundClose: false,
1922
+ hasContent: false
1923
+ };
1924
+ for (let pos = start;pos < tokens.length; pos++) {
1925
+ const cached = cache.get(pos);
1926
+ if (cached) {
1927
+ result = cached;
1928
+ break;
1929
+ }
1930
+ const end = mathCloseEnd(tokens, pos);
1931
+ if (end > pos) {
1932
+ result = { closeStart: pos, end, foundClose: true, hasContent: false };
1933
+ cache.set(pos, result);
1934
+ break;
1935
+ }
1936
+ visited.push(pos);
1937
+ }
1938
+ for (let i = visited.length - 1;i >= 0; i--) {
1939
+ const pos = visited[i];
1940
+ const token5 = tokens[pos];
1941
+ if (!result.hasContent && (token5.type === "BACKSLASH_BREAK" || token5.value.trim() !== "")) {
1942
+ result = { ...result, hasContent: true };
1943
+ }
1944
+ cache.set(pos, result);
1945
+ }
1946
+ return result;
1947
+ }
1948
+
1949
+ // packages/parser/src/parser/rules/opaque-probe.ts
1950
+ function createOpaqueProbe(ctx) {
1951
+ return {
1952
+ ...ctx,
1953
+ diagnostics: [],
1954
+ footnotes: [],
1955
+ tocEntries: [],
1956
+ codeBlocks: [],
1957
+ htmlBlocks: [],
1958
+ bibcites: [],
1959
+ scope: {
1960
+ ...ctx.scope,
1961
+ inlineEnd: undefined,
1962
+ tableFormatting: undefined,
1963
+ blockCloseCondition: () => true
1964
+ }
1965
+ };
1966
+ }
1967
+ function opaqueRuleEnd(probe, pos, rules, requireClosed = false) {
1968
+ const token5 = probe.tokens[pos];
1969
+ if (!token5)
1970
+ return pos;
1971
+ probe.pos = pos;
1972
+ for (const rule of rules) {
1973
+ if (!rule.startTokens.includes(token5.type))
1974
+ continue;
1975
+ if (rule.name === "code") {
1976
+ const open = findCodeOpen(probe.tokens, pos);
1977
+ if (!open)
1978
+ continue;
1979
+ if (open.closingSwallowed) {
1980
+ if (!requireClosed)
1981
+ return open.bodyStart;
1982
+ continue;
1983
+ }
1984
+ const bounds = findCodeBodyBounds(probe.tokens, open.bodyStart);
1985
+ if (!requireClosed || bounds.foundClose)
1986
+ return bounds.end;
1987
+ continue;
1988
+ }
1989
+ if (rule.name === "math") {
1990
+ const open = findMathOpen(probe.tokens, pos);
1991
+ if (!open)
1992
+ continue;
1993
+ const bounds = findMathBodyBounds(probe.tokens, open.bodyStart);
1994
+ if (bounds.hasContent && (!requireClosed || bounds.foundClose))
1995
+ return bounds.end;
1996
+ continue;
1997
+ }
1998
+ const result = rule.parse(probe);
1999
+ if (result.success)
2000
+ return pos + result.consumed;
2001
+ }
2002
+ return pos;
2003
+ }
2004
+
2005
+ // packages/parser/src/parser/rules/tokens.ts
2006
+ function currentToken(ctx) {
2007
+ return ctx.tokens[ctx.pos] ?? eofToken();
2008
+ }
2009
+ function eofToken() {
2010
+ return {
2011
+ type: "EOF",
2012
+ value: "",
2013
+ position: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 } },
2014
+ lineStart: false
2015
+ };
2016
+ }
2017
+ function hasClosingMarkerBeforeNewline(ctx, markerType, markerValue) {
2018
+ let pos = ctx.pos;
2019
+ while (pos < (ctx.scope.inlineEnd ?? ctx.tokens.length)) {
2020
+ const token5 = ctx.tokens[pos];
2021
+ if (!token5 || token5.type === "NEWLINE" || token5.type === "EOF") {
2022
+ return false;
2023
+ }
2024
+ if (token5.type === markerType) {
2025
+ if (markerValue === undefined || token5.value === markerValue) {
2026
+ return true;
2027
+ }
2028
+ }
2029
+ pos++;
2030
+ }
2031
+ return false;
2032
+ }
2033
+ // packages/parser/src/parser/rules/block/code/attributes.ts
2034
+ function repairSwallowedCodeClose(ctx, pos, attrs) {
2035
+ const prevToken = ctx.tokens[pos - 1];
2036
+ if (prevToken?.type !== "QUOTED_STRING" || !prevToken.value.includes("]]")) {
2037
+ return null;
2038
+ }
2039
+ const rawValue = prevToken.value;
2040
+ const bracketIdx = rawValue.indexOf("]]");
2041
+ const truncatedValue = rawValue.startsWith('"') ? rawValue.slice(1, bracketIdx) : rawValue.slice(0, bracketIdx);
2042
+ for (const key of Object.keys(attrs)) {
2043
+ const stored = attrs[key];
2044
+ if (stored === rawValue || stored === rawValue.slice(1, -1) || stored === rawValue.slice(1)) {
2045
+ attrs[key] = truncatedValue;
2046
+ break;
2047
+ }
2048
+ }
2049
+ return {
2050
+ closingSwallowed: rawValue.includes("[[/code]]")
2051
+ };
2052
+ }
2053
+
2054
+ // packages/parser/src/parser/rules/block/paragraph/span-markers.ts
2055
+ function processCloseSpanMarkers(elements) {
2056
+ let result = null;
2057
+ for (let i = 0;i < elements.length; i++) {
2058
+ const elem = elements[i];
2059
+ if (!elem)
2060
+ continue;
2061
+ if (isCloseSpanMarker(elem)) {
2062
+ if (result === null) {
2063
+ result = elements.slice(0, i);
2064
+ }
2065
+ if (result.length > 0) {
2066
+ const spanContent = [...result];
2067
+ result.length = 0;
2068
+ result.push({
2069
+ element: "container",
2070
+ data: {
2071
+ type: "span",
2072
+ attributes: {},
2073
+ elements: spanContent
2074
+ }
2075
+ });
2076
+ }
2077
+ } else {
2078
+ result?.push(elem);
2079
+ }
2080
+ }
2081
+ return result ?? elements;
2082
+ }
2083
+ function isCloseSpanMarker(elem) {
2084
+ return elem.element === "container" && elem.data && typeof elem.data === "object" && "type" in elem.data && elem.data.type === "span" && "attributes" in elem.data && typeof elem.data.attributes === "object" && elem.data.attributes !== null && "_closeSpan" in elem.data.attributes;
2085
+ }
2086
+
2087
+ // packages/parser/src/parser/rules/inline/parsing/preserved-line-break.ts
2088
+ function createPreservedTrailingLineBreak() {
2089
+ const lineBreak = { element: "line-break" };
2090
+ lineBreak._preservedTrailingBreak = true;
2091
+ return lineBreak;
2092
+ }
2093
+ function createPreservedLeadingLineBreak() {
2094
+ const lineBreak = { element: "line-break", _preservedLeadingBreak: true };
2095
+ return lineBreak;
2096
+ }
2097
+ function isPreservedLeadingLineBreak(element) {
2098
+ return element?.element === "line-break" && element._preservedLeadingBreak === true;
2099
+ }
2100
+
2101
+ // packages/parser/src/parser/rules/block/paragraph/normalize.ts
2102
+ function normalizeParagraphElements(source) {
2103
+ let elements = processCloseSpanMarkers(source);
2104
+ removeTrailingLineBreaks(elements);
2105
+ removeTrailingWhitespaceText(elements);
2106
+ elements = removeLeadingLineBreaks(elements);
2107
+ return elements;
2108
+ }
2109
+ function removeTrailingLineBreaks(elements) {
2110
+ while (elements.length > 0 && elements[elements.length - 1]?.element === "line-break") {
2111
+ const lastEl = elements[elements.length - 1];
2112
+ if (lastEl._preservedTrailingBreak) {
2113
+ delete lastEl._preservedTrailingBreak;
2114
+ break;
2115
+ }
2116
+ elements.pop();
2117
+ }
2118
+ }
2119
+ function removeTrailingWhitespaceText(elements) {
2120
+ while (elements.length > 0) {
2121
+ const last = elements[elements.length - 1];
2122
+ if (last?.element === "text" && "data" in last && typeof last.data === "string" && last.data.trim() === "") {
2123
+ elements.pop();
2124
+ } else {
2125
+ break;
2126
+ }
2127
+ }
2128
+ }
2129
+ function removeLeadingLineBreaks(elements) {
2130
+ let first = 0;
2131
+ while (first < elements.length && elements[first]?.element === "line-break" && !isPreservedLeadingLineBreak(elements[first])) {
2132
+ first++;
2133
+ }
2134
+ return first > 0 ? elements.slice(first) : elements;
2135
+ }
2136
+
2137
+ // packages/parser/src/parser/rules/block/code/content.ts
2138
+ function collectCodeContent(ctx, startPos, closingSwallowed) {
2139
+ if (closingSwallowed)
2140
+ return { contents: "", consumed: 0, foundClose: true };
2141
+ const bounds = findCodeBodyBounds(ctx.tokens, startPos);
2142
+ return {
2143
+ contents: ctx.tokens.slice(startPos, bounds.closeStart).map((token5) => token5.value).join(""),
2144
+ consumed: bounds.end - startPos,
2145
+ foundClose: bounds.foundClose
2146
+ };
2147
+ }
2148
+
2149
+ // packages/parser/src/parser/rules/block/code/index.ts
2150
+ var codeBlockRule = {
2151
+ name: "code",
2152
+ startTokens: ["BLOCK_OPEN"],
2153
+ requiresLineStart: false,
2154
+ parse(ctx) {
2155
+ const openToken = currentToken(ctx);
2156
+ if (openToken.type !== "BLOCK_OPEN") {
2157
+ return { success: false };
2158
+ }
2159
+ const open = findCodeOpen(ctx.tokens, ctx.pos);
2160
+ if (!open)
2161
+ return { success: false };
2162
+ const attrResult = parseAttributesRaw(ctx, ctx.pos + 2);
2163
+ if (open.repaired)
2164
+ repairSwallowedCodeClose(ctx, open.attributesEnd, attrResult.attrs);
2165
+ let pos = open.bodyStart;
2166
+ let consumed = pos - ctx.pos;
2167
+ const contentResult = collectCodeContent(ctx, pos, open.closingSwallowed);
2168
+ let codeContent = contentResult.contents.replace(/\n$/, "");
2169
+ consumed += contentResult.consumed;
2170
+ pos += contentResult.consumed;
2171
+ if (!contentResult.foundClose) {
2172
+ ctx.diagnostics.push({
2173
+ severity: "warning",
2174
+ code: "unclosed-block",
2175
+ message: "Missing closing tag [[/code]] for [[code]]",
2176
+ position: openToken.position
2177
+ });
2178
+ }
2179
+ const codeBlockData = {
2180
+ contents: codeContent,
2181
+ language: attrResult.attrs.type ?? null,
2182
+ name: attrResult.attrs.name ?? null
2183
+ };
2184
+ ctx.codeBlocks.push(codeBlockData);
2185
+ const elements = [{ element: "code", data: codeBlockData }];
2186
+ if (ctx.tokens[pos]?.type === "NEWLINE" && !getParagraphNewlineBoundary(ctx, pos, false).shouldBreak && !ctx.scope.blockCloseCondition?.({ ...ctx, pos: pos + 1 })) {
2187
+ const after = parseInlineUntil({ ...ctx, pos: pos + 1 }, "PARAGRAPH_BREAK");
2188
+ elements.push({ element: "line-break" }, ...normalizeParagraphElements(after.elements));
2189
+ consumed += 1 + after.consumed;
2190
+ }
2191
+ return { success: true, elements, consumed };
2192
+ }
2193
+ };
2194
+
2195
+ // packages/parser/src/parser/rules/block/math/index.ts
2196
+ var mathBlockRule = {
2197
+ name: "math",
2198
+ startTokens: ["BLOCK_OPEN"],
2199
+ requiresLineStart: false,
2200
+ parse(ctx) {
2201
+ const openToken = currentToken(ctx);
2202
+ const open = findMathOpen(ctx.tokens, ctx.pos);
2203
+ if (!open)
2204
+ return { success: false };
2205
+ const bounds = findMathBodyBounds(ctx.tokens, open.bodyStart);
2206
+ if (!bounds.foundClose) {
2207
+ ctx.diagnostics.push({
2208
+ severity: "warning",
2209
+ code: "unclosed-block",
2210
+ message: "Missing closing tag [[/math]] for [[math]]",
2211
+ position: openToken.position
2212
+ });
2213
+ }
2214
+ if (!bounds.hasContent) {
2215
+ return { success: false };
2216
+ }
2217
+ const latexSource = ctx.tokens.slice(open.bodyStart, bounds.closeStart).map((token5) => token5.type === "BACKSLASH_BREAK" ? "\\\n" : token5.value).join("").trim();
2218
+ const name = ctx.tokens.slice(open.nameStart, open.nameEnd).map((token5) => token5.value).join("");
2219
+ return {
2220
+ success: true,
2221
+ elements: [
2222
+ {
2223
+ element: "math",
2224
+ data: {
2225
+ name: name || null,
2226
+ "latex-source": latexSource
2227
+ }
2228
+ }
2229
+ ],
2230
+ consumed: bounds.end - ctx.pos
2231
+ };
2232
+ }
2233
+ };
2234
+
2235
+ // packages/parser/src/parser/rules/inline/link-triple/fallback.ts
2236
+ function literalOpenLink(value) {
2237
+ return {
2238
+ success: true,
2239
+ elements: [{ element: "text", data: value }],
2240
+ consumed: 1
2241
+ };
2242
+ }
2243
+
2244
+ // packages/parser/src/parser/rules/inline/link-triple/label.ts
2245
+ function buildTripleLinkLabel(args) {
2246
+ if (args.isPage && args.foundPipe && !args.labelText.trim())
2247
+ return "page";
2248
+ return {
2249
+ text: getTripleLinkDisplayText({
2250
+ ...args,
2251
+ originalTarget: args.isPage && !args.foundPipe ? args.originalTarget.split("#")[0] : args.originalTarget
2252
+ })
2253
+ };
2254
+ }
2255
+ function getTripleLinkDisplayText(args) {
2256
+ const trimmedLabel = args.labelText.trim();
2257
+ if (args.foundPipe) {
2258
+ return trimmedLabel || args.finalTarget;
2259
+ }
2260
+ const colonIdx = args.originalTarget.indexOf(":");
2261
+ if (colonIdx !== -1 && !args.originalTarget.startsWith("http") && !args.originalTarget.startsWith("*")) {
2262
+ return args.originalTarget.slice(colonIdx + 1).trim();
2263
+ }
2264
+ return args.originalTarget;
2265
+ }
2266
+
2267
+ // packages/parser/src/parser/rules/inline/link-triple/syntax.ts
2268
+ function hasClosingLinkMarker(ctx, startPos) {
2269
+ let pos = startPos;
2270
+ while (pos < ctx.tokens.length) {
2271
+ const token5 = ctx.tokens[pos];
2272
+ if (!token5 || token5.type === "EOF") {
2273
+ return false;
2274
+ }
2275
+ if (token5.type === "LINK_CLOSE") {
2276
+ return true;
2277
+ }
2278
+ if (token5.type === "NEWLINE") {
2279
+ const next = ctx.tokens[pos + 1];
2280
+ if (next?.type === "NEWLINE" || next?.type === "LINK_CLOSE") {
2281
+ return false;
2282
+ }
2283
+ }
2284
+ pos++;
2285
+ }
2286
+ return false;
2287
+ }
2288
+ function collectTripleLinkParts(ctx, startPos) {
2289
+ let target = "";
2290
+ let labelText = "";
2291
+ let foundPipe = false;
2292
+ let consumed = 1;
2293
+ let pos = startPos;
2294
+ while (pos < ctx.tokens.length) {
2295
+ const token5 = ctx.tokens[pos];
2296
+ if (!token5 || token5.type === "LINK_CLOSE" || token5.type === "EOF") {
2297
+ break;
2298
+ }
2299
+ if (token5.type === "NEWLINE") {
2300
+ if (foundPipe) {
2301
+ labelText += " ";
2302
+ } else {
2303
+ target += " ";
2304
+ }
2305
+ consumed++;
2306
+ pos++;
2307
+ continue;
2308
+ }
2309
+ if (token5.type === "PIPE" && !foundPipe) {
2310
+ foundPipe = true;
2311
+ } else if (foundPipe) {
2312
+ labelText += token5.value;
2313
+ } else {
2314
+ target += token5.value;
2315
+ }
2316
+ consumed++;
2317
+ pos++;
2318
+ }
2319
+ if (ctx.tokens[pos]?.type === "LINK_CLOSE") {
2320
+ consumed++;
2321
+ }
2322
+ return { target, labelText, foundPipe, consumed };
2323
+ }
2324
+
2325
+ // packages/parser/src/parser/rules/inline/link-triple/interwiki.ts
2326
+ var INTERWIKI_PREFIXES = new Set(["wikipedia", "google", "dictionary", "wikidot"]);
2327
+ function isInterwikiTarget(target) {
2328
+ const colonIdx = target.indexOf(":");
2329
+ if (colonIdx <= 0 || target.includes("/")) {
2330
+ return false;
2331
+ }
2332
+ const prefix = target.slice(0, colonIdx).toLowerCase();
2333
+ return INTERWIKI_PREFIXES.has(prefix);
2334
+ }
2335
+
2336
+ // packages/parser/src/parser/rules/inline/link-triple/target.ts
2337
+ function normalizeTripleLinkTarget(trimmedTarget) {
2338
+ if (trimmedTarget.startsWith("*")) {
2339
+ return { target: trimmedTarget.slice(1), hasStar: true };
2340
+ }
2341
+ return { target: trimmedTarget, hasStar: false };
2342
+ }
2343
+ function isInvalidTripleLinkTarget(trimmedTarget, foundPipe) {
2344
+ return trimmedTarget === "" && foundPipe || /#{2,}/.test(trimmedTarget);
2345
+ }
2346
+ function determineLinkTypeAndLocation(target) {
2347
+ if (target.startsWith("#")) {
2348
+ return { linkType: "anchor", link: target };
2349
+ }
2350
+ if (target.startsWith("http://") || target.startsWith("https://")) {
2351
+ return { linkType: "direct", link: target };
2352
+ }
2353
+ if (isInterwikiTarget(target)) {
2354
+ return { linkType: "interwiki", link: target };
2355
+ }
2356
+ return { linkType: "page", link: { site: null, page: target } };
2357
+ }
2358
+
2359
+ // packages/parser/src/parser/rules/inline/link-triple/index.ts
2360
+ var linkTripleRule = {
2361
+ name: "linkTriple",
2362
+ startTokens: ["LINK_OPEN"],
2363
+ parse(ctx) {
2364
+ const startToken = currentToken(ctx);
2365
+ if (!hasClosingLinkMarker(ctx, ctx.pos + 1)) {
2366
+ return literalOpenLink(startToken.value);
2367
+ }
2368
+ const parts = collectTripleLinkParts(ctx, ctx.pos + 1);
2369
+ const trimmedTarget = parts.target.trim();
2370
+ if (isInvalidTripleLinkTarget(trimmedTarget, parts.foundPipe)) {
2371
+ return literalOpenLink(startToken.value);
2372
+ }
2373
+ const normalized = normalizeTripleLinkTarget(trimmedTarget);
2374
+ const { linkType, link } = determineLinkTypeAndLocation(normalized.target);
2375
+ const label = buildTripleLinkLabel({
2376
+ isPage: linkType === "page",
2377
+ foundPipe: parts.foundPipe,
2378
+ labelText: parts.labelText,
2379
+ finalTarget: normalized.target,
2380
+ originalTarget: trimmedTarget
2381
+ });
2382
+ return {
2383
+ success: true,
2384
+ elements: [
2385
+ {
2386
+ element: "link",
2387
+ data: {
2388
+ type: linkType,
2389
+ link,
2390
+ extra: null,
2391
+ label,
2392
+ target: normalized.hasStar && linkType === "direct" ? "new-tab" : null
2393
+ }
2394
+ }
2395
+ ],
2396
+ consumed: parts.consumed
2397
+ };
2398
+ }
2399
+ };
2400
+
2401
+ // packages/parser/src/parser/rules/block/note/boundary.ts
2402
+ var opaqueRules = [codeBlockRule, mathBlockRule, linkTripleRule];
2403
+ var boundsCache = new WeakMap;
2404
+ var closeCache = new WeakMap;
2405
+ function tagEnd(tokens, pos, name, close = false) {
2406
+ return tokens[pos]?.type === (close ? "BLOCK_END_OPEN" : "BLOCK_OPEN") && tokens[pos + 1]?.value.toLowerCase() === name && tokens[pos + 2]?.type === "BLOCK_CLOSE" ? pos + 3 : pos;
2407
+ }
2408
+ function findClose(probe, start, name) {
2409
+ let byName = closeCache.get(probe.tokens);
2410
+ if (!byName) {
2411
+ byName = new Map;
2412
+ closeCache.set(probe.tokens, byName);
2413
+ }
2414
+ let cache = byName.get(name);
2415
+ if (!cache) {
2416
+ cache = new Map;
2417
+ byName.set(name, cache);
2418
+ }
2419
+ if (cache.has(start))
2420
+ return cache.get(start);
2421
+ const visited = [];
2422
+ let result = -1;
2423
+ for (let pos = start;pos < probe.tokens.length; pos++) {
2424
+ if (cache.has(pos)) {
2425
+ result = cache.get(pos);
2426
+ break;
2427
+ }
2428
+ visited.push(pos);
2429
+ const protectedEnd = protectedInlineRegionEnd(probe.tokens, pos, probe.tokens.length);
2430
+ if (protectedEnd > pos) {
2431
+ pos = protectedEnd - 1;
2432
+ continue;
2433
+ }
2434
+ if (tagEnd(probe.tokens, pos, name, true) > pos) {
2435
+ result = pos;
2436
+ break;
2437
+ }
2438
+ const opaqueEnd = opaqueRuleEnd(probe, pos, opaqueRules, true);
2439
+ if (opaqueEnd > pos) {
2440
+ pos = opaqueEnd - 1;
2441
+ continue;
2442
+ }
2443
+ if (name === "note") {
2444
+ const footnoteStart = tagEnd(probe.tokens, pos, "footnote");
2445
+ if (footnoteStart > pos) {
2446
+ const end = findClose(probe, footnoteStart, "footnote");
2447
+ if (end >= 0)
2448
+ pos = end + 2;
2449
+ }
2450
+ }
2451
+ }
2452
+ for (const pos of visited)
2453
+ cache.set(pos, result);
2454
+ return result;
2455
+ }
2456
+ function findNoteBounds(ctx, start = ctx.pos) {
2457
+ const openEnd = tagEnd(ctx.tokens, start, "note");
2458
+ if (openEnd === start || ctx.tokens[openEnd]?.type !== "NEWLINE")
2459
+ return null;
2460
+ let cache = boundsCache.get(ctx.tokens);
2461
+ if (!cache) {
2462
+ cache = new Map;
2463
+ boundsCache.set(ctx.tokens, cache);
2464
+ }
2465
+ if (cache.has(start))
2466
+ return cache.get(start);
2467
+ const close = findClose(createOpaqueProbe(ctx), openEnd + 1, "note");
2468
+ const result = close < 0 ? null : { bodyStart: openEnd + 1, close, end: close + 3 };
2469
+ cache.set(start, result);
2470
+ return result;
2471
+ }
2472
+
2473
+ // packages/parser/src/parser/rules/inline/parsing/block-boundary.ts
2474
+ function isExcludedBlockToken(ctx, tokenPos) {
2475
+ const excluded = ctx.scope.excludedBlockNames;
2476
+ if (!excluded?.size)
2477
+ return false;
2478
+ const token5 = ctx.tokens[tokenPos];
2479
+ if (token5?.type !== "BLOCK_OPEN" && token5?.type !== "BLOCK_END_OPEN")
2480
+ return false;
1219
2481
  const nameResult = parseBlockName(ctx, tokenPos + 1);
1220
2482
  return nameResult !== null && excluded.has(nameResult.name);
1221
2483
  }
@@ -1230,6 +2492,8 @@ function isUnknownBlockToken(ctx, tokenPos) {
1230
2492
  }
1231
2493
  return true;
1232
2494
  }
2495
+ if (nameResult.name === "note")
2496
+ return findNoteBounds(ctx, tokenPos) === null;
1233
2497
  return !KNOWN_BLOCK_NAMES.has(nameResult.name);
1234
2498
  }
1235
2499
  function isIndentAcceptingBlock(ctx, tokenPos) {
@@ -1253,7 +2517,13 @@ function isParagraphBreakingBlockStart(ctx, newlinePos, lookAhead) {
1253
2517
  if (!nextMeaningfulToken.lineStart && !isIndentedBlockOpener) {
1254
2518
  return false;
1255
2519
  }
1256
- return !parseImageOpen({ ...ctx, pos: nextPos }) && !isOrphanCloseSpan(ctx, nextPos) && !isAnchorName(ctx, nextPos) && !isInvalidBlockOpen(ctx, nextPos) && !isInvalidHeading(ctx, nextPos) && !isExcludedBlockStart(ctx, nextPos) && !isUnknownBlockStart(ctx, nextPos);
2520
+ return !parseImageOpen({ ...ctx, pos: nextPos }) && !isOrphanCloseSpan(ctx, nextPos) && !isAnchorName(ctx, nextPos) && !isInvalidBlockOpen(ctx, nextPos) && !isInvalidHeading(ctx, nextPos) && !isInvalidListMarker(ctx, nextPos) && !isExcludedBlockStart(ctx, nextPos) && !isUnknownBlockStart(ctx, nextPos);
2521
+ }
2522
+ function isInvalidListMarker(ctx, markerPos) {
2523
+ const marker = ctx.tokens[markerPos];
2524
+ if (marker?.type !== "LIST_BULLET" && marker?.type !== "LIST_NUMBER")
2525
+ return false;
2526
+ return ctx.tokens[markerPos + 1]?.type !== "WHITESPACE";
1257
2527
  }
1258
2528
  function isOrphanCloseSpan(ctx, blockEndOpenPos) {
1259
2529
  const token5 = ctx.tokens[blockEndOpenPos];
@@ -1339,13 +2609,6 @@ function skipWhitespaceAfterNewline(ctx, newlinePos) {
1339
2609
  return lookAhead;
1340
2610
  }
1341
2611
 
1342
- // packages/parser/src/parser/rules/inline/parsing/preserved-line-break.ts
1343
- function createPreservedTrailingLineBreak() {
1344
- const lineBreak = { element: "line-break" };
1345
- lineBreak._preservedTrailingBreak = true;
1346
- return lineBreak;
1347
- }
1348
-
1349
2612
  // packages/parser/src/parser/rules/inline/parsing/simple-token.ts
1350
2613
  function parseSimpleInlineToken(token5, nextToken) {
1351
2614
  if (token5.type === "TEXT" && token5.value !== "(") {
@@ -1362,12 +2625,13 @@ function parseInlineUntil(ctx, endType) {
1362
2625
  const nodes = [];
1363
2626
  let consumed = 0;
1364
2627
  let pos = ctx.pos;
2628
+ let consumedEmptyRaw = false;
1365
2629
  const paragraphMode = endType === "PARAGRAPH_BREAK";
1366
2630
  const multiline = paragraphMode || FORMATTING_CLOSE_TOKENS.has(endType);
1367
2631
  let inlineEnd = ctx.scope.inlineEnd ?? ctx.tokens.length;
1368
2632
  if (!multiline) {
1369
2633
  for (let end = ctx.pos;end < inlineEnd; end++) {
1370
- const protectedEnd = protectedInlineRegionEnd(ctx.tokens, end, inlineEnd);
2634
+ const protectedEnd = Math.max(parseButtonSyntax(ctx, end, inlineEnd)?.end ?? end, parseSocialSyntax(ctx, end, inlineEnd)?.end ?? end, parseDateSyntax(ctx, end, inlineEnd)?.end ?? end, emailRegionEnd(ctx.tokens, end, inlineEnd), protectedInlineRegionEnd(ctx.tokens, end, inlineEnd));
1371
2635
  if (protectedEnd > end) {
1372
2636
  end = protectedEnd - 1;
1373
2637
  continue;
@@ -1416,10 +2680,11 @@ function parseInlineUntil(ctx, endType) {
1416
2680
  consumed++;
1417
2681
  continue;
1418
2682
  }
1419
- if (token5.type === endType) {
2683
+ const hasEmail = emailRegionEnd(ctx.tokens, pos, inlineEnd) > pos;
2684
+ if (token5.type === endType && !hasEmail) {
1420
2685
  break;
1421
2686
  }
1422
- if (canCollectLongPlainTextRuns) {
2687
+ if (canCollectLongPlainTextRuns && !hasEmail) {
1423
2688
  const plainTextRun = collectLongPlainTextRun(ctx, pos, endType);
1424
2689
  if (plainTextRun) {
1425
2690
  nodes.push({ element: "text", data: plainTextRun.value });
@@ -1429,7 +2694,7 @@ function parseInlineUntil(ctx, endType) {
1429
2694
  }
1430
2695
  }
1431
2696
  const simpleToken = parseSimpleInlineToken(token5, ctx.tokens[pos + 1]);
1432
- if (simpleToken) {
2697
+ if (simpleToken && !hasEmail) {
1433
2698
  nodes.push(simpleToken.element);
1434
2699
  consumed += simpleToken.consumed;
1435
2700
  pos += simpleToken.consumed;
@@ -1440,6 +2705,10 @@ function parseInlineUntil(ctx, endType) {
1440
2705
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
1441
2706
  const result = rule.parse(inlineCtx);
1442
2707
  if (result.success) {
2708
+ stripAutomaticLineBreak(nodes, result.stripLeadingLineBreak);
2709
+ if (rule.name === "raw" && result.elements.length === 0 && nodes.length === 0) {
2710
+ consumedEmptyRaw = true;
2711
+ }
1443
2712
  if (rule.name === "comment") {
1444
2713
  let after = pos + result.consumed;
1445
2714
  while (ctx.tokens[after]?.type === "WHITESPACE")
@@ -1458,7 +2727,9 @@ function parseInlineUntil(ctx, endType) {
1458
2727
  nodes.pop();
1459
2728
  }
1460
2729
  }
1461
- nodes.push(...result.elements);
2730
+ for (const element of result.elements) {
2731
+ nodes.push(paragraphMode && consumedEmptyRaw && nodes.length === 0 && token5.type === "NEWLINE" && element.element === "line-break" ? createPreservedLeadingLineBreak() : element);
2732
+ }
1462
2733
  consumed += result.consumed;
1463
2734
  pos += result.consumed;
1464
2735
  matched = true;
@@ -1483,138 +2754,10 @@ var FORMATTING_CLOSE_TOKENS = new Set([
1483
2754
  "MONO_CLOSE",
1484
2755
  "COLOR_MARKER"
1485
2756
  ]);
1486
- // packages/parser/src/parser/rules/block/parsing/attributes/names.ts
1487
- function consumeAttributeName(ctx, startPos, startConsumed, startName, options) {
1488
- if (startName === "_" && isAttributeWordToken(ctx.tokens[startPos])) {
1489
- startName += ctx.tokens[startPos]?.value ?? "";
1490
- startPos++;
1491
- startConsumed++;
1492
- }
1493
- return options.strikeHyphens ? consumeRawNameSuffix(ctx, startPos, startConsumed, startName, options.hyphenatedNames) : consumeSafeNameSuffix(ctx, startPos, startConsumed, startName);
1494
- }
1495
- function isAttributeNameToken(token5) {
1496
- return isAttributeWordToken(token5) || token5?.type === "UNDERSCORE";
1497
- }
1498
- function consumeSafeNameSuffix(ctx, startPos, startConsumed, startName) {
1499
- let name = startName;
1500
- let pos = startPos;
1501
- let consumed = startConsumed;
1502
- while (isAttributeNameSeparator(ctx.tokens[pos]) && isAttributeWordToken(ctx.tokens[pos + 1])) {
1503
- name += ctx.tokens[pos]?.value ?? "";
1504
- pos++;
1505
- consumed++;
1506
- name += ctx.tokens[pos]?.value ?? "";
1507
- pos++;
1508
- consumed++;
1509
- }
1510
- return { name, pos, consumed };
1511
- }
1512
- function consumeRawNameSuffix(ctx, startPos, startConsumed, startName, hyphenatedNames) {
1513
- let name = startName;
1514
- let pos = startPos;
1515
- let consumed = startConsumed;
1516
- while (isAttributeNameSeparator(ctx.tokens[pos])) {
1517
- while (isAttributeNameSeparator(ctx.tokens[pos])) {
1518
- if (hyphenatedNames || ctx.tokens[pos]?.type === "UNDERSCORE") {
1519
- name += ctx.tokens[pos]?.value ?? "-";
1520
- }
1521
- pos++;
1522
- consumed++;
1523
- }
1524
- if (!isAttributeWordToken(ctx.tokens[pos])) {
1525
- break;
1526
- }
1527
- if (hyphenatedNames || name.endsWith("_")) {
1528
- name += ctx.tokens[pos]?.value ?? "";
1529
- }
1530
- pos++;
1531
- consumed++;
1532
- }
1533
- return { name, pos, consumed };
1534
- }
1535
- function isAttributeWordToken(token5) {
1536
- return token5?.type === "TEXT" || token5?.type === "IDENTIFIER";
1537
- }
1538
- function isAttributeNameSeparator(token5) {
1539
- return token5?.type === "TEXT" && token5.value === "-" || token5?.type === "STRIKE_MARKER" || token5?.type === "UNDERSCORE";
1540
- }
1541
-
1542
- // packages/parser/src/parser/rules/block/parsing/attributes/values.ts
1543
- function consumeAttributeValue(token5) {
1544
- if (!token5) {
1545
- return null;
1546
- }
1547
- if (token5.type === "QUOTED_STRING") {
1548
- return { value: stripQuotes(token5.value) };
1549
- }
1550
- if (isAttributeNameToken(token5)) {
1551
- return { value: token5.value };
1552
- }
1553
- return null;
1554
- }
1555
- function stripQuotes(value) {
1556
- return value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
1557
- }
1558
-
1559
- // packages/parser/src/parser/rules/block/parsing/attributes/scanner.ts
1560
- function scanAttributes(ctx, startPos, options) {
1561
- const attrs = {};
1562
- let pos = startPos;
1563
- let consumed = 0;
1564
- while (pos < ctx.tokens.length) {
1565
- const token5 = ctx.tokens[pos];
1566
- if (!token5 || isAttributeTerminator(token5)) {
1567
- break;
1568
- }
1569
- if (token5.type === "WHITESPACE") {
1570
- pos++;
1571
- consumed++;
1572
- continue;
1573
- }
1574
- if (!isAttributeNameToken(token5)) {
1575
- pos++;
1576
- consumed++;
1577
- continue;
1578
- }
1579
- let name = token5.value;
1580
- pos++;
1581
- consumed++;
1582
- const nameResult = consumeAttributeName(ctx, pos, consumed, name, options);
1583
- pos = nameResult.pos;
1584
- consumed = nameResult.consumed;
1585
- name = nameResult.name.toLowerCase();
1586
- if (ctx.tokens[pos]?.type !== "EQUALS") {
1587
- attrs[name] = "true";
1588
- continue;
1589
- }
1590
- pos++;
1591
- consumed++;
1592
- const valueResult = consumeAttributeValue(ctx.tokens[pos]);
1593
- if (!valueResult) {
1594
- continue;
1595
- }
1596
- attrs[name] = valueResult.value;
1597
- pos++;
1598
- consumed++;
1599
- }
1600
- return { attrs, consumed };
1601
- }
1602
- function isAttributeTerminator(token5) {
1603
- return token5.type === "BLOCK_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF";
1604
- }
1605
-
1606
- // packages/parser/src/parser/rules/block/parsing/attributes/index.ts
1607
- function parseAttributes(ctx, startPos) {
1608
- const result = scanAttributes(ctx, startPos, { hyphenatedNames: true, strikeHyphens: false });
1609
- return { attrs: filterUnsafeAttributes(result.attrs), consumed: result.consumed };
1610
- }
1611
- function parseAttributesRaw(ctx, startPos, hyphenatedNames = true) {
1612
- return scanAttributes(ctx, startPos, { hyphenatedNames, strikeHyphens: true });
1613
- }
1614
2757
  // packages/parser/src/parser/parse/block.ts
1615
- function parseNextBlock(ctx, skipWhitespace2, isAtEnd2) {
2758
+ function parseNextBlock(ctx, skipWhitespace2, isAtEnd3) {
1616
2759
  skipWhitespace2();
1617
- if (isAtEnd2()) {
2760
+ if (isAtEnd3()) {
1618
2761
  return [];
1619
2762
  }
1620
2763
  const token5 = ctx.tokens[ctx.pos];
@@ -1633,7 +2776,7 @@ function parseNextBlock(ctx, skipWhitespace2, isAtEnd2) {
1633
2776
  }
1634
2777
  }
1635
2778
  const result = ctx.blockFallbackRule.parse(ctx);
1636
- if (result.success && result.elements.length > 0) {
2779
+ if (result.success) {
1637
2780
  ctx.pos += result.consumed;
1638
2781
  return result.elements;
1639
2782
  }
@@ -1643,35 +2786,6 @@ function parseNextBlock(ctx, skipWhitespace2, isAtEnd2) {
1643
2786
 
1644
2787
  // packages/parser/src/parser/parse/context.ts
1645
2788
  import { DEFAULT_SETTINGS } from "@wdprlib/ast";
1646
-
1647
- // packages/parser/src/parser/rules/tokens.ts
1648
- function currentToken(ctx) {
1649
- return ctx.tokens[ctx.pos] ?? eofToken();
1650
- }
1651
- function eofToken() {
1652
- return {
1653
- type: "EOF",
1654
- value: "",
1655
- position: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 } },
1656
- lineStart: false
1657
- };
1658
- }
1659
- function hasClosingMarkerBeforeNewline(ctx, markerType, markerValue) {
1660
- let pos = ctx.pos;
1661
- while (pos < (ctx.scope.inlineEnd ?? ctx.tokens.length)) {
1662
- const token5 = ctx.tokens[pos];
1663
- if (!token5 || token5.type === "NEWLINE" || token5.type === "EOF") {
1664
- return false;
1665
- }
1666
- if (token5.type === markerType) {
1667
- if (markerValue === undefined || token5.value === markerValue) {
1668
- return true;
1669
- }
1670
- }
1671
- pos++;
1672
- }
1673
- return false;
1674
- }
1675
2789
  // packages/parser/src/parser/rules/block/heading/open.ts
1676
2790
  function parseHeadingOpen(ctx) {
1677
2791
  const marker = ctx.tokens[ctx.pos];
@@ -1917,10 +3031,11 @@ function parseNativeListLine(ctx, startPos) {
1917
3031
  const ltype = markerToken.type === "LIST_BULLET" ? "bullet" : "numbered";
1918
3032
  pos++;
1919
3033
  consumed++;
1920
- if (ctx.tokens[pos]?.type === "WHITESPACE") {
1921
- pos++;
1922
- consumed++;
3034
+ if (ctx.tokens[pos]?.type !== "WHITESPACE") {
3035
+ return { kind: "stop" };
1923
3036
  }
3037
+ pos++;
3038
+ consumed++;
1924
3039
  const inlineCtx = { ...ctx, pos };
1925
3040
  const inlineResult = parseInlineUntil(inlineCtx, "NEWLINE");
1926
3041
  consumed += inlineResult.consumed;
@@ -2030,9 +3145,9 @@ function appendBareParagraphElements(state, elements) {
2030
3145
  function appendBareParagraphText(state, text) {
2031
3146
  state.current.push({ element: "text", data: text });
2032
3147
  }
2033
- function appendBareParagraphLineBreakIfNeeded(state) {
3148
+ function appendBareParagraphLineBreakIfNeeded(state, token5) {
2034
3149
  if (state.current.length > 0) {
2035
- state.current.push({ element: "line-break" });
3150
+ state.current.push(createAutomaticLineBreak(token5));
2036
3151
  }
2037
3152
  }
2038
3153
  function flushBareParagraph(state) {
@@ -2137,7 +3252,7 @@ function parseBareListContent(ctx, startPos, listType) {
2137
3252
  if (consecutiveNewlines >= 2) {
2138
3253
  flushBareParagraph(paragraphState);
2139
3254
  } else {
2140
- appendBareParagraphLineBreakIfNeeded(paragraphState);
3255
+ appendBareParagraphLineBreakIfNeeded(paragraphState, token5);
2141
3256
  }
2142
3257
  continue;
2143
3258
  }
@@ -2149,6 +3264,7 @@ function parseBareListContent(ctx, startPos, listType) {
2149
3264
  for (const rule of getCandidateInlineRules(ctx.inlineRules, token5.type)) {
2150
3265
  const result = rule.parse(inlineCtx);
2151
3266
  if (result.success) {
3267
+ stripAutomaticLineBreak(paragraphState.current, result.stripLeadingLineBreak);
2152
3268
  appendBareParagraphElements(paragraphState, result.elements);
2153
3269
  consumed += result.consumed;
2154
3270
  pos += result.consumed;
@@ -2187,7 +3303,7 @@ function parseListItemBlockContent(ctx, pos, token5) {
2187
3303
  for (const rule of getCandidateBlockRules(filteredBlockRules, token5)) {
2188
3304
  const result = rule.parse(blockCtx);
2189
3305
  if (result.success) {
2190
- return { matched: true, elements: result.elements, consumed: result.consumed };
3306
+ return { matched: true, ...result };
2191
3307
  }
2192
3308
  }
2193
3309
  return { matched: false, elements: [], consumed: 0 };
@@ -2206,7 +3322,7 @@ function parseListItemInlineContent(ctx, pos, tokenType) {
2206
3322
  for (const rule of getCandidateInlineRules(ctx.inlineRules, tokenType)) {
2207
3323
  const result = rule.parse(inlineCtx);
2208
3324
  if (result.success) {
2209
- return { matched: true, elements: result.elements, consumed: result.consumed };
3325
+ return { matched: true, ...result };
2210
3326
  }
2211
3327
  }
2212
3328
  return { matched: false, elements: [], consumed: 0 };
@@ -2248,7 +3364,9 @@ function collectPostLiTrailingContent(ctx, startPos, listType) {
2248
3364
  }
2249
3365
  const inlineResult = parseListItemInlineContent(ctx, pos, token5.type);
2250
3366
  if (inlineResult.matched) {
2251
- elements.push(...inlineResult.elements);
3367
+ stripAutomaticLineBreak(elements, inlineResult.stripLeadingLineBreak);
3368
+ for (const element of inlineResult.elements)
3369
+ elements.push(element);
2252
3370
  consumed += inlineResult.consumed;
2253
3371
  pos += inlineResult.consumed;
2254
3372
  continue;
@@ -2293,7 +3411,7 @@ function collectLiItemContent(ctx, startPos, listType, parseNestedList) {
2293
3411
  if (token5.type === "NEWLINE") {
2294
3412
  const newlineResult = consumeLiItemNewlines(ctx, pos, elements.length > 0);
2295
3413
  if (newlineResult.addLineBreak) {
2296
- elements.push({ element: "line-break" });
3414
+ elements.push(createAutomaticLineBreak(token5));
2297
3415
  }
2298
3416
  pos += newlineResult.consumed;
2299
3417
  consumed += newlineResult.consumed;
@@ -2301,14 +3419,17 @@ function collectLiItemContent(ctx, startPos, listType, parseNestedList) {
2301
3419
  }
2302
3420
  const blockResult = parseListItemBlockContent(ctx, pos, token5);
2303
3421
  if (blockResult.matched) {
2304
- elements.push(...blockResult.elements);
3422
+ for (const element of blockResult.elements)
3423
+ elements.push(element);
2305
3424
  consumed += blockResult.consumed;
2306
3425
  pos += blockResult.consumed;
2307
3426
  continue;
2308
3427
  }
2309
3428
  const inlineResult = parseListItemInlineContent(ctx, pos, token5.type);
2310
3429
  if (inlineResult.matched) {
2311
- elements.push(...inlineResult.elements);
3430
+ stripAutomaticLineBreak(elements, inlineResult.stripLeadingLineBreak);
3431
+ for (const element of inlineResult.elements)
3432
+ elements.push(element);
2312
3433
  consumed += inlineResult.consumed;
2313
3434
  pos += inlineResult.consumed;
2314
3435
  continue;
@@ -2379,7 +3500,8 @@ function parseLiItem(ctx, startPos, listType, parseNestedList) {
2379
3500
  consumed += closeConsumed;
2380
3501
  pos += closeConsumed;
2381
3502
  const trailingResult = collectPostLiTrailingContent(ctx, pos, listType);
2382
- contentElements.push(...trailingResult.elements);
3503
+ for (const element of trailingResult.elements)
3504
+ contentElements.push(element);
2383
3505
  consumed += trailingResult.consumed;
2384
3506
  }
2385
3507
  return {
@@ -2639,7 +3761,7 @@ function sliceLineTokens(ctx, lines) {
2639
3761
  for (const { start, end } of lines) {
2640
3762
  for (let pos = start;pos < end; pos++) {
2641
3763
  const token5 = ctx.tokens[pos];
2642
- if (token5) {
3764
+ if (token5 && token5.type !== "BACKSLASH_BREAK") {
2643
3765
  tokens.push(token5);
2644
3766
  }
2645
3767
  }
@@ -2809,7 +3931,8 @@ function parseDefinitionItemKey(ctx, startPos) {
2809
3931
  const inlineCtx = { ...ctx, pos };
2810
3932
  const result = parseInlineUntil(inlineCtx, "COLON");
2811
3933
  if (result.elements.length > 0) {
2812
- key.push(...result.elements);
3934
+ for (const element of result.elements)
3935
+ key.push(element);
2813
3936
  for (let i = 0;i < result.consumed; i++) {
2814
3937
  const t = ctx.tokens[pos + i];
2815
3938
  if (t)
@@ -2865,7 +3988,8 @@ function parseDefinitionItemValue(ctx, startPos) {
2865
3988
  const inlineCtx = { ...ctx, pos };
2866
3989
  const result = parseInlineUntil(inlineCtx, "NEWLINE");
2867
3990
  if (result.elements.length > 0) {
2868
- value.push(...result.elements);
3991
+ for (const element of result.elements)
3992
+ value.push(element);
2869
3993
  pos += result.consumed;
2870
3994
  consumed += result.consumed;
2871
3995
  } else {
@@ -2945,80 +4069,95 @@ var definitionListRule = {
2945
4069
  consumed: result.consumed
2946
4070
  };
2947
4071
  }
2948
- };
2949
-
2950
- // packages/parser/src/parser/rules/block/paragraph/content.ts
2951
- function parseInlineContent(ctx) {
2952
- return parseInlineUntil(ctx, "PARAGRAPH_BREAK");
2953
- }
2954
-
2955
- // packages/parser/src/parser/rules/block/paragraph/span-markers.ts
2956
- function processCloseSpanMarkers(elements) {
2957
- let result = null;
4072
+ };
4073
+
4074
+ // packages/parser/src/parser/postprocess/divAdjacentParagraph.ts
4075
+ var unparsedDivStarts = new WeakSet;
4076
+ function markUnparsedDivStart(elements) {
4077
+ const first = elements[0];
4078
+ const text = first?.element === "container" && first.data.type === "paragraph" ? first.data.elements[0] : first;
4079
+ if (text?.element === "text")
4080
+ unparsedDivStarts.add(text);
4081
+ }
4082
+ function isUnparsedDivParagraph(el) {
4083
+ if (!el || el.element !== "container")
4084
+ return false;
4085
+ return el.data.type === "paragraph" && el.data.elements.some((child) => unparsedDivStarts.has(child));
4086
+ }
4087
+ function isDivContainer(el) {
4088
+ if (!el || el.element !== "container")
4089
+ return false;
4090
+ return el.data.type === "div";
4091
+ }
4092
+ function suppressAtLevel(elements) {
4093
+ if (elements.length <= 1)
4094
+ return elements;
4095
+ const unwrap = Array.from({ length: elements.length }, () => false);
4096
+ for (let i = 0;i < elements.length; i++) {
4097
+ if (!isUnparsedDivParagraph(elements[i]))
4098
+ continue;
4099
+ const prevIsDiv = i > 0 && isDivContainer(elements[i - 1]);
4100
+ const nextIsDiv = i < elements.length - 1 && isDivContainer(elements[i + 1]);
4101
+ if (prevIsDiv || nextIsDiv) {
4102
+ unwrap[i] = true;
4103
+ }
4104
+ }
4105
+ const result = [];
2958
4106
  for (let i = 0;i < elements.length; i++) {
2959
- const elem = elements[i];
2960
- if (!elem)
4107
+ const el = elements[i];
4108
+ if (!el)
2961
4109
  continue;
2962
- if (isCloseSpanMarker(elem)) {
2963
- if (result === null) {
2964
- result = elements.slice(0, i);
2965
- }
2966
- if (result.length > 0) {
2967
- const spanContent = [...result];
2968
- result.length = 0;
2969
- result.push({
2970
- element: "container",
2971
- data: {
2972
- type: "span",
2973
- attributes: {},
2974
- elements: spanContent
2975
- }
2976
- });
4110
+ if (unwrap[i] && el.element === "container") {
4111
+ const inner = el.data.elements;
4112
+ if (i > 0 && isDivContainer(elements[i - 1])) {
4113
+ result.push({ element: "line-break" });
2977
4114
  }
4115
+ result.push(...inner);
2978
4116
  } else {
2979
- result?.push(elem);
4117
+ result.push(el);
2980
4118
  }
2981
4119
  }
2982
- return result ?? elements;
4120
+ return result;
2983
4121
  }
2984
- function isCloseSpanMarker(elem) {
2985
- return elem.element === "container" && elem.data && typeof elem.data === "object" && "type" in elem.data && elem.data.type === "span" && "attributes" in elem.data && typeof elem.data.attributes === "object" && elem.data.attributes !== null && "_closeSpan" in elem.data.attributes;
4122
+ function suppressDivAdjacentParagraphs(elements) {
4123
+ return suppressAtLevel(elements);
2986
4124
  }
2987
4125
 
2988
- // packages/parser/src/parser/rules/block/paragraph/normalize.ts
2989
- function normalizeParagraphElements(source) {
2990
- let elements = processCloseSpanMarkers(source);
2991
- removeTrailingLineBreaks2(elements);
2992
- removeTrailingWhitespaceText(elements);
2993
- elements = removeLeadingLineBreaks(elements);
2994
- return elements;
4126
+ // packages/parser/src/parser/rules/block/div/close.ts
4127
+ function isDivClose(ctx) {
4128
+ const token5 = ctx.tokens[ctx.pos];
4129
+ if (token5?.type !== "BLOCK_END_OPEN")
4130
+ return false;
4131
+ const closeNameResult = parseBlockName(ctx, ctx.pos + 1);
4132
+ return closeNameResult?.name === "div";
2995
4133
  }
2996
- function removeTrailingLineBreaks2(elements) {
2997
- while (elements.length > 0 && elements[elements.length - 1]?.element === "line-break") {
2998
- const lastEl = elements[elements.length - 1];
2999
- if (lastEl._preservedTrailingBreak) {
3000
- delete lastEl._preservedTrailingBreak;
3001
- break;
3002
- }
3003
- elements.pop();
4134
+ function consumeDivClose(ctx, startPos) {
4135
+ let pos = startPos;
4136
+ let consumed = 0;
4137
+ if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
4138
+ return { pos, consumed };
3004
4139
  }
3005
- }
3006
- function removeTrailingWhitespaceText(elements) {
3007
- while (elements.length > 0) {
3008
- const last = elements[elements.length - 1];
3009
- if (last?.element === "text" && "data" in last && typeof last.data === "string" && last.data.trim() === "") {
3010
- elements.pop();
3011
- } else {
3012
- break;
3013
- }
4140
+ pos++;
4141
+ consumed++;
4142
+ const closeNameResult = parseBlockName(ctx, pos);
4143
+ if (closeNameResult) {
4144
+ pos += closeNameResult.consumed;
4145
+ consumed += closeNameResult.consumed;
3014
4146
  }
3015
- }
3016
- function removeLeadingLineBreaks(elements) {
3017
- let first = 0;
3018
- while (first < elements.length && elements[first]?.element === "line-break") {
3019
- first++;
4147
+ if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
4148
+ pos++;
4149
+ consumed++;
3020
4150
  }
3021
- return first > 0 ? elements.slice(first) : elements;
4151
+ if (ctx.tokens[pos]?.type === "NEWLINE") {
4152
+ pos++;
4153
+ consumed++;
4154
+ }
4155
+ return { pos, consumed };
4156
+ }
4157
+
4158
+ // packages/parser/src/parser/rules/block/paragraph/content.ts
4159
+ function parseInlineContent(ctx) {
4160
+ return parseInlineUntil(ctx, "PARAGRAPH_BREAK");
3022
4161
  }
3023
4162
 
3024
4163
  // packages/parser/src/parser/rules/block/paragraph/index.ts
@@ -3028,25 +4167,30 @@ var paragraphRule = {
3028
4167
  requiresLineStart: false,
3029
4168
  parse(ctx) {
3030
4169
  const result = parseInlineContent(ctx);
3031
- if (result.elements.length === 0) {
4170
+ if (result.consumed === 0) {
3032
4171
  return { success: false };
3033
4172
  }
3034
4173
  const elements = normalizeParagraphElements(result.elements);
3035
4174
  if (elements.length === 0) {
3036
- return { success: false };
4175
+ return { success: true, elements: [], consumed: result.consumed };
3037
4176
  }
3038
4177
  const nextPos = ctx.pos + result.consumed;
3039
4178
  const nextToken = ctx.tokens[nextPos];
3040
4179
  if (nextToken?.type === "COLON" && nextToken.lineStart) {
4180
+ if (isPreservedLeadingLineBreak(elements[0]))
4181
+ elements[0] = { element: "line-break" };
3041
4182
  return {
3042
4183
  success: true,
3043
4184
  elements: [...elements, { element: "line-break" }],
3044
4185
  consumed: result.consumed
3045
4186
  };
3046
4187
  }
4188
+ const wrapped = wrapParagraphElements(elements);
4189
+ if (isDivClose(ctx))
4190
+ markUnparsedDivStart(wrapped);
3047
4191
  return {
3048
4192
  success: true,
3049
- elements: wrapParagraphElements(elements),
4193
+ elements: wrapped,
3050
4194
  consumed: result.consumed
3051
4195
  };
3052
4196
  }
@@ -3057,7 +4201,7 @@ function wrapParagraphElements(elements) {
3057
4201
  let bare = false;
3058
4202
  const flush = (trimBreaks = false) => {
3059
4203
  const content = trimBreaks ? normalizeParagraphElements(group) : group;
3060
- while (content[0]?.element === "line-break")
4204
+ while (content[0]?.element === "line-break" && !isPreservedLeadingLineBreak(content[0]))
3061
4205
  content.shift();
3062
4206
  while (content.length) {
3063
4207
  const last = content.at(-1);
@@ -3069,6 +4213,8 @@ function wrapParagraphElements(elements) {
3069
4213
  content.shift();
3070
4214
  if (content[0]?.element === "text")
3071
4215
  content[0] = { element: "text", data: content[0].data.trimStart() };
4216
+ if (isPreservedLeadingLineBreak(content[0]))
4217
+ content[0] = { element: "line-break" };
3072
4218
  if (content.length)
3073
4219
  output.push(...bare || content.some((el) => el.element === "image") ? content : [
3074
4220
  {
@@ -3090,37 +4236,36 @@ function wrapParagraphElements(elements) {
3090
4236
  return output;
3091
4237
  }
3092
4238
 
3093
- // packages/parser/src/parser/rules/block/div/close.ts
3094
- function isDivClose(ctx) {
3095
- const token5 = ctx.tokens[ctx.pos];
3096
- if (token5?.type !== "BLOCK_END_OPEN")
3097
- return false;
3098
- const closeNameResult = parseBlockName(ctx, ctx.pos + 1);
3099
- return closeNameResult?.name === "div";
3100
- }
3101
- function consumeDivClose(ctx, startPos) {
3102
- let pos = startPos;
3103
- let consumed = 0;
3104
- if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
3105
- return { pos, consumed };
3106
- }
3107
- pos++;
3108
- consumed++;
3109
- const closeNameResult = parseBlockName(ctx, pos);
3110
- if (closeNameResult) {
3111
- pos += closeNameResult.consumed;
3112
- consumed += closeNameResult.consumed;
3113
- }
3114
- if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
3115
- pos++;
3116
- consumed++;
3117
- }
3118
- if (ctx.tokens[pos]?.type === "NEWLINE") {
3119
- pos++;
3120
- consumed++;
4239
+ // packages/parser/src/parser/rules/block/note/index.ts
4240
+ var excludedBlockNames = new Set(["note"]);
4241
+ var noteRule = {
4242
+ name: "note",
4243
+ startTokens: ["BLOCK_OPEN"],
4244
+ requiresLineStart: false,
4245
+ parse(ctx) {
4246
+ const bounds = findNoteBounds(ctx);
4247
+ if (!bounds)
4248
+ return { success: false };
4249
+ const body = parseBlocksUntil({
4250
+ ...ctx,
4251
+ tokens: ctx.tokens.slice(bounds.bodyStart, bounds.close),
4252
+ pos: 0,
4253
+ scope: {
4254
+ ...ctx.scope,
4255
+ inlineEnd: undefined,
4256
+ tableFormatting: undefined,
4257
+ blockCloseCondition: undefined
4258
+ }
4259
+ }, () => false, { excludedBlockNames });
4260
+ return {
4261
+ success: true,
4262
+ consumed: bounds.end - ctx.pos,
4263
+ elements: [
4264
+ { element: "container", data: { type: "note", attributes: {}, elements: body.elements } }
4265
+ ]
4266
+ };
3121
4267
  }
3122
- return { pos, consumed };
3123
- }
4268
+ };
3124
4269
 
3125
4270
  // packages/parser/src/parser/rules/block/div/nesting.ts
3126
4271
  var divCloseCountCache = new WeakMap;
@@ -3289,6 +4434,7 @@ function consumeFailedDiv(ctx) {
3289
4434
  pos++;
3290
4435
  consumed++;
3291
4436
  }
4437
+ markUnparsedDivStart(elements);
3292
4438
  return {
3293
4439
  success: true,
3294
4440
  elements: [
@@ -3331,135 +4477,22 @@ function parseDivOpen(ctx) {
3331
4477
  pos++;
3332
4478
  consumed++;
3333
4479
  }
3334
- return {
3335
- blockName: nameResult.name,
3336
- paragraphStrip: nameResult.name === "div_",
3337
- attributes: attrResult.attrs,
3338
- bodyStart: pos,
3339
- consumed,
3340
- hasRequiredNewline
3341
- };
3342
- }
3343
- function isDivBlockName(name) {
3344
- return name === "div" || name === "div_";
3345
- }
3346
-
3347
- // packages/parser/src/parser/rules/block/div/index.ts
3348
- var divRule = {
3349
- name: "div",
3350
- startTokens: ["BLOCK_OPEN"],
3351
- requiresLineStart: false,
3352
- parse(ctx) {
3353
- const openToken = currentToken(ctx);
3354
- if (openToken.type !== "BLOCK_OPEN") {
3355
- return { success: false };
3356
- }
3357
- const openResult = parseDivOpen(ctx);
3358
- if (!openResult) {
3359
- return { success: false };
3360
- }
3361
- if (!openResult.hasRequiredNewline) {
3362
- return consumeFailedDiv(ctx);
3363
- }
3364
- if (ctx.scope.divClosesBudget === 0) {
3365
- return { success: false };
3366
- }
3367
- const openPosition = openToken.position;
3368
- let pos = openResult.bodyStart;
3369
- let consumed = openResult.consumed;
3370
- const bodyResult = parseDivBody(ctx, pos, openResult.paragraphStrip);
3371
- consumed += bodyResult.consumed;
3372
- pos += bodyResult.consumed;
3373
- if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
3374
- ctx.diagnostics.push({
3375
- severity: "warning",
3376
- code: "unclosed-block",
3377
- message: `Missing closing tag [[/div]] for [[${openResult.blockName}]]`,
3378
- position: openPosition
3379
- });
3380
- }
3381
- if (ctx.tokens[pos]?.type === "BLOCK_END_OPEN") {
3382
- const closeResult = consumeDivClose(ctx, pos);
3383
- pos = closeResult.pos;
3384
- consumed += closeResult.consumed;
3385
- }
3386
- return {
3387
- success: true,
3388
- elements: [
3389
- {
3390
- element: "container",
3391
- data: {
3392
- type: "div",
3393
- attributes: openResult.attributes,
3394
- elements: bodyResult.elements
3395
- }
3396
- }
3397
- ],
3398
- consumed
3399
- };
3400
- }
3401
- };
3402
-
3403
- // packages/parser/src/parser/rules/block/code/attributes.ts
3404
- function repairSwallowedCodeClose(ctx, pos, attrs) {
3405
- const prevToken = ctx.tokens[pos - 1];
3406
- if (prevToken?.type !== "QUOTED_STRING" || !prevToken.value.includes("]]")) {
3407
- return null;
3408
- }
3409
- const rawValue = prevToken.value;
3410
- const bracketIdx = rawValue.indexOf("]]");
3411
- const truncatedValue = rawValue.startsWith('"') ? rawValue.slice(1, bracketIdx) : rawValue.slice(0, bracketIdx);
3412
- for (const key of Object.keys(attrs)) {
3413
- const stored = attrs[key];
3414
- if (stored === rawValue || stored === rawValue.slice(1, -1) || stored === rawValue.slice(1)) {
3415
- attrs[key] = truncatedValue;
3416
- break;
3417
- }
3418
- }
3419
- return {
3420
- closingSwallowed: rawValue.includes("[[/code]]")
3421
- };
3422
- }
3423
-
3424
- // packages/parser/src/parser/rules/block/code/content.ts
3425
- function collectCodeContent(ctx, startPos, closingSwallowed) {
3426
- const contentParts = [];
3427
- let pos = startPos;
3428
- let consumed = 0;
3429
- let foundClose = closingSwallowed;
3430
- while (!closingSwallowed && pos < ctx.tokens.length) {
3431
- const token5 = ctx.tokens[pos];
3432
- if (!token5 || token5.type === "EOF") {
3433
- break;
3434
- }
3435
- if (token5.type === "BLOCK_END_OPEN") {
3436
- const closeNameResult = parseBlockName(ctx, pos + 1);
3437
- if (closeNameResult?.name === "code") {
3438
- foundClose = true;
3439
- const closeConsumed = consumeCodeClose(ctx, pos, closeNameResult.consumed);
3440
- consumed += closeConsumed;
3441
- break;
3442
- }
3443
- }
3444
- contentParts.push(token5.value);
3445
- pos++;
3446
- consumed++;
3447
- }
3448
- return { contents: contentParts.join(""), consumed, foundClose };
4480
+ return {
4481
+ blockName: nameResult.name,
4482
+ paragraphStrip: nameResult.name === "div_",
4483
+ attributes: attrResult.attrs,
4484
+ bodyStart: pos,
4485
+ consumed,
4486
+ hasRequiredNewline
4487
+ };
3449
4488
  }
3450
- function consumeCodeClose(ctx, startPos, closeNameConsumed) {
3451
- let pos = startPos + 1 + closeNameConsumed;
3452
- let consumed = 1 + closeNameConsumed;
3453
- if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
3454
- pos++;
3455
- consumed++;
3456
- }
3457
- return consumed;
4489
+ function isDivBlockName(name) {
4490
+ return name === "div" || name === "div_";
3458
4491
  }
3459
4492
 
3460
- // packages/parser/src/parser/rules/block/code/index.ts
3461
- var codeBlockRule = {
3462
- name: "code",
4493
+ // packages/parser/src/parser/rules/block/div/index.ts
4494
+ var divRule = {
4495
+ name: "div",
3463
4496
  startTokens: ["BLOCK_OPEN"],
3464
4497
  requiresLineStart: false,
3465
4498
  parse(ctx) {
@@ -3467,56 +4500,49 @@ var codeBlockRule = {
3467
4500
  if (openToken.type !== "BLOCK_OPEN") {
3468
4501
  return { success: false };
3469
4502
  }
3470
- let pos = ctx.pos + 1;
3471
- let consumed = 1;
3472
- const nameResult = parseBlockName(ctx, pos);
3473
- if (!nameResult || nameResult.name !== "code") {
4503
+ const openResult = parseDivOpen(ctx);
4504
+ if (!openResult) {
3474
4505
  return { success: false };
3475
4506
  }
3476
- pos += nameResult.consumed;
3477
- consumed += nameResult.consumed;
3478
- const attrResult = parseAttributesRaw(ctx, pos);
3479
- pos += attrResult.consumed;
3480
- consumed += attrResult.consumed;
3481
- let closingSwallowed = false;
3482
- if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
3483
- const repaired = repairSwallowedCodeClose(ctx, pos, attrResult.attrs);
3484
- if (!repaired)
3485
- return { success: false };
3486
- closingSwallowed = repaired.closingSwallowed;
3487
- } else {
3488
- pos++;
3489
- consumed++;
4507
+ if (!openResult.hasRequiredNewline) {
4508
+ return consumeFailedDiv(ctx);
3490
4509
  }
3491
- if (ctx.tokens[pos]?.type === "NEWLINE") {
3492
- pos++;
3493
- consumed++;
4510
+ if (ctx.scope.divClosesBudget === 0) {
4511
+ return { success: false };
3494
4512
  }
3495
- const contentResult = collectCodeContent(ctx, pos, closingSwallowed);
3496
- let codeContent = contentResult.contents.replace(/\n$/, "");
3497
- consumed += contentResult.consumed;
3498
- pos += contentResult.consumed;
3499
- if (!contentResult.foundClose) {
4513
+ const openPosition = openToken.position;
4514
+ let pos = openResult.bodyStart;
4515
+ let consumed = openResult.consumed;
4516
+ const bodyResult = parseDivBody(ctx, pos, openResult.paragraphStrip);
4517
+ consumed += bodyResult.consumed;
4518
+ pos += bodyResult.consumed;
4519
+ if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
3500
4520
  ctx.diagnostics.push({
3501
4521
  severity: "warning",
3502
4522
  code: "unclosed-block",
3503
- message: "Missing closing tag [[/code]] for [[code]]",
3504
- position: openToken.position
4523
+ message: `Missing closing tag [[/div]] for [[${openResult.blockName}]]`,
4524
+ position: openPosition
3505
4525
  });
3506
4526
  }
3507
- const codeBlockData = {
3508
- contents: codeContent,
3509
- language: attrResult.attrs.type ?? null,
3510
- name: attrResult.attrs.name ?? null
3511
- };
3512
- ctx.codeBlocks.push(codeBlockData);
3513
- const elements = [{ element: "code", data: codeBlockData }];
3514
- if (ctx.tokens[pos]?.type === "NEWLINE" && !getParagraphNewlineBoundary(ctx, pos, false).shouldBreak && !ctx.scope.blockCloseCondition?.({ ...ctx, pos: pos + 1 })) {
3515
- const after = parseInlineUntil({ ...ctx, pos: pos + 1 }, "PARAGRAPH_BREAK");
3516
- elements.push({ element: "line-break" }, ...normalizeParagraphElements(after.elements));
3517
- consumed += 1 + after.consumed;
4527
+ if (ctx.tokens[pos]?.type === "BLOCK_END_OPEN") {
4528
+ const closeResult = consumeDivClose(ctx, pos);
4529
+ pos = closeResult.pos;
4530
+ consumed += closeResult.consumed;
3518
4531
  }
3519
- return { success: true, elements, consumed };
4532
+ return {
4533
+ success: true,
4534
+ elements: [
4535
+ {
4536
+ element: "container",
4537
+ data: {
4538
+ type: "div",
4539
+ attributes: openResult.attributes,
4540
+ elements: bodyResult.elements
4541
+ }
4542
+ }
4543
+ ],
4544
+ consumed
4545
+ };
3520
4546
  }
3521
4547
  };
3522
4548
 
@@ -3948,7 +4974,9 @@ function parseTableCell(ctx, startPos, cellStart) {
3948
4974
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
3949
4975
  const result = rule.parse(inlineCtx);
3950
4976
  if (result.success) {
3951
- children.push(...result.elements);
4977
+ stripAutomaticLineBreak(children, result.stripLeadingLineBreak);
4978
+ for (const element of result.elements)
4979
+ children.push(element);
3952
4980
  consumed += result.consumed;
3953
4981
  pos += result.consumed;
3954
4982
  matched = true;
@@ -4215,7 +5243,8 @@ function createCellContentAccumulator() {
4215
5243
  addInline(element) {
4216
5244
  currentSegment.push(element);
4217
5245
  },
4218
- addInlineElements(nextElements) {
5246
+ addInlineElements(nextElements, stripLeadingLineBreak) {
5247
+ stripAutomaticLineBreak(currentSegment, stripLeadingLineBreak);
4219
5248
  currentSegment.push(...nextElements);
4220
5249
  },
4221
5250
  addBlockElements(blockElements) {
@@ -4315,7 +5344,7 @@ function consumeCellContentNewline(ctx, startPos, content) {
4315
5344
  return { consumed };
4316
5345
  }
4317
5346
  if (!content.isEmpty()) {
4318
- content.addInline({ element: "line-break" });
5347
+ content.addInline(createAutomaticLineBreak(ctx.tokens[startPos]));
4319
5348
  }
4320
5349
  return { consumed };
4321
5350
  }
@@ -4366,7 +5395,7 @@ function parseCellContent(ctx, closeCondition) {
4366
5395
  for (const rule of getCandidateInlineRules(ctx.inlineRules, token5.type)) {
4367
5396
  const result = rule.parse(inlineCtx);
4368
5397
  if (result.success) {
4369
- content.addInlineElements(result.elements);
5398
+ content.addInlineElements(result.elements, result.stripLeadingLineBreak);
4370
5399
  consumed += result.consumed;
4371
5400
  pos += result.consumed;
4372
5401
  matched = true;
@@ -6186,9 +7215,9 @@ function renderListPagesItems(module, data, compiledTemplate, parse) {
6186
7215
  continue;
6187
7216
  const ctx = {
6188
7217
  page,
6189
- index: i + 1,
7218
+ index: (data.pagination?.offset ?? 0) + i + 1,
6190
7219
  total: data.totalCount,
6191
- limit: module.limit,
7220
+ limit: data.pagination ? data.pagination.limit : module.limit,
6192
7221
  site: data.site
6193
7222
  };
6194
7223
  const itemAst = getModuleParseAst(parse(compiledTemplate(ctx)));
@@ -6208,9 +7237,46 @@ function renderListPagesItems(module, data, compiledTemplate, parse) {
6208
7237
  return items;
6209
7238
  }
6210
7239
 
7240
+ // packages/parser/src/parser/rules/block/module/listpages/resolution/pager.ts
7241
+ function createListPagesPager(data) {
7242
+ const pagination = data.pagination;
7243
+ if (!pagination || pagination.totalPages <= 1)
7244
+ return [];
7245
+ const { currentPage, totalPages, urlPath, parameter } = pagination;
7246
+ const pages = new Set([1, totalPages]);
7247
+ for (let page = Math.max(1, currentPage - 2);page <= Math.min(totalPages, currentPage + 2); page++) {
7248
+ pages.add(page);
7249
+ }
7250
+ return [
7251
+ {
7252
+ element: "pager",
7253
+ data: {
7254
+ currentPage,
7255
+ totalPages,
7256
+ pages: [...pages].sort((a, b) => a - b).map((page) => ({
7257
+ page,
7258
+ href: pageUrl(urlPath, parameter, page)
7259
+ }))
7260
+ }
7261
+ }
7262
+ ];
7263
+ }
7264
+ function pageUrl(urlPath, parameter, page) {
7265
+ const suffixStart = urlPath.search(/[?#]/);
7266
+ const path = suffixStart === -1 ? urlPath : urlPath.slice(0, suffixStart);
7267
+ const suffix = suffixStart === -1 ? "" : urlPath.slice(suffixStart);
7268
+ const parts = path.split("/");
7269
+ const result = parts.slice(0, 2);
7270
+ for (let index = 2;index < parts.length; index += 2) {
7271
+ if (parts[index] !== parameter)
7272
+ result.push(...parts.slice(index, index + 2));
7273
+ }
7274
+ return `${result.join("/").replace(/\/$/, "")}/${parameter}/${page}${suffix}`;
7275
+ }
7276
+
6211
7277
  // packages/parser/src/parser/rules/block/module/listpages/resolution/wrapper.ts
6212
- function wrapListPagesResult(module, items, parse) {
6213
- if (items.length === 0) {
7278
+ function wrapListPagesResult(module, items, parse, data) {
7279
+ if (items.length === 0 && (!data || data.pages.length === 0)) {
6214
7280
  return [];
6215
7281
  }
6216
7282
  const result = [];
@@ -6223,6 +7289,8 @@ function wrapListPagesResult(module, items, parse) {
6223
7289
  const appendAst = getModuleParseAst(parse(module["append-line"]));
6224
7290
  result.push(...appendAst.elements);
6225
7291
  }
7292
+ if (data)
7293
+ result.push(...createListPagesPager(data));
6226
7294
  if (module.wrapper) {
6227
7295
  return [
6228
7296
  {
@@ -6244,7 +7312,7 @@ function isListPagesModule2(module) {
6244
7312
  }
6245
7313
  function resolveListPages(module, data, compiledTemplate, parse) {
6246
7314
  const items = renderListPagesItems(module, data, compiledTemplate, parse);
6247
- return wrapListPagesResult(module, items, parse);
7315
+ return wrapListPagesResult(module, items, parse, data);
6248
7316
  }
6249
7317
  // packages/parser/src/parser/rules/block/module/listpages/types/external-data.ts
6250
7318
  function definePageData(input) {
@@ -6341,10 +7409,10 @@ var URL_RESOLVABLE_FIELDS = [
6341
7409
  ];
6342
7410
 
6343
7411
  // packages/parser/src/parser/rules/block/module/listpages/url-resolution/params.ts
6344
- function parseUrlParams(url) {
7412
+ function parseUrlParams(url, hasPageName = false) {
6345
7413
  const params = new Map;
6346
- const parts = url.split("/").filter(Boolean);
6347
- const pairStart = isUrlParameter(parts[0]) ? 0 : 1;
7414
+ const parts = url.split(/[?#]/, 1)[0].split("/").filter(Boolean);
7415
+ const pairStart = !hasPageName && isUrlParameter(parts[0]) ? 0 : 1;
6348
7416
  for (let i = pairStart;i < parts.length - 1; i += 2) {
6349
7417
  const key = parts[i];
6350
7418
  const value = parts[i + 1];
@@ -6747,7 +7815,7 @@ function resolveIfTags(data, pageTags) {
6747
7815
  // packages/parser/src/parser/preprocess/utils/raw-regions.ts
6748
7816
  var BASE_PLACEHOLDER_OPEN = "";
6749
7817
  var BASE_PLACEHOLDER_CLOSE = "";
6750
- var RAW_BLOCK_OPEN_PATTERN = /\[\[\s*(code|html)\b[^\]]*\]\]/iy;
7818
+ var RAW_BLOCK_OPEN_PATTERN = /\[\[html\b[^\]]*\]\]/iy;
6751
7819
  function makeUniqueSentinels(source) {
6752
7820
  let openRun = 0;
6753
7821
  let closeRun = 0;
@@ -6766,10 +7834,21 @@ function makeUniqueSentinels(source) {
6766
7834
  }
6767
7835
  function maskRawRegions(source, sentinels) {
6768
7836
  const placeholders = [];
7837
+ let tokens;
7838
+ const getTokens = () => tokens ??= tokenize(source);
6769
7839
  let masked = "";
6770
7840
  let i = 0;
6771
7841
  while (i < source.length) {
6772
- const rawBlock = tryMaskRawBlock(source, i, placeholders, sentinels);
7842
+ if (source.startsWith("[!--", i)) {
7843
+ const close = source.indexOf("--]", i + 4);
7844
+ if (close !== -1) {
7845
+ const end = close + 3;
7846
+ masked += source.slice(i, end);
7847
+ i = end;
7848
+ continue;
7849
+ }
7850
+ }
7851
+ const rawBlock = tryMaskRawBlock(source, i, placeholders, sentinels, getTokens);
6773
7852
  if (rawBlock) {
6774
7853
  masked += rawBlock.placeholder;
6775
7854
  i = rawBlock.end;
@@ -6790,16 +7869,25 @@ function restorePlaceholders(source, placeholders, sentinels) {
6790
7869
  const pattern = new RegExp(`${escapeRegex(sentinels.open)}(\\d+)${escapeRegex(sentinels.close)}`, "g");
6791
7870
  return source.replace(pattern, (_, idx) => placeholders[Number(idx)] ?? "");
6792
7871
  }
6793
- function tryMaskRawBlock(source, pos, placeholders, sentinels) {
7872
+ function tryMaskRawBlock(source, pos, placeholders, sentinels, getTokens) {
6794
7873
  if (source[pos] !== "[" || source[pos + 1] !== "[")
6795
7874
  return null;
7875
+ if (source.slice(pos, pos + 6).toLowerCase() === "[[code") {
7876
+ const tokens = getTokens();
7877
+ const start = tokenAtOffset(tokens, pos);
7878
+ const open = findCodeOpen(tokens, start);
7879
+ if (!open)
7880
+ return null;
7881
+ const bounds = open.closingSwallowed ? null : findCodeBodyBounds(tokens, open.bodyStart);
7882
+ const end = open.closingSwallowed ? tokens[open.attributesEnd - 1].position.end.offset : bounds.foundClose ? tokens[bounds.end - 1].position.end.offset : source.length;
7883
+ return { placeholder: pushPlaceholder(placeholders, source.slice(pos, end), sentinels), end };
7884
+ }
6796
7885
  RAW_BLOCK_OPEN_PATTERN.lastIndex = pos;
6797
7886
  const openMatch = RAW_BLOCK_OPEN_PATTERN.exec(source);
6798
7887
  if (!openMatch)
6799
7888
  return null;
6800
- const name = openMatch[1].toLowerCase();
6801
7889
  const openLen = openMatch[0].length;
6802
- const closePattern = new RegExp(`\\[\\[\\/\\s*${name}\\s*\\]\\]`, "ig");
7890
+ const closePattern = /\[\[\/\s*html\s*\]\]/gi;
6803
7891
  closePattern.lastIndex = pos + openLen;
6804
7892
  const closeMatch = closePattern.exec(source);
6805
7893
  if (closeMatch) {
@@ -6809,12 +7897,19 @@ function tryMaskRawBlock(source, pos, placeholders, sentinels) {
6809
7897
  end
6810
7898
  };
6811
7899
  }
6812
- if (name !== "code")
6813
- return null;
6814
- return {
6815
- placeholder: pushPlaceholder(placeholders, source.slice(pos), sentinels),
6816
- end: source.length
6817
- };
7900
+ return null;
7901
+ }
7902
+ function tokenAtOffset(tokens, offset) {
7903
+ let low = 0;
7904
+ let high = tokens.length;
7905
+ while (low < high) {
7906
+ const middle = Math.floor((low + high) / 2);
7907
+ if (tokens[middle].position.start.offset < offset)
7908
+ low = middle + 1;
7909
+ else
7910
+ high = middle;
7911
+ }
7912
+ return tokens[low]?.position.start.offset === offset ? low : tokens.length;
6818
7913
  }
6819
7914
  function tryMaskRawInline(source, pos, placeholders, sentinels) {
6820
7915
  if (source[pos] === "@" && source[pos + 1] === "<") {
@@ -6826,17 +7921,22 @@ function tryMaskRawInline(source, pos, placeholders, sentinels) {
6826
7921
  return null;
6827
7922
  }
6828
7923
  function tryMaskSingleLineRaw(source, pos, openerLength, close, placeholders, sentinels) {
6829
- const closePos = source.indexOf(close, pos + openerLength);
6830
- const newline = source.indexOf(`
6831
- `, pos + openerLength);
6832
- if (closePos === -1 || newline !== -1 && newline < closePos)
7924
+ const end = singleLineRawEnd(source, pos, openerLength, close);
7925
+ if (end === pos)
6833
7926
  return null;
6834
- const end = closePos + close.length;
6835
7927
  return {
6836
7928
  placeholder: pushPlaceholder(placeholders, source.slice(pos, end), sentinels),
6837
7929
  end
6838
7930
  };
6839
7931
  }
7932
+ function singleLineRawEnd(source, pos, openerLength, close) {
7933
+ const closePos = source.indexOf(close, pos + openerLength);
7934
+ const newline = source.indexOf(`
7935
+ `, pos + openerLength);
7936
+ if (closePos === -1 || newline !== -1 && newline < closePos)
7937
+ return pos;
7938
+ return closePos + close.length;
7939
+ }
6840
7940
  function pushPlaceholder(placeholders, text, sentinels) {
6841
7941
  const idx = placeholders.length;
6842
7942
  placeholders.push(text);
@@ -7442,18 +8542,66 @@ function resolveListUsers(_module, data, compiledTemplate, parse) {
7442
8542
  return itemAst.elements;
7443
8543
  }
7444
8544
  // packages/parser/src/parser/rules/block/module/resolution/data-maps.ts
7445
- async function buildListPagesDataMap(dataProvider, requirements, urlPath) {
8545
+ async function buildListPagesDataMap(dataProvider, requirements, urlPath, paginationState = { nextUnprefixed: 1 }) {
7446
8546
  const dataMap = new Map;
7447
- const urlParams = parseUrlParams(urlPath ?? "");
8547
+ const urlParams = parseUrlParams(urlPath ?? "", true);
7448
8548
  for (const req of requirements) {
7449
8549
  const normalizedQuery = resolveAndNormalizeQuery(req, urlParams);
7450
- const data = await dataProvider.fetchListPages?.(normalizedQuery, req);
8550
+ if (!urlPath || !urlPath.startsWith("/") || urlPath.startsWith("//")) {
8551
+ const data2 = await dataProvider.fetchListPages?.(normalizedQuery, req);
8552
+ if (data2)
8553
+ dataMap.set(req.id, data2);
8554
+ continue;
8555
+ }
8556
+ const ordinal = req.urlAttrPrefix ? 0 : paginationState.nextUnprefixed++;
8557
+ const parameter = req.urlAttrPrefix ? `${encodeURIComponent(req.urlAttrPrefix)}_p` : ordinal === 1 ? "p" : `p${ordinal}`;
8558
+ const perPage = Math.min(250, positiveInteger(normalizedQuery.perPage, 20));
8559
+ const baseOffset = positiveInteger(normalizedQuery.offset, 0);
8560
+ const limit = normalizedQuery.limit !== undefined && normalizedQuery.limit >= 0 ? Math.floor(normalizedQuery.limit) : undefined;
8561
+ const rawPage = urlParams.get(parameter) ?? "1";
8562
+ let currentPage = /^\d+$/.test(rawPage) ? positiveInteger(Number(rawPage), 1) : 1;
8563
+ currentPage = Math.min(currentPage, Math.floor((Number.MAX_SAFE_INTEGER - baseOffset) / perPage) + 1);
8564
+ if (limit !== undefined)
8565
+ currentPage = Math.min(currentPage, Math.max(1, Math.ceil(limit / perPage)));
8566
+ const fetchPage = () => {
8567
+ const relativeOffset = (currentPage - 1) * perPage;
8568
+ return dataProvider.fetchListPages?.({
8569
+ ...normalizedQuery,
8570
+ offset: baseOffset + relativeOffset,
8571
+ limit: Math.min(perPage, limit === undefined ? perPage : Math.max(0, limit - relativeOffset)),
8572
+ perPage
8573
+ }, req);
8574
+ };
8575
+ let data = await fetchPage();
7451
8576
  if (data) {
7452
- dataMap.set(req.id, data);
8577
+ const count = Math.min(Math.max(0, data.totalCount - baseOffset), limit ?? Infinity);
8578
+ const totalPages = Math.ceil(count / perPage);
8579
+ const lastPage = Math.max(1, totalPages);
8580
+ if (currentPage > lastPage) {
8581
+ currentPage = lastPage;
8582
+ data = await fetchPage();
8583
+ }
8584
+ if (data)
8585
+ dataMap.set(req.id, {
8586
+ ...data,
8587
+ pages: data.pages.slice(0, Math.min(perPage, Math.max(0, count - (currentPage - 1) * perPage))),
8588
+ pagination: {
8589
+ currentPage,
8590
+ perPage,
8591
+ totalPages,
8592
+ offset: baseOffset + (currentPage - 1) * perPage,
8593
+ limit,
8594
+ urlPath,
8595
+ parameter
8596
+ }
8597
+ });
7453
8598
  }
7454
8599
  }
7455
8600
  return dataMap;
7456
8601
  }
8602
+ function positiveInteger(value, fallback) {
8603
+ return value !== undefined && Number.isSafeInteger(value) && value > 0 ? value : fallback;
8604
+ }
7457
8605
  async function buildListUsersDataMap(dataProvider, requirements) {
7458
8606
  const dataMap = new Map;
7459
8607
  for (const req of requirements) {
@@ -8958,167 +10106,32 @@ function parseVariables(tokens) {
8958
10106
  for (const token5 of tokens) {
8959
10107
  if (token5 === "|") {
8960
10108
  assignVariable(vars, current2);
8961
- current2 = "";
8962
- } else {
8963
- current2 += token5;
8964
- }
8965
- }
8966
- assignVariable(vars, current2);
8967
- return vars;
8968
- }
8969
- function assignVariable(vars, source) {
8970
- if (!source.trim()) {
8971
- return;
8972
- }
8973
- const eqIndex = source.indexOf("=");
8974
- if (eqIndex === -1) {
8975
- return;
8976
- }
8977
- const key = source.slice(0, eqIndex).trim();
8978
- if (!key) {
8979
- return;
8980
- }
8981
- vars[key] = source.slice(eqIndex + 1).trim();
8982
- }
8983
-
8984
- // packages/parser/src/parser/rules/block/include/index.ts
8985
- var includeRule = {
8986
- name: "include",
8987
- startTokens: ["BLOCK_OPEN"],
8988
- requiresLineStart: false,
8989
- parse(ctx) {
8990
- const openToken = currentToken(ctx);
8991
- if (openToken.type !== "BLOCK_OPEN") {
8992
- return { success: false };
8993
- }
8994
- let pos = ctx.pos + 1;
8995
- let consumed = 1;
8996
- const nameResult = parseBlockName(ctx, pos);
8997
- if (!nameResult || nameResult.name.toLowerCase() !== "include") {
8998
- return { success: false };
8999
- }
9000
- if (!ctx.settings.enablePageSyntax) {
9001
- return { success: false };
9002
- }
9003
- pos += nameResult.consumed;
9004
- consumed += nameResult.consumed;
9005
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
9006
- pos++;
9007
- consumed++;
9008
- }
9009
- const args = collectIncludeArguments(ctx, pos);
9010
- pos += args.consumed;
9011
- consumed += args.consumed;
9012
- if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
9013
- return { success: false };
9014
- }
9015
- pos++;
9016
- consumed++;
9017
- const directiveEnd = pos;
9018
- if (ctx.tokens[pos]?.type === "NEWLINE") {
9019
- pos++;
9020
- consumed++;
9021
- }
9022
- if (!args.target) {
9023
- return { success: false };
9024
- }
9025
- const location = parsePageRef(args.target);
9026
- if (ctx.deferInclude?.(location)) {
9027
- const source = ctx.tokens.slice(ctx.pos, directiveEnd).map((token5) => token5.value).join("");
9028
- const elements = [];
9029
- const lines = source.split(`
9030
- `);
9031
- for (let index = 0;index < lines.length; index++) {
9032
- if (index > 0)
9033
- elements.push(lineBreak());
9034
- if (lines[index] !== "")
9035
- elements.push(text(lines[index]));
9036
- }
9037
- return { success: true, elements: [paragraph(elements)], consumed };
9038
- }
9039
- return {
9040
- success: true,
9041
- elements: [
9042
- {
9043
- element: "include",
9044
- data: {
9045
- "paragraph-safe": false,
9046
- variables: parseVariables(args.argumentTokens),
9047
- location,
9048
- elements: []
9049
- }
9050
- }
9051
- ],
9052
- consumed
9053
- };
9054
- }
9055
- };
9056
-
9057
- // packages/parser/src/parser/rules/block/math/content.ts
9058
- function collectMathContent(ctx, startPos) {
9059
- let latexSource = "";
9060
- let pos = startPos;
9061
- let consumed = 0;
9062
- let foundClose = false;
9063
- while (pos < ctx.tokens.length) {
9064
- const token5 = ctx.tokens[pos];
9065
- if (!token5)
9066
- break;
9067
- if (token5.type === "BLOCK_END_OPEN") {
9068
- const closeNameResult = parseBlockName(ctx, pos + 1);
9069
- if (closeNameResult?.name === "math") {
9070
- foundClose = true;
9071
- break;
9072
- }
9073
- }
9074
- latexSource += token5.type === "BACKSLASH_BREAK" ? "\\\n" : token5.value;
9075
- pos++;
9076
- consumed++;
9077
- }
9078
- return { latexSource, consumed, foundClose };
9079
- }
9080
- function consumeMathClose(ctx, startPos) {
9081
- let pos = startPos + 1;
9082
- let consumed = 1;
9083
- const closeNameResult = parseBlockName(ctx, pos);
9084
- if (closeNameResult) {
9085
- pos += closeNameResult.consumed;
9086
- consumed += closeNameResult.consumed;
9087
- }
9088
- if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
9089
- pos++;
9090
- consumed++;
9091
- }
9092
- if (ctx.tokens[pos]?.type === "NEWLINE") {
9093
- consumed++;
10109
+ current2 = "";
10110
+ } else {
10111
+ current2 += token5;
10112
+ }
9094
10113
  }
9095
- return consumed;
10114
+ assignVariable(vars, current2);
10115
+ return vars;
9096
10116
  }
9097
-
9098
- // packages/parser/src/parser/rules/block/math/name.ts
9099
- function parseMathName(ctx, startPos) {
9100
- let pos = startPos;
9101
- let consumed = 0;
9102
- let name = "";
9103
- const first = ctx.tokens[pos];
9104
- if (first?.type !== "IDENTIFIER" && first?.type !== "TEXT") {
9105
- return { name: null, consumed: 0 };
10117
+ function assignVariable(vars, source) {
10118
+ if (!source.trim()) {
10119
+ return;
9106
10120
  }
9107
- while (pos < ctx.tokens.length) {
9108
- const token5 = ctx.tokens[pos];
9109
- if (!token5 || token5.type === "BLOCK_CLOSE" || token5.type === "WHITESPACE" || token5.type === "NEWLINE") {
9110
- break;
9111
- }
9112
- name += token5.value;
9113
- pos++;
9114
- consumed++;
10121
+ const eqIndex = source.indexOf("=");
10122
+ if (eqIndex === -1) {
10123
+ return;
10124
+ }
10125
+ const key = source.slice(0, eqIndex).trim();
10126
+ if (!key) {
10127
+ return;
9115
10128
  }
9116
- return { name: name === "" ? null : name, consumed };
10129
+ vars[key] = source.slice(eqIndex + 1).trim();
9117
10130
  }
9118
10131
 
9119
- // packages/parser/src/parser/rules/block/math/index.ts
9120
- var mathBlockRule = {
9121
- name: "math",
10132
+ // packages/parser/src/parser/rules/block/include/index.ts
10133
+ var includeRule = {
10134
+ name: "include",
9122
10135
  startTokens: ["BLOCK_OPEN"],
9123
10136
  requiresLineStart: false,
9124
10137
  parse(ctx) {
@@ -9129,7 +10142,10 @@ var mathBlockRule = {
9129
10142
  let pos = ctx.pos + 1;
9130
10143
  let consumed = 1;
9131
10144
  const nameResult = parseBlockName(ctx, pos);
9132
- if (!nameResult || nameResult.name !== "math") {
10145
+ if (!nameResult || nameResult.name.toLowerCase() !== "include") {
10146
+ return { success: false };
10147
+ }
10148
+ if (!ctx.settings.enablePageSyntax) {
9133
10149
  return { success: false };
9134
10150
  }
9135
10151
  pos += nameResult.consumed;
@@ -9138,49 +10154,46 @@ var mathBlockRule = {
9138
10154
  pos++;
9139
10155
  consumed++;
9140
10156
  }
9141
- const mathName = parseMathName(ctx, pos);
9142
- pos += mathName.consumed;
9143
- consumed += mathName.consumed;
9144
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
9145
- pos++;
9146
- consumed++;
9147
- }
10157
+ const args = collectIncludeArguments(ctx, pos);
10158
+ pos += args.consumed;
10159
+ consumed += args.consumed;
9148
10160
  if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
9149
10161
  return { success: false };
9150
10162
  }
9151
10163
  pos++;
9152
10164
  consumed++;
10165
+ const directiveEnd = pos;
9153
10166
  if (ctx.tokens[pos]?.type === "NEWLINE") {
9154
10167
  pos++;
9155
10168
  consumed++;
9156
10169
  }
9157
- const contentResult = collectMathContent(ctx, pos);
9158
- const latexSource = contentResult.latexSource.trim();
9159
- consumed += contentResult.consumed;
9160
- pos += contentResult.consumed;
9161
- if (!contentResult.foundClose) {
9162
- ctx.diagnostics.push({
9163
- severity: "warning",
9164
- code: "unclosed-block",
9165
- message: "Missing closing tag [[/math]] for [[math]]",
9166
- position: openToken.position
9167
- });
9168
- } else {
9169
- const closeConsumed = consumeMathClose(ctx, pos);
9170
- pos += closeConsumed;
9171
- consumed += closeConsumed;
9172
- }
9173
- if (!latexSource) {
10170
+ if (!args.target) {
9174
10171
  return { success: false };
9175
10172
  }
10173
+ const location = parsePageRef(args.target);
10174
+ if (ctx.deferInclude?.(location)) {
10175
+ const source = ctx.tokens.slice(ctx.pos, directiveEnd).map((token5) => token5.value).join("");
10176
+ const elements = [];
10177
+ const lines = source.split(`
10178
+ `);
10179
+ for (let index = 0;index < lines.length; index++) {
10180
+ if (index > 0)
10181
+ elements.push(lineBreak());
10182
+ if (lines[index] !== "")
10183
+ elements.push(text(lines[index]));
10184
+ }
10185
+ return { success: true, elements: [paragraph(elements)], consumed };
10186
+ }
9176
10187
  return {
9177
10188
  success: true,
9178
10189
  elements: [
9179
10190
  {
9180
- element: "math",
10191
+ element: "include",
9181
10192
  data: {
9182
- name: mathName.name,
9183
- "latex-source": latexSource
10193
+ "paragraph-safe": false,
10194
+ variables: parseVariables(args.argumentTokens),
10195
+ location,
10196
+ elements: []
9184
10197
  }
9185
10198
  }
9186
10199
  ],
@@ -10050,7 +11063,8 @@ function parseBibliographyContent(ctx, startPos) {
10050
11063
  const inlineCtx = { ...ctx, pos };
10051
11064
  const result = parseInlineUntil(inlineCtx, "NEWLINE");
10052
11065
  if (result.elements.length > 0) {
10053
- content.push(...result.elements);
11066
+ for (const element of result.elements)
11067
+ content.push(element);
10054
11068
  pos += result.consumed;
10055
11069
  consumed += result.consumed;
10056
11070
  } else {
@@ -10479,6 +11493,7 @@ var blockRules = [
10479
11493
  iftagsRule,
10480
11494
  bibliographyRule,
10481
11495
  galleryRule,
11496
+ noteRule,
10482
11497
  divRule
10483
11498
  ];
10484
11499
  // packages/parser/src/parser/rules/inline/formatting/close.ts
@@ -10491,6 +11506,11 @@ function findFormattingClose(ctx, start, marker) {
10491
11506
  return null;
10492
11507
  if (!table && token5.type === "NEWLINE" && getParagraphNewlineBoundary(ctx, pos, true).shouldBreak)
10493
11508
  return null;
11509
+ const inlineRegionEnd = Math.max(emailRegionEnd(ctx.tokens, pos, end), parseButtonSyntax(ctx, pos, end)?.end ?? pos, parseSocialSyntax(ctx, pos, end)?.end ?? pos);
11510
+ if (inlineRegionEnd > pos) {
11511
+ pos = inlineRegionEnd - 1;
11512
+ continue;
11513
+ }
10494
11514
  if (token5.type === marker && !table?.suppressedClosers.has(pos))
10495
11515
  return pos;
10496
11516
  const protectedEnd = protectedInlineRegionEnd(ctx.tokens, pos, end);
@@ -10536,253 +11556,93 @@ function parseDelimitedContainer(ctx, closeToken, type, options = {}) {
10536
11556
  consumed
10537
11557
  };
10538
11558
  }
10539
- return {
10540
- success: true,
10541
- elements: [createInlineContainer(type, result.elements)],
10542
- consumed
10543
- };
10544
- }
10545
-
10546
- // packages/parser/src/parser/rules/inline/bold.ts
10547
- var boldRule = {
10548
- name: "bold",
10549
- startTokens: ["BOLD_MARKER"],
10550
- parse(ctx) {
10551
- return parseDelimitedContainer(ctx, "BOLD_MARKER", "bold", { discardEmpty: true });
10552
- }
10553
- };
10554
-
10555
- // packages/parser/src/parser/rules/inline/italic.ts
10556
- var italicRule = {
10557
- name: "italic",
10558
- startTokens: ["ITALIC_MARKER"],
10559
- parse(ctx) {
10560
- return parseDelimitedContainer(ctx, "ITALIC_MARKER", "italics");
10561
- }
10562
- };
10563
-
10564
- // packages/parser/src/parser/rules/inline/underline/index.ts
10565
- var underlineRule = {
10566
- name: "underline",
10567
- startTokens: ["UNDERLINE_MARKER"],
10568
- parse(ctx) {
10569
- return parseDelimitedContainer(ctx, "UNDERLINE_MARKER", "underline", { discardEmpty: true });
10570
- }
10571
- };
10572
-
10573
- // packages/parser/src/parser/rules/inline/strikethrough/parse.ts
10574
- function parseStrikethroughContent(ctx) {
10575
- return parseDelimitedContainer(ctx, "STRIKE_MARKER", "strikethrough");
10576
- }
10577
-
10578
- // packages/parser/src/parser/rules/inline/strikethrough/syntax.ts
10579
- function hasValidStrikethroughClose(ctx) {
10580
- const close = findFormattingClose(ctx, ctx.pos + 1, "STRIKE_MARKER");
10581
- return close !== null && close > ctx.pos + 1 && ctx.tokens[close - 1]?.type !== "WHITESPACE";
10582
- }
10583
-
10584
- // packages/parser/src/parser/rules/inline/strikethrough/index.ts
10585
- var strikethroughRule = {
10586
- name: "strikethrough",
10587
- startTokens: ["STRIKE_MARKER"],
10588
- parse(ctx) {
10589
- if (!hasValidStrikethroughClose(ctx)) {
10590
- return {
10591
- success: true,
10592
- elements: [{ element: "text", data: "—" }],
10593
- consumed: 1
10594
- };
10595
- }
10596
- return parseStrikethroughContent(ctx);
10597
- }
10598
- };
10599
-
10600
- // packages/parser/src/parser/rules/inline/superscript.ts
10601
- var superscriptRule = {
10602
- name: "superscript",
10603
- startTokens: ["SUPER_MARKER"],
10604
- parse(ctx) {
10605
- return parseDelimitedContainer(ctx, "SUPER_MARKER", "superscript", {
10606
- discardEmpty: true
10607
- });
10608
- }
10609
- };
10610
-
10611
- // packages/parser/src/parser/rules/inline/subscript.ts
10612
- var subscriptRule = {
10613
- name: "subscript",
10614
- startTokens: ["SUB_MARKER"],
10615
- parse(ctx) {
10616
- return parseDelimitedContainer(ctx, "SUB_MARKER", "subscript", { discardEmpty: true });
10617
- }
10618
- };
10619
-
10620
- // packages/parser/src/parser/rules/inline/monospace.ts
10621
- var monospaceRule = {
10622
- name: "monospace",
10623
- startTokens: ["MONO_MARKER"],
10624
- parse(ctx) {
10625
- return parseDelimitedContainer(ctx, "MONO_CLOSE", "monospace");
10626
- }
10627
- };
10628
-
10629
- // packages/parser/src/parser/rules/inline/link-triple/fallback.ts
10630
- function literalOpenLink(value) {
10631
- return {
10632
- success: true,
10633
- elements: [{ element: "text", data: value }],
10634
- consumed: 1
10635
- };
10636
- }
10637
-
10638
- // packages/parser/src/parser/rules/inline/link-triple/label.ts
10639
- function buildTripleLinkLabel(args) {
10640
- return {
10641
- text: getTripleLinkDisplayText(args)
10642
- };
10643
- }
10644
- function getTripleLinkDisplayText(args) {
10645
- const trimmedLabel = args.labelText.trim();
10646
- if (args.foundPipe) {
10647
- return trimmedLabel || args.finalTarget;
10648
- }
10649
- const colonIdx = args.originalTarget.indexOf(":");
10650
- if (colonIdx !== -1 && !args.originalTarget.startsWith("http") && !args.originalTarget.startsWith("*")) {
10651
- return args.originalTarget.slice(colonIdx + 1).trim();
10652
- }
10653
- return args.originalTarget;
10654
- }
10655
-
10656
- // packages/parser/src/parser/rules/inline/link-triple/syntax.ts
10657
- function hasClosingLinkMarker(ctx, startPos) {
10658
- let pos = startPos;
10659
- while (pos < ctx.tokens.length) {
10660
- const token5 = ctx.tokens[pos];
10661
- if (!token5 || token5.type === "EOF") {
10662
- return false;
10663
- }
10664
- if (token5.type === "LINK_CLOSE") {
10665
- return true;
10666
- }
10667
- if (token5.type === "NEWLINE") {
10668
- const next = ctx.tokens[pos + 1];
10669
- if (next?.type === "NEWLINE" || next?.type === "LINK_CLOSE") {
10670
- return false;
10671
- }
10672
- }
10673
- pos++;
10674
- }
10675
- return false;
10676
- }
10677
- function collectTripleLinkParts(ctx, startPos) {
10678
- let target = "";
10679
- let labelText = "";
10680
- let foundPipe = false;
10681
- let consumed = 1;
10682
- let pos = startPos;
10683
- while (pos < ctx.tokens.length) {
10684
- const token5 = ctx.tokens[pos];
10685
- if (!token5 || token5.type === "LINK_CLOSE" || token5.type === "EOF") {
10686
- break;
10687
- }
10688
- if (token5.type === "NEWLINE") {
10689
- if (foundPipe) {
10690
- labelText += " ";
10691
- } else {
10692
- target += " ";
10693
- }
10694
- consumed++;
10695
- pos++;
10696
- continue;
10697
- }
10698
- if (token5.type === "PIPE" && !foundPipe) {
10699
- foundPipe = true;
10700
- } else if (foundPipe) {
10701
- labelText += token5.value;
10702
- } else {
10703
- target += token5.value;
10704
- }
10705
- consumed++;
10706
- pos++;
10707
- }
10708
- if (ctx.tokens[pos]?.type === "LINK_CLOSE") {
10709
- consumed++;
10710
- }
10711
- return { target, labelText, foundPipe, consumed };
11559
+ return {
11560
+ success: true,
11561
+ elements: [createInlineContainer(type, result.elements)],
11562
+ consumed
11563
+ };
10712
11564
  }
10713
11565
 
10714
- // packages/parser/src/parser/rules/inline/link-triple/interwiki.ts
10715
- var INTERWIKI_PREFIXES = new Set(["wikipedia", "google", "dictionary", "wikidot"]);
10716
- function isInterwikiTarget(target) {
10717
- const colonIdx = target.indexOf(":");
10718
- if (colonIdx <= 0 || target.includes("/")) {
10719
- return false;
11566
+ // packages/parser/src/parser/rules/inline/bold.ts
11567
+ var boldRule = {
11568
+ name: "bold",
11569
+ startTokens: ["BOLD_MARKER"],
11570
+ parse(ctx) {
11571
+ return parseDelimitedContainer(ctx, "BOLD_MARKER", "bold", { discardEmpty: true });
10720
11572
  }
10721
- const prefix = target.slice(0, colonIdx).toLowerCase();
10722
- return INTERWIKI_PREFIXES.has(prefix);
10723
- }
11573
+ };
10724
11574
 
10725
- // packages/parser/src/parser/rules/inline/link-triple/target.ts
10726
- function normalizeTripleLinkTarget(trimmedTarget) {
10727
- if (trimmedTarget.startsWith("*")) {
10728
- return { target: trimmedTarget.slice(1), hasStar: true };
11575
+ // packages/parser/src/parser/rules/inline/italic.ts
11576
+ var italicRule = {
11577
+ name: "italic",
11578
+ startTokens: ["ITALIC_MARKER"],
11579
+ parse(ctx) {
11580
+ return parseDelimitedContainer(ctx, "ITALIC_MARKER", "italics");
10729
11581
  }
10730
- return { target: trimmedTarget, hasStar: false };
11582
+ };
11583
+
11584
+ // packages/parser/src/parser/rules/inline/underline/index.ts
11585
+ var underlineRule = {
11586
+ name: "underline",
11587
+ startTokens: ["UNDERLINE_MARKER"],
11588
+ parse(ctx) {
11589
+ return parseDelimitedContainer(ctx, "UNDERLINE_MARKER", "underline", { discardEmpty: true });
11590
+ }
11591
+ };
11592
+
11593
+ // packages/parser/src/parser/rules/inline/strikethrough/parse.ts
11594
+ function parseStrikethroughContent(ctx) {
11595
+ return parseDelimitedContainer(ctx, "STRIKE_MARKER", "strikethrough");
10731
11596
  }
10732
- function isInvalidTripleLinkTarget(trimmedTarget, foundPipe) {
10733
- return trimmedTarget === "" && foundPipe || /#{2,}/.test(trimmedTarget);
11597
+
11598
+ // packages/parser/src/parser/rules/inline/strikethrough/syntax.ts
11599
+ function hasValidStrikethroughClose(ctx) {
11600
+ const close = findFormattingClose(ctx, ctx.pos + 1, "STRIKE_MARKER");
11601
+ return close !== null && close > ctx.pos + 1 && ctx.tokens[close - 1]?.type !== "WHITESPACE";
10734
11602
  }
10735
- function determineLinkTypeAndLocation(target) {
10736
- if (target.startsWith("#")) {
10737
- return { linkType: "anchor", link: target };
11603
+
11604
+ // packages/parser/src/parser/rules/inline/strikethrough/index.ts
11605
+ var strikethroughRule = {
11606
+ name: "strikethrough",
11607
+ startTokens: ["STRIKE_MARKER"],
11608
+ parse(ctx) {
11609
+ if (!hasValidStrikethroughClose(ctx)) {
11610
+ return {
11611
+ success: true,
11612
+ elements: [{ element: "text", data: "—" }],
11613
+ consumed: 1
11614
+ };
11615
+ }
11616
+ return parseStrikethroughContent(ctx);
10738
11617
  }
10739
- if (target.startsWith("http://") || target.startsWith("https://")) {
10740
- return { linkType: "direct", link: target };
11618
+ };
11619
+
11620
+ // packages/parser/src/parser/rules/inline/superscript.ts
11621
+ var superscriptRule = {
11622
+ name: "superscript",
11623
+ startTokens: ["SUPER_MARKER"],
11624
+ parse(ctx) {
11625
+ return parseDelimitedContainer(ctx, "SUPER_MARKER", "superscript", {
11626
+ discardEmpty: true
11627
+ });
10741
11628
  }
10742
- if (isInterwikiTarget(target)) {
10743
- return { linkType: "interwiki", link: target };
11629
+ };
11630
+
11631
+ // packages/parser/src/parser/rules/inline/subscript.ts
11632
+ var subscriptRule = {
11633
+ name: "subscript",
11634
+ startTokens: ["SUB_MARKER"],
11635
+ parse(ctx) {
11636
+ return parseDelimitedContainer(ctx, "SUB_MARKER", "subscript", { discardEmpty: true });
10744
11637
  }
10745
- return { linkType: "page", link: { site: null, page: target } };
10746
- }
11638
+ };
10747
11639
 
10748
- // packages/parser/src/parser/rules/inline/link-triple/index.ts
10749
- var linkTripleRule = {
10750
- name: "linkTriple",
10751
- startTokens: ["LINK_OPEN"],
11640
+ // packages/parser/src/parser/rules/inline/monospace.ts
11641
+ var monospaceRule = {
11642
+ name: "monospace",
11643
+ startTokens: ["MONO_MARKER"],
10752
11644
  parse(ctx) {
10753
- const startToken = currentToken(ctx);
10754
- if (!hasClosingLinkMarker(ctx, ctx.pos + 1)) {
10755
- return literalOpenLink(startToken.value);
10756
- }
10757
- const parts = collectTripleLinkParts(ctx, ctx.pos + 1);
10758
- const trimmedTarget = parts.target.trim();
10759
- if (isInvalidTripleLinkTarget(trimmedTarget, parts.foundPipe)) {
10760
- return literalOpenLink(startToken.value);
10761
- }
10762
- const normalized = normalizeTripleLinkTarget(trimmedTarget);
10763
- const { linkType, link } = determineLinkTypeAndLocation(normalized.target);
10764
- const label = buildTripleLinkLabel({
10765
- foundPipe: parts.foundPipe,
10766
- labelText: parts.labelText,
10767
- finalTarget: normalized.target,
10768
- originalTarget: trimmedTarget
10769
- });
10770
- return {
10771
- success: true,
10772
- elements: [
10773
- {
10774
- element: "link",
10775
- data: {
10776
- type: linkType,
10777
- link,
10778
- extra: null,
10779
- label,
10780
- target: normalized.hasStar && linkType === "direct" ? "new-tab" : null
10781
- }
10782
- }
10783
- ],
10784
- consumed: parts.consumed
10785
- };
11645
+ return parseDelimitedContainer(ctx, "MONO_CLOSE", "monospace");
10786
11646
  }
10787
11647
  };
10788
11648
 
@@ -10793,7 +11653,7 @@ function normalizeAnchor(anchor) {
10793
11653
 
10794
11654
  // packages/parser/src/parser/rules/inline/link-bracket/direct-url.ts
10795
11655
  function isDirectBracketUrl(url) {
10796
- return url !== "" && (url.startsWith("/") || url.startsWith("http://") || url.startsWith("https://"));
11656
+ return url !== "" && (url.startsWith("/") || startsWithUrlScheme(url, 0));
10797
11657
  }
10798
11658
 
10799
11659
  // packages/parser/src/parser/rules/inline/link-bracket/parts.ts
@@ -10802,35 +11662,24 @@ function collectBracketLinkParts(ctx, startPos) {
10802
11662
  return null;
10803
11663
  }
10804
11664
  let pos = startPos;
10805
- let consumed = 0;
10806
- let first = "";
10807
- while (pos < ctx.tokens.length) {
10808
- const token5 = ctx.tokens[pos];
10809
- if (!token5 || token5.type === "WHITESPACE" || token5.type === "BRACKET_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF") {
10810
- break;
10811
- }
10812
- first += token5.value;
10813
- pos++;
10814
- consumed++;
10815
- }
10816
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
10817
- pos++;
10818
- consumed++;
10819
- }
10820
- let label = "";
10821
- while (pos < ctx.tokens.length) {
11665
+ const end = Math.min(ctx.scope.inlineEnd ?? ctx.tokens.length, ctx.tokens.length);
11666
+ const values = [];
11667
+ while (pos < end) {
10822
11668
  const token5 = ctx.tokens[pos];
10823
- if (!token5 || token5.type === "BRACKET_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF") {
11669
+ if (!token5 || token5.type === "BRACKET_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF")
10824
11670
  break;
10825
- }
10826
- label += token5.value;
11671
+ values.push(token5.value);
10827
11672
  pos++;
10828
- consumed++;
10829
11673
  }
10830
- if (ctx.tokens[pos]?.type !== "BRACKET_CLOSE") {
11674
+ if (pos >= end || ctx.tokens[pos]?.type !== "BRACKET_CLOSE")
10831
11675
  return null;
10832
- }
10833
- return { first, label, consumed: consumed + 1 };
11676
+ const content = values.join("");
11677
+ const separator = content.search(/[ \t]/);
11678
+ return {
11679
+ first: separator === -1 ? content : content.slice(0, separator),
11680
+ label: separator === -1 ? "" : content.slice(separator).trimStart(),
11681
+ consumed: pos - startPos + 1
11682
+ };
10834
11683
  }
10835
11684
 
10836
11685
  // packages/parser/src/parser/rules/inline/link-bracket/prefix.ts
@@ -10841,6 +11690,14 @@ function parseBracketLinkPrefix(ctx, startPos) {
10841
11690
  return { target: null, bodyStart: startPos, consumed: 0 };
10842
11691
  }
10843
11692
 
11693
+ // packages/parser/src/parser/rules/inline/link-bracket/special-target.ts
11694
+ function isBracketEmail(target) {
11695
+ return /^[_a-z0-9-]+(?:\.[_a-z0-9-]+)*@[a-z0-9-]+(?:\.[a-z0-9-]+)+$/i.test(target);
11696
+ }
11697
+ function wikipediaPage(target) {
11698
+ return /^wikipedia:((?!:)[A-Za-z0-9_/=&~#.:;+-]+)$/.exec(target)?.[1] ?? null;
11699
+ }
11700
+
10844
11701
  // packages/parser/src/parser/rules/inline/link-bracket/parsed.ts
10845
11702
  function parseSingleBracketLink(ctx) {
10846
11703
  const prefix = parseBracketLinkPrefix(ctx, ctx.pos + 1);
@@ -10849,17 +11706,20 @@ function parseSingleBracketLink(ctx) {
10849
11706
  return null;
10850
11707
  }
10851
11708
  const link = parts.first.trim();
10852
- if (!isDirectBracketUrl(link)) {
11709
+ const email = isBracketEmail(link);
11710
+ const wikiPage = wikipediaPage(link);
11711
+ if (!email && wikiPage === null && !isDirectBracketUrl(link)) {
10853
11712
  return null;
10854
11713
  }
10855
- const labelText = parts.label.trim();
11714
+ const labelText = parts.label.trim() || wikiPage;
10856
11715
  if (!labelText) {
10857
11716
  return null;
10858
11717
  }
10859
11718
  return {
10860
- link,
11719
+ link: email ? `mailto:${link}` : link,
11720
+ interwiki: wikiPage !== null,
10861
11721
  labelText,
10862
- target: prefix.target,
11722
+ target: wikiPage !== null ? "new-tab" : prefix.target,
10863
11723
  consumed: 1 + prefix.consumed + parts.consumed
10864
11724
  };
10865
11725
  }
@@ -10912,7 +11772,7 @@ var linkSingleRule = {
10912
11772
  {
10913
11773
  element: "link",
10914
11774
  data: {
10915
- type: "direct",
11775
+ type: parsed.interwiki ? "interwiki" : "direct",
10916
11776
  link: parsed.link,
10917
11777
  extra: null,
10918
11778
  label: linkLabel,
@@ -10981,6 +11841,134 @@ var linkStarRule = {
10981
11841
  }
10982
11842
  };
10983
11843
 
11844
+ // packages/parser/src/parser/rules/inline/autolink.ts
11845
+ var URL_BOUNDARY_TOKENS = new Set([
11846
+ "WHITESPACE",
11847
+ "NEWLINE",
11848
+ "EOF",
11849
+ "BLOCK_OPEN",
11850
+ "BLOCK_END_OPEN",
11851
+ "BLOCK_CLOSE",
11852
+ "LINK_OPEN",
11853
+ "LINK_CLOSE",
11854
+ "COMMENT_OPEN",
11855
+ "BACKSLASH_BREAK"
11856
+ ]);
11857
+ var autolinkRule = {
11858
+ name: "autolink",
11859
+ startTokens: ["IDENTIFIER", "STAR", "LIST_BULLET"],
11860
+ parse(ctx) {
11861
+ let pos = ctx.pos;
11862
+ let target = null;
11863
+ const first = ctx.tokens[pos];
11864
+ if (!first) {
11865
+ return { success: false };
11866
+ }
11867
+ if (first.type === "STAR" || first.type === "LIST_BULLET") {
11868
+ if (first.value !== "*") {
11869
+ return { success: false };
11870
+ }
11871
+ target = "new-tab";
11872
+ pos++;
11873
+ }
11874
+ const scheme = ctx.tokens[pos];
11875
+ if (scheme?.type !== "IDENTIFIER" || !URL_SCHEME_NAMES.has(scheme.value)) {
11876
+ return { success: false };
11877
+ }
11878
+ if (ctx.tokens[pos + 1]?.type !== "COLON") {
11879
+ return { success: false };
11880
+ }
11881
+ const prev = ctx.tokens[ctx.pos - 1];
11882
+ if (prev && !first.lineStart) {
11883
+ const lastChar = prev.value[prev.value.length - 1] ?? "";
11884
+ if (/[A-Za-z]/.test(lastChar)) {
11885
+ return { success: false };
11886
+ }
11887
+ }
11888
+ const values = [];
11889
+ let end = pos;
11890
+ const inlineEnd = ctx.scope.inlineEnd ?? ctx.tokens.length;
11891
+ while (end < inlineEnd) {
11892
+ const token5 = ctx.tokens[end];
11893
+ if (!token5 || URL_BOUNDARY_TOKENS.has(token5.type)) {
11894
+ break;
11895
+ }
11896
+ if (token5.type === "RAW_OPEN" || token5.type === "RAW_BLOCK_OPEN") {
11897
+ if (rawRegionEnd(ctx.tokens, end, inlineEnd) > end)
11898
+ break;
11899
+ }
11900
+ values.push(token5.value);
11901
+ end++;
11902
+ if (/[ \t\n\\"']/.test(token5.value))
11903
+ break;
11904
+ }
11905
+ const candidate = values.join("");
11906
+ const match = URL_PATTERN.exec(candidate);
11907
+ if (!match) {
11908
+ return { success: false };
11909
+ }
11910
+ const url = match[0];
11911
+ let length = 0;
11912
+ let count = 0;
11913
+ while (length < url.length) {
11914
+ length += values[count]?.length ?? 0;
11915
+ count++;
11916
+ }
11917
+ const rest = candidate.slice(url.length, length);
11918
+ const elements = [
11919
+ {
11920
+ element: "link",
11921
+ data: {
11922
+ type: "direct",
11923
+ link: url,
11924
+ extra: null,
11925
+ label: { text: url },
11926
+ target
11927
+ }
11928
+ }
11929
+ ];
11930
+ if (rest !== "") {
11931
+ elements.push({ element: "text", data: rest });
11932
+ }
11933
+ return {
11934
+ success: true,
11935
+ elements,
11936
+ consumed: pos - ctx.pos + count
11937
+ };
11938
+ }
11939
+ };
11940
+
11941
+ // packages/parser/src/parser/rules/inline/email/index.ts
11942
+ var emailRule = {
11943
+ name: "email",
11944
+ startTokens: EMAIL_START_TOKENS,
11945
+ parse(ctx) {
11946
+ const group = getEmailGroup(ctx.tokens, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
11947
+ if (!group)
11948
+ return { success: false };
11949
+ const elements = [];
11950
+ let copied = group.start;
11951
+ for (const candidate of group.candidates) {
11952
+ if (copied < candidate.start)
11953
+ elements.push({ element: "text", data: group.source.slice(copied, candidate.start) });
11954
+ elements.push(ctx.scope.suppressEmailLinks ? { element: "text", data: candidate.address } : {
11955
+ element: "link",
11956
+ data: {
11957
+ type: "direct",
11958
+ link: `mailto:${candidate.address}`,
11959
+ label: { text: candidate.address },
11960
+ target: null,
11961
+ extra: null
11962
+ }
11963
+ });
11964
+ copied = candidate.end;
11965
+ }
11966
+ if (copied < group.end)
11967
+ elements.push({ element: "text", data: group.source.slice(copied, group.end) });
11968
+ return { success: true, elements, consumed: group.endToken - ctx.pos };
11969
+ }
11970
+ };
11971
+
10984
11972
  // packages/parser/src/parser/rules/inline/color/syntax.ts
10985
11973
  function parseColorContent(ctx) {
10986
11974
  const close = findFormattingClose(ctx, ctx.pos + 1, "COLOR_MARKER");
@@ -11067,7 +12055,7 @@ var newlineLineBreakRule = {
11067
12055
  }
11068
12056
  return {
11069
12057
  success: true,
11070
- elements: [{ element: "line-break" }],
12058
+ elements: [createAutomaticLineBreak(currentTok)],
11071
12059
  consumed: 1
11072
12060
  };
11073
12061
  }
@@ -11090,6 +12078,9 @@ function isValidBlockStartAfterNewline(ctx, tokenPos) {
11090
12078
  if (!isBlockStartToken(token5?.type) || !token5?.lineStart) {
11091
12079
  return false;
11092
12080
  }
12081
+ if (token5.type === "LIST_BULLET" || token5.type === "LIST_NUMBER") {
12082
+ return ctx.tokens[tokenPos + 1]?.type === "WHITESPACE";
12083
+ }
11093
12084
  if (token5.type !== "HEADING_MARKER") {
11094
12085
  return true;
11095
12086
  }
@@ -11347,6 +12338,9 @@ var htmlInlineRule = {
11347
12338
  }
11348
12339
  };
11349
12340
 
12341
+ // packages/parser/src/parser/rules/inline/raw/angle.ts
12342
+ import { decodeHTML } from "entities";
12343
+
11350
12344
  // packages/parser/src/parser/rules/inline/raw/result.ts
11351
12345
  function rawElement(value, consumed) {
11352
12346
  return {
@@ -11394,7 +12388,7 @@ function parseAngleRaw(ctx) {
11394
12388
  if (ctx.tokens[pos]?.type === "RAW_BLOCK_CLOSE") {
11395
12389
  consumed++;
11396
12390
  }
11397
- return rawElement(value, consumed);
12391
+ return rawElement(decodeHTML(value), consumed);
11398
12392
  }
11399
12393
 
11400
12394
  // packages/parser/src/parser/rules/inline/raw/double-at.ts
@@ -11617,7 +12611,7 @@ function consumeSpanNewline(ctx, pos, paragraphStrip, afterBlankLine, children,
11617
12611
  return { consumed: consumed2, afterBlankLine: paragraphStrip };
11618
12612
  }
11619
12613
  const targetChildren = paragraphStrip && afterBlankLine ? escapedChildren : children;
11620
- targetChildren.push({ element: "line-break" });
12614
+ targetChildren.push(createAutomaticLineBreak(ctx.tokens[pos]));
11621
12615
  let consumed = 1;
11622
12616
  let nextPos = pos + 1;
11623
12617
  while (ctx.tokens[nextPos]?.type === "WHITESPACE" && ctx.tokens[nextPos]?.lineStart) {
@@ -11705,7 +12699,9 @@ function parseOneSpanChild(ctx, pos, targetChildren) {
11705
12699
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
11706
12700
  const result = rule.parse(inlineCtx);
11707
12701
  if (result.success) {
11708
- targetChildren.push(...result.elements);
12702
+ stripAutomaticLineBreak(targetChildren, result.stripLeadingLineBreak);
12703
+ for (const element of result.elements)
12704
+ targetChildren.push(element);
11709
12705
  return { consumed: result.consumed };
11710
12706
  }
11711
12707
  }
@@ -11773,6 +12769,48 @@ var closeSpanRule = {
11773
12769
  }
11774
12770
  };
11775
12771
 
12772
+ // packages/parser/src/parser/rules/inline/button/index.ts
12773
+ var buttonRule = {
12774
+ name: "button",
12775
+ startTokens: ["BLOCK_OPEN"],
12776
+ parse(ctx) {
12777
+ const result = parseButtonSyntax(ctx, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
12778
+ return result ? {
12779
+ success: true,
12780
+ consumed: result.end - ctx.pos,
12781
+ elements: [{ element: "button", data: result.data }]
12782
+ } : { success: false };
12783
+ }
12784
+ };
12785
+
12786
+ // packages/parser/src/parser/rules/inline/social/index.ts
12787
+ var socialRule = {
12788
+ name: "social",
12789
+ startTokens: ["BLOCK_OPEN"],
12790
+ parse(ctx) {
12791
+ const result = parseSocialSyntax(ctx, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
12792
+ return result ? {
12793
+ success: true,
12794
+ consumed: result.end - ctx.pos,
12795
+ elements: [{ element: "social", data: result.data }]
12796
+ } : { success: false };
12797
+ }
12798
+ };
12799
+
12800
+ // packages/parser/src/parser/rules/inline/date/index.ts
12801
+ var dateRule = {
12802
+ name: "date",
12803
+ startTokens: ["BLOCK_OPEN"],
12804
+ parse(ctx) {
12805
+ const parsed = parseDateSyntax(ctx, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
12806
+ return parsed ? {
12807
+ success: true,
12808
+ consumed: parsed.end - ctx.pos,
12809
+ elements: [{ element: "date", data: parsed.data }]
12810
+ } : { success: false };
12811
+ }
12812
+ };
12813
+
11776
12814
  // packages/parser/src/parser/rules/inline/size/content.ts
11777
12815
  function parseSizeContent(ctx, startPos) {
11778
12816
  const children = [];
@@ -11794,7 +12832,8 @@ function parseSizeContent(ctx, startPos) {
11794
12832
  const inlineCtx = { ...ctx, pos };
11795
12833
  const inlineResult = parseInlineUntil(inlineCtx, "BLOCK_END_OPEN");
11796
12834
  if (inlineResult.elements.length > 0) {
11797
- children.push(...inlineResult.elements);
12835
+ for (const element of inlineResult.elements)
12836
+ children.push(element);
11798
12837
  pos += inlineResult.consumed;
11799
12838
  consumed += inlineResult.consumed;
11800
12839
  } else {
@@ -11823,6 +12862,16 @@ function tryConsumeSizeClose(ctx, pos) {
11823
12862
 
11824
12863
  // packages/parser/src/parser/rules/inline/size/value.ts
11825
12864
  var VALID_SIZE_UNITS = ["px", "em", "rem", "ex", "%", "cm", "mm", "in", "pc"];
12865
+ var VALID_SIZE_KEYWORDS = [
12866
+ "smaller",
12867
+ "larger",
12868
+ "xx-small",
12869
+ "x-small",
12870
+ "small",
12871
+ "large",
12872
+ "x-large",
12873
+ "xx-large"
12874
+ ];
11826
12875
  function parseSizeValue(ctx, startPos) {
11827
12876
  let pos = startPos;
11828
12877
  let consumed = 0;
@@ -11847,6 +12896,8 @@ function parseSizeValue(ctx, startPos) {
11847
12896
  return isValidSizeValue(size) ? { size, consumed } : null;
11848
12897
  }
11849
12898
  function isValidSizeValue(size) {
12899
+ if (VALID_SIZE_KEYWORDS.includes(size))
12900
+ return true;
11850
12901
  const unitPattern = VALID_SIZE_UNITS.join("|");
11851
12902
  return new RegExp(`^(\\d+(?:\\.\\d+)?)(${unitPattern})$`, "i").test(size);
11852
12903
  }
@@ -11918,19 +12969,6 @@ var sizeRule = {
11918
12969
  }
11919
12970
  };
11920
12971
 
11921
- // packages/parser/src/parser/rules/inline/footnote/child.ts
11922
- function parseFootnoteChild(ctx, pos) {
11923
- const token5 = ctx.tokens[pos];
11924
- if (!token5) {
11925
- return { elements: [], consumed: 0 };
11926
- }
11927
- const inlineResult = parseInlineUntil({ ...ctx, pos }, "BLOCK_END_OPEN");
11928
- if (inlineResult.elements.length > 0) {
11929
- return { elements: inlineResult.elements, consumed: inlineResult.consumed };
11930
- }
11931
- return { elements: [{ element: "text", data: token5.value }], consumed: 1 };
11932
- }
11933
-
11934
12972
  // packages/parser/src/parser/rules/inline/footnote/close.ts
11935
12973
  function tryConsumeFootnoteClose(ctx, pos) {
11936
12974
  if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
@@ -11952,96 +12990,73 @@ function tryConsumeFootnoteClose(ctx, pos) {
11952
12990
  return { consumed };
11953
12991
  }
11954
12992
 
11955
- // packages/parser/src/parser/rules/inline/footnote/newline.ts
11956
- function consumeFootnoteNewline(ctx, pos) {
11957
- let nextPos = pos + 1;
11958
- let consumed = 1;
11959
- while (ctx.tokens[nextPos]?.type === "WHITESPACE") {
11960
- nextPos++;
11961
- consumed++;
11962
- }
11963
- if (ctx.tokens[nextPos]?.type !== "NEWLINE") {
11964
- return { consumed, paragraphBreak: false };
12993
+ // packages/parser/src/parser/rules/inline/math-inline/syntax.ts
12994
+ function parseInlineMathSource(ctx, startPos) {
12995
+ let pos = startPos;
12996
+ let consumed = 0;
12997
+ if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
12998
+ return null;
11965
12999
  }
11966
- while (ctx.tokens[nextPos]?.type === "NEWLINE") {
11967
- nextPos++;
13000
+ pos++;
13001
+ consumed++;
13002
+ while (ctx.tokens[pos]?.type === "WHITESPACE") {
13003
+ pos++;
11968
13004
  consumed++;
11969
13005
  }
11970
- return { consumed, paragraphBreak: true };
11971
- }
11972
-
11973
- // packages/parser/src/parser/rules/inline/footnote/content.ts
11974
- function parseFootnoteContent(ctx, startPos) {
11975
- const paragraphs = [[]];
11976
- let currentParagraph = 0;
11977
- let pos = startPos;
11978
- let consumed = 0;
13006
+ let latexSource = "";
11979
13007
  while (pos < ctx.tokens.length) {
11980
13008
  const token5 = ctx.tokens[pos];
11981
- if (!token5 || token5.type === "EOF") {
11982
- break;
11983
- }
11984
- const closeResult = tryConsumeFootnoteClose(ctx, pos);
11985
- if (closeResult) {
11986
- return {
11987
- paragraphs,
11988
- consumed: consumed + closeResult.consumed,
11989
- foundClose: true
11990
- };
13009
+ if (!token5 || token5.type === "NEWLINE") {
13010
+ return null;
11991
13011
  }
11992
- if (token5.type === "NEWLINE") {
11993
- const newlineResult = consumeFootnoteNewline(ctx, pos);
11994
- pos += newlineResult.consumed;
11995
- consumed += newlineResult.consumed;
11996
- if (newlineResult.paragraphBreak) {
11997
- currentParagraph++;
11998
- paragraphs[currentParagraph] = [];
11999
- } else {
12000
- paragraphs[currentParagraph].push({ element: "line-break" });
12001
- }
12002
- continue;
13012
+ if (token5.type === "TEXT" && token5.value === "$" && ctx.tokens[pos + 1]?.type === "BLOCK_CLOSE") {
13013
+ break;
12003
13014
  }
12004
- const child = parseFootnoteChild(ctx, pos);
12005
- paragraphs[currentParagraph].push(...child.elements);
12006
- pos += child.consumed;
12007
- consumed += child.consumed;
13015
+ latexSource += token5.value;
13016
+ pos++;
13017
+ consumed++;
13018
+ }
13019
+ if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
13020
+ return null;
13021
+ }
13022
+ pos++;
13023
+ consumed++;
13024
+ if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
13025
+ return null;
12008
13026
  }
12009
- return { paragraphs, consumed, foundClose: false };
13027
+ return {
13028
+ latexSource: latexSource.trim(),
13029
+ consumed: consumed + 1
13030
+ };
12010
13031
  }
12011
13032
 
12012
- // packages/parser/src/parser/rules/inline/footnote/elements.ts
12013
- function buildFootnoteChildren(paragraphs) {
12014
- const children = [];
12015
- for (let i = 0;i < paragraphs.length; i++) {
12016
- const para = trimLineBreaks(paragraphs[i] ?? []);
12017
- if (para.length === 0) {
12018
- continue;
13033
+ // packages/parser/src/parser/rules/inline/math-inline/index.ts
13034
+ var mathInlineRule = {
13035
+ name: "math-inline",
13036
+ startTokens: ["BLOCK_OPEN"],
13037
+ parse(ctx) {
13038
+ const openToken = currentToken(ctx);
13039
+ if (openToken.type !== "BLOCK_OPEN") {
13040
+ return { success: false };
12019
13041
  }
12020
- if (i === 0) {
12021
- children.push(...para);
12022
- } else {
12023
- children.push({
12024
- element: "container",
12025
- data: {
12026
- type: "paragraph",
12027
- attributes: {},
12028
- elements: para
12029
- }
12030
- });
13042
+ const parsed = parseInlineMathSource(ctx, ctx.pos + 1);
13043
+ if (!parsed) {
13044
+ return { success: false };
12031
13045
  }
13046
+ return {
13047
+ success: true,
13048
+ elements: [
13049
+ {
13050
+ element: "math-inline",
13051
+ data: {
13052
+ "latex-source": parsed.latexSource
13053
+ }
13054
+ }
13055
+ ],
13056
+ consumed: 1 + parsed.consumed
13057
+ };
12032
13058
  }
12033
- return children;
12034
- }
12035
- function trimLineBreaks(elements) {
12036
- const result = [...elements];
12037
- while (result.length > 0 && result[0]?.element === "line-break") {
12038
- result.shift();
12039
- }
12040
- while (result.length > 0 && result[result.length - 1]?.element === "line-break") {
12041
- result.pop();
12042
- }
12043
- return result;
12044
- }
13059
+ };
12045
13060
 
12046
13061
  // packages/parser/src/parser/rules/inline/footnote/open.ts
12047
13062
  function parseFootnoteOpen(ctx) {
@@ -12068,6 +13083,91 @@ function parseFootnoteOpen(ctx) {
12068
13083
  return { bodyStart: pos, consumed };
12069
13084
  }
12070
13085
 
13086
+ // packages/parser/src/parser/rules/inline/footnote/boundary.ts
13087
+ var opaqueRules2 = [
13088
+ codeBlockRule,
13089
+ htmlBlockRule,
13090
+ mathBlockRule,
13091
+ mathInlineRule,
13092
+ linkTripleRule,
13093
+ linkSingleRule,
13094
+ linkStarRule,
13095
+ linkAnchorRule
13096
+ ];
13097
+ function findFootnoteEnd(ctx, start) {
13098
+ const probe = createOpaqueProbe(ctx);
13099
+ let depth = 0;
13100
+ for (let pos = start;pos < ctx.tokens.length; pos++) {
13101
+ if (ctx.tokens[pos]?.type === "EOF")
13102
+ return pos;
13103
+ const protectedEnd = protectedInlineRegionEnd(ctx.tokens, pos, ctx.tokens.length);
13104
+ if (protectedEnd > pos) {
13105
+ pos = protectedEnd - 1;
13106
+ continue;
13107
+ }
13108
+ probe.pos = pos;
13109
+ const close = tryConsumeFootnoteClose(probe, pos);
13110
+ if (close) {
13111
+ if (depth === 0)
13112
+ return pos;
13113
+ depth--;
13114
+ pos += close.consumed - 1;
13115
+ continue;
13116
+ }
13117
+ const open = parseFootnoteOpen(probe);
13118
+ if (open) {
13119
+ depth++;
13120
+ pos += open.consumed - 1;
13121
+ continue;
13122
+ }
13123
+ const opaqueEnd = opaqueRuleEnd(probe, pos, opaqueRules2);
13124
+ if (opaqueEnd > pos)
13125
+ pos = opaqueEnd - 1;
13126
+ }
13127
+ return ctx.tokens.length;
13128
+ }
13129
+
13130
+ // packages/parser/src/parser/rules/inline/footnote/content.ts
13131
+ function parseFootnoteContent(ctx, startPos) {
13132
+ const end = findFootnoteEnd(ctx, startPos);
13133
+ let leadingNewlines = 0;
13134
+ for (let pos = startPos;pos < end; pos++) {
13135
+ const type = ctx.tokens[pos]?.type;
13136
+ if (type === "NEWLINE")
13137
+ leadingNewlines++;
13138
+ else if (type !== "WHITESPACE")
13139
+ break;
13140
+ }
13141
+ const bodyCtx = {
13142
+ ...ctx,
13143
+ tokens: ctx.tokens.slice(startPos, end),
13144
+ pos: 0,
13145
+ scope: {
13146
+ ...ctx.scope,
13147
+ inlineEnd: undefined,
13148
+ tableFormatting: undefined,
13149
+ blockCloseCondition: undefined
13150
+ }
13151
+ };
13152
+ const result = parseBlocksUntil(bodyCtx, () => false);
13153
+ const close = tryConsumeFootnoteClose(ctx, end);
13154
+ return {
13155
+ elements: result.elements,
13156
+ consumed: end - startPos + (close?.consumed ?? 0),
13157
+ foundClose: close !== null,
13158
+ leadingParagraphBreak: leadingNewlines >= 2
13159
+ };
13160
+ }
13161
+
13162
+ // packages/parser/src/parser/rules/inline/footnote/elements.ts
13163
+ function buildFootnoteChildren(elements, leadingParagraphBreak) {
13164
+ const first = elements[0];
13165
+ if (!leadingParagraphBreak && first?.element === "container" && first.data.type === "paragraph") {
13166
+ return [...first.data.elements, ...elements.slice(1)];
13167
+ }
13168
+ return elements;
13169
+ }
13170
+
12071
13171
  // packages/parser/src/parser/rules/inline/footnote/index.ts
12072
13172
  var footnoteRule = {
12073
13173
  name: "footnote",
@@ -12083,7 +13183,7 @@ var footnoteRule = {
12083
13183
  }
12084
13184
  const contentResult = parseFootnoteContent(ctx, openResult.bodyStart);
12085
13185
  const consumed = openResult.consumed + contentResult.consumed;
12086
- const children = buildFootnoteChildren(contentResult.paragraphs);
13186
+ const children = buildFootnoteChildren(contentResult.elements, contentResult.leadingParagraphBreak);
12087
13187
  if (!contentResult.foundClose) {
12088
13188
  ctx.diagnostics.push({
12089
13189
  severity: "warning",
@@ -12443,11 +13543,15 @@ function parseAnchorChild(ctx, pos) {
12443
13543
  if (!token5) {
12444
13544
  return { elements: [], consumed: 0 };
12445
13545
  }
12446
- const inlineCtx = { ...ctx, pos };
13546
+ const inlineCtx = {
13547
+ ...ctx,
13548
+ pos,
13549
+ scope: { ...ctx.scope, suppressEmailLinks: true }
13550
+ };
12447
13551
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
12448
13552
  const result = rule.parse(inlineCtx);
12449
13553
  if (result.success) {
12450
- return { elements: result.elements, consumed: result.consumed };
13554
+ return result;
12451
13555
  }
12452
13556
  }
12453
13557
  return { elements: [{ element: "text", data: token5.value }], consumed: 1 };
@@ -12507,7 +13611,7 @@ function consumeAnchorNewline(ctx, pos, paragraphStrip, children) {
12507
13611
  let consumed = 1;
12508
13612
  let nextPos = pos + 1;
12509
13613
  if (!paragraphStrip) {
12510
- children.push({ element: "line-break" });
13614
+ children.push(createAutomaticLineBreak(ctx.tokens[pos]));
12511
13615
  while (ctx.tokens[nextPos]?.type === "WHITESPACE" && ctx.tokens[nextPos]?.lineStart) {
12512
13616
  nextPos++;
12513
13617
  consumed++;
@@ -12560,7 +13664,9 @@ function parseAnchorContent(ctx, startPos, paragraphStrip) {
12560
13664
  continue;
12561
13665
  }
12562
13666
  const child = parseAnchorChild(ctx, pos);
12563
- children.push(...child.elements);
13667
+ stripAutomaticLineBreak(children, child.stripLeadingLineBreak);
13668
+ for (const element of child.elements)
13669
+ children.push(element);
12564
13670
  pos += child.consumed;
12565
13671
  consumed += child.consumed;
12566
13672
  }
@@ -12634,75 +13740,8 @@ var anchorRule = {
12634
13740
  }
12635
13741
  }
12636
13742
  ],
12637
- consumed
12638
- };
12639
- }
12640
- };
12641
-
12642
- // packages/parser/src/parser/rules/inline/math-inline/syntax.ts
12643
- function parseInlineMathSource(ctx, startPos) {
12644
- let pos = startPos;
12645
- let consumed = 0;
12646
- if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
12647
- return null;
12648
- }
12649
- pos++;
12650
- consumed++;
12651
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
12652
- pos++;
12653
- consumed++;
12654
- }
12655
- let latexSource = "";
12656
- while (pos < ctx.tokens.length) {
12657
- const token5 = ctx.tokens[pos];
12658
- if (!token5 || token5.type === "NEWLINE") {
12659
- return null;
12660
- }
12661
- if (token5.type === "TEXT" && token5.value === "$" && ctx.tokens[pos + 1]?.type === "BLOCK_CLOSE") {
12662
- break;
12663
- }
12664
- latexSource += token5.value;
12665
- pos++;
12666
- consumed++;
12667
- }
12668
- if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
12669
- return null;
12670
- }
12671
- pos++;
12672
- consumed++;
12673
- if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
12674
- return null;
12675
- }
12676
- return {
12677
- latexSource: latexSource.trim(),
12678
- consumed: consumed + 1
12679
- };
12680
- }
12681
-
12682
- // packages/parser/src/parser/rules/inline/math-inline/index.ts
12683
- var mathInlineRule = {
12684
- name: "math-inline",
12685
- startTokens: ["BLOCK_OPEN"],
12686
- parse(ctx) {
12687
- const openToken = currentToken(ctx);
12688
- if (openToken.type !== "BLOCK_OPEN") {
12689
- return { success: false };
12690
- }
12691
- const parsed = parseInlineMathSource(ctx, ctx.pos + 1);
12692
- if (!parsed) {
12693
- return { success: false };
12694
- }
12695
- return {
12696
- success: true,
12697
- elements: [
12698
- {
12699
- element: "math-inline",
12700
- data: {
12701
- "latex-source": parsed.latexSource
12702
- }
12703
- }
12704
- ],
12705
- consumed: 1 + parsed.consumed
13743
+ consumed,
13744
+ stripLeadingLineBreak: openResult.paragraphStrip ? precedingSingleNewline(ctx) : undefined
12706
13745
  };
12707
13746
  }
12708
13747
  };
@@ -12813,7 +13852,9 @@ function parseInlineBranch(ctx, startPos) {
12813
13852
  }
12814
13853
  const result = rule.parse(inlineCtx);
12815
13854
  if (result.success) {
12816
- elements.push(...result.elements);
13855
+ stripAutomaticLineBreak(elements, result.stripLeadingLineBreak);
13856
+ for (const element of result.elements)
13857
+ elements.push(element);
12817
13858
  consumed += result.consumed;
12818
13859
  pos += result.consumed;
12819
13860
  matched = true;
@@ -13136,6 +14177,7 @@ var textRule = {
13136
14177
 
13137
14178
  // packages/parser/src/parser/rules/inline/index.ts
13138
14179
  var inlineRules = [
14180
+ emailRule,
13139
14181
  boldRule,
13140
14182
  italicRule,
13141
14183
  underlineRule,
@@ -13147,6 +14189,7 @@ var inlineRules = [
13147
14189
  linkSingleRule,
13148
14190
  linkAnchorRule,
13149
14191
  linkStarRule,
14192
+ autolinkRule,
13150
14193
  colorRule,
13151
14194
  backslashLineBreakRule,
13152
14195
  underscoreLineBreakRule,
@@ -13156,6 +14199,9 @@ var inlineRules = [
13156
14199
  rawRule,
13157
14200
  imageRule,
13158
14201
  embedBlockRule,
14202
+ buttonRule,
14203
+ socialRule,
14204
+ dateRule,
13159
14205
  sizeRule,
13160
14206
  footnoteRule,
13161
14207
  spanRule,
@@ -13411,6 +14457,8 @@ function mergeSpanStripParagraphs(children) {
13411
14457
  i++;
13412
14458
  continue;
13413
14459
  }
14460
+ const firstMeaningful = paraData.elements.find((child) => child.element !== "line-break" && !isWhitespaceText(child));
14461
+ const keepParagraph = firstMeaningful && !isSpanStripMarker(firstMeaningful);
13414
14462
  const mergedChildren = [...paraData.elements];
13415
14463
  i++;
13416
14464
  while (i < expandedChildren.length) {
@@ -13423,7 +14471,8 @@ function mergeSpanStripParagraphs(children) {
13423
14471
  break;
13424
14472
  }
13425
14473
  const hasSpanStrip = hasParagraphStripSpan(nextPara);
13426
- mergedChildren.push(...nextParaData.elements);
14474
+ for (const element of nextParaData.elements)
14475
+ mergedChildren.push(element);
13427
14476
  i++;
13428
14477
  if (!hasSpanStrip) {
13429
14478
  const peekNext = expandedChildren[i];
@@ -13434,7 +14483,7 @@ function mergeSpanStripParagraphs(children) {
13434
14483
  }
13435
14484
  const escapedSpans = extractEscapedSpans(mergedChildren);
13436
14485
  removeLineBreaksAroundSpanStrip(mergedChildren);
13437
- if (escapedSpans.length > 0) {
14486
+ if (escapedSpans.length > 0 || keepParagraph) {
13438
14487
  if (mergedChildren.length > 0) {
13439
14488
  result.push(paragraphElement(mergedChildren));
13440
14489
  }
@@ -13655,50 +14704,6 @@ function cleanInternalFlags(elements) {
13655
14704
  }
13656
14705
  return removeEmptySpansAndAdjacentWhitespace(cleaned ?? elements);
13657
14706
  }
13658
- // packages/parser/src/parser/postprocess/divAdjacentParagraph.ts
13659
- function isParagraphContainer2(el) {
13660
- if (!el || el.element !== "container")
13661
- return false;
13662
- return el.data.type === "paragraph";
13663
- }
13664
- function isDivContainer(el) {
13665
- if (!el || el.element !== "container")
13666
- return false;
13667
- return el.data.type === "div";
13668
- }
13669
- function suppressAtLevel(elements) {
13670
- if (elements.length <= 1)
13671
- return elements;
13672
- const unwrap = Array.from({ length: elements.length }, () => false);
13673
- for (let i = 0;i < elements.length; i++) {
13674
- if (!isParagraphContainer2(elements[i]))
13675
- continue;
13676
- const prevIsDiv = i > 0 && isDivContainer(elements[i - 1]);
13677
- const nextIsDiv = i < elements.length - 1 && isDivContainer(elements[i + 1]);
13678
- if (prevIsDiv || nextIsDiv) {
13679
- unwrap[i] = true;
13680
- }
13681
- }
13682
- const result = [];
13683
- for (let i = 0;i < elements.length; i++) {
13684
- const el = elements[i];
13685
- if (!el)
13686
- continue;
13687
- if (unwrap[i] && el.element === "container") {
13688
- const inner = el.data.elements;
13689
- if (i > 0 && isDivContainer(elements[i - 1])) {
13690
- result.push({ element: "line-break" });
13691
- }
13692
- result.push(...inner);
13693
- } else {
13694
- result.push(el);
13695
- }
13696
- }
13697
- return result;
13698
- }
13699
- function suppressDivAdjacentParagraphs(elements) {
13700
- return suppressAtLevel(elements);
13701
- }
13702
14707
  // packages/parser/src/parser/parse/footnotes.ts
13703
14708
  function containsFootnoteBlock(elements) {
13704
14709
  let found = false;
@@ -13976,7 +14981,9 @@ function substitute(text2) {
13976
14981
  result = result.replace(WHITESPACE_ONLY_LINE, "");
13977
14982
  }
13978
14983
  if (result.indexOf("\\\n") !== -1) {
13979
- result = result.replace(CONCAT_LINES, String.fromCharCode(57344));
14984
+ const sentinels = makeUniqueSentinels(result);
14985
+ const { masked, placeholders } = maskRawRegions(result, sentinels);
14986
+ result = restorePlaceholders(masked.replace(CONCAT_LINES, String.fromCharCode(57344)), placeholders, sentinels);
13980
14987
  }
13981
14988
  if (result.indexOf("\t") !== -1) {
13982
14989
  result = result.replace(TABS, " ");
@@ -14043,14 +15050,26 @@ function replaceDelimitedTypography(text2, opener, closer, leftQuote, rightQuote
14043
15050
  let searchFrom = 0;
14044
15051
  let result = "";
14045
15052
  let lastCopied = 0;
15053
+ let closeIndex = -1;
15054
+ let newlineIndex = -1;
14046
15055
  while (searchFrom < text2.length) {
14047
15056
  const openIndex = text2.indexOf(opener, searchFrom);
14048
15057
  if (openIndex === -1)
14049
15058
  break;
14050
15059
  const contentStart = openIndex + opener.length;
14051
- const closeIndex = text2.indexOf(closer, contentStart);
15060
+ if (closeIndex < contentStart)
15061
+ closeIndex = text2.indexOf(closer, contentStart);
14052
15062
  if (closeIndex === -1)
14053
15063
  break;
15064
+ if (newlineIndex < contentStart) {
15065
+ const nextNewline = text2.indexOf(`
15066
+ `, contentStart);
15067
+ newlineIndex = nextNewline === -1 ? text2.length : nextNewline;
15068
+ }
15069
+ if (newlineIndex < closeIndex) {
15070
+ searchFrom = newlineIndex + 1;
15071
+ continue;
15072
+ }
14054
15073
  result += text2.slice(lastCopied, openIndex);
14055
15074
  result += leftQuote;
14056
15075
  result += text2.slice(contentStart, closeIndex);
@@ -14061,7 +15080,12 @@ function replaceDelimitedTypography(text2, opener, closer, leftQuote, rightQuote
14061
15080
  return lastCopied === 0 ? text2 : result + text2.slice(lastCopied);
14062
15081
  }
14063
15082
  function substitute2(text2) {
14064
- let result = text2;
15083
+ if (!text2.includes("`") && !text2.includes(",,") && !text2.includes("...") && !text2.includes(". . .")) {
15084
+ return text2;
15085
+ }
15086
+ const sentinels = makeUniqueSentinels(text2);
15087
+ const { masked, placeholders } = maskRawRegions(text2, sentinels);
15088
+ let result = masked;
14065
15089
  if (result.includes("``") && result.includes("''")) {
14066
15090
  result = replaceDelimitedTypography(result, "``", "''", LEFT_DOUBLE_QUOTE, RIGHT_DOUBLE_QUOTE);
14067
15091
  }
@@ -14077,7 +15101,7 @@ function substitute2(text2) {
14077
15101
  if (result.includes(". . .")) {
14078
15102
  result = replaceExactEllipsisPattern(result, ". . .");
14079
15103
  }
14080
- return result;
15104
+ return restorePlaceholders(result, placeholders, sentinels);
14081
15105
  }
14082
15106
 
14083
15107
  // packages/parser/src/parser/preprocess/index.ts
@@ -14326,7 +15350,7 @@ async function resolveModulesWithAsyncParse(ast, dataProvider, options) {
14326
15350
  registry.register(ast);
14327
15351
  const parse2 = async (source) => registry.register(await options.parse(source));
14328
15352
  const [listPagesData, listUsersData, tagCloudData] = await Promise.all([
14329
- buildListPagesDataMap(dataProvider, options.requirements.listPages ?? [], options.urlPath),
15353
+ buildListPagesDataMap(dataProvider, options.requirements.listPages ?? [], options.urlPath, options.paginationState),
14330
15354
  buildListUsersDataMap(dataProvider, options.requirements.listUsers ?? []),
14331
15355
  buildTagCloudDataMap(dataProvider, options.requirements.tagCloud ?? [])
14332
15356
  ]);
@@ -14414,9 +15438,9 @@ async function resolveListPagesAsync(module, data, template, parse2) {
14414
15438
  continue;
14415
15439
  const variableContext = {
14416
15440
  page,
14417
- index: i + 1,
15441
+ index: (data.pagination?.offset ?? 0) + i + 1,
14418
15442
  total: data.totalCount,
14419
- limit: module.limit,
15443
+ limit: data.pagination ? data.pagination.limit : module.limit,
14420
15444
  site: data.site
14421
15445
  };
14422
15446
  const parsed = await parse2(template(variableContext));
@@ -14436,6 +15460,7 @@ async function resolveListPagesAsync(module, data, template, parse2) {
14436
15460
  if (module["append-line"] && !module.separate) {
14437
15461
  result.push(...(await parse2(module["append-line"])).elements);
14438
15462
  }
15463
+ result.push(...createListPagesPager(data));
14439
15464
  return module.wrapper ? [
14440
15465
  {
14441
15466
  element: "container",
@@ -14536,6 +15561,9 @@ async function processWikitext(source, options) {
14536
15561
  const dataProvider = createModuleDataProvider(options, callbackContext);
14537
15562
  const parseFragment = parseSource2;
14538
15563
  let ast = initial.ast;
15564
+ const paginationState = { nextUnprefixed: 1 };
15565
+ const requestedPath = options.page.urlPath;
15566
+ const urlPath = !requestedPath || /^\/(?:[?#]|$)/.test(requestedPath) ? `/${options.page.fullName}${requestedPath?.slice(1) ?? ""}` : requestedPath;
14539
15567
  for (let pass = 0;pass < DEFAULT_MODULE_MAX_PASSES; pass++) {
14540
15568
  const extraction = extractDataRequirements(ast);
14541
15569
  if (pass > 0 && !hasResolvableRequirements(extraction.requirements, options.dataProvider)) {
@@ -14546,7 +15574,8 @@ async function processWikitext(source, options) {
14546
15574
  compiledListPagesTemplates: extraction.compiledListPagesTemplates,
14547
15575
  compiledListUsersTemplates: extraction.compiledListUsersTemplates,
14548
15576
  requirements: extraction.requirements,
14549
- urlPath: options.page.urlPath,
15577
+ urlPath,
15578
+ paginationState,
14550
15579
  pageTags: options.page.tags
14551
15580
  });
14552
15581
  ast = resolved.ast;