@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.cjs CHANGED
@@ -82,8 +82,8 @@ module.exports = __toCommonJS(exports_src);
82
82
 
83
83
  // packages/parser/src/build-info.generated.ts
84
84
  var buildInfo = Object.freeze({
85
- version: "5.2.0",
86
- sha: "cc290b0d882de3ed9ea33ec060ddfc8a7b9da59a",
85
+ version: "5.4.0",
86
+ sha: "5e221ddeccc585e64dd1a2eb49b14817c3c1be5d",
87
87
  dirty: false
88
88
  });
89
89
 
@@ -272,13 +272,13 @@ function isValidAnchorNameChar(code) {
272
272
  }
273
273
 
274
274
  // packages/parser/src/lexer/quoted-string.ts
275
- function scanQuotedString(state) {
275
+ function scanQuotedString(state, end = state.source.length) {
276
276
  let quoted = advance(state);
277
- while (!isAtEnd(state) && current(state) !== '"' && current(state) !== `
277
+ while (state.pos < end && current(state) !== '"' && current(state) !== `
278
278
  `) {
279
279
  quoted += advance(state);
280
280
  }
281
- if (current(state) === '"') {
281
+ if (state.pos < end && current(state) === '"') {
282
282
  quoted += advance(state);
283
283
  }
284
284
  return quoted;
@@ -363,6 +363,26 @@ function isCompactPlainTextBoundary(code) {
363
363
  }
364
364
  }
365
365
 
366
+ // packages/parser/src/lexer/url-schemes.ts
367
+ var URL_SCHEME_NAMES = new Set([
368
+ "http",
369
+ "https",
370
+ "ftp",
371
+ "gopher",
372
+ "news",
373
+ "mailto",
374
+ "mms"
375
+ ]);
376
+ var TRAILING_URL_SCHEME = /(?:^|[^A-Za-z0-9])(https?|ftp|gopher|news|mailto|mms)$/;
377
+ var URL_SCHEME_PREFIX = /^(?:(?:https?|ftp|gopher|news|mms):\/\/|mailto:)/;
378
+ var URL_PATTERN = /^(?:(?:https?|ftp|gopher|news|mms):\/\/|mailto:)(?:[^ \\/"']*\/)*[^ \t\n\\/"']*[A-Za-z%0-9/?=&~_]/;
379
+ function startsWithUrlScheme(src, pos) {
380
+ return URL_SCHEME_PREFIX.test(src.slice(pos, pos + 10));
381
+ }
382
+ function startsWithUrl(src, pos) {
383
+ return URL_PATTERN.test(src.slice(pos, pos + 2048));
384
+ }
385
+
366
386
  // packages/parser/src/lexer/syntax-actions.ts
367
387
  function scanSimpleSyntaxToken(src, pos, isLineStart) {
368
388
  switch (src[pos]) {
@@ -399,7 +419,7 @@ function scanSimpleSyntaxToken(src, pos, isLineStart) {
399
419
  }
400
420
  }
401
421
  function scanStarToken(src, pos, isLineStart) {
402
- if (src[pos + 1] === "*") {
422
+ if (src[pos + 1] === "*" && !startsWithUrl(src, pos + 2)) {
403
423
  return token("BOLD_MARKER", "**");
404
424
  }
405
425
  return isLineStart ? token("LIST_BULLET", "*") : token("STAR", "*");
@@ -585,7 +605,13 @@ function scanTextToken(src, pos) {
585
605
  return token3("TEXT", char);
586
606
  }
587
607
  function scanCompactTextToken(src, pos) {
588
- const end = findCompactPlainTextRunEnd(src, pos);
608
+ let end = findCompactPlainTextRunEnd(src, pos);
609
+ if (end > pos && src[end] === ":") {
610
+ const match = TRAILING_URL_SCHEME.exec(src.slice(pos, end));
611
+ if (match) {
612
+ end -= match[1].length;
613
+ }
614
+ }
589
615
  return end > pos ? runToken3(src, pos, end, "TEXT") : null;
590
616
  }
591
617
  function token3(type, value) {
@@ -631,6 +657,8 @@ class Lexer {
631
657
  splitBlockClosePositions = new Set;
632
658
  lastNonWhitespaceType = null;
633
659
  blockOpenerDepth = 0;
660
+ rawTagBounds = null;
661
+ rawClosesExhausted = false;
634
662
  constructor(source, options = {}) {
635
663
  this.options = {
636
664
  trackPositions: options.trackPositions ?? true,
@@ -651,8 +679,8 @@ class Lexer {
651
679
  current() {
652
680
  return current(this.state);
653
681
  }
654
- findInvalidAnchorNameEnd() {
655
- return findInvalidAnchorNameEnd(this.state.source, this.state.pos);
682
+ findInvalidAnchorNameEnd(source) {
683
+ return findInvalidAnchorNameEnd(source, this.state.pos);
656
684
  }
657
685
  advance(n = 1) {
658
686
  return advance(this.state, n);
@@ -664,6 +692,18 @@ class Lexer {
664
692
  this.state.tokens.push(createLexerToken(this.state, type, value, this.options.trackPositions));
665
693
  this.lastNonWhitespaceType = updateLastNonWhitespaceType(this.lastNonWhitespaceType, type);
666
694
  this.blockOpenerDepth = nextBlockOpenerDepth(this.blockOpenerDepth, type);
695
+ if (this.rawTagBounds === null && !this.rawClosesExhausted && (value.toLowerCase() === "button" || value.toLowerCase() === "social") && this.state.tokens.at(-2)?.type === "BLOCK_OPEN" && /\s/.test(this.current())) {
696
+ const close = this.state.source.indexOf("]]", this.state.pos);
697
+ if (close < 0) {
698
+ this.rawClosesExhausted = true;
699
+ } else {
700
+ this.rawTagBounds = {
701
+ source: this.state.source.slice(0, close),
702
+ close,
703
+ outerDepth: this.blockOpenerDepth - 1
704
+ };
705
+ }
706
+ }
667
707
  }
668
708
  emitTokenAction(action) {
669
709
  advanceByToken(this.state, action.type, action.length, action.value);
@@ -681,7 +721,14 @@ class Lexer {
681
721
  scanToken() {
682
722
  const char = this.current();
683
723
  const isLineStart = isSyntaxLineStart(this.state);
684
- const src = this.state.source;
724
+ const bounds = this.rawTagBounds;
725
+ const src = bounds?.source ?? this.state.source;
726
+ if (bounds && this.state.pos === bounds.close) {
727
+ this.emitTokenAction({ type: "BLOCK_CLOSE", value: "]]", length: 2 });
728
+ this.blockOpenerDepth = bounds.outerDepth;
729
+ this.rawTagBounds = null;
730
+ return;
731
+ }
685
732
  const spacingAction = scanSpacingToken(src, this.state.pos);
686
733
  if (spacingAction) {
687
734
  this.emitTokenAction(limitBlockquotePrefixSpace(spacingAction, this.state.tokens.at(-1)));
@@ -694,7 +741,7 @@ class Lexer {
694
741
  lineStart: isLineStart,
695
742
  physicalLineStart: this.state.lineStart,
696
743
  splitBlockClose: this.splitBlockClosePositions.has(this.state.pos),
697
- findInvalidAnchorNameEnd: () => this.findInvalidAnchorNameEnd()
744
+ findInvalidAnchorNameEnd: () => this.findInvalidAnchorNameEnd(src)
698
745
  });
699
746
  if (punctuation.handled) {
700
747
  if (punctuation.clearSplitBlockCloseAt !== undefined) {
@@ -709,7 +756,7 @@ class Lexer {
709
756
  if (char === '"') {
710
757
  const lastNonWs = this.lastNonWhitespaceTokenType();
711
758
  if (this.blockOpenerDepth > 0 && lastNonWs === "EQUALS") {
712
- this.addToken("QUOTED_STRING", scanQuotedString(this.state));
759
+ this.addToken("QUOTED_STRING", scanQuotedString(this.state, src.length));
713
760
  return;
714
761
  }
715
762
  this.advance();
@@ -897,7 +944,7 @@ function parseBlockItem(ctx) {
897
944
  }
898
945
  }
899
946
  const fallback = ctx.blockFallbackRule.parse(ctx);
900
- if (fallback.success && fallback.elements.length > 0) {
947
+ if (fallback.success) {
901
948
  return { elements: fallback.elements, consumed: fallback.consumed };
902
949
  }
903
950
  return { elements: [], consumed: 1 };
@@ -945,7 +992,8 @@ function parseBlocksUntil(ctx, closeCondition, options) {
945
992
  blockCtx.pos = pos;
946
993
  blockCtx.scope = blockScope;
947
994
  const result = parseBlockItem(blockCtx);
948
- elements.push(...result.elements);
995
+ for (const element of result.elements)
996
+ elements.push(element);
949
997
  consumed += result.consumed;
950
998
  pos += result.consumed;
951
999
  }
@@ -972,6 +1020,42 @@ function getExcludedBlockRules(blockRules, excluded) {
972
1020
  byExcluded.set(excluded, filtered);
973
1021
  return filtered;
974
1022
  }
1023
+ // packages/parser/src/parser/rules/inline/parsing/automatic-line-break.ts
1024
+ var origins = new WeakMap;
1025
+ function createAutomaticLineBreak(token5) {
1026
+ const element = { element: "line-break" };
1027
+ origins.set(element, token5);
1028
+ return element;
1029
+ }
1030
+ function precedingSingleNewline(ctx) {
1031
+ let pos = ctx.pos - 1;
1032
+ while (ctx.tokens[pos]?.type === "WHITESPACE")
1033
+ pos--;
1034
+ const newline = ctx.tokens[pos];
1035
+ if (newline?.type !== "NEWLINE")
1036
+ return;
1037
+ pos--;
1038
+ while (ctx.tokens[pos]?.type === "WHITESPACE")
1039
+ pos--;
1040
+ if (ctx.tokens[pos]?.type === "NEWLINE")
1041
+ return;
1042
+ return newline;
1043
+ }
1044
+ function stripAutomaticLineBreak(elements, token5) {
1045
+ if (!token5)
1046
+ return;
1047
+ let index = elements.length - 1;
1048
+ while (index >= 0) {
1049
+ const element2 = elements[index];
1050
+ if (element2?.element !== "text" || !/^[ \t]*$/.test(element2.data))
1051
+ break;
1052
+ index--;
1053
+ }
1054
+ const element = elements[index];
1055
+ if (element && origins.get(element) === token5)
1056
+ elements.length = index;
1057
+ }
1058
+
975
1059
  // packages/parser/src/parser/rules/inline/parsing/rules.ts
976
1060
  function canApplyInlineRule(rule, token5) {
977
1061
  if (rule.startTokens.length === 0) {
@@ -994,18 +1078,479 @@ function getCandidateInlineRules(inlineRules, tokenType) {
994
1078
  byType.set(tokenType, candidates);
995
1079
  return candidates;
996
1080
  }
1081
+ // packages/parser/src/parser/rules/inline/parsing/raw-tag.ts
1082
+ var unclosedRanges = new WeakMap;
1083
+ function findRawTagClose(tokens, start, end) {
1084
+ const unclosed = unclosedRanges.get(tokens);
1085
+ if (unclosed && start >= unclosed.start && end <= unclosed.end)
1086
+ return null;
1087
+ for (let close = start;close < end; close++) {
1088
+ if (tokens[close]?.type === "BLOCK_CLOSE")
1089
+ return close;
1090
+ if (!tokens[close] || tokens[close]?.type === "EOF")
1091
+ break;
1092
+ }
1093
+ unclosedRanges.set(tokens, { start, end });
1094
+ return null;
1095
+ }
1096
+
1097
+ // packages/parser/src/parser/rules/inline/button/attributes.ts
1098
+ function parseButtonAttributes(source) {
1099
+ const sections = source.trim().split('="');
1100
+ const attrs = {};
1101
+ let key = sections[0].trim();
1102
+ for (const section of sections.slice(1)) {
1103
+ const quote = section.lastIndexOf('"');
1104
+ if (quote < 0)
1105
+ continue;
1106
+ if (key === "text" || key === "class" || key === "style") {
1107
+ attrs[key] = section.slice(0, quote).replace(/\\([\s\S]|$)/g, (_match, char) => char === "0" ? "\x00" : char);
1108
+ }
1109
+ key = section.slice(quote + 1).trim();
1110
+ }
1111
+ return attrs;
1112
+ }
1113
+
1114
+ // packages/parser/src/parser/rules/inline/button/syntax.ts
1115
+ function parseButtonSyntax(ctx, start, end) {
1116
+ const tokens = ctx.tokens;
1117
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "button")
1118
+ return null;
1119
+ let pos = start + 2;
1120
+ const skipSpace = () => {
1121
+ const before = pos;
1122
+ while (pos < end && /^\s+$/.test(tokens[pos]?.value ?? ""))
1123
+ pos++;
1124
+ return pos > before;
1125
+ };
1126
+ if (!skipSpace())
1127
+ return null;
1128
+ let action = "";
1129
+ while (pos < end && tokens[pos]?.type !== "BLOCK_CLOSE" && !/^\s+$/.test(tokens[pos]?.value ?? "")) {
1130
+ const part = tokens[pos]?.value ?? "";
1131
+ if (!/^[a-z0-9_-]+$/i.test(part))
1132
+ return null;
1133
+ action += part;
1134
+ pos++;
1135
+ }
1136
+ if (!action)
1137
+ return null;
1138
+ action = action.replaceAll("_", "-");
1139
+ skipSpace();
1140
+ const close = findRawTagClose(tokens, pos, end);
1141
+ if (close === null)
1142
+ return null;
1143
+ const attrs = parseButtonAttributes(tokens.slice(pos, close).map((token5) => token5.value).join(""));
1144
+ const value = (key) => attrs[key] && attrs[key] !== "0" ? attrs[key] : null;
1145
+ const attributes = {};
1146
+ for (const name of ["class", "style"]) {
1147
+ const attr = value(name);
1148
+ if (attr !== null)
1149
+ attributes[name] = attr;
1150
+ }
1151
+ return { data: { action, text: value("text"), attributes }, end: close + 1 };
1152
+ }
1153
+
1154
+ // packages/parser/src/parser/rules/inline/social/syntax.ts
1155
+ var invalidBodies = new WeakMap;
1156
+ function parseSocialSyntax(ctx, start, end) {
1157
+ const tokens = ctx.tokens;
1158
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "social")
1159
+ return null;
1160
+ const bodyStart = start + 2;
1161
+ if (tokens[bodyStart]?.type !== "BLOCK_CLOSE" && !/^\s+$/.test(tokens[bodyStart]?.value ?? ""))
1162
+ return null;
1163
+ const invalid = invalidBodies.get(tokens);
1164
+ if (invalid && bodyStart >= invalid.start && bodyStart <= invalid.bracket)
1165
+ return null;
1166
+ const close = findRawTagClose(tokens, bodyStart, end);
1167
+ if (close === null)
1168
+ return null;
1169
+ for (let pos = bodyStart;pos < close; pos++) {
1170
+ if (tokens[pos].value.includes("]")) {
1171
+ invalidBodies.set(tokens, { start: bodyStart, bracket: pos });
1172
+ return null;
1173
+ }
1174
+ }
1175
+ const body = tokens.slice(bodyStart, close).map((token5) => token5.value).join("");
1176
+ const sites = body.trim() ? body.split(",").map((site) => site.trim().toLowerCase()).filter(Boolean) : null;
1177
+ return { data: { sites }, end: close + 1 };
1178
+ }
1179
+
1180
+ // packages/parser/src/parser/rules/block/parsing/attributes/names.ts
1181
+ function consumeAttributeName(ctx, startPos, startConsumed, startName, options) {
1182
+ if (startName === "_" && isAttributeWordToken(ctx.tokens[startPos])) {
1183
+ startName += ctx.tokens[startPos]?.value ?? "";
1184
+ startPos++;
1185
+ startConsumed++;
1186
+ }
1187
+ return options.strikeHyphens ? consumeRawNameSuffix(ctx, startPos, startConsumed, startName, options.hyphenatedNames) : consumeSafeNameSuffix(ctx, startPos, startConsumed, startName);
1188
+ }
1189
+ function isAttributeNameToken(token5) {
1190
+ return isAttributeWordToken(token5) || token5?.type === "UNDERSCORE";
1191
+ }
1192
+ function consumeSafeNameSuffix(ctx, startPos, startConsumed, startName) {
1193
+ let name = startName;
1194
+ let pos = startPos;
1195
+ let consumed = startConsumed;
1196
+ while (isAttributeNameSeparator(ctx.tokens[pos]) && isAttributeWordToken(ctx.tokens[pos + 1])) {
1197
+ name += ctx.tokens[pos]?.value ?? "";
1198
+ pos++;
1199
+ consumed++;
1200
+ name += ctx.tokens[pos]?.value ?? "";
1201
+ pos++;
1202
+ consumed++;
1203
+ }
1204
+ return { name, pos, consumed };
1205
+ }
1206
+ function consumeRawNameSuffix(ctx, startPos, startConsumed, startName, hyphenatedNames) {
1207
+ let name = startName;
1208
+ let pos = startPos;
1209
+ let consumed = startConsumed;
1210
+ while (isAttributeNameSeparator(ctx.tokens[pos])) {
1211
+ while (isAttributeNameSeparator(ctx.tokens[pos])) {
1212
+ if (hyphenatedNames || ctx.tokens[pos]?.type === "UNDERSCORE") {
1213
+ name += ctx.tokens[pos]?.value ?? "-";
1214
+ }
1215
+ pos++;
1216
+ consumed++;
1217
+ }
1218
+ if (!isAttributeWordToken(ctx.tokens[pos])) {
1219
+ break;
1220
+ }
1221
+ if (hyphenatedNames || name.endsWith("_")) {
1222
+ name += ctx.tokens[pos]?.value ?? "";
1223
+ }
1224
+ pos++;
1225
+ consumed++;
1226
+ }
1227
+ return { name, pos, consumed };
1228
+ }
1229
+ function isAttributeWordToken(token5) {
1230
+ return token5?.type === "TEXT" || token5?.type === "IDENTIFIER";
1231
+ }
1232
+ function isAttributeNameSeparator(token5) {
1233
+ return token5?.type === "TEXT" && token5.value === "-" || token5?.type === "STRIKE_MARKER" || token5?.type === "UNDERSCORE";
1234
+ }
1235
+
1236
+ // packages/parser/src/parser/rules/block/parsing/attributes/values.ts
1237
+ function consumeAttributeValue(token5) {
1238
+ if (!token5) {
1239
+ return null;
1240
+ }
1241
+ if (token5.type === "QUOTED_STRING") {
1242
+ return { value: stripQuotes(token5.value) };
1243
+ }
1244
+ if (isAttributeNameToken(token5)) {
1245
+ return { value: token5.value };
1246
+ }
1247
+ return null;
1248
+ }
1249
+ function stripQuotes(value) {
1250
+ return value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
1251
+ }
1252
+
1253
+ // packages/parser/src/parser/rules/block/parsing/attributes/scanner.ts
1254
+ function scanAttributes(ctx, startPos, options) {
1255
+ const attrs = {};
1256
+ let pos = startPos;
1257
+ let consumed = 0;
1258
+ while (pos < ctx.tokens.length) {
1259
+ const token5 = ctx.tokens[pos];
1260
+ if (!token5 || isAttributeTerminator(token5)) {
1261
+ break;
1262
+ }
1263
+ if (token5.type === "WHITESPACE") {
1264
+ pos++;
1265
+ consumed++;
1266
+ continue;
1267
+ }
1268
+ if (!isAttributeNameToken(token5)) {
1269
+ pos++;
1270
+ consumed++;
1271
+ continue;
1272
+ }
1273
+ let name = token5.value;
1274
+ pos++;
1275
+ consumed++;
1276
+ const nameResult = consumeAttributeName(ctx, pos, consumed, name, options);
1277
+ pos = nameResult.pos;
1278
+ consumed = nameResult.consumed;
1279
+ name = nameResult.name.toLowerCase();
1280
+ if (ctx.tokens[pos]?.type !== "EQUALS") {
1281
+ attrs[name] = "true";
1282
+ continue;
1283
+ }
1284
+ pos++;
1285
+ consumed++;
1286
+ const valueResult = consumeAttributeValue(ctx.tokens[pos]);
1287
+ if (!valueResult) {
1288
+ continue;
1289
+ }
1290
+ attrs[name] = valueResult.value;
1291
+ pos++;
1292
+ consumed++;
1293
+ }
1294
+ return { attrs, consumed };
1295
+ }
1296
+ function isAttributeTerminator(token5) {
1297
+ return token5.type === "BLOCK_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF";
1298
+ }
1299
+
1300
+ // packages/parser/src/parser/rules/block/parsing/attributes/index.ts
1301
+ function parseAttributes(ctx, startPos) {
1302
+ const result = scanAttributes(ctx, startPos, { hyphenatedNames: true, strikeHyphens: false });
1303
+ return { attrs: filterUnsafeAttributes(result.attrs), consumed: result.consumed };
1304
+ }
1305
+ function parseAttributesRaw(ctx, startPos, hyphenatedNames = true) {
1306
+ return scanAttributes(ctx, startPos, { hyphenatedNames, strikeHyphens: true });
1307
+ }
1308
+
1309
+ // packages/parser/src/parser/rules/inline/date/syntax.ts
1310
+ function parseDateSyntax(ctx, start, end) {
1311
+ const tokens = ctx.tokens;
1312
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value !== "date")
1313
+ return null;
1314
+ let pos = start + 2;
1315
+ const skipSpace = () => {
1316
+ const before = pos;
1317
+ while (pos < end && /^(?:\s+)$/.test(tokens[pos]?.value ?? ""))
1318
+ pos++;
1319
+ return pos > before;
1320
+ };
1321
+ if (!skipSpace())
1322
+ return null;
1323
+ const value = tokens[pos]?.value ?? "";
1324
+ if (pos >= end || !/^\d+$/.test(value))
1325
+ return null;
1326
+ const timestamp = Number(value);
1327
+ if (!Number.isSafeInteger(timestamp) || timestamp > 8640000000000)
1328
+ return null;
1329
+ pos++;
1330
+ const separator = skipSpace();
1331
+ if (tokens[pos]?.type !== "BLOCK_CLOSE" && !separator)
1332
+ return null;
1333
+ let close = pos;
1334
+ for (;close < end; close++) {
1335
+ const token5 = tokens[close];
1336
+ if (!token5 || token5.type === "EOF" || token5.type === "BLOCK_OPEN" || /[\r\n]/.test(token5.value))
1337
+ return null;
1338
+ if (token5.type === "BLOCK_CLOSE")
1339
+ break;
1340
+ }
1341
+ if (close >= end)
1342
+ return null;
1343
+ const attributes = parseAttributesRaw(ctx, pos).attrs;
1344
+ const format = attributes.format || null;
1345
+ return {
1346
+ data: {
1347
+ value: { timestamp, timezone: "UTC" },
1348
+ format,
1349
+ hover: format?.split("|").slice(1).includes("agohover") ?? false
1350
+ },
1351
+ end: close + 1
1352
+ };
1353
+ }
1354
+
1355
+ // packages/parser/src/parser/rules/inline/email/scan.ts
1356
+ function isDomainChar(code) {
1357
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 45;
1358
+ }
1359
+ function isLocalChar(code) {
1360
+ return isDomainChar(code) || code === 95;
1361
+ }
1362
+ function scanEmail(source, start, commentEnd) {
1363
+ const comments = [];
1364
+ let pos = start;
1365
+ const skipComments = () => {
1366
+ let end2 = commentEnd(pos);
1367
+ while (end2 > pos) {
1368
+ comments.push({ start: pos, end: end2 });
1369
+ pos = end2;
1370
+ end2 = commentEnd(pos);
1371
+ }
1372
+ };
1373
+ let localPart = false;
1374
+ while (pos < source.length) {
1375
+ skipComments();
1376
+ if (isLocalChar(source.charCodeAt(pos))) {
1377
+ localPart = true;
1378
+ pos++;
1379
+ } else if (source[pos] === "." && localPart) {
1380
+ localPart = false;
1381
+ pos++;
1382
+ } else
1383
+ break;
1384
+ }
1385
+ const localEnd = pos;
1386
+ if (!localPart || source[pos] !== "@")
1387
+ return { localEnd, comments };
1388
+ pos++;
1389
+ let domainPart = false;
1390
+ let dots = 0;
1391
+ let end = -1;
1392
+ while (pos < source.length) {
1393
+ skipComments();
1394
+ if (isDomainChar(source.charCodeAt(pos))) {
1395
+ domainPart = true;
1396
+ pos++;
1397
+ if (dots > 0)
1398
+ end = pos;
1399
+ } else if (source[pos] === "." && domainPart) {
1400
+ domainPart = false;
1401
+ dots++;
1402
+ pos++;
1403
+ } else
1404
+ break;
1405
+ }
1406
+ if (end === -1)
1407
+ return { localEnd, comments };
1408
+ const parts = [];
1409
+ let copied = start;
1410
+ for (const comment of comments) {
1411
+ if (comment.start >= end)
1412
+ break;
1413
+ parts.push(source.slice(copied, comment.start));
1414
+ copied = comment.end;
1415
+ }
1416
+ parts.push(source.slice(copied, end));
1417
+ return { end, address: parts.join(""), localEnd, comments };
1418
+ }
1419
+
1420
+ // packages/parser/src/parser/rules/inline/email/candidates.ts
1421
+ var caches = new WeakMap;
1422
+ var EMAIL_START_TOKENS = [
1423
+ "TEXT",
1424
+ "IDENTIFIER",
1425
+ "UNDERSCORE",
1426
+ "UNDERLINE_MARKER",
1427
+ "STRIKE_MARKER"
1428
+ ];
1429
+ function getCache(tokens) {
1430
+ let cache = caches.get(tokens);
1431
+ if (cache)
1432
+ return cache;
1433
+ const offsets = [0];
1434
+ const values = [];
1435
+ for (const token5 of tokens) {
1436
+ values.push(token5.value);
1437
+ offsets.push(offsets.at(-1) + token5.value.length);
1438
+ }
1439
+ cache = {
1440
+ source: values.join(""),
1441
+ offsets,
1442
+ candidates: new Map,
1443
+ commentEnds: new Map,
1444
+ unclosedComment: Infinity
1445
+ };
1446
+ caches.set(tokens, cache);
1447
+ return cache;
1448
+ }
1449
+ function commentEnd(cache, pos) {
1450
+ const opener = cache.source[pos] === `
1451
+ ` ? pos + 1 : pos;
1452
+ if (!cache.source.startsWith("[!--", opener))
1453
+ return pos;
1454
+ const cached = cache.commentEnds.get(pos);
1455
+ if (cached !== undefined)
1456
+ return cached;
1457
+ const close = opener >= cache.unclosedComment ? -1 : cache.source.indexOf("--]", opener + 4);
1458
+ if (close === -1)
1459
+ cache.unclosedComment = Math.min(cache.unclosedComment, opener);
1460
+ const end = close === -1 ? pos : close + 3;
1461
+ cache.commentEnds.set(pos, end);
1462
+ return end;
1463
+ }
1464
+ function getEmailCandidate(tokens, index) {
1465
+ const token5 = tokens[index];
1466
+ if (!token5 || !EMAIL_START_TOKENS.includes(token5.type))
1467
+ return null;
1468
+ const cache = getCache(tokens);
1469
+ if (cache.candidates.has(index))
1470
+ return cache.candidates.get(index);
1471
+ const value = tokens[index]?.value ?? "";
1472
+ let suffix = value.length;
1473
+ while (suffix > 0 && (isLocalChar(value.charCodeAt(suffix - 1)) || value[suffix - 1] === "."))
1474
+ suffix--;
1475
+ const doubled = value.lastIndexOf("..");
1476
+ if (doubled >= suffix)
1477
+ suffix = doubled + 2;
1478
+ while (value[suffix] === ".")
1479
+ suffix++;
1480
+ if (suffix >= value.length || !isLocalChar(value.charCodeAt(suffix))) {
1481
+ cache.candidates.set(index, null);
1482
+ return null;
1483
+ }
1484
+ const start = cache.offsets[index] + suffix;
1485
+ const scanned = scanEmail(cache.source, start, (pos) => commentEnd(cache, pos));
1486
+ if (scanned.end === undefined) {
1487
+ cache.candidates.set(index, null);
1488
+ let comment = 0;
1489
+ for (let i = index + 1;i < tokens.length && cache.offsets[i + 1] <= scanned.localEnd; i++) {
1490
+ const offset = cache.offsets[i];
1491
+ while (scanned.comments[comment] && scanned.comments[comment].end <= offset)
1492
+ comment++;
1493
+ const region = scanned.comments[comment];
1494
+ if (!region || offset < region.start)
1495
+ cache.candidates.set(i, null);
1496
+ }
1497
+ return null;
1498
+ }
1499
+ let endToken = index;
1500
+ while (cache.offsets[endToken + 1] < scanned.end)
1501
+ endToken++;
1502
+ const candidate = { start, end: scanned.end, endToken, address: scanned.address };
1503
+ cache.candidates.set(index, candidate);
1504
+ return candidate;
1505
+ }
1506
+ function getEmailGroup(tokens, index, limit) {
1507
+ const first = getEmailCandidate(tokens, index);
1508
+ if (!first || first.endToken >= limit)
1509
+ return null;
1510
+ const candidates = [first];
1511
+ let last = first;
1512
+ while (last.endToken > index) {
1513
+ const next = getEmailCandidate(tokens, last.endToken);
1514
+ if (!next || next.start < last.end || next.endToken >= limit)
1515
+ break;
1516
+ candidates.push(next);
1517
+ last = next;
1518
+ }
1519
+ const cache = getCache(tokens);
1520
+ return {
1521
+ candidates,
1522
+ source: cache.source,
1523
+ start: cache.offsets[index],
1524
+ end: cache.offsets[last.endToken + 1],
1525
+ endToken: last.endToken + 1
1526
+ };
1527
+ }
1528
+ function emailRegionEnd(tokens, index, limit) {
1529
+ return getEmailGroup(tokens, index, limit)?.endToken ?? index;
1530
+ }
1531
+
997
1532
  // packages/parser/src/parser/rules/inline/raw/end.ts
1533
+ var unclosedAngleRanges = new WeakMap;
998
1534
  function rawRegionEnd(tokens, start, end) {
999
1535
  const type = tokens[start]?.type;
1000
1536
  const close = type === "RAW_OPEN" ? "RAW_OPEN" : type === "RAW_BLOCK_OPEN" ? "RAW_BLOCK_CLOSE" : null;
1001
1537
  if (!close)
1002
1538
  return start;
1539
+ const cached = type === "RAW_BLOCK_OPEN" ? unclosedAngleRanges.get(tokens) : undefined;
1540
+ if (cached && start >= cached.start && start < cached.end && (cached.lineEnd || end <= cached.end)) {
1541
+ return start;
1542
+ }
1003
1543
  for (let pos = start + 1;pos < end; pos++) {
1004
- if (tokens[pos]?.type === "NEWLINE" || tokens[pos]?.type === "EOF")
1005
- break;
1544
+ if (tokens[pos]?.type === "NEWLINE" || tokens[pos]?.type === "EOF") {
1545
+ if (type === "RAW_BLOCK_OPEN")
1546
+ unclosedAngleRanges.set(tokens, { start, end: pos, lineEnd: true });
1547
+ return start;
1548
+ }
1006
1549
  if (tokens[pos]?.type === close)
1007
1550
  return pos + 1;
1008
1551
  }
1552
+ if (type === "RAW_BLOCK_OPEN")
1553
+ unclosedAngleRanges.set(tokens, { start, end, lineEnd: false });
1009
1554
  return start;
1010
1555
  }
1011
1556
  function protectedInlineRegionEnd(tokens, start, end) {
@@ -1024,7 +1569,7 @@ var MIN_INLINE_TEXT_RUN_LENGTH = 32;
1024
1569
  var MIN_INLINE_TEXT_RUN_DOCUMENT_TOKENS = 1e5;
1025
1570
  function collectLongPlainTextRun(ctx, startPos, endType) {
1026
1571
  const firstToken = ctx.tokens[startPos];
1027
- if (firstToken?.type === "TEXT" && firstToken.value.length >= MIN_INLINE_TEXT_RUN_LENGTH && firstToken.value !== "(") {
1572
+ if (!getEmailCandidate(ctx.tokens, startPos) && firstToken?.type === "TEXT" && firstToken.value.length >= MIN_INLINE_TEXT_RUN_LENGTH && firstToken.value !== "(") {
1028
1573
  return { value: firstToken.value, consumed: 1 };
1029
1574
  }
1030
1575
  let pos = startPos;
@@ -1046,10 +1591,10 @@ function collectLongPlainTextRun(ctx, startPos, endType) {
1046
1591
  }
1047
1592
  function isPlainTextRunToken(ctx, pos) {
1048
1593
  const token5 = ctx.tokens[pos];
1049
- if (!token5)
1594
+ if (!token5 || getEmailCandidate(ctx.tokens, pos))
1050
1595
  return false;
1051
1596
  if (token5.type === "IDENTIFIER") {
1052
- return true;
1597
+ return !(URL_SCHEME_NAMES.has(token5.value) && ctx.tokens[pos + 1]?.type === "COLON");
1053
1598
  }
1054
1599
  if (token5.type === "WHITESPACE") {
1055
1600
  const next = ctx.tokens[pos + 1];
@@ -1198,6 +1743,10 @@ var BLOCK_START_TOKENS = [
1198
1743
  "HEADING_MARKER",
1199
1744
  "HR_MARKER",
1200
1745
  "TABLE_MARKER",
1746
+ "TABLE_HEADER",
1747
+ "TABLE_LEFT",
1748
+ "TABLE_CENTER",
1749
+ "TABLE_RIGHT",
1201
1750
  "COLON",
1202
1751
  "BLOCK_OPEN",
1203
1752
  "BLOCK_END_OPEN",
@@ -1275,14 +1824,727 @@ var INDENT_ACCEPTING_BLOCK_NAMES = new Set([
1275
1824
  "tabs"
1276
1825
  ]);
1277
1826
 
1278
- // packages/parser/src/parser/rules/inline/parsing/block-boundary.ts
1279
- function isExcludedBlockToken(ctx, tokenPos) {
1280
- const excluded = ctx.scope.excludedBlockNames;
1281
- if (!excluded?.size)
1282
- return false;
1283
- const token5 = ctx.tokens[tokenPos];
1284
- if (token5?.type !== "BLOCK_OPEN" && token5?.type !== "BLOCK_END_OPEN")
1285
- return false;
1827
+ // packages/parser/src/parser/rules/block/code/open.ts
1828
+ var attributeRanges = new WeakMap;
1829
+ function findAttributesEnd(tokens, start) {
1830
+ const cached = attributeRanges.get(tokens);
1831
+ if (cached && start >= cached.start && start <= cached.end)
1832
+ return cached.end;
1833
+ let end = start;
1834
+ while (end < tokens.length) {
1835
+ const type = tokens[end]?.type;
1836
+ if (type === "BLOCK_CLOSE" || type === "NEWLINE" || type === "EOF")
1837
+ break;
1838
+ end++;
1839
+ }
1840
+ attributeRanges.set(tokens, { start, end });
1841
+ return end;
1842
+ }
1843
+ function findCodeOpen(tokens, start) {
1844
+ const name = tokens[start + 1];
1845
+ if (tokens[start]?.type !== "BLOCK_OPEN" || name?.type !== "IDENTIFIER" && name?.type !== "TEXT" || name.value.toLowerCase() !== "code" || tokens[start + 2]?.type === "UNDERSCORE")
1846
+ return null;
1847
+ const attributesEnd = findAttributesEnd(tokens, start + 2);
1848
+ const repaired = tokens[attributesEnd]?.type !== "BLOCK_CLOSE";
1849
+ const last = tokens[attributesEnd - 1];
1850
+ if (repaired && (last?.type !== "QUOTED_STRING" || !last.value.includes("]]")))
1851
+ return null;
1852
+ let bodyStart = attributesEnd + (repaired ? 0 : 1);
1853
+ if (tokens[bodyStart]?.type === "NEWLINE")
1854
+ bodyStart++;
1855
+ return {
1856
+ attributesEnd,
1857
+ bodyStart,
1858
+ repaired,
1859
+ closingSwallowed: repaired && last.value.includes("[[/code]]")
1860
+ };
1861
+ }
1862
+
1863
+ // packages/parser/src/parser/rules/block/code/boundary.ts
1864
+ var caches2 = new WeakMap;
1865
+ function codeCloseEnd(tokens, start) {
1866
+ if (tokens[start]?.type !== "BLOCK_END_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "code")
1867
+ return start;
1868
+ let end = start + 2;
1869
+ while (tokens[end]?.type === "WHITESPACE")
1870
+ end++;
1871
+ return tokens[end]?.type === "BLOCK_CLOSE" ? end + 1 : start;
1872
+ }
1873
+ function nestedCodeBodyStart(tokens, start) {
1874
+ if (tokens[start]?.type !== "BLOCK_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "code")
1875
+ return start;
1876
+ for (let pos = start + 2;pos < tokens.length; pos++) {
1877
+ const type = tokens[pos]?.type;
1878
+ if (type === "NEWLINE" || type === "EOF" || type === "BLOCK_OPEN")
1879
+ return start;
1880
+ if (type === "BLOCK_CLOSE") {
1881
+ const open = findCodeOpen(tokens, start);
1882
+ return open && !open.repaired ? open.bodyStart : start;
1883
+ }
1884
+ }
1885
+ return start;
1886
+ }
1887
+ function findCodeBodyBounds(tokens, start) {
1888
+ let cache = caches2.get(tokens);
1889
+ if (!cache) {
1890
+ cache = new Map;
1891
+ caches2.set(tokens, cache);
1892
+ }
1893
+ const cached = cache.get(start);
1894
+ if (cached)
1895
+ return cached;
1896
+ const stack = [start];
1897
+ let pos = start;
1898
+ while (pos < tokens.length && tokens[pos]?.type !== "EOF") {
1899
+ const rawEnd = rawRegionEnd(tokens, pos, tokens.length);
1900
+ if (rawEnd > pos) {
1901
+ pos = rawEnd;
1902
+ continue;
1903
+ }
1904
+ const end = codeCloseEnd(tokens, pos);
1905
+ if (end > pos) {
1906
+ const result2 = { closeStart: pos, end, foundClose: true };
1907
+ cache.set(stack.pop(), result2);
1908
+ if (stack.length === 0)
1909
+ return result2;
1910
+ pos = end;
1911
+ continue;
1912
+ }
1913
+ const bodyStart = nestedCodeBodyStart(tokens, pos);
1914
+ if (bodyStart > pos) {
1915
+ const child = cache.get(bodyStart);
1916
+ if (child) {
1917
+ pos = child.end;
1918
+ if (!child.foundClose)
1919
+ break;
1920
+ } else {
1921
+ stack.push(bodyStart);
1922
+ pos = bodyStart;
1923
+ }
1924
+ continue;
1925
+ }
1926
+ pos++;
1927
+ }
1928
+ const result = { closeStart: pos, end: pos, foundClose: false };
1929
+ for (const bodyStart of stack)
1930
+ cache.set(bodyStart, result);
1931
+ return result;
1932
+ }
1933
+
1934
+ // packages/parser/src/parser/rules/block/math/boundary.ts
1935
+ var nameRanges = new WeakMap;
1936
+ var bodyCaches = new WeakMap;
1937
+ function findNameEnd(tokens, start) {
1938
+ const cached = nameRanges.get(tokens);
1939
+ if (cached && start >= cached.start && start <= cached.end)
1940
+ return cached.end;
1941
+ let end = start;
1942
+ while (end < tokens.length) {
1943
+ const type = tokens[end]?.type;
1944
+ if (type === "BLOCK_CLOSE" || type === "WHITESPACE" || type === "NEWLINE" || type === "EOF")
1945
+ break;
1946
+ end++;
1947
+ }
1948
+ nameRanges.set(tokens, { start, end });
1949
+ return end;
1950
+ }
1951
+ function findMathOpen(tokens, start) {
1952
+ const name = tokens[start + 1];
1953
+ if (tokens[start]?.type !== "BLOCK_OPEN" || name?.type !== "IDENTIFIER" && name?.type !== "TEXT" || name.value.toLowerCase() !== "math" || tokens[start + 2]?.type === "UNDERSCORE")
1954
+ return null;
1955
+ let pos = start + 2;
1956
+ while (tokens[pos]?.type === "WHITESPACE")
1957
+ pos++;
1958
+ const nameStart = pos;
1959
+ if (tokens[pos]?.type === "IDENTIFIER" || tokens[pos]?.type === "TEXT") {
1960
+ pos = findNameEnd(tokens, pos);
1961
+ }
1962
+ const nameEnd = pos;
1963
+ while (tokens[pos]?.type === "WHITESPACE")
1964
+ pos++;
1965
+ if (tokens[pos]?.type !== "BLOCK_CLOSE")
1966
+ return null;
1967
+ pos++;
1968
+ if (tokens[pos]?.type === "NEWLINE")
1969
+ pos++;
1970
+ return { nameStart, nameEnd, bodyStart: pos };
1971
+ }
1972
+ function mathCloseEnd(tokens, start) {
1973
+ if (tokens[start]?.type !== "BLOCK_END_OPEN" || tokens[start + 1]?.value.toLowerCase() !== "math" || tokens[start + 2]?.type !== "BLOCK_CLOSE")
1974
+ return start;
1975
+ const end = start + 3;
1976
+ return tokens[end]?.type === "NEWLINE" ? end + 1 : end;
1977
+ }
1978
+ function findMathBodyBounds(tokens, start) {
1979
+ let cache = bodyCaches.get(tokens);
1980
+ if (!cache) {
1981
+ cache = new Map;
1982
+ bodyCaches.set(tokens, cache);
1983
+ }
1984
+ const visited = [];
1985
+ let result = {
1986
+ closeStart: tokens.length,
1987
+ end: tokens.length,
1988
+ foundClose: false,
1989
+ hasContent: false
1990
+ };
1991
+ for (let pos = start;pos < tokens.length; pos++) {
1992
+ const cached = cache.get(pos);
1993
+ if (cached) {
1994
+ result = cached;
1995
+ break;
1996
+ }
1997
+ const end = mathCloseEnd(tokens, pos);
1998
+ if (end > pos) {
1999
+ result = { closeStart: pos, end, foundClose: true, hasContent: false };
2000
+ cache.set(pos, result);
2001
+ break;
2002
+ }
2003
+ visited.push(pos);
2004
+ }
2005
+ for (let i = visited.length - 1;i >= 0; i--) {
2006
+ const pos = visited[i];
2007
+ const token5 = tokens[pos];
2008
+ if (!result.hasContent && (token5.type === "BACKSLASH_BREAK" || token5.value.trim() !== "")) {
2009
+ result = { ...result, hasContent: true };
2010
+ }
2011
+ cache.set(pos, result);
2012
+ }
2013
+ return result;
2014
+ }
2015
+
2016
+ // packages/parser/src/parser/rules/opaque-probe.ts
2017
+ function createOpaqueProbe(ctx) {
2018
+ return {
2019
+ ...ctx,
2020
+ diagnostics: [],
2021
+ footnotes: [],
2022
+ tocEntries: [],
2023
+ codeBlocks: [],
2024
+ htmlBlocks: [],
2025
+ bibcites: [],
2026
+ scope: {
2027
+ ...ctx.scope,
2028
+ inlineEnd: undefined,
2029
+ tableFormatting: undefined,
2030
+ blockCloseCondition: () => true
2031
+ }
2032
+ };
2033
+ }
2034
+ function opaqueRuleEnd(probe, pos, rules, requireClosed = false) {
2035
+ const token5 = probe.tokens[pos];
2036
+ if (!token5)
2037
+ return pos;
2038
+ probe.pos = pos;
2039
+ for (const rule of rules) {
2040
+ if (!rule.startTokens.includes(token5.type))
2041
+ continue;
2042
+ if (rule.name === "code") {
2043
+ const open = findCodeOpen(probe.tokens, pos);
2044
+ if (!open)
2045
+ continue;
2046
+ if (open.closingSwallowed) {
2047
+ if (!requireClosed)
2048
+ return open.bodyStart;
2049
+ continue;
2050
+ }
2051
+ const bounds = findCodeBodyBounds(probe.tokens, open.bodyStart);
2052
+ if (!requireClosed || bounds.foundClose)
2053
+ return bounds.end;
2054
+ continue;
2055
+ }
2056
+ if (rule.name === "math") {
2057
+ const open = findMathOpen(probe.tokens, pos);
2058
+ if (!open)
2059
+ continue;
2060
+ const bounds = findMathBodyBounds(probe.tokens, open.bodyStart);
2061
+ if (bounds.hasContent && (!requireClosed || bounds.foundClose))
2062
+ return bounds.end;
2063
+ continue;
2064
+ }
2065
+ const result = rule.parse(probe);
2066
+ if (result.success)
2067
+ return pos + result.consumed;
2068
+ }
2069
+ return pos;
2070
+ }
2071
+
2072
+ // packages/parser/src/parser/rules/tokens.ts
2073
+ function currentToken(ctx) {
2074
+ return ctx.tokens[ctx.pos] ?? eofToken();
2075
+ }
2076
+ function eofToken() {
2077
+ return {
2078
+ type: "EOF",
2079
+ value: "",
2080
+ position: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 } },
2081
+ lineStart: false
2082
+ };
2083
+ }
2084
+ function hasClosingMarkerBeforeNewline(ctx, markerType, markerValue) {
2085
+ let pos = ctx.pos;
2086
+ while (pos < (ctx.scope.inlineEnd ?? ctx.tokens.length)) {
2087
+ const token5 = ctx.tokens[pos];
2088
+ if (!token5 || token5.type === "NEWLINE" || token5.type === "EOF") {
2089
+ return false;
2090
+ }
2091
+ if (token5.type === markerType) {
2092
+ if (markerValue === undefined || token5.value === markerValue) {
2093
+ return true;
2094
+ }
2095
+ }
2096
+ pos++;
2097
+ }
2098
+ return false;
2099
+ }
2100
+ // packages/parser/src/parser/rules/block/code/attributes.ts
2101
+ function repairSwallowedCodeClose(ctx, pos, attrs) {
2102
+ const prevToken = ctx.tokens[pos - 1];
2103
+ if (prevToken?.type !== "QUOTED_STRING" || !prevToken.value.includes("]]")) {
2104
+ return null;
2105
+ }
2106
+ const rawValue = prevToken.value;
2107
+ const bracketIdx = rawValue.indexOf("]]");
2108
+ const truncatedValue = rawValue.startsWith('"') ? rawValue.slice(1, bracketIdx) : rawValue.slice(0, bracketIdx);
2109
+ for (const key of Object.keys(attrs)) {
2110
+ const stored = attrs[key];
2111
+ if (stored === rawValue || stored === rawValue.slice(1, -1) || stored === rawValue.slice(1)) {
2112
+ attrs[key] = truncatedValue;
2113
+ break;
2114
+ }
2115
+ }
2116
+ return {
2117
+ closingSwallowed: rawValue.includes("[[/code]]")
2118
+ };
2119
+ }
2120
+
2121
+ // packages/parser/src/parser/rules/block/paragraph/span-markers.ts
2122
+ function processCloseSpanMarkers(elements) {
2123
+ let result = null;
2124
+ for (let i = 0;i < elements.length; i++) {
2125
+ const elem = elements[i];
2126
+ if (!elem)
2127
+ continue;
2128
+ if (isCloseSpanMarker(elem)) {
2129
+ if (result === null) {
2130
+ result = elements.slice(0, i);
2131
+ }
2132
+ if (result.length > 0) {
2133
+ const spanContent = [...result];
2134
+ result.length = 0;
2135
+ result.push({
2136
+ element: "container",
2137
+ data: {
2138
+ type: "span",
2139
+ attributes: {},
2140
+ elements: spanContent
2141
+ }
2142
+ });
2143
+ }
2144
+ } else {
2145
+ result?.push(elem);
2146
+ }
2147
+ }
2148
+ return result ?? elements;
2149
+ }
2150
+ function isCloseSpanMarker(elem) {
2151
+ 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;
2152
+ }
2153
+
2154
+ // packages/parser/src/parser/rules/inline/parsing/preserved-line-break.ts
2155
+ function createPreservedTrailingLineBreak() {
2156
+ const lineBreak = { element: "line-break" };
2157
+ lineBreak._preservedTrailingBreak = true;
2158
+ return lineBreak;
2159
+ }
2160
+ function createPreservedLeadingLineBreak() {
2161
+ const lineBreak = { element: "line-break", _preservedLeadingBreak: true };
2162
+ return lineBreak;
2163
+ }
2164
+ function isPreservedLeadingLineBreak(element) {
2165
+ return element?.element === "line-break" && element._preservedLeadingBreak === true;
2166
+ }
2167
+
2168
+ // packages/parser/src/parser/rules/block/paragraph/normalize.ts
2169
+ function normalizeParagraphElements(source) {
2170
+ let elements = processCloseSpanMarkers(source);
2171
+ removeTrailingLineBreaks(elements);
2172
+ removeTrailingWhitespaceText(elements);
2173
+ elements = removeLeadingLineBreaks(elements);
2174
+ return elements;
2175
+ }
2176
+ function removeTrailingLineBreaks(elements) {
2177
+ while (elements.length > 0 && elements[elements.length - 1]?.element === "line-break") {
2178
+ const lastEl = elements[elements.length - 1];
2179
+ if (lastEl._preservedTrailingBreak) {
2180
+ delete lastEl._preservedTrailingBreak;
2181
+ break;
2182
+ }
2183
+ elements.pop();
2184
+ }
2185
+ }
2186
+ function removeTrailingWhitespaceText(elements) {
2187
+ while (elements.length > 0) {
2188
+ const last = elements[elements.length - 1];
2189
+ if (last?.element === "text" && "data" in last && typeof last.data === "string" && last.data.trim() === "") {
2190
+ elements.pop();
2191
+ } else {
2192
+ break;
2193
+ }
2194
+ }
2195
+ }
2196
+ function removeLeadingLineBreaks(elements) {
2197
+ let first = 0;
2198
+ while (first < elements.length && elements[first]?.element === "line-break" && !isPreservedLeadingLineBreak(elements[first])) {
2199
+ first++;
2200
+ }
2201
+ return first > 0 ? elements.slice(first) : elements;
2202
+ }
2203
+
2204
+ // packages/parser/src/parser/rules/block/code/content.ts
2205
+ function collectCodeContent(ctx, startPos, closingSwallowed) {
2206
+ if (closingSwallowed)
2207
+ return { contents: "", consumed: 0, foundClose: true };
2208
+ const bounds = findCodeBodyBounds(ctx.tokens, startPos);
2209
+ return {
2210
+ contents: ctx.tokens.slice(startPos, bounds.closeStart).map((token5) => token5.value).join(""),
2211
+ consumed: bounds.end - startPos,
2212
+ foundClose: bounds.foundClose
2213
+ };
2214
+ }
2215
+
2216
+ // packages/parser/src/parser/rules/block/code/index.ts
2217
+ var codeBlockRule = {
2218
+ name: "code",
2219
+ startTokens: ["BLOCK_OPEN"],
2220
+ requiresLineStart: false,
2221
+ parse(ctx) {
2222
+ const openToken = currentToken(ctx);
2223
+ if (openToken.type !== "BLOCK_OPEN") {
2224
+ return { success: false };
2225
+ }
2226
+ const open = findCodeOpen(ctx.tokens, ctx.pos);
2227
+ if (!open)
2228
+ return { success: false };
2229
+ const attrResult = parseAttributesRaw(ctx, ctx.pos + 2);
2230
+ if (open.repaired)
2231
+ repairSwallowedCodeClose(ctx, open.attributesEnd, attrResult.attrs);
2232
+ let pos = open.bodyStart;
2233
+ let consumed = pos - ctx.pos;
2234
+ const contentResult = collectCodeContent(ctx, pos, open.closingSwallowed);
2235
+ let codeContent = contentResult.contents.replace(/\n$/, "");
2236
+ consumed += contentResult.consumed;
2237
+ pos += contentResult.consumed;
2238
+ if (!contentResult.foundClose) {
2239
+ ctx.diagnostics.push({
2240
+ severity: "warning",
2241
+ code: "unclosed-block",
2242
+ message: "Missing closing tag [[/code]] for [[code]]",
2243
+ position: openToken.position
2244
+ });
2245
+ }
2246
+ const codeBlockData = {
2247
+ contents: codeContent,
2248
+ language: attrResult.attrs.type ?? null,
2249
+ name: attrResult.attrs.name ?? null
2250
+ };
2251
+ ctx.codeBlocks.push(codeBlockData);
2252
+ const elements = [{ element: "code", data: codeBlockData }];
2253
+ if (ctx.tokens[pos]?.type === "NEWLINE" && !getParagraphNewlineBoundary(ctx, pos, false).shouldBreak && !ctx.scope.blockCloseCondition?.({ ...ctx, pos: pos + 1 })) {
2254
+ const after = parseInlineUntil({ ...ctx, pos: pos + 1 }, "PARAGRAPH_BREAK");
2255
+ elements.push({ element: "line-break" }, ...normalizeParagraphElements(after.elements));
2256
+ consumed += 1 + after.consumed;
2257
+ }
2258
+ return { success: true, elements, consumed };
2259
+ }
2260
+ };
2261
+
2262
+ // packages/parser/src/parser/rules/block/math/index.ts
2263
+ var mathBlockRule = {
2264
+ name: "math",
2265
+ startTokens: ["BLOCK_OPEN"],
2266
+ requiresLineStart: false,
2267
+ parse(ctx) {
2268
+ const openToken = currentToken(ctx);
2269
+ const open = findMathOpen(ctx.tokens, ctx.pos);
2270
+ if (!open)
2271
+ return { success: false };
2272
+ const bounds = findMathBodyBounds(ctx.tokens, open.bodyStart);
2273
+ if (!bounds.foundClose) {
2274
+ ctx.diagnostics.push({
2275
+ severity: "warning",
2276
+ code: "unclosed-block",
2277
+ message: "Missing closing tag [[/math]] for [[math]]",
2278
+ position: openToken.position
2279
+ });
2280
+ }
2281
+ if (!bounds.hasContent) {
2282
+ return { success: false };
2283
+ }
2284
+ const latexSource = ctx.tokens.slice(open.bodyStart, bounds.closeStart).map((token5) => token5.type === "BACKSLASH_BREAK" ? "\\\n" : token5.value).join("").trim();
2285
+ const name = ctx.tokens.slice(open.nameStart, open.nameEnd).map((token5) => token5.value).join("");
2286
+ return {
2287
+ success: true,
2288
+ elements: [
2289
+ {
2290
+ element: "math",
2291
+ data: {
2292
+ name: name || null,
2293
+ "latex-source": latexSource
2294
+ }
2295
+ }
2296
+ ],
2297
+ consumed: bounds.end - ctx.pos
2298
+ };
2299
+ }
2300
+ };
2301
+
2302
+ // packages/parser/src/parser/rules/inline/link-triple/fallback.ts
2303
+ function literalOpenLink(value) {
2304
+ return {
2305
+ success: true,
2306
+ elements: [{ element: "text", data: value }],
2307
+ consumed: 1
2308
+ };
2309
+ }
2310
+
2311
+ // packages/parser/src/parser/rules/inline/link-triple/label.ts
2312
+ function buildTripleLinkLabel(args) {
2313
+ if (args.isPage && args.foundPipe && !args.labelText.trim())
2314
+ return "page";
2315
+ return {
2316
+ text: getTripleLinkDisplayText({
2317
+ ...args,
2318
+ originalTarget: args.isPage && !args.foundPipe ? args.originalTarget.split("#")[0] : args.originalTarget
2319
+ })
2320
+ };
2321
+ }
2322
+ function getTripleLinkDisplayText(args) {
2323
+ const trimmedLabel = args.labelText.trim();
2324
+ if (args.foundPipe) {
2325
+ return trimmedLabel || args.finalTarget;
2326
+ }
2327
+ const colonIdx = args.originalTarget.indexOf(":");
2328
+ if (colonIdx !== -1 && !args.originalTarget.startsWith("http") && !args.originalTarget.startsWith("*")) {
2329
+ return args.originalTarget.slice(colonIdx + 1).trim();
2330
+ }
2331
+ return args.originalTarget;
2332
+ }
2333
+
2334
+ // packages/parser/src/parser/rules/inline/link-triple/syntax.ts
2335
+ function hasClosingLinkMarker(ctx, startPos) {
2336
+ let pos = startPos;
2337
+ while (pos < ctx.tokens.length) {
2338
+ const token5 = ctx.tokens[pos];
2339
+ if (!token5 || token5.type === "EOF") {
2340
+ return false;
2341
+ }
2342
+ if (token5.type === "LINK_CLOSE") {
2343
+ return true;
2344
+ }
2345
+ if (token5.type === "NEWLINE") {
2346
+ const next = ctx.tokens[pos + 1];
2347
+ if (next?.type === "NEWLINE" || next?.type === "LINK_CLOSE") {
2348
+ return false;
2349
+ }
2350
+ }
2351
+ pos++;
2352
+ }
2353
+ return false;
2354
+ }
2355
+ function collectTripleLinkParts(ctx, startPos) {
2356
+ let target = "";
2357
+ let labelText = "";
2358
+ let foundPipe = false;
2359
+ let consumed = 1;
2360
+ let pos = startPos;
2361
+ while (pos < ctx.tokens.length) {
2362
+ const token5 = ctx.tokens[pos];
2363
+ if (!token5 || token5.type === "LINK_CLOSE" || token5.type === "EOF") {
2364
+ break;
2365
+ }
2366
+ if (token5.type === "NEWLINE") {
2367
+ if (foundPipe) {
2368
+ labelText += " ";
2369
+ } else {
2370
+ target += " ";
2371
+ }
2372
+ consumed++;
2373
+ pos++;
2374
+ continue;
2375
+ }
2376
+ if (token5.type === "PIPE" && !foundPipe) {
2377
+ foundPipe = true;
2378
+ } else if (foundPipe) {
2379
+ labelText += token5.value;
2380
+ } else {
2381
+ target += token5.value;
2382
+ }
2383
+ consumed++;
2384
+ pos++;
2385
+ }
2386
+ if (ctx.tokens[pos]?.type === "LINK_CLOSE") {
2387
+ consumed++;
2388
+ }
2389
+ return { target, labelText, foundPipe, consumed };
2390
+ }
2391
+
2392
+ // packages/parser/src/parser/rules/inline/link-triple/interwiki.ts
2393
+ var INTERWIKI_PREFIXES = new Set(["wikipedia", "google", "dictionary", "wikidot"]);
2394
+ function isInterwikiTarget(target) {
2395
+ const colonIdx = target.indexOf(":");
2396
+ if (colonIdx <= 0 || target.includes("/")) {
2397
+ return false;
2398
+ }
2399
+ const prefix = target.slice(0, colonIdx).toLowerCase();
2400
+ return INTERWIKI_PREFIXES.has(prefix);
2401
+ }
2402
+
2403
+ // packages/parser/src/parser/rules/inline/link-triple/target.ts
2404
+ function normalizeTripleLinkTarget(trimmedTarget) {
2405
+ if (trimmedTarget.startsWith("*")) {
2406
+ return { target: trimmedTarget.slice(1), hasStar: true };
2407
+ }
2408
+ return { target: trimmedTarget, hasStar: false };
2409
+ }
2410
+ function isInvalidTripleLinkTarget(trimmedTarget, foundPipe) {
2411
+ return trimmedTarget === "" && foundPipe || /#{2,}/.test(trimmedTarget);
2412
+ }
2413
+ function determineLinkTypeAndLocation(target) {
2414
+ if (target.startsWith("#")) {
2415
+ return { linkType: "anchor", link: target };
2416
+ }
2417
+ if (target.startsWith("http://") || target.startsWith("https://")) {
2418
+ return { linkType: "direct", link: target };
2419
+ }
2420
+ if (isInterwikiTarget(target)) {
2421
+ return { linkType: "interwiki", link: target };
2422
+ }
2423
+ return { linkType: "page", link: { site: null, page: target } };
2424
+ }
2425
+
2426
+ // packages/parser/src/parser/rules/inline/link-triple/index.ts
2427
+ var linkTripleRule = {
2428
+ name: "linkTriple",
2429
+ startTokens: ["LINK_OPEN"],
2430
+ parse(ctx) {
2431
+ const startToken = currentToken(ctx);
2432
+ if (!hasClosingLinkMarker(ctx, ctx.pos + 1)) {
2433
+ return literalOpenLink(startToken.value);
2434
+ }
2435
+ const parts = collectTripleLinkParts(ctx, ctx.pos + 1);
2436
+ const trimmedTarget = parts.target.trim();
2437
+ if (isInvalidTripleLinkTarget(trimmedTarget, parts.foundPipe)) {
2438
+ return literalOpenLink(startToken.value);
2439
+ }
2440
+ const normalized = normalizeTripleLinkTarget(trimmedTarget);
2441
+ const { linkType, link } = determineLinkTypeAndLocation(normalized.target);
2442
+ const label = buildTripleLinkLabel({
2443
+ isPage: linkType === "page",
2444
+ foundPipe: parts.foundPipe,
2445
+ labelText: parts.labelText,
2446
+ finalTarget: normalized.target,
2447
+ originalTarget: trimmedTarget
2448
+ });
2449
+ return {
2450
+ success: true,
2451
+ elements: [
2452
+ {
2453
+ element: "link",
2454
+ data: {
2455
+ type: linkType,
2456
+ link,
2457
+ extra: null,
2458
+ label,
2459
+ target: normalized.hasStar && linkType === "direct" ? "new-tab" : null
2460
+ }
2461
+ }
2462
+ ],
2463
+ consumed: parts.consumed
2464
+ };
2465
+ }
2466
+ };
2467
+
2468
+ // packages/parser/src/parser/rules/block/note/boundary.ts
2469
+ var opaqueRules = [codeBlockRule, mathBlockRule, linkTripleRule];
2470
+ var boundsCache = new WeakMap;
2471
+ var closeCache = new WeakMap;
2472
+ function tagEnd(tokens, pos, name, close = false) {
2473
+ 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;
2474
+ }
2475
+ function findClose(probe, start, name) {
2476
+ let byName = closeCache.get(probe.tokens);
2477
+ if (!byName) {
2478
+ byName = new Map;
2479
+ closeCache.set(probe.tokens, byName);
2480
+ }
2481
+ let cache = byName.get(name);
2482
+ if (!cache) {
2483
+ cache = new Map;
2484
+ byName.set(name, cache);
2485
+ }
2486
+ if (cache.has(start))
2487
+ return cache.get(start);
2488
+ const visited = [];
2489
+ let result = -1;
2490
+ for (let pos = start;pos < probe.tokens.length; pos++) {
2491
+ if (cache.has(pos)) {
2492
+ result = cache.get(pos);
2493
+ break;
2494
+ }
2495
+ visited.push(pos);
2496
+ const protectedEnd = protectedInlineRegionEnd(probe.tokens, pos, probe.tokens.length);
2497
+ if (protectedEnd > pos) {
2498
+ pos = protectedEnd - 1;
2499
+ continue;
2500
+ }
2501
+ if (tagEnd(probe.tokens, pos, name, true) > pos) {
2502
+ result = pos;
2503
+ break;
2504
+ }
2505
+ const opaqueEnd = opaqueRuleEnd(probe, pos, opaqueRules, true);
2506
+ if (opaqueEnd > pos) {
2507
+ pos = opaqueEnd - 1;
2508
+ continue;
2509
+ }
2510
+ if (name === "note") {
2511
+ const footnoteStart = tagEnd(probe.tokens, pos, "footnote");
2512
+ if (footnoteStart > pos) {
2513
+ const end = findClose(probe, footnoteStart, "footnote");
2514
+ if (end >= 0)
2515
+ pos = end + 2;
2516
+ }
2517
+ }
2518
+ }
2519
+ for (const pos of visited)
2520
+ cache.set(pos, result);
2521
+ return result;
2522
+ }
2523
+ function findNoteBounds(ctx, start = ctx.pos) {
2524
+ const openEnd = tagEnd(ctx.tokens, start, "note");
2525
+ if (openEnd === start || ctx.tokens[openEnd]?.type !== "NEWLINE")
2526
+ return null;
2527
+ let cache = boundsCache.get(ctx.tokens);
2528
+ if (!cache) {
2529
+ cache = new Map;
2530
+ boundsCache.set(ctx.tokens, cache);
2531
+ }
2532
+ if (cache.has(start))
2533
+ return cache.get(start);
2534
+ const close = findClose(createOpaqueProbe(ctx), openEnd + 1, "note");
2535
+ const result = close < 0 ? null : { bodyStart: openEnd + 1, close, end: close + 3 };
2536
+ cache.set(start, result);
2537
+ return result;
2538
+ }
2539
+
2540
+ // packages/parser/src/parser/rules/inline/parsing/block-boundary.ts
2541
+ function isExcludedBlockToken(ctx, tokenPos) {
2542
+ const excluded = ctx.scope.excludedBlockNames;
2543
+ if (!excluded?.size)
2544
+ return false;
2545
+ const token5 = ctx.tokens[tokenPos];
2546
+ if (token5?.type !== "BLOCK_OPEN" && token5?.type !== "BLOCK_END_OPEN")
2547
+ return false;
1286
2548
  const nameResult = parseBlockName(ctx, tokenPos + 1);
1287
2549
  return nameResult !== null && excluded.has(nameResult.name);
1288
2550
  }
@@ -1297,6 +2559,8 @@ function isUnknownBlockToken(ctx, tokenPos) {
1297
2559
  }
1298
2560
  return true;
1299
2561
  }
2562
+ if (nameResult.name === "note")
2563
+ return findNoteBounds(ctx, tokenPos) === null;
1300
2564
  return !KNOWN_BLOCK_NAMES.has(nameResult.name);
1301
2565
  }
1302
2566
  function isIndentAcceptingBlock(ctx, tokenPos) {
@@ -1320,7 +2584,13 @@ function isParagraphBreakingBlockStart(ctx, newlinePos, lookAhead) {
1320
2584
  if (!nextMeaningfulToken.lineStart && !isIndentedBlockOpener) {
1321
2585
  return false;
1322
2586
  }
1323
- return !parseImageOpen({ ...ctx, pos: nextPos }) && !isOrphanCloseSpan(ctx, nextPos) && !isAnchorName(ctx, nextPos) && !isInvalidBlockOpen(ctx, nextPos) && !isInvalidHeading(ctx, nextPos) && !isExcludedBlockStart(ctx, nextPos) && !isUnknownBlockStart(ctx, nextPos);
2587
+ 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);
2588
+ }
2589
+ function isInvalidListMarker(ctx, markerPos) {
2590
+ const marker = ctx.tokens[markerPos];
2591
+ if (marker?.type !== "LIST_BULLET" && marker?.type !== "LIST_NUMBER")
2592
+ return false;
2593
+ return ctx.tokens[markerPos + 1]?.type !== "WHITESPACE";
1324
2594
  }
1325
2595
  function isOrphanCloseSpan(ctx, blockEndOpenPos) {
1326
2596
  const token5 = ctx.tokens[blockEndOpenPos];
@@ -1406,13 +2676,6 @@ function skipWhitespaceAfterNewline(ctx, newlinePos) {
1406
2676
  return lookAhead;
1407
2677
  }
1408
2678
 
1409
- // packages/parser/src/parser/rules/inline/parsing/preserved-line-break.ts
1410
- function createPreservedTrailingLineBreak() {
1411
- const lineBreak = { element: "line-break" };
1412
- lineBreak._preservedTrailingBreak = true;
1413
- return lineBreak;
1414
- }
1415
-
1416
2679
  // packages/parser/src/parser/rules/inline/parsing/simple-token.ts
1417
2680
  function parseSimpleInlineToken(token5, nextToken) {
1418
2681
  if (token5.type === "TEXT" && token5.value !== "(") {
@@ -1429,12 +2692,13 @@ function parseInlineUntil(ctx, endType) {
1429
2692
  const nodes = [];
1430
2693
  let consumed = 0;
1431
2694
  let pos = ctx.pos;
2695
+ let consumedEmptyRaw = false;
1432
2696
  const paragraphMode = endType === "PARAGRAPH_BREAK";
1433
2697
  const multiline = paragraphMode || FORMATTING_CLOSE_TOKENS.has(endType);
1434
2698
  let inlineEnd = ctx.scope.inlineEnd ?? ctx.tokens.length;
1435
2699
  if (!multiline) {
1436
2700
  for (let end = ctx.pos;end < inlineEnd; end++) {
1437
- const protectedEnd = protectedInlineRegionEnd(ctx.tokens, end, inlineEnd);
2701
+ 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));
1438
2702
  if (protectedEnd > end) {
1439
2703
  end = protectedEnd - 1;
1440
2704
  continue;
@@ -1483,10 +2747,11 @@ function parseInlineUntil(ctx, endType) {
1483
2747
  consumed++;
1484
2748
  continue;
1485
2749
  }
1486
- if (token5.type === endType) {
2750
+ const hasEmail = emailRegionEnd(ctx.tokens, pos, inlineEnd) > pos;
2751
+ if (token5.type === endType && !hasEmail) {
1487
2752
  break;
1488
2753
  }
1489
- if (canCollectLongPlainTextRuns) {
2754
+ if (canCollectLongPlainTextRuns && !hasEmail) {
1490
2755
  const plainTextRun = collectLongPlainTextRun(ctx, pos, endType);
1491
2756
  if (plainTextRun) {
1492
2757
  nodes.push({ element: "text", data: plainTextRun.value });
@@ -1496,7 +2761,7 @@ function parseInlineUntil(ctx, endType) {
1496
2761
  }
1497
2762
  }
1498
2763
  const simpleToken = parseSimpleInlineToken(token5, ctx.tokens[pos + 1]);
1499
- if (simpleToken) {
2764
+ if (simpleToken && !hasEmail) {
1500
2765
  nodes.push(simpleToken.element);
1501
2766
  consumed += simpleToken.consumed;
1502
2767
  pos += simpleToken.consumed;
@@ -1507,6 +2772,10 @@ function parseInlineUntil(ctx, endType) {
1507
2772
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
1508
2773
  const result = rule.parse(inlineCtx);
1509
2774
  if (result.success) {
2775
+ stripAutomaticLineBreak(nodes, result.stripLeadingLineBreak);
2776
+ if (rule.name === "raw" && result.elements.length === 0 && nodes.length === 0) {
2777
+ consumedEmptyRaw = true;
2778
+ }
1510
2779
  if (rule.name === "comment") {
1511
2780
  let after = pos + result.consumed;
1512
2781
  while (ctx.tokens[after]?.type === "WHITESPACE")
@@ -1525,7 +2794,9 @@ function parseInlineUntil(ctx, endType) {
1525
2794
  nodes.pop();
1526
2795
  }
1527
2796
  }
1528
- nodes.push(...result.elements);
2797
+ for (const element of result.elements) {
2798
+ nodes.push(paragraphMode && consumedEmptyRaw && nodes.length === 0 && token5.type === "NEWLINE" && element.element === "line-break" ? createPreservedLeadingLineBreak() : element);
2799
+ }
1529
2800
  consumed += result.consumed;
1530
2801
  pos += result.consumed;
1531
2802
  matched = true;
@@ -1550,138 +2821,10 @@ var FORMATTING_CLOSE_TOKENS = new Set([
1550
2821
  "MONO_CLOSE",
1551
2822
  "COLOR_MARKER"
1552
2823
  ]);
1553
- // packages/parser/src/parser/rules/block/parsing/attributes/names.ts
1554
- function consumeAttributeName(ctx, startPos, startConsumed, startName, options) {
1555
- if (startName === "_" && isAttributeWordToken(ctx.tokens[startPos])) {
1556
- startName += ctx.tokens[startPos]?.value ?? "";
1557
- startPos++;
1558
- startConsumed++;
1559
- }
1560
- return options.strikeHyphens ? consumeRawNameSuffix(ctx, startPos, startConsumed, startName, options.hyphenatedNames) : consumeSafeNameSuffix(ctx, startPos, startConsumed, startName);
1561
- }
1562
- function isAttributeNameToken(token5) {
1563
- return isAttributeWordToken(token5) || token5?.type === "UNDERSCORE";
1564
- }
1565
- function consumeSafeNameSuffix(ctx, startPos, startConsumed, startName) {
1566
- let name = startName;
1567
- let pos = startPos;
1568
- let consumed = startConsumed;
1569
- while (isAttributeNameSeparator(ctx.tokens[pos]) && isAttributeWordToken(ctx.tokens[pos + 1])) {
1570
- name += ctx.tokens[pos]?.value ?? "";
1571
- pos++;
1572
- consumed++;
1573
- name += ctx.tokens[pos]?.value ?? "";
1574
- pos++;
1575
- consumed++;
1576
- }
1577
- return { name, pos, consumed };
1578
- }
1579
- function consumeRawNameSuffix(ctx, startPos, startConsumed, startName, hyphenatedNames) {
1580
- let name = startName;
1581
- let pos = startPos;
1582
- let consumed = startConsumed;
1583
- while (isAttributeNameSeparator(ctx.tokens[pos])) {
1584
- while (isAttributeNameSeparator(ctx.tokens[pos])) {
1585
- if (hyphenatedNames || ctx.tokens[pos]?.type === "UNDERSCORE") {
1586
- name += ctx.tokens[pos]?.value ?? "-";
1587
- }
1588
- pos++;
1589
- consumed++;
1590
- }
1591
- if (!isAttributeWordToken(ctx.tokens[pos])) {
1592
- break;
1593
- }
1594
- if (hyphenatedNames || name.endsWith("_")) {
1595
- name += ctx.tokens[pos]?.value ?? "";
1596
- }
1597
- pos++;
1598
- consumed++;
1599
- }
1600
- return { name, pos, consumed };
1601
- }
1602
- function isAttributeWordToken(token5) {
1603
- return token5?.type === "TEXT" || token5?.type === "IDENTIFIER";
1604
- }
1605
- function isAttributeNameSeparator(token5) {
1606
- return token5?.type === "TEXT" && token5.value === "-" || token5?.type === "STRIKE_MARKER" || token5?.type === "UNDERSCORE";
1607
- }
1608
-
1609
- // packages/parser/src/parser/rules/block/parsing/attributes/values.ts
1610
- function consumeAttributeValue(token5) {
1611
- if (!token5) {
1612
- return null;
1613
- }
1614
- if (token5.type === "QUOTED_STRING") {
1615
- return { value: stripQuotes(token5.value) };
1616
- }
1617
- if (isAttributeNameToken(token5)) {
1618
- return { value: token5.value };
1619
- }
1620
- return null;
1621
- }
1622
- function stripQuotes(value) {
1623
- return value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value;
1624
- }
1625
-
1626
- // packages/parser/src/parser/rules/block/parsing/attributes/scanner.ts
1627
- function scanAttributes(ctx, startPos, options) {
1628
- const attrs = {};
1629
- let pos = startPos;
1630
- let consumed = 0;
1631
- while (pos < ctx.tokens.length) {
1632
- const token5 = ctx.tokens[pos];
1633
- if (!token5 || isAttributeTerminator(token5)) {
1634
- break;
1635
- }
1636
- if (token5.type === "WHITESPACE") {
1637
- pos++;
1638
- consumed++;
1639
- continue;
1640
- }
1641
- if (!isAttributeNameToken(token5)) {
1642
- pos++;
1643
- consumed++;
1644
- continue;
1645
- }
1646
- let name = token5.value;
1647
- pos++;
1648
- consumed++;
1649
- const nameResult = consumeAttributeName(ctx, pos, consumed, name, options);
1650
- pos = nameResult.pos;
1651
- consumed = nameResult.consumed;
1652
- name = nameResult.name.toLowerCase();
1653
- if (ctx.tokens[pos]?.type !== "EQUALS") {
1654
- attrs[name] = "true";
1655
- continue;
1656
- }
1657
- pos++;
1658
- consumed++;
1659
- const valueResult = consumeAttributeValue(ctx.tokens[pos]);
1660
- if (!valueResult) {
1661
- continue;
1662
- }
1663
- attrs[name] = valueResult.value;
1664
- pos++;
1665
- consumed++;
1666
- }
1667
- return { attrs, consumed };
1668
- }
1669
- function isAttributeTerminator(token5) {
1670
- return token5.type === "BLOCK_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF";
1671
- }
1672
-
1673
- // packages/parser/src/parser/rules/block/parsing/attributes/index.ts
1674
- function parseAttributes(ctx, startPos) {
1675
- const result = scanAttributes(ctx, startPos, { hyphenatedNames: true, strikeHyphens: false });
1676
- return { attrs: filterUnsafeAttributes(result.attrs), consumed: result.consumed };
1677
- }
1678
- function parseAttributesRaw(ctx, startPos, hyphenatedNames = true) {
1679
- return scanAttributes(ctx, startPos, { hyphenatedNames, strikeHyphens: true });
1680
- }
1681
2824
  // packages/parser/src/parser/parse/block.ts
1682
- function parseNextBlock(ctx, skipWhitespace2, isAtEnd2) {
2825
+ function parseNextBlock(ctx, skipWhitespace2, isAtEnd3) {
1683
2826
  skipWhitespace2();
1684
- if (isAtEnd2()) {
2827
+ if (isAtEnd3()) {
1685
2828
  return [];
1686
2829
  }
1687
2830
  const token5 = ctx.tokens[ctx.pos];
@@ -1700,7 +2843,7 @@ function parseNextBlock(ctx, skipWhitespace2, isAtEnd2) {
1700
2843
  }
1701
2844
  }
1702
2845
  const result = ctx.blockFallbackRule.parse(ctx);
1703
- if (result.success && result.elements.length > 0) {
2846
+ if (result.success) {
1704
2847
  ctx.pos += result.consumed;
1705
2848
  return result.elements;
1706
2849
  }
@@ -1710,35 +2853,6 @@ function parseNextBlock(ctx, skipWhitespace2, isAtEnd2) {
1710
2853
 
1711
2854
  // packages/parser/src/parser/parse/context.ts
1712
2855
  var import_ast6 = require("@wdprlib/ast");
1713
-
1714
- // packages/parser/src/parser/rules/tokens.ts
1715
- function currentToken(ctx) {
1716
- return ctx.tokens[ctx.pos] ?? eofToken();
1717
- }
1718
- function eofToken() {
1719
- return {
1720
- type: "EOF",
1721
- value: "",
1722
- position: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 0, offset: 0 } },
1723
- lineStart: false
1724
- };
1725
- }
1726
- function hasClosingMarkerBeforeNewline(ctx, markerType, markerValue) {
1727
- let pos = ctx.pos;
1728
- while (pos < (ctx.scope.inlineEnd ?? ctx.tokens.length)) {
1729
- const token5 = ctx.tokens[pos];
1730
- if (!token5 || token5.type === "NEWLINE" || token5.type === "EOF") {
1731
- return false;
1732
- }
1733
- if (token5.type === markerType) {
1734
- if (markerValue === undefined || token5.value === markerValue) {
1735
- return true;
1736
- }
1737
- }
1738
- pos++;
1739
- }
1740
- return false;
1741
- }
1742
2856
  // packages/parser/src/parser/rules/block/heading/open.ts
1743
2857
  function parseHeadingOpen(ctx) {
1744
2858
  const marker = ctx.tokens[ctx.pos];
@@ -1984,10 +3098,11 @@ function parseNativeListLine(ctx, startPos) {
1984
3098
  const ltype = markerToken.type === "LIST_BULLET" ? "bullet" : "numbered";
1985
3099
  pos++;
1986
3100
  consumed++;
1987
- if (ctx.tokens[pos]?.type === "WHITESPACE") {
1988
- pos++;
1989
- consumed++;
3101
+ if (ctx.tokens[pos]?.type !== "WHITESPACE") {
3102
+ return { kind: "stop" };
1990
3103
  }
3104
+ pos++;
3105
+ consumed++;
1991
3106
  const inlineCtx = { ...ctx, pos };
1992
3107
  const inlineResult = parseInlineUntil(inlineCtx, "NEWLINE");
1993
3108
  consumed += inlineResult.consumed;
@@ -2097,9 +3212,9 @@ function appendBareParagraphElements(state, elements) {
2097
3212
  function appendBareParagraphText(state, text) {
2098
3213
  state.current.push({ element: "text", data: text });
2099
3214
  }
2100
- function appendBareParagraphLineBreakIfNeeded(state) {
3215
+ function appendBareParagraphLineBreakIfNeeded(state, token5) {
2101
3216
  if (state.current.length > 0) {
2102
- state.current.push({ element: "line-break" });
3217
+ state.current.push(createAutomaticLineBreak(token5));
2103
3218
  }
2104
3219
  }
2105
3220
  function flushBareParagraph(state) {
@@ -2204,7 +3319,7 @@ function parseBareListContent(ctx, startPos, listType) {
2204
3319
  if (consecutiveNewlines >= 2) {
2205
3320
  flushBareParagraph(paragraphState);
2206
3321
  } else {
2207
- appendBareParagraphLineBreakIfNeeded(paragraphState);
3322
+ appendBareParagraphLineBreakIfNeeded(paragraphState, token5);
2208
3323
  }
2209
3324
  continue;
2210
3325
  }
@@ -2216,6 +3331,7 @@ function parseBareListContent(ctx, startPos, listType) {
2216
3331
  for (const rule of getCandidateInlineRules(ctx.inlineRules, token5.type)) {
2217
3332
  const result = rule.parse(inlineCtx);
2218
3333
  if (result.success) {
3334
+ stripAutomaticLineBreak(paragraphState.current, result.stripLeadingLineBreak);
2219
3335
  appendBareParagraphElements(paragraphState, result.elements);
2220
3336
  consumed += result.consumed;
2221
3337
  pos += result.consumed;
@@ -2254,7 +3370,7 @@ function parseListItemBlockContent(ctx, pos, token5) {
2254
3370
  for (const rule of getCandidateBlockRules(filteredBlockRules, token5)) {
2255
3371
  const result = rule.parse(blockCtx);
2256
3372
  if (result.success) {
2257
- return { matched: true, elements: result.elements, consumed: result.consumed };
3373
+ return { matched: true, ...result };
2258
3374
  }
2259
3375
  }
2260
3376
  return { matched: false, elements: [], consumed: 0 };
@@ -2273,7 +3389,7 @@ function parseListItemInlineContent(ctx, pos, tokenType) {
2273
3389
  for (const rule of getCandidateInlineRules(ctx.inlineRules, tokenType)) {
2274
3390
  const result = rule.parse(inlineCtx);
2275
3391
  if (result.success) {
2276
- return { matched: true, elements: result.elements, consumed: result.consumed };
3392
+ return { matched: true, ...result };
2277
3393
  }
2278
3394
  }
2279
3395
  return { matched: false, elements: [], consumed: 0 };
@@ -2315,7 +3431,9 @@ function collectPostLiTrailingContent(ctx, startPos, listType) {
2315
3431
  }
2316
3432
  const inlineResult = parseListItemInlineContent(ctx, pos, token5.type);
2317
3433
  if (inlineResult.matched) {
2318
- elements.push(...inlineResult.elements);
3434
+ stripAutomaticLineBreak(elements, inlineResult.stripLeadingLineBreak);
3435
+ for (const element of inlineResult.elements)
3436
+ elements.push(element);
2319
3437
  consumed += inlineResult.consumed;
2320
3438
  pos += inlineResult.consumed;
2321
3439
  continue;
@@ -2360,7 +3478,7 @@ function collectLiItemContent(ctx, startPos, listType, parseNestedList) {
2360
3478
  if (token5.type === "NEWLINE") {
2361
3479
  const newlineResult = consumeLiItemNewlines(ctx, pos, elements.length > 0);
2362
3480
  if (newlineResult.addLineBreak) {
2363
- elements.push({ element: "line-break" });
3481
+ elements.push(createAutomaticLineBreak(token5));
2364
3482
  }
2365
3483
  pos += newlineResult.consumed;
2366
3484
  consumed += newlineResult.consumed;
@@ -2368,14 +3486,17 @@ function collectLiItemContent(ctx, startPos, listType, parseNestedList) {
2368
3486
  }
2369
3487
  const blockResult = parseListItemBlockContent(ctx, pos, token5);
2370
3488
  if (blockResult.matched) {
2371
- elements.push(...blockResult.elements);
3489
+ for (const element of blockResult.elements)
3490
+ elements.push(element);
2372
3491
  consumed += blockResult.consumed;
2373
3492
  pos += blockResult.consumed;
2374
3493
  continue;
2375
3494
  }
2376
3495
  const inlineResult = parseListItemInlineContent(ctx, pos, token5.type);
2377
3496
  if (inlineResult.matched) {
2378
- elements.push(...inlineResult.elements);
3497
+ stripAutomaticLineBreak(elements, inlineResult.stripLeadingLineBreak);
3498
+ for (const element of inlineResult.elements)
3499
+ elements.push(element);
2379
3500
  consumed += inlineResult.consumed;
2380
3501
  pos += inlineResult.consumed;
2381
3502
  continue;
@@ -2446,7 +3567,8 @@ function parseLiItem(ctx, startPos, listType, parseNestedList) {
2446
3567
  consumed += closeConsumed;
2447
3568
  pos += closeConsumed;
2448
3569
  const trailingResult = collectPostLiTrailingContent(ctx, pos, listType);
2449
- contentElements.push(...trailingResult.elements);
3570
+ for (const element of trailingResult.elements)
3571
+ contentElements.push(element);
2450
3572
  consumed += trailingResult.consumed;
2451
3573
  }
2452
3574
  return {
@@ -2706,7 +3828,7 @@ function sliceLineTokens(ctx, lines) {
2706
3828
  for (const { start, end } of lines) {
2707
3829
  for (let pos = start;pos < end; pos++) {
2708
3830
  const token5 = ctx.tokens[pos];
2709
- if (token5) {
3831
+ if (token5 && token5.type !== "BACKSLASH_BREAK") {
2710
3832
  tokens.push(token5);
2711
3833
  }
2712
3834
  }
@@ -2876,7 +3998,8 @@ function parseDefinitionItemKey(ctx, startPos) {
2876
3998
  const inlineCtx = { ...ctx, pos };
2877
3999
  const result = parseInlineUntil(inlineCtx, "COLON");
2878
4000
  if (result.elements.length > 0) {
2879
- key.push(...result.elements);
4001
+ for (const element of result.elements)
4002
+ key.push(element);
2880
4003
  for (let i = 0;i < result.consumed; i++) {
2881
4004
  const t = ctx.tokens[pos + i];
2882
4005
  if (t)
@@ -2932,7 +4055,8 @@ function parseDefinitionItemValue(ctx, startPos) {
2932
4055
  const inlineCtx = { ...ctx, pos };
2933
4056
  const result = parseInlineUntil(inlineCtx, "NEWLINE");
2934
4057
  if (result.elements.length > 0) {
2935
- value.push(...result.elements);
4058
+ for (const element of result.elements)
4059
+ value.push(element);
2936
4060
  pos += result.consumed;
2937
4061
  consumed += result.consumed;
2938
4062
  } else {
@@ -3012,80 +4136,95 @@ var definitionListRule = {
3012
4136
  consumed: result.consumed
3013
4137
  };
3014
4138
  }
3015
- };
3016
-
3017
- // packages/parser/src/parser/rules/block/paragraph/content.ts
3018
- function parseInlineContent(ctx) {
3019
- return parseInlineUntil(ctx, "PARAGRAPH_BREAK");
3020
- }
3021
-
3022
- // packages/parser/src/parser/rules/block/paragraph/span-markers.ts
3023
- function processCloseSpanMarkers(elements) {
3024
- let result = null;
4139
+ };
4140
+
4141
+ // packages/parser/src/parser/postprocess/divAdjacentParagraph.ts
4142
+ var unparsedDivStarts = new WeakSet;
4143
+ function markUnparsedDivStart(elements) {
4144
+ const first = elements[0];
4145
+ const text = first?.element === "container" && first.data.type === "paragraph" ? first.data.elements[0] : first;
4146
+ if (text?.element === "text")
4147
+ unparsedDivStarts.add(text);
4148
+ }
4149
+ function isUnparsedDivParagraph(el) {
4150
+ if (!el || el.element !== "container")
4151
+ return false;
4152
+ return el.data.type === "paragraph" && el.data.elements.some((child) => unparsedDivStarts.has(child));
4153
+ }
4154
+ function isDivContainer(el) {
4155
+ if (!el || el.element !== "container")
4156
+ return false;
4157
+ return el.data.type === "div";
4158
+ }
4159
+ function suppressAtLevel(elements) {
4160
+ if (elements.length <= 1)
4161
+ return elements;
4162
+ const unwrap = Array.from({ length: elements.length }, () => false);
4163
+ for (let i = 0;i < elements.length; i++) {
4164
+ if (!isUnparsedDivParagraph(elements[i]))
4165
+ continue;
4166
+ const prevIsDiv = i > 0 && isDivContainer(elements[i - 1]);
4167
+ const nextIsDiv = i < elements.length - 1 && isDivContainer(elements[i + 1]);
4168
+ if (prevIsDiv || nextIsDiv) {
4169
+ unwrap[i] = true;
4170
+ }
4171
+ }
4172
+ const result = [];
3025
4173
  for (let i = 0;i < elements.length; i++) {
3026
- const elem = elements[i];
3027
- if (!elem)
4174
+ const el = elements[i];
4175
+ if (!el)
3028
4176
  continue;
3029
- if (isCloseSpanMarker(elem)) {
3030
- if (result === null) {
3031
- result = elements.slice(0, i);
3032
- }
3033
- if (result.length > 0) {
3034
- const spanContent = [...result];
3035
- result.length = 0;
3036
- result.push({
3037
- element: "container",
3038
- data: {
3039
- type: "span",
3040
- attributes: {},
3041
- elements: spanContent
3042
- }
3043
- });
4177
+ if (unwrap[i] && el.element === "container") {
4178
+ const inner = el.data.elements;
4179
+ if (i > 0 && isDivContainer(elements[i - 1])) {
4180
+ result.push({ element: "line-break" });
3044
4181
  }
4182
+ result.push(...inner);
3045
4183
  } else {
3046
- result?.push(elem);
4184
+ result.push(el);
3047
4185
  }
3048
4186
  }
3049
- return result ?? elements;
4187
+ return result;
3050
4188
  }
3051
- function isCloseSpanMarker(elem) {
3052
- 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;
4189
+ function suppressDivAdjacentParagraphs(elements) {
4190
+ return suppressAtLevel(elements);
3053
4191
  }
3054
4192
 
3055
- // packages/parser/src/parser/rules/block/paragraph/normalize.ts
3056
- function normalizeParagraphElements(source) {
3057
- let elements = processCloseSpanMarkers(source);
3058
- removeTrailingLineBreaks2(elements);
3059
- removeTrailingWhitespaceText(elements);
3060
- elements = removeLeadingLineBreaks(elements);
3061
- return elements;
4193
+ // packages/parser/src/parser/rules/block/div/close.ts
4194
+ function isDivClose(ctx) {
4195
+ const token5 = ctx.tokens[ctx.pos];
4196
+ if (token5?.type !== "BLOCK_END_OPEN")
4197
+ return false;
4198
+ const closeNameResult = parseBlockName(ctx, ctx.pos + 1);
4199
+ return closeNameResult?.name === "div";
3062
4200
  }
3063
- function removeTrailingLineBreaks2(elements) {
3064
- while (elements.length > 0 && elements[elements.length - 1]?.element === "line-break") {
3065
- const lastEl = elements[elements.length - 1];
3066
- if (lastEl._preservedTrailingBreak) {
3067
- delete lastEl._preservedTrailingBreak;
3068
- break;
3069
- }
3070
- elements.pop();
4201
+ function consumeDivClose(ctx, startPos) {
4202
+ let pos = startPos;
4203
+ let consumed = 0;
4204
+ if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
4205
+ return { pos, consumed };
3071
4206
  }
3072
- }
3073
- function removeTrailingWhitespaceText(elements) {
3074
- while (elements.length > 0) {
3075
- const last = elements[elements.length - 1];
3076
- if (last?.element === "text" && "data" in last && typeof last.data === "string" && last.data.trim() === "") {
3077
- elements.pop();
3078
- } else {
3079
- break;
3080
- }
4207
+ pos++;
4208
+ consumed++;
4209
+ const closeNameResult = parseBlockName(ctx, pos);
4210
+ if (closeNameResult) {
4211
+ pos += closeNameResult.consumed;
4212
+ consumed += closeNameResult.consumed;
3081
4213
  }
3082
- }
3083
- function removeLeadingLineBreaks(elements) {
3084
- let first = 0;
3085
- while (first < elements.length && elements[first]?.element === "line-break") {
3086
- first++;
4214
+ if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
4215
+ pos++;
4216
+ consumed++;
3087
4217
  }
3088
- return first > 0 ? elements.slice(first) : elements;
4218
+ if (ctx.tokens[pos]?.type === "NEWLINE") {
4219
+ pos++;
4220
+ consumed++;
4221
+ }
4222
+ return { pos, consumed };
4223
+ }
4224
+
4225
+ // packages/parser/src/parser/rules/block/paragraph/content.ts
4226
+ function parseInlineContent(ctx) {
4227
+ return parseInlineUntil(ctx, "PARAGRAPH_BREAK");
3089
4228
  }
3090
4229
 
3091
4230
  // packages/parser/src/parser/rules/block/paragraph/index.ts
@@ -3095,25 +4234,30 @@ var paragraphRule = {
3095
4234
  requiresLineStart: false,
3096
4235
  parse(ctx) {
3097
4236
  const result = parseInlineContent(ctx);
3098
- if (result.elements.length === 0) {
4237
+ if (result.consumed === 0) {
3099
4238
  return { success: false };
3100
4239
  }
3101
4240
  const elements = normalizeParagraphElements(result.elements);
3102
4241
  if (elements.length === 0) {
3103
- return { success: false };
4242
+ return { success: true, elements: [], consumed: result.consumed };
3104
4243
  }
3105
4244
  const nextPos = ctx.pos + result.consumed;
3106
4245
  const nextToken = ctx.tokens[nextPos];
3107
4246
  if (nextToken?.type === "COLON" && nextToken.lineStart) {
4247
+ if (isPreservedLeadingLineBreak(elements[0]))
4248
+ elements[0] = { element: "line-break" };
3108
4249
  return {
3109
4250
  success: true,
3110
4251
  elements: [...elements, { element: "line-break" }],
3111
4252
  consumed: result.consumed
3112
4253
  };
3113
4254
  }
4255
+ const wrapped = wrapParagraphElements(elements);
4256
+ if (isDivClose(ctx))
4257
+ markUnparsedDivStart(wrapped);
3114
4258
  return {
3115
4259
  success: true,
3116
- elements: wrapParagraphElements(elements),
4260
+ elements: wrapped,
3117
4261
  consumed: result.consumed
3118
4262
  };
3119
4263
  }
@@ -3124,7 +4268,7 @@ function wrapParagraphElements(elements) {
3124
4268
  let bare = false;
3125
4269
  const flush = (trimBreaks = false) => {
3126
4270
  const content = trimBreaks ? normalizeParagraphElements(group) : group;
3127
- while (content[0]?.element === "line-break")
4271
+ while (content[0]?.element === "line-break" && !isPreservedLeadingLineBreak(content[0]))
3128
4272
  content.shift();
3129
4273
  while (content.length) {
3130
4274
  const last = content.at(-1);
@@ -3136,6 +4280,8 @@ function wrapParagraphElements(elements) {
3136
4280
  content.shift();
3137
4281
  if (content[0]?.element === "text")
3138
4282
  content[0] = { element: "text", data: content[0].data.trimStart() };
4283
+ if (isPreservedLeadingLineBreak(content[0]))
4284
+ content[0] = { element: "line-break" };
3139
4285
  if (content.length)
3140
4286
  output.push(...bare || content.some((el) => el.element === "image") ? content : [
3141
4287
  {
@@ -3157,37 +4303,36 @@ function wrapParagraphElements(elements) {
3157
4303
  return output;
3158
4304
  }
3159
4305
 
3160
- // packages/parser/src/parser/rules/block/div/close.ts
3161
- function isDivClose(ctx) {
3162
- const token5 = ctx.tokens[ctx.pos];
3163
- if (token5?.type !== "BLOCK_END_OPEN")
3164
- return false;
3165
- const closeNameResult = parseBlockName(ctx, ctx.pos + 1);
3166
- return closeNameResult?.name === "div";
3167
- }
3168
- function consumeDivClose(ctx, startPos) {
3169
- let pos = startPos;
3170
- let consumed = 0;
3171
- if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
3172
- return { pos, consumed };
3173
- }
3174
- pos++;
3175
- consumed++;
3176
- const closeNameResult = parseBlockName(ctx, pos);
3177
- if (closeNameResult) {
3178
- pos += closeNameResult.consumed;
3179
- consumed += closeNameResult.consumed;
3180
- }
3181
- if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
3182
- pos++;
3183
- consumed++;
3184
- }
3185
- if (ctx.tokens[pos]?.type === "NEWLINE") {
3186
- pos++;
3187
- consumed++;
4306
+ // packages/parser/src/parser/rules/block/note/index.ts
4307
+ var excludedBlockNames = new Set(["note"]);
4308
+ var noteRule = {
4309
+ name: "note",
4310
+ startTokens: ["BLOCK_OPEN"],
4311
+ requiresLineStart: false,
4312
+ parse(ctx) {
4313
+ const bounds = findNoteBounds(ctx);
4314
+ if (!bounds)
4315
+ return { success: false };
4316
+ const body = parseBlocksUntil({
4317
+ ...ctx,
4318
+ tokens: ctx.tokens.slice(bounds.bodyStart, bounds.close),
4319
+ pos: 0,
4320
+ scope: {
4321
+ ...ctx.scope,
4322
+ inlineEnd: undefined,
4323
+ tableFormatting: undefined,
4324
+ blockCloseCondition: undefined
4325
+ }
4326
+ }, () => false, { excludedBlockNames });
4327
+ return {
4328
+ success: true,
4329
+ consumed: bounds.end - ctx.pos,
4330
+ elements: [
4331
+ { element: "container", data: { type: "note", attributes: {}, elements: body.elements } }
4332
+ ]
4333
+ };
3188
4334
  }
3189
- return { pos, consumed };
3190
- }
4335
+ };
3191
4336
 
3192
4337
  // packages/parser/src/parser/rules/block/div/nesting.ts
3193
4338
  var divCloseCountCache = new WeakMap;
@@ -3356,6 +4501,7 @@ function consumeFailedDiv(ctx) {
3356
4501
  pos++;
3357
4502
  consumed++;
3358
4503
  }
4504
+ markUnparsedDivStart(elements);
3359
4505
  return {
3360
4506
  success: true,
3361
4507
  elements: [
@@ -3398,135 +4544,22 @@ function parseDivOpen(ctx) {
3398
4544
  pos++;
3399
4545
  consumed++;
3400
4546
  }
3401
- return {
3402
- blockName: nameResult.name,
3403
- paragraphStrip: nameResult.name === "div_",
3404
- attributes: attrResult.attrs,
3405
- bodyStart: pos,
3406
- consumed,
3407
- hasRequiredNewline
3408
- };
3409
- }
3410
- function isDivBlockName(name) {
3411
- return name === "div" || name === "div_";
3412
- }
3413
-
3414
- // packages/parser/src/parser/rules/block/div/index.ts
3415
- var divRule = {
3416
- name: "div",
3417
- startTokens: ["BLOCK_OPEN"],
3418
- requiresLineStart: false,
3419
- parse(ctx) {
3420
- const openToken = currentToken(ctx);
3421
- if (openToken.type !== "BLOCK_OPEN") {
3422
- return { success: false };
3423
- }
3424
- const openResult = parseDivOpen(ctx);
3425
- if (!openResult) {
3426
- return { success: false };
3427
- }
3428
- if (!openResult.hasRequiredNewline) {
3429
- return consumeFailedDiv(ctx);
3430
- }
3431
- if (ctx.scope.divClosesBudget === 0) {
3432
- return { success: false };
3433
- }
3434
- const openPosition = openToken.position;
3435
- let pos = openResult.bodyStart;
3436
- let consumed = openResult.consumed;
3437
- const bodyResult = parseDivBody(ctx, pos, openResult.paragraphStrip);
3438
- consumed += bodyResult.consumed;
3439
- pos += bodyResult.consumed;
3440
- if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
3441
- ctx.diagnostics.push({
3442
- severity: "warning",
3443
- code: "unclosed-block",
3444
- message: `Missing closing tag [[/div]] for [[${openResult.blockName}]]`,
3445
- position: openPosition
3446
- });
3447
- }
3448
- if (ctx.tokens[pos]?.type === "BLOCK_END_OPEN") {
3449
- const closeResult = consumeDivClose(ctx, pos);
3450
- pos = closeResult.pos;
3451
- consumed += closeResult.consumed;
3452
- }
3453
- return {
3454
- success: true,
3455
- elements: [
3456
- {
3457
- element: "container",
3458
- data: {
3459
- type: "div",
3460
- attributes: openResult.attributes,
3461
- elements: bodyResult.elements
3462
- }
3463
- }
3464
- ],
3465
- consumed
3466
- };
3467
- }
3468
- };
3469
-
3470
- // packages/parser/src/parser/rules/block/code/attributes.ts
3471
- function repairSwallowedCodeClose(ctx, pos, attrs) {
3472
- const prevToken = ctx.tokens[pos - 1];
3473
- if (prevToken?.type !== "QUOTED_STRING" || !prevToken.value.includes("]]")) {
3474
- return null;
3475
- }
3476
- const rawValue = prevToken.value;
3477
- const bracketIdx = rawValue.indexOf("]]");
3478
- const truncatedValue = rawValue.startsWith('"') ? rawValue.slice(1, bracketIdx) : rawValue.slice(0, bracketIdx);
3479
- for (const key of Object.keys(attrs)) {
3480
- const stored = attrs[key];
3481
- if (stored === rawValue || stored === rawValue.slice(1, -1) || stored === rawValue.slice(1)) {
3482
- attrs[key] = truncatedValue;
3483
- break;
3484
- }
3485
- }
3486
- return {
3487
- closingSwallowed: rawValue.includes("[[/code]]")
3488
- };
3489
- }
3490
-
3491
- // packages/parser/src/parser/rules/block/code/content.ts
3492
- function collectCodeContent(ctx, startPos, closingSwallowed) {
3493
- const contentParts = [];
3494
- let pos = startPos;
3495
- let consumed = 0;
3496
- let foundClose = closingSwallowed;
3497
- while (!closingSwallowed && pos < ctx.tokens.length) {
3498
- const token5 = ctx.tokens[pos];
3499
- if (!token5 || token5.type === "EOF") {
3500
- break;
3501
- }
3502
- if (token5.type === "BLOCK_END_OPEN") {
3503
- const closeNameResult = parseBlockName(ctx, pos + 1);
3504
- if (closeNameResult?.name === "code") {
3505
- foundClose = true;
3506
- const closeConsumed = consumeCodeClose(ctx, pos, closeNameResult.consumed);
3507
- consumed += closeConsumed;
3508
- break;
3509
- }
3510
- }
3511
- contentParts.push(token5.value);
3512
- pos++;
3513
- consumed++;
3514
- }
3515
- return { contents: contentParts.join(""), consumed, foundClose };
4547
+ return {
4548
+ blockName: nameResult.name,
4549
+ paragraphStrip: nameResult.name === "div_",
4550
+ attributes: attrResult.attrs,
4551
+ bodyStart: pos,
4552
+ consumed,
4553
+ hasRequiredNewline
4554
+ };
3516
4555
  }
3517
- function consumeCodeClose(ctx, startPos, closeNameConsumed) {
3518
- let pos = startPos + 1 + closeNameConsumed;
3519
- let consumed = 1 + closeNameConsumed;
3520
- if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
3521
- pos++;
3522
- consumed++;
3523
- }
3524
- return consumed;
4556
+ function isDivBlockName(name) {
4557
+ return name === "div" || name === "div_";
3525
4558
  }
3526
4559
 
3527
- // packages/parser/src/parser/rules/block/code/index.ts
3528
- var codeBlockRule = {
3529
- name: "code",
4560
+ // packages/parser/src/parser/rules/block/div/index.ts
4561
+ var divRule = {
4562
+ name: "div",
3530
4563
  startTokens: ["BLOCK_OPEN"],
3531
4564
  requiresLineStart: false,
3532
4565
  parse(ctx) {
@@ -3534,56 +4567,49 @@ var codeBlockRule = {
3534
4567
  if (openToken.type !== "BLOCK_OPEN") {
3535
4568
  return { success: false };
3536
4569
  }
3537
- let pos = ctx.pos + 1;
3538
- let consumed = 1;
3539
- const nameResult = parseBlockName(ctx, pos);
3540
- if (!nameResult || nameResult.name !== "code") {
4570
+ const openResult = parseDivOpen(ctx);
4571
+ if (!openResult) {
3541
4572
  return { success: false };
3542
4573
  }
3543
- pos += nameResult.consumed;
3544
- consumed += nameResult.consumed;
3545
- const attrResult = parseAttributesRaw(ctx, pos);
3546
- pos += attrResult.consumed;
3547
- consumed += attrResult.consumed;
3548
- let closingSwallowed = false;
3549
- if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
3550
- const repaired = repairSwallowedCodeClose(ctx, pos, attrResult.attrs);
3551
- if (!repaired)
3552
- return { success: false };
3553
- closingSwallowed = repaired.closingSwallowed;
3554
- } else {
3555
- pos++;
3556
- consumed++;
4574
+ if (!openResult.hasRequiredNewline) {
4575
+ return consumeFailedDiv(ctx);
3557
4576
  }
3558
- if (ctx.tokens[pos]?.type === "NEWLINE") {
3559
- pos++;
3560
- consumed++;
4577
+ if (ctx.scope.divClosesBudget === 0) {
4578
+ return { success: false };
3561
4579
  }
3562
- const contentResult = collectCodeContent(ctx, pos, closingSwallowed);
3563
- let codeContent = contentResult.contents.replace(/\n$/, "");
3564
- consumed += contentResult.consumed;
3565
- pos += contentResult.consumed;
3566
- if (!contentResult.foundClose) {
4580
+ const openPosition = openToken.position;
4581
+ let pos = openResult.bodyStart;
4582
+ let consumed = openResult.consumed;
4583
+ const bodyResult = parseDivBody(ctx, pos, openResult.paragraphStrip);
4584
+ consumed += bodyResult.consumed;
4585
+ pos += bodyResult.consumed;
4586
+ if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
3567
4587
  ctx.diagnostics.push({
3568
4588
  severity: "warning",
3569
4589
  code: "unclosed-block",
3570
- message: "Missing closing tag [[/code]] for [[code]]",
3571
- position: openToken.position
4590
+ message: `Missing closing tag [[/div]] for [[${openResult.blockName}]]`,
4591
+ position: openPosition
3572
4592
  });
3573
4593
  }
3574
- const codeBlockData = {
3575
- contents: codeContent,
3576
- language: attrResult.attrs.type ?? null,
3577
- name: attrResult.attrs.name ?? null
3578
- };
3579
- ctx.codeBlocks.push(codeBlockData);
3580
- const elements = [{ element: "code", data: codeBlockData }];
3581
- if (ctx.tokens[pos]?.type === "NEWLINE" && !getParagraphNewlineBoundary(ctx, pos, false).shouldBreak && !ctx.scope.blockCloseCondition?.({ ...ctx, pos: pos + 1 })) {
3582
- const after = parseInlineUntil({ ...ctx, pos: pos + 1 }, "PARAGRAPH_BREAK");
3583
- elements.push({ element: "line-break" }, ...normalizeParagraphElements(after.elements));
3584
- consumed += 1 + after.consumed;
4594
+ if (ctx.tokens[pos]?.type === "BLOCK_END_OPEN") {
4595
+ const closeResult = consumeDivClose(ctx, pos);
4596
+ pos = closeResult.pos;
4597
+ consumed += closeResult.consumed;
3585
4598
  }
3586
- return { success: true, elements, consumed };
4599
+ return {
4600
+ success: true,
4601
+ elements: [
4602
+ {
4603
+ element: "container",
4604
+ data: {
4605
+ type: "div",
4606
+ attributes: openResult.attributes,
4607
+ elements: bodyResult.elements
4608
+ }
4609
+ }
4610
+ ],
4611
+ consumed
4612
+ };
3587
4613
  }
3588
4614
  };
3589
4615
 
@@ -4015,7 +5041,9 @@ function parseTableCell(ctx, startPos, cellStart) {
4015
5041
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
4016
5042
  const result = rule.parse(inlineCtx);
4017
5043
  if (result.success) {
4018
- children.push(...result.elements);
5044
+ stripAutomaticLineBreak(children, result.stripLeadingLineBreak);
5045
+ for (const element of result.elements)
5046
+ children.push(element);
4019
5047
  consumed += result.consumed;
4020
5048
  pos += result.consumed;
4021
5049
  matched = true;
@@ -4282,7 +5310,8 @@ function createCellContentAccumulator() {
4282
5310
  addInline(element) {
4283
5311
  currentSegment.push(element);
4284
5312
  },
4285
- addInlineElements(nextElements) {
5313
+ addInlineElements(nextElements, stripLeadingLineBreak) {
5314
+ stripAutomaticLineBreak(currentSegment, stripLeadingLineBreak);
4286
5315
  currentSegment.push(...nextElements);
4287
5316
  },
4288
5317
  addBlockElements(blockElements) {
@@ -4382,7 +5411,7 @@ function consumeCellContentNewline(ctx, startPos, content) {
4382
5411
  return { consumed };
4383
5412
  }
4384
5413
  if (!content.isEmpty()) {
4385
- content.addInline({ element: "line-break" });
5414
+ content.addInline(createAutomaticLineBreak(ctx.tokens[startPos]));
4386
5415
  }
4387
5416
  return { consumed };
4388
5417
  }
@@ -4433,7 +5462,7 @@ function parseCellContent(ctx, closeCondition) {
4433
5462
  for (const rule of getCandidateInlineRules(ctx.inlineRules, token5.type)) {
4434
5463
  const result = rule.parse(inlineCtx);
4435
5464
  if (result.success) {
4436
- content.addInlineElements(result.elements);
5465
+ content.addInlineElements(result.elements, result.stripLeadingLineBreak);
4437
5466
  consumed += result.consumed;
4438
5467
  pos += result.consumed;
4439
5468
  matched = true;
@@ -6253,9 +7282,9 @@ function renderListPagesItems(module2, data, compiledTemplate, parse) {
6253
7282
  continue;
6254
7283
  const ctx = {
6255
7284
  page,
6256
- index: i + 1,
7285
+ index: (data.pagination?.offset ?? 0) + i + 1,
6257
7286
  total: data.totalCount,
6258
- limit: module2.limit,
7287
+ limit: data.pagination ? data.pagination.limit : module2.limit,
6259
7288
  site: data.site
6260
7289
  };
6261
7290
  const itemAst = getModuleParseAst(parse(compiledTemplate(ctx)));
@@ -6275,9 +7304,46 @@ function renderListPagesItems(module2, data, compiledTemplate, parse) {
6275
7304
  return items;
6276
7305
  }
6277
7306
 
7307
+ // packages/parser/src/parser/rules/block/module/listpages/resolution/pager.ts
7308
+ function createListPagesPager(data) {
7309
+ const pagination = data.pagination;
7310
+ if (!pagination || pagination.totalPages <= 1)
7311
+ return [];
7312
+ const { currentPage, totalPages, urlPath, parameter } = pagination;
7313
+ const pages = new Set([1, totalPages]);
7314
+ for (let page = Math.max(1, currentPage - 2);page <= Math.min(totalPages, currentPage + 2); page++) {
7315
+ pages.add(page);
7316
+ }
7317
+ return [
7318
+ {
7319
+ element: "pager",
7320
+ data: {
7321
+ currentPage,
7322
+ totalPages,
7323
+ pages: [...pages].sort((a, b) => a - b).map((page) => ({
7324
+ page,
7325
+ href: pageUrl(urlPath, parameter, page)
7326
+ }))
7327
+ }
7328
+ }
7329
+ ];
7330
+ }
7331
+ function pageUrl(urlPath, parameter, page) {
7332
+ const suffixStart = urlPath.search(/[?#]/);
7333
+ const path = suffixStart === -1 ? urlPath : urlPath.slice(0, suffixStart);
7334
+ const suffix = suffixStart === -1 ? "" : urlPath.slice(suffixStart);
7335
+ const parts = path.split("/");
7336
+ const result = parts.slice(0, 2);
7337
+ for (let index = 2;index < parts.length; index += 2) {
7338
+ if (parts[index] !== parameter)
7339
+ result.push(...parts.slice(index, index + 2));
7340
+ }
7341
+ return `${result.join("/").replace(/\/$/, "")}/${parameter}/${page}${suffix}`;
7342
+ }
7343
+
6278
7344
  // packages/parser/src/parser/rules/block/module/listpages/resolution/wrapper.ts
6279
- function wrapListPagesResult(module2, items, parse) {
6280
- if (items.length === 0) {
7345
+ function wrapListPagesResult(module2, items, parse, data) {
7346
+ if (items.length === 0 && (!data || data.pages.length === 0)) {
6281
7347
  return [];
6282
7348
  }
6283
7349
  const result = [];
@@ -6290,6 +7356,8 @@ function wrapListPagesResult(module2, items, parse) {
6290
7356
  const appendAst = getModuleParseAst(parse(module2["append-line"]));
6291
7357
  result.push(...appendAst.elements);
6292
7358
  }
7359
+ if (data)
7360
+ result.push(...createListPagesPager(data));
6293
7361
  if (module2.wrapper) {
6294
7362
  return [
6295
7363
  {
@@ -6311,7 +7379,7 @@ function isListPagesModule2(module2) {
6311
7379
  }
6312
7380
  function resolveListPages(module2, data, compiledTemplate, parse) {
6313
7381
  const items = renderListPagesItems(module2, data, compiledTemplate, parse);
6314
- return wrapListPagesResult(module2, items, parse);
7382
+ return wrapListPagesResult(module2, items, parse, data);
6315
7383
  }
6316
7384
  // packages/parser/src/parser/rules/block/module/listpages/types/external-data.ts
6317
7385
  function definePageData(input) {
@@ -6408,10 +7476,10 @@ var URL_RESOLVABLE_FIELDS = [
6408
7476
  ];
6409
7477
 
6410
7478
  // packages/parser/src/parser/rules/block/module/listpages/url-resolution/params.ts
6411
- function parseUrlParams(url) {
7479
+ function parseUrlParams(url, hasPageName = false) {
6412
7480
  const params = new Map;
6413
- const parts = url.split("/").filter(Boolean);
6414
- const pairStart = isUrlParameter(parts[0]) ? 0 : 1;
7481
+ const parts = url.split(/[?#]/, 1)[0].split("/").filter(Boolean);
7482
+ const pairStart = !hasPageName && isUrlParameter(parts[0]) ? 0 : 1;
6415
7483
  for (let i = pairStart;i < parts.length - 1; i += 2) {
6416
7484
  const key = parts[i];
6417
7485
  const value = parts[i + 1];
@@ -6814,7 +7882,7 @@ function resolveIfTags(data, pageTags) {
6814
7882
  // packages/parser/src/parser/preprocess/utils/raw-regions.ts
6815
7883
  var BASE_PLACEHOLDER_OPEN = "";
6816
7884
  var BASE_PLACEHOLDER_CLOSE = "";
6817
- var RAW_BLOCK_OPEN_PATTERN = /\[\[\s*(code|html)\b[^\]]*\]\]/iy;
7885
+ var RAW_BLOCK_OPEN_PATTERN = /\[\[html\b[^\]]*\]\]/iy;
6818
7886
  function makeUniqueSentinels(source) {
6819
7887
  let openRun = 0;
6820
7888
  let closeRun = 0;
@@ -6833,10 +7901,21 @@ function makeUniqueSentinels(source) {
6833
7901
  }
6834
7902
  function maskRawRegions(source, sentinels) {
6835
7903
  const placeholders = [];
7904
+ let tokens;
7905
+ const getTokens = () => tokens ??= tokenize(source);
6836
7906
  let masked = "";
6837
7907
  let i = 0;
6838
7908
  while (i < source.length) {
6839
- const rawBlock = tryMaskRawBlock(source, i, placeholders, sentinels);
7909
+ if (source.startsWith("[!--", i)) {
7910
+ const close = source.indexOf("--]", i + 4);
7911
+ if (close !== -1) {
7912
+ const end = close + 3;
7913
+ masked += source.slice(i, end);
7914
+ i = end;
7915
+ continue;
7916
+ }
7917
+ }
7918
+ const rawBlock = tryMaskRawBlock(source, i, placeholders, sentinels, getTokens);
6840
7919
  if (rawBlock) {
6841
7920
  masked += rawBlock.placeholder;
6842
7921
  i = rawBlock.end;
@@ -6857,16 +7936,25 @@ function restorePlaceholders(source, placeholders, sentinels) {
6857
7936
  const pattern = new RegExp(`${escapeRegex(sentinels.open)}(\\d+)${escapeRegex(sentinels.close)}`, "g");
6858
7937
  return source.replace(pattern, (_, idx) => placeholders[Number(idx)] ?? "");
6859
7938
  }
6860
- function tryMaskRawBlock(source, pos, placeholders, sentinels) {
7939
+ function tryMaskRawBlock(source, pos, placeholders, sentinels, getTokens) {
6861
7940
  if (source[pos] !== "[" || source[pos + 1] !== "[")
6862
7941
  return null;
7942
+ if (source.slice(pos, pos + 6).toLowerCase() === "[[code") {
7943
+ const tokens = getTokens();
7944
+ const start = tokenAtOffset(tokens, pos);
7945
+ const open = findCodeOpen(tokens, start);
7946
+ if (!open)
7947
+ return null;
7948
+ const bounds = open.closingSwallowed ? null : findCodeBodyBounds(tokens, open.bodyStart);
7949
+ const end = open.closingSwallowed ? tokens[open.attributesEnd - 1].position.end.offset : bounds.foundClose ? tokens[bounds.end - 1].position.end.offset : source.length;
7950
+ return { placeholder: pushPlaceholder(placeholders, source.slice(pos, end), sentinels), end };
7951
+ }
6863
7952
  RAW_BLOCK_OPEN_PATTERN.lastIndex = pos;
6864
7953
  const openMatch = RAW_BLOCK_OPEN_PATTERN.exec(source);
6865
7954
  if (!openMatch)
6866
7955
  return null;
6867
- const name = openMatch[1].toLowerCase();
6868
7956
  const openLen = openMatch[0].length;
6869
- const closePattern = new RegExp(`\\[\\[\\/\\s*${name}\\s*\\]\\]`, "ig");
7957
+ const closePattern = /\[\[\/\s*html\s*\]\]/gi;
6870
7958
  closePattern.lastIndex = pos + openLen;
6871
7959
  const closeMatch = closePattern.exec(source);
6872
7960
  if (closeMatch) {
@@ -6876,12 +7964,19 @@ function tryMaskRawBlock(source, pos, placeholders, sentinels) {
6876
7964
  end
6877
7965
  };
6878
7966
  }
6879
- if (name !== "code")
6880
- return null;
6881
- return {
6882
- placeholder: pushPlaceholder(placeholders, source.slice(pos), sentinels),
6883
- end: source.length
6884
- };
7967
+ return null;
7968
+ }
7969
+ function tokenAtOffset(tokens, offset) {
7970
+ let low = 0;
7971
+ let high = tokens.length;
7972
+ while (low < high) {
7973
+ const middle = Math.floor((low + high) / 2);
7974
+ if (tokens[middle].position.start.offset < offset)
7975
+ low = middle + 1;
7976
+ else
7977
+ high = middle;
7978
+ }
7979
+ return tokens[low]?.position.start.offset === offset ? low : tokens.length;
6885
7980
  }
6886
7981
  function tryMaskRawInline(source, pos, placeholders, sentinels) {
6887
7982
  if (source[pos] === "@" && source[pos + 1] === "<") {
@@ -6893,17 +7988,22 @@ function tryMaskRawInline(source, pos, placeholders, sentinels) {
6893
7988
  return null;
6894
7989
  }
6895
7990
  function tryMaskSingleLineRaw(source, pos, openerLength, close, placeholders, sentinels) {
6896
- const closePos = source.indexOf(close, pos + openerLength);
6897
- const newline = source.indexOf(`
6898
- `, pos + openerLength);
6899
- if (closePos === -1 || newline !== -1 && newline < closePos)
7991
+ const end = singleLineRawEnd(source, pos, openerLength, close);
7992
+ if (end === pos)
6900
7993
  return null;
6901
- const end = closePos + close.length;
6902
7994
  return {
6903
7995
  placeholder: pushPlaceholder(placeholders, source.slice(pos, end), sentinels),
6904
7996
  end
6905
7997
  };
6906
7998
  }
7999
+ function singleLineRawEnd(source, pos, openerLength, close) {
8000
+ const closePos = source.indexOf(close, pos + openerLength);
8001
+ const newline = source.indexOf(`
8002
+ `, pos + openerLength);
8003
+ if (closePos === -1 || newline !== -1 && newline < closePos)
8004
+ return pos;
8005
+ return closePos + close.length;
8006
+ }
6907
8007
  function pushPlaceholder(placeholders, text, sentinels) {
6908
8008
  const idx = placeholders.length;
6909
8009
  placeholders.push(text);
@@ -7509,18 +8609,66 @@ function resolveListUsers(_module, data, compiledTemplate, parse) {
7509
8609
  return itemAst.elements;
7510
8610
  }
7511
8611
  // packages/parser/src/parser/rules/block/module/resolution/data-maps.ts
7512
- async function buildListPagesDataMap(dataProvider, requirements, urlPath) {
8612
+ async function buildListPagesDataMap(dataProvider, requirements, urlPath, paginationState = { nextUnprefixed: 1 }) {
7513
8613
  const dataMap = new Map;
7514
- const urlParams = parseUrlParams(urlPath ?? "");
8614
+ const urlParams = parseUrlParams(urlPath ?? "", true);
7515
8615
  for (const req of requirements) {
7516
8616
  const normalizedQuery = resolveAndNormalizeQuery(req, urlParams);
7517
- const data = await dataProvider.fetchListPages?.(normalizedQuery, req);
8617
+ if (!urlPath || !urlPath.startsWith("/") || urlPath.startsWith("//")) {
8618
+ const data2 = await dataProvider.fetchListPages?.(normalizedQuery, req);
8619
+ if (data2)
8620
+ dataMap.set(req.id, data2);
8621
+ continue;
8622
+ }
8623
+ const ordinal = req.urlAttrPrefix ? 0 : paginationState.nextUnprefixed++;
8624
+ const parameter = req.urlAttrPrefix ? `${encodeURIComponent(req.urlAttrPrefix)}_p` : ordinal === 1 ? "p" : `p${ordinal}`;
8625
+ const perPage = Math.min(250, positiveInteger(normalizedQuery.perPage, 20));
8626
+ const baseOffset = positiveInteger(normalizedQuery.offset, 0);
8627
+ const limit = normalizedQuery.limit !== undefined && normalizedQuery.limit >= 0 ? Math.floor(normalizedQuery.limit) : undefined;
8628
+ const rawPage = urlParams.get(parameter) ?? "1";
8629
+ let currentPage = /^\d+$/.test(rawPage) ? positiveInteger(Number(rawPage), 1) : 1;
8630
+ currentPage = Math.min(currentPage, Math.floor((Number.MAX_SAFE_INTEGER - baseOffset) / perPage) + 1);
8631
+ if (limit !== undefined)
8632
+ currentPage = Math.min(currentPage, Math.max(1, Math.ceil(limit / perPage)));
8633
+ const fetchPage = () => {
8634
+ const relativeOffset = (currentPage - 1) * perPage;
8635
+ return dataProvider.fetchListPages?.({
8636
+ ...normalizedQuery,
8637
+ offset: baseOffset + relativeOffset,
8638
+ limit: Math.min(perPage, limit === undefined ? perPage : Math.max(0, limit - relativeOffset)),
8639
+ perPage
8640
+ }, req);
8641
+ };
8642
+ let data = await fetchPage();
7518
8643
  if (data) {
7519
- dataMap.set(req.id, data);
8644
+ const count = Math.min(Math.max(0, data.totalCount - baseOffset), limit ?? Infinity);
8645
+ const totalPages = Math.ceil(count / perPage);
8646
+ const lastPage = Math.max(1, totalPages);
8647
+ if (currentPage > lastPage) {
8648
+ currentPage = lastPage;
8649
+ data = await fetchPage();
8650
+ }
8651
+ if (data)
8652
+ dataMap.set(req.id, {
8653
+ ...data,
8654
+ pages: data.pages.slice(0, Math.min(perPage, Math.max(0, count - (currentPage - 1) * perPage))),
8655
+ pagination: {
8656
+ currentPage,
8657
+ perPage,
8658
+ totalPages,
8659
+ offset: baseOffset + (currentPage - 1) * perPage,
8660
+ limit,
8661
+ urlPath,
8662
+ parameter
8663
+ }
8664
+ });
7520
8665
  }
7521
8666
  }
7522
8667
  return dataMap;
7523
8668
  }
8669
+ function positiveInteger(value, fallback) {
8670
+ return value !== undefined && Number.isSafeInteger(value) && value > 0 ? value : fallback;
8671
+ }
7524
8672
  async function buildListUsersDataMap(dataProvider, requirements) {
7525
8673
  const dataMap = new Map;
7526
8674
  for (const req of requirements) {
@@ -9022,167 +10170,32 @@ function parseVariables(tokens) {
9022
10170
  for (const token5 of tokens) {
9023
10171
  if (token5 === "|") {
9024
10172
  assignVariable(vars, current2);
9025
- current2 = "";
9026
- } else {
9027
- current2 += token5;
9028
- }
9029
- }
9030
- assignVariable(vars, current2);
9031
- return vars;
9032
- }
9033
- function assignVariable(vars, source) {
9034
- if (!source.trim()) {
9035
- return;
9036
- }
9037
- const eqIndex = source.indexOf("=");
9038
- if (eqIndex === -1) {
9039
- return;
9040
- }
9041
- const key = source.slice(0, eqIndex).trim();
9042
- if (!key) {
9043
- return;
9044
- }
9045
- vars[key] = source.slice(eqIndex + 1).trim();
9046
- }
9047
-
9048
- // packages/parser/src/parser/rules/block/include/index.ts
9049
- var includeRule = {
9050
- name: "include",
9051
- startTokens: ["BLOCK_OPEN"],
9052
- requiresLineStart: false,
9053
- parse(ctx) {
9054
- const openToken = currentToken(ctx);
9055
- if (openToken.type !== "BLOCK_OPEN") {
9056
- return { success: false };
9057
- }
9058
- let pos = ctx.pos + 1;
9059
- let consumed = 1;
9060
- const nameResult = parseBlockName(ctx, pos);
9061
- if (!nameResult || nameResult.name.toLowerCase() !== "include") {
9062
- return { success: false };
9063
- }
9064
- if (!ctx.settings.enablePageSyntax) {
9065
- return { success: false };
9066
- }
9067
- pos += nameResult.consumed;
9068
- consumed += nameResult.consumed;
9069
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
9070
- pos++;
9071
- consumed++;
9072
- }
9073
- const args = collectIncludeArguments(ctx, pos);
9074
- pos += args.consumed;
9075
- consumed += args.consumed;
9076
- if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
9077
- return { success: false };
9078
- }
9079
- pos++;
9080
- consumed++;
9081
- const directiveEnd = pos;
9082
- if (ctx.tokens[pos]?.type === "NEWLINE") {
9083
- pos++;
9084
- consumed++;
9085
- }
9086
- if (!args.target) {
9087
- return { success: false };
9088
- }
9089
- const location = parsePageRef(args.target);
9090
- if (ctx.deferInclude?.(location)) {
9091
- const source = ctx.tokens.slice(ctx.pos, directiveEnd).map((token5) => token5.value).join("");
9092
- const elements = [];
9093
- const lines = source.split(`
9094
- `);
9095
- for (let index = 0;index < lines.length; index++) {
9096
- if (index > 0)
9097
- elements.push(import_ast5.lineBreak());
9098
- if (lines[index] !== "")
9099
- elements.push(import_ast5.text(lines[index]));
9100
- }
9101
- return { success: true, elements: [import_ast5.paragraph(elements)], consumed };
9102
- }
9103
- return {
9104
- success: true,
9105
- elements: [
9106
- {
9107
- element: "include",
9108
- data: {
9109
- "paragraph-safe": false,
9110
- variables: parseVariables(args.argumentTokens),
9111
- location,
9112
- elements: []
9113
- }
9114
- }
9115
- ],
9116
- consumed
9117
- };
9118
- }
9119
- };
9120
-
9121
- // packages/parser/src/parser/rules/block/math/content.ts
9122
- function collectMathContent(ctx, startPos) {
9123
- let latexSource = "";
9124
- let pos = startPos;
9125
- let consumed = 0;
9126
- let foundClose = false;
9127
- while (pos < ctx.tokens.length) {
9128
- const token5 = ctx.tokens[pos];
9129
- if (!token5)
9130
- break;
9131
- if (token5.type === "BLOCK_END_OPEN") {
9132
- const closeNameResult = parseBlockName(ctx, pos + 1);
9133
- if (closeNameResult?.name === "math") {
9134
- foundClose = true;
9135
- break;
9136
- }
9137
- }
9138
- latexSource += token5.type === "BACKSLASH_BREAK" ? "\\\n" : token5.value;
9139
- pos++;
9140
- consumed++;
9141
- }
9142
- return { latexSource, consumed, foundClose };
9143
- }
9144
- function consumeMathClose(ctx, startPos) {
9145
- let pos = startPos + 1;
9146
- let consumed = 1;
9147
- const closeNameResult = parseBlockName(ctx, pos);
9148
- if (closeNameResult) {
9149
- pos += closeNameResult.consumed;
9150
- consumed += closeNameResult.consumed;
9151
- }
9152
- if (ctx.tokens[pos]?.type === "BLOCK_CLOSE") {
9153
- pos++;
9154
- consumed++;
9155
- }
9156
- if (ctx.tokens[pos]?.type === "NEWLINE") {
9157
- consumed++;
10173
+ current2 = "";
10174
+ } else {
10175
+ current2 += token5;
10176
+ }
9158
10177
  }
9159
- return consumed;
10178
+ assignVariable(vars, current2);
10179
+ return vars;
9160
10180
  }
9161
-
9162
- // packages/parser/src/parser/rules/block/math/name.ts
9163
- function parseMathName(ctx, startPos) {
9164
- let pos = startPos;
9165
- let consumed = 0;
9166
- let name = "";
9167
- const first = ctx.tokens[pos];
9168
- if (first?.type !== "IDENTIFIER" && first?.type !== "TEXT") {
9169
- return { name: null, consumed: 0 };
10181
+ function assignVariable(vars, source) {
10182
+ if (!source.trim()) {
10183
+ return;
9170
10184
  }
9171
- while (pos < ctx.tokens.length) {
9172
- const token5 = ctx.tokens[pos];
9173
- if (!token5 || token5.type === "BLOCK_CLOSE" || token5.type === "WHITESPACE" || token5.type === "NEWLINE") {
9174
- break;
9175
- }
9176
- name += token5.value;
9177
- pos++;
9178
- consumed++;
10185
+ const eqIndex = source.indexOf("=");
10186
+ if (eqIndex === -1) {
10187
+ return;
10188
+ }
10189
+ const key = source.slice(0, eqIndex).trim();
10190
+ if (!key) {
10191
+ return;
9179
10192
  }
9180
- return { name: name === "" ? null : name, consumed };
10193
+ vars[key] = source.slice(eqIndex + 1).trim();
9181
10194
  }
9182
10195
 
9183
- // packages/parser/src/parser/rules/block/math/index.ts
9184
- var mathBlockRule = {
9185
- name: "math",
10196
+ // packages/parser/src/parser/rules/block/include/index.ts
10197
+ var includeRule = {
10198
+ name: "include",
9186
10199
  startTokens: ["BLOCK_OPEN"],
9187
10200
  requiresLineStart: false,
9188
10201
  parse(ctx) {
@@ -9193,7 +10206,10 @@ var mathBlockRule = {
9193
10206
  let pos = ctx.pos + 1;
9194
10207
  let consumed = 1;
9195
10208
  const nameResult = parseBlockName(ctx, pos);
9196
- if (!nameResult || nameResult.name !== "math") {
10209
+ if (!nameResult || nameResult.name.toLowerCase() !== "include") {
10210
+ return { success: false };
10211
+ }
10212
+ if (!ctx.settings.enablePageSyntax) {
9197
10213
  return { success: false };
9198
10214
  }
9199
10215
  pos += nameResult.consumed;
@@ -9202,49 +10218,46 @@ var mathBlockRule = {
9202
10218
  pos++;
9203
10219
  consumed++;
9204
10220
  }
9205
- const mathName = parseMathName(ctx, pos);
9206
- pos += mathName.consumed;
9207
- consumed += mathName.consumed;
9208
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
9209
- pos++;
9210
- consumed++;
9211
- }
10221
+ const args = collectIncludeArguments(ctx, pos);
10222
+ pos += args.consumed;
10223
+ consumed += args.consumed;
9212
10224
  if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
9213
10225
  return { success: false };
9214
10226
  }
9215
10227
  pos++;
9216
10228
  consumed++;
10229
+ const directiveEnd = pos;
9217
10230
  if (ctx.tokens[pos]?.type === "NEWLINE") {
9218
10231
  pos++;
9219
10232
  consumed++;
9220
10233
  }
9221
- const contentResult = collectMathContent(ctx, pos);
9222
- const latexSource = contentResult.latexSource.trim();
9223
- consumed += contentResult.consumed;
9224
- pos += contentResult.consumed;
9225
- if (!contentResult.foundClose) {
9226
- ctx.diagnostics.push({
9227
- severity: "warning",
9228
- code: "unclosed-block",
9229
- message: "Missing closing tag [[/math]] for [[math]]",
9230
- position: openToken.position
9231
- });
9232
- } else {
9233
- const closeConsumed = consumeMathClose(ctx, pos);
9234
- pos += closeConsumed;
9235
- consumed += closeConsumed;
9236
- }
9237
- if (!latexSource) {
10234
+ if (!args.target) {
9238
10235
  return { success: false };
9239
10236
  }
10237
+ const location = parsePageRef(args.target);
10238
+ if (ctx.deferInclude?.(location)) {
10239
+ const source = ctx.tokens.slice(ctx.pos, directiveEnd).map((token5) => token5.value).join("");
10240
+ const elements = [];
10241
+ const lines = source.split(`
10242
+ `);
10243
+ for (let index = 0;index < lines.length; index++) {
10244
+ if (index > 0)
10245
+ elements.push(import_ast5.lineBreak());
10246
+ if (lines[index] !== "")
10247
+ elements.push(import_ast5.text(lines[index]));
10248
+ }
10249
+ return { success: true, elements: [import_ast5.paragraph(elements)], consumed };
10250
+ }
9240
10251
  return {
9241
10252
  success: true,
9242
10253
  elements: [
9243
10254
  {
9244
- element: "math",
10255
+ element: "include",
9245
10256
  data: {
9246
- name: mathName.name,
9247
- "latex-source": latexSource
10257
+ "paragraph-safe": false,
10258
+ variables: parseVariables(args.argumentTokens),
10259
+ location,
10260
+ elements: []
9248
10261
  }
9249
10262
  }
9250
10263
  ],
@@ -10114,7 +11127,8 @@ function parseBibliographyContent(ctx, startPos) {
10114
11127
  const inlineCtx = { ...ctx, pos };
10115
11128
  const result = parseInlineUntil(inlineCtx, "NEWLINE");
10116
11129
  if (result.elements.length > 0) {
10117
- content.push(...result.elements);
11130
+ for (const element of result.elements)
11131
+ content.push(element);
10118
11132
  pos += result.consumed;
10119
11133
  consumed += result.consumed;
10120
11134
  } else {
@@ -10543,6 +11557,7 @@ var blockRules = [
10543
11557
  iftagsRule,
10544
11558
  bibliographyRule,
10545
11559
  galleryRule,
11560
+ noteRule,
10546
11561
  divRule
10547
11562
  ];
10548
11563
  // packages/parser/src/parser/rules/inline/formatting/close.ts
@@ -10555,6 +11570,11 @@ function findFormattingClose(ctx, start, marker) {
10555
11570
  return null;
10556
11571
  if (!table && token5.type === "NEWLINE" && getParagraphNewlineBoundary(ctx, pos, true).shouldBreak)
10557
11572
  return null;
11573
+ const inlineRegionEnd = Math.max(emailRegionEnd(ctx.tokens, pos, end), parseButtonSyntax(ctx, pos, end)?.end ?? pos, parseSocialSyntax(ctx, pos, end)?.end ?? pos);
11574
+ if (inlineRegionEnd > pos) {
11575
+ pos = inlineRegionEnd - 1;
11576
+ continue;
11577
+ }
10558
11578
  if (token5.type === marker && !table?.suppressedClosers.has(pos))
10559
11579
  return pos;
10560
11580
  const protectedEnd = protectedInlineRegionEnd(ctx.tokens, pos, end);
@@ -10600,253 +11620,93 @@ function parseDelimitedContainer(ctx, closeToken, type, options = {}) {
10600
11620
  consumed
10601
11621
  };
10602
11622
  }
10603
- return {
10604
- success: true,
10605
- elements: [createInlineContainer(type, result.elements)],
10606
- consumed
10607
- };
10608
- }
10609
-
10610
- // packages/parser/src/parser/rules/inline/bold.ts
10611
- var boldRule = {
10612
- name: "bold",
10613
- startTokens: ["BOLD_MARKER"],
10614
- parse(ctx) {
10615
- return parseDelimitedContainer(ctx, "BOLD_MARKER", "bold", { discardEmpty: true });
10616
- }
10617
- };
10618
-
10619
- // packages/parser/src/parser/rules/inline/italic.ts
10620
- var italicRule = {
10621
- name: "italic",
10622
- startTokens: ["ITALIC_MARKER"],
10623
- parse(ctx) {
10624
- return parseDelimitedContainer(ctx, "ITALIC_MARKER", "italics");
10625
- }
10626
- };
10627
-
10628
- // packages/parser/src/parser/rules/inline/underline/index.ts
10629
- var underlineRule = {
10630
- name: "underline",
10631
- startTokens: ["UNDERLINE_MARKER"],
10632
- parse(ctx) {
10633
- return parseDelimitedContainer(ctx, "UNDERLINE_MARKER", "underline", { discardEmpty: true });
10634
- }
10635
- };
10636
-
10637
- // packages/parser/src/parser/rules/inline/strikethrough/parse.ts
10638
- function parseStrikethroughContent(ctx) {
10639
- return parseDelimitedContainer(ctx, "STRIKE_MARKER", "strikethrough");
10640
- }
10641
-
10642
- // packages/parser/src/parser/rules/inline/strikethrough/syntax.ts
10643
- function hasValidStrikethroughClose(ctx) {
10644
- const close = findFormattingClose(ctx, ctx.pos + 1, "STRIKE_MARKER");
10645
- return close !== null && close > ctx.pos + 1 && ctx.tokens[close - 1]?.type !== "WHITESPACE";
10646
- }
10647
-
10648
- // packages/parser/src/parser/rules/inline/strikethrough/index.ts
10649
- var strikethroughRule = {
10650
- name: "strikethrough",
10651
- startTokens: ["STRIKE_MARKER"],
10652
- parse(ctx) {
10653
- if (!hasValidStrikethroughClose(ctx)) {
10654
- return {
10655
- success: true,
10656
- elements: [{ element: "text", data: "—" }],
10657
- consumed: 1
10658
- };
10659
- }
10660
- return parseStrikethroughContent(ctx);
10661
- }
10662
- };
10663
-
10664
- // packages/parser/src/parser/rules/inline/superscript.ts
10665
- var superscriptRule = {
10666
- name: "superscript",
10667
- startTokens: ["SUPER_MARKER"],
10668
- parse(ctx) {
10669
- return parseDelimitedContainer(ctx, "SUPER_MARKER", "superscript", {
10670
- discardEmpty: true
10671
- });
10672
- }
10673
- };
10674
-
10675
- // packages/parser/src/parser/rules/inline/subscript.ts
10676
- var subscriptRule = {
10677
- name: "subscript",
10678
- startTokens: ["SUB_MARKER"],
10679
- parse(ctx) {
10680
- return parseDelimitedContainer(ctx, "SUB_MARKER", "subscript", { discardEmpty: true });
10681
- }
10682
- };
10683
-
10684
- // packages/parser/src/parser/rules/inline/monospace.ts
10685
- var monospaceRule = {
10686
- name: "monospace",
10687
- startTokens: ["MONO_MARKER"],
10688
- parse(ctx) {
10689
- return parseDelimitedContainer(ctx, "MONO_CLOSE", "monospace");
10690
- }
10691
- };
10692
-
10693
- // packages/parser/src/parser/rules/inline/link-triple/fallback.ts
10694
- function literalOpenLink(value) {
10695
- return {
10696
- success: true,
10697
- elements: [{ element: "text", data: value }],
10698
- consumed: 1
10699
- };
10700
- }
10701
-
10702
- // packages/parser/src/parser/rules/inline/link-triple/label.ts
10703
- function buildTripleLinkLabel(args) {
10704
- return {
10705
- text: getTripleLinkDisplayText(args)
10706
- };
10707
- }
10708
- function getTripleLinkDisplayText(args) {
10709
- const trimmedLabel = args.labelText.trim();
10710
- if (args.foundPipe) {
10711
- return trimmedLabel || args.finalTarget;
10712
- }
10713
- const colonIdx = args.originalTarget.indexOf(":");
10714
- if (colonIdx !== -1 && !args.originalTarget.startsWith("http") && !args.originalTarget.startsWith("*")) {
10715
- return args.originalTarget.slice(colonIdx + 1).trim();
10716
- }
10717
- return args.originalTarget;
10718
- }
10719
-
10720
- // packages/parser/src/parser/rules/inline/link-triple/syntax.ts
10721
- function hasClosingLinkMarker(ctx, startPos) {
10722
- let pos = startPos;
10723
- while (pos < ctx.tokens.length) {
10724
- const token5 = ctx.tokens[pos];
10725
- if (!token5 || token5.type === "EOF") {
10726
- return false;
10727
- }
10728
- if (token5.type === "LINK_CLOSE") {
10729
- return true;
10730
- }
10731
- if (token5.type === "NEWLINE") {
10732
- const next = ctx.tokens[pos + 1];
10733
- if (next?.type === "NEWLINE" || next?.type === "LINK_CLOSE") {
10734
- return false;
10735
- }
10736
- }
10737
- pos++;
10738
- }
10739
- return false;
10740
- }
10741
- function collectTripleLinkParts(ctx, startPos) {
10742
- let target = "";
10743
- let labelText = "";
10744
- let foundPipe = false;
10745
- let consumed = 1;
10746
- let pos = startPos;
10747
- while (pos < ctx.tokens.length) {
10748
- const token5 = ctx.tokens[pos];
10749
- if (!token5 || token5.type === "LINK_CLOSE" || token5.type === "EOF") {
10750
- break;
10751
- }
10752
- if (token5.type === "NEWLINE") {
10753
- if (foundPipe) {
10754
- labelText += " ";
10755
- } else {
10756
- target += " ";
10757
- }
10758
- consumed++;
10759
- pos++;
10760
- continue;
10761
- }
10762
- if (token5.type === "PIPE" && !foundPipe) {
10763
- foundPipe = true;
10764
- } else if (foundPipe) {
10765
- labelText += token5.value;
10766
- } else {
10767
- target += token5.value;
10768
- }
10769
- consumed++;
10770
- pos++;
10771
- }
10772
- if (ctx.tokens[pos]?.type === "LINK_CLOSE") {
10773
- consumed++;
10774
- }
10775
- return { target, labelText, foundPipe, consumed };
11623
+ return {
11624
+ success: true,
11625
+ elements: [createInlineContainer(type, result.elements)],
11626
+ consumed
11627
+ };
10776
11628
  }
10777
11629
 
10778
- // packages/parser/src/parser/rules/inline/link-triple/interwiki.ts
10779
- var INTERWIKI_PREFIXES = new Set(["wikipedia", "google", "dictionary", "wikidot"]);
10780
- function isInterwikiTarget(target) {
10781
- const colonIdx = target.indexOf(":");
10782
- if (colonIdx <= 0 || target.includes("/")) {
10783
- return false;
11630
+ // packages/parser/src/parser/rules/inline/bold.ts
11631
+ var boldRule = {
11632
+ name: "bold",
11633
+ startTokens: ["BOLD_MARKER"],
11634
+ parse(ctx) {
11635
+ return parseDelimitedContainer(ctx, "BOLD_MARKER", "bold", { discardEmpty: true });
10784
11636
  }
10785
- const prefix = target.slice(0, colonIdx).toLowerCase();
10786
- return INTERWIKI_PREFIXES.has(prefix);
10787
- }
11637
+ };
10788
11638
 
10789
- // packages/parser/src/parser/rules/inline/link-triple/target.ts
10790
- function normalizeTripleLinkTarget(trimmedTarget) {
10791
- if (trimmedTarget.startsWith("*")) {
10792
- return { target: trimmedTarget.slice(1), hasStar: true };
11639
+ // packages/parser/src/parser/rules/inline/italic.ts
11640
+ var italicRule = {
11641
+ name: "italic",
11642
+ startTokens: ["ITALIC_MARKER"],
11643
+ parse(ctx) {
11644
+ return parseDelimitedContainer(ctx, "ITALIC_MARKER", "italics");
10793
11645
  }
10794
- return { target: trimmedTarget, hasStar: false };
11646
+ };
11647
+
11648
+ // packages/parser/src/parser/rules/inline/underline/index.ts
11649
+ var underlineRule = {
11650
+ name: "underline",
11651
+ startTokens: ["UNDERLINE_MARKER"],
11652
+ parse(ctx) {
11653
+ return parseDelimitedContainer(ctx, "UNDERLINE_MARKER", "underline", { discardEmpty: true });
11654
+ }
11655
+ };
11656
+
11657
+ // packages/parser/src/parser/rules/inline/strikethrough/parse.ts
11658
+ function parseStrikethroughContent(ctx) {
11659
+ return parseDelimitedContainer(ctx, "STRIKE_MARKER", "strikethrough");
10795
11660
  }
10796
- function isInvalidTripleLinkTarget(trimmedTarget, foundPipe) {
10797
- return trimmedTarget === "" && foundPipe || /#{2,}/.test(trimmedTarget);
11661
+
11662
+ // packages/parser/src/parser/rules/inline/strikethrough/syntax.ts
11663
+ function hasValidStrikethroughClose(ctx) {
11664
+ const close = findFormattingClose(ctx, ctx.pos + 1, "STRIKE_MARKER");
11665
+ return close !== null && close > ctx.pos + 1 && ctx.tokens[close - 1]?.type !== "WHITESPACE";
10798
11666
  }
10799
- function determineLinkTypeAndLocation(target) {
10800
- if (target.startsWith("#")) {
10801
- return { linkType: "anchor", link: target };
11667
+
11668
+ // packages/parser/src/parser/rules/inline/strikethrough/index.ts
11669
+ var strikethroughRule = {
11670
+ name: "strikethrough",
11671
+ startTokens: ["STRIKE_MARKER"],
11672
+ parse(ctx) {
11673
+ if (!hasValidStrikethroughClose(ctx)) {
11674
+ return {
11675
+ success: true,
11676
+ elements: [{ element: "text", data: "—" }],
11677
+ consumed: 1
11678
+ };
11679
+ }
11680
+ return parseStrikethroughContent(ctx);
10802
11681
  }
10803
- if (target.startsWith("http://") || target.startsWith("https://")) {
10804
- return { linkType: "direct", link: target };
11682
+ };
11683
+
11684
+ // packages/parser/src/parser/rules/inline/superscript.ts
11685
+ var superscriptRule = {
11686
+ name: "superscript",
11687
+ startTokens: ["SUPER_MARKER"],
11688
+ parse(ctx) {
11689
+ return parseDelimitedContainer(ctx, "SUPER_MARKER", "superscript", {
11690
+ discardEmpty: true
11691
+ });
10805
11692
  }
10806
- if (isInterwikiTarget(target)) {
10807
- return { linkType: "interwiki", link: target };
11693
+ };
11694
+
11695
+ // packages/parser/src/parser/rules/inline/subscript.ts
11696
+ var subscriptRule = {
11697
+ name: "subscript",
11698
+ startTokens: ["SUB_MARKER"],
11699
+ parse(ctx) {
11700
+ return parseDelimitedContainer(ctx, "SUB_MARKER", "subscript", { discardEmpty: true });
10808
11701
  }
10809
- return { linkType: "page", link: { site: null, page: target } };
10810
- }
11702
+ };
10811
11703
 
10812
- // packages/parser/src/parser/rules/inline/link-triple/index.ts
10813
- var linkTripleRule = {
10814
- name: "linkTriple",
10815
- startTokens: ["LINK_OPEN"],
11704
+ // packages/parser/src/parser/rules/inline/monospace.ts
11705
+ var monospaceRule = {
11706
+ name: "monospace",
11707
+ startTokens: ["MONO_MARKER"],
10816
11708
  parse(ctx) {
10817
- const startToken = currentToken(ctx);
10818
- if (!hasClosingLinkMarker(ctx, ctx.pos + 1)) {
10819
- return literalOpenLink(startToken.value);
10820
- }
10821
- const parts = collectTripleLinkParts(ctx, ctx.pos + 1);
10822
- const trimmedTarget = parts.target.trim();
10823
- if (isInvalidTripleLinkTarget(trimmedTarget, parts.foundPipe)) {
10824
- return literalOpenLink(startToken.value);
10825
- }
10826
- const normalized = normalizeTripleLinkTarget(trimmedTarget);
10827
- const { linkType, link } = determineLinkTypeAndLocation(normalized.target);
10828
- const label = buildTripleLinkLabel({
10829
- foundPipe: parts.foundPipe,
10830
- labelText: parts.labelText,
10831
- finalTarget: normalized.target,
10832
- originalTarget: trimmedTarget
10833
- });
10834
- return {
10835
- success: true,
10836
- elements: [
10837
- {
10838
- element: "link",
10839
- data: {
10840
- type: linkType,
10841
- link,
10842
- extra: null,
10843
- label,
10844
- target: normalized.hasStar && linkType === "direct" ? "new-tab" : null
10845
- }
10846
- }
10847
- ],
10848
- consumed: parts.consumed
10849
- };
11709
+ return parseDelimitedContainer(ctx, "MONO_CLOSE", "monospace");
10850
11710
  }
10851
11711
  };
10852
11712
 
@@ -10857,7 +11717,7 @@ function normalizeAnchor(anchor) {
10857
11717
 
10858
11718
  // packages/parser/src/parser/rules/inline/link-bracket/direct-url.ts
10859
11719
  function isDirectBracketUrl(url) {
10860
- return url !== "" && (url.startsWith("/") || url.startsWith("http://") || url.startsWith("https://"));
11720
+ return url !== "" && (url.startsWith("/") || startsWithUrlScheme(url, 0));
10861
11721
  }
10862
11722
 
10863
11723
  // packages/parser/src/parser/rules/inline/link-bracket/parts.ts
@@ -10866,35 +11726,24 @@ function collectBracketLinkParts(ctx, startPos) {
10866
11726
  return null;
10867
11727
  }
10868
11728
  let pos = startPos;
10869
- let consumed = 0;
10870
- let first = "";
10871
- while (pos < ctx.tokens.length) {
10872
- const token5 = ctx.tokens[pos];
10873
- if (!token5 || token5.type === "WHITESPACE" || token5.type === "BRACKET_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF") {
10874
- break;
10875
- }
10876
- first += token5.value;
10877
- pos++;
10878
- consumed++;
10879
- }
10880
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
10881
- pos++;
10882
- consumed++;
10883
- }
10884
- let label = "";
10885
- while (pos < ctx.tokens.length) {
11729
+ const end = Math.min(ctx.scope.inlineEnd ?? ctx.tokens.length, ctx.tokens.length);
11730
+ const values = [];
11731
+ while (pos < end) {
10886
11732
  const token5 = ctx.tokens[pos];
10887
- if (!token5 || token5.type === "BRACKET_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF") {
11733
+ if (!token5 || token5.type === "BRACKET_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF")
10888
11734
  break;
10889
- }
10890
- label += token5.value;
11735
+ values.push(token5.value);
10891
11736
  pos++;
10892
- consumed++;
10893
11737
  }
10894
- if (ctx.tokens[pos]?.type !== "BRACKET_CLOSE") {
11738
+ if (pos >= end || ctx.tokens[pos]?.type !== "BRACKET_CLOSE")
10895
11739
  return null;
10896
- }
10897
- return { first, label, consumed: consumed + 1 };
11740
+ const content = values.join("");
11741
+ const separator = content.search(/[ \t]/);
11742
+ return {
11743
+ first: separator === -1 ? content : content.slice(0, separator),
11744
+ label: separator === -1 ? "" : content.slice(separator).trimStart(),
11745
+ consumed: pos - startPos + 1
11746
+ };
10898
11747
  }
10899
11748
 
10900
11749
  // packages/parser/src/parser/rules/inline/link-bracket/prefix.ts
@@ -10905,6 +11754,14 @@ function parseBracketLinkPrefix(ctx, startPos) {
10905
11754
  return { target: null, bodyStart: startPos, consumed: 0 };
10906
11755
  }
10907
11756
 
11757
+ // packages/parser/src/parser/rules/inline/link-bracket/special-target.ts
11758
+ function isBracketEmail(target) {
11759
+ return /^[_a-z0-9-]+(?:\.[_a-z0-9-]+)*@[a-z0-9-]+(?:\.[a-z0-9-]+)+$/i.test(target);
11760
+ }
11761
+ function wikipediaPage(target) {
11762
+ return /^wikipedia:((?!:)[A-Za-z0-9_/=&~#.:;+-]+)$/.exec(target)?.[1] ?? null;
11763
+ }
11764
+
10908
11765
  // packages/parser/src/parser/rules/inline/link-bracket/parsed.ts
10909
11766
  function parseSingleBracketLink(ctx) {
10910
11767
  const prefix = parseBracketLinkPrefix(ctx, ctx.pos + 1);
@@ -10913,17 +11770,20 @@ function parseSingleBracketLink(ctx) {
10913
11770
  return null;
10914
11771
  }
10915
11772
  const link = parts.first.trim();
10916
- if (!isDirectBracketUrl(link)) {
11773
+ const email = isBracketEmail(link);
11774
+ const wikiPage = wikipediaPage(link);
11775
+ if (!email && wikiPage === null && !isDirectBracketUrl(link)) {
10917
11776
  return null;
10918
11777
  }
10919
- const labelText = parts.label.trim();
11778
+ const labelText = parts.label.trim() || wikiPage;
10920
11779
  if (!labelText) {
10921
11780
  return null;
10922
11781
  }
10923
11782
  return {
10924
- link,
11783
+ link: email ? `mailto:${link}` : link,
11784
+ interwiki: wikiPage !== null,
10925
11785
  labelText,
10926
- target: prefix.target,
11786
+ target: wikiPage !== null ? "new-tab" : prefix.target,
10927
11787
  consumed: 1 + prefix.consumed + parts.consumed
10928
11788
  };
10929
11789
  }
@@ -10976,7 +11836,7 @@ var linkSingleRule = {
10976
11836
  {
10977
11837
  element: "link",
10978
11838
  data: {
10979
- type: "direct",
11839
+ type: parsed.interwiki ? "interwiki" : "direct",
10980
11840
  link: parsed.link,
10981
11841
  extra: null,
10982
11842
  label: linkLabel,
@@ -11045,6 +11905,134 @@ var linkStarRule = {
11045
11905
  }
11046
11906
  };
11047
11907
 
11908
+ // packages/parser/src/parser/rules/inline/autolink.ts
11909
+ var URL_BOUNDARY_TOKENS = new Set([
11910
+ "WHITESPACE",
11911
+ "NEWLINE",
11912
+ "EOF",
11913
+ "BLOCK_OPEN",
11914
+ "BLOCK_END_OPEN",
11915
+ "BLOCK_CLOSE",
11916
+ "LINK_OPEN",
11917
+ "LINK_CLOSE",
11918
+ "COMMENT_OPEN",
11919
+ "BACKSLASH_BREAK"
11920
+ ]);
11921
+ var autolinkRule = {
11922
+ name: "autolink",
11923
+ startTokens: ["IDENTIFIER", "STAR", "LIST_BULLET"],
11924
+ parse(ctx) {
11925
+ let pos = ctx.pos;
11926
+ let target = null;
11927
+ const first = ctx.tokens[pos];
11928
+ if (!first) {
11929
+ return { success: false };
11930
+ }
11931
+ if (first.type === "STAR" || first.type === "LIST_BULLET") {
11932
+ if (first.value !== "*") {
11933
+ return { success: false };
11934
+ }
11935
+ target = "new-tab";
11936
+ pos++;
11937
+ }
11938
+ const scheme = ctx.tokens[pos];
11939
+ if (scheme?.type !== "IDENTIFIER" || !URL_SCHEME_NAMES.has(scheme.value)) {
11940
+ return { success: false };
11941
+ }
11942
+ if (ctx.tokens[pos + 1]?.type !== "COLON") {
11943
+ return { success: false };
11944
+ }
11945
+ const prev = ctx.tokens[ctx.pos - 1];
11946
+ if (prev && !first.lineStart) {
11947
+ const lastChar = prev.value[prev.value.length - 1] ?? "";
11948
+ if (/[A-Za-z]/.test(lastChar)) {
11949
+ return { success: false };
11950
+ }
11951
+ }
11952
+ const values = [];
11953
+ let end = pos;
11954
+ const inlineEnd = ctx.scope.inlineEnd ?? ctx.tokens.length;
11955
+ while (end < inlineEnd) {
11956
+ const token5 = ctx.tokens[end];
11957
+ if (!token5 || URL_BOUNDARY_TOKENS.has(token5.type)) {
11958
+ break;
11959
+ }
11960
+ if (token5.type === "RAW_OPEN" || token5.type === "RAW_BLOCK_OPEN") {
11961
+ if (rawRegionEnd(ctx.tokens, end, inlineEnd) > end)
11962
+ break;
11963
+ }
11964
+ values.push(token5.value);
11965
+ end++;
11966
+ if (/[ \t\n\\"']/.test(token5.value))
11967
+ break;
11968
+ }
11969
+ const candidate = values.join("");
11970
+ const match = URL_PATTERN.exec(candidate);
11971
+ if (!match) {
11972
+ return { success: false };
11973
+ }
11974
+ const url = match[0];
11975
+ let length = 0;
11976
+ let count = 0;
11977
+ while (length < url.length) {
11978
+ length += values[count]?.length ?? 0;
11979
+ count++;
11980
+ }
11981
+ const rest = candidate.slice(url.length, length);
11982
+ const elements = [
11983
+ {
11984
+ element: "link",
11985
+ data: {
11986
+ type: "direct",
11987
+ link: url,
11988
+ extra: null,
11989
+ label: { text: url },
11990
+ target
11991
+ }
11992
+ }
11993
+ ];
11994
+ if (rest !== "") {
11995
+ elements.push({ element: "text", data: rest });
11996
+ }
11997
+ return {
11998
+ success: true,
11999
+ elements,
12000
+ consumed: pos - ctx.pos + count
12001
+ };
12002
+ }
12003
+ };
12004
+
12005
+ // packages/parser/src/parser/rules/inline/email/index.ts
12006
+ var emailRule = {
12007
+ name: "email",
12008
+ startTokens: EMAIL_START_TOKENS,
12009
+ parse(ctx) {
12010
+ const group = getEmailGroup(ctx.tokens, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
12011
+ if (!group)
12012
+ return { success: false };
12013
+ const elements = [];
12014
+ let copied = group.start;
12015
+ for (const candidate of group.candidates) {
12016
+ if (copied < candidate.start)
12017
+ elements.push({ element: "text", data: group.source.slice(copied, candidate.start) });
12018
+ elements.push(ctx.scope.suppressEmailLinks ? { element: "text", data: candidate.address } : {
12019
+ element: "link",
12020
+ data: {
12021
+ type: "direct",
12022
+ link: `mailto:${candidate.address}`,
12023
+ label: { text: candidate.address },
12024
+ target: null,
12025
+ extra: null
12026
+ }
12027
+ });
12028
+ copied = candidate.end;
12029
+ }
12030
+ if (copied < group.end)
12031
+ elements.push({ element: "text", data: group.source.slice(copied, group.end) });
12032
+ return { success: true, elements, consumed: group.endToken - ctx.pos };
12033
+ }
12034
+ };
12035
+
11048
12036
  // packages/parser/src/parser/rules/inline/color/syntax.ts
11049
12037
  function parseColorContent(ctx) {
11050
12038
  const close = findFormattingClose(ctx, ctx.pos + 1, "COLOR_MARKER");
@@ -11131,7 +12119,7 @@ var newlineLineBreakRule = {
11131
12119
  }
11132
12120
  return {
11133
12121
  success: true,
11134
- elements: [{ element: "line-break" }],
12122
+ elements: [createAutomaticLineBreak(currentTok)],
11135
12123
  consumed: 1
11136
12124
  };
11137
12125
  }
@@ -11154,6 +12142,9 @@ function isValidBlockStartAfterNewline(ctx, tokenPos) {
11154
12142
  if (!isBlockStartToken(token5?.type) || !token5?.lineStart) {
11155
12143
  return false;
11156
12144
  }
12145
+ if (token5.type === "LIST_BULLET" || token5.type === "LIST_NUMBER") {
12146
+ return ctx.tokens[tokenPos + 1]?.type === "WHITESPACE";
12147
+ }
11157
12148
  if (token5.type !== "HEADING_MARKER") {
11158
12149
  return true;
11159
12150
  }
@@ -11411,6 +12402,9 @@ var htmlInlineRule = {
11411
12402
  }
11412
12403
  };
11413
12404
 
12405
+ // packages/parser/src/parser/rules/inline/raw/angle.ts
12406
+ var import_entities = require("entities");
12407
+
11414
12408
  // packages/parser/src/parser/rules/inline/raw/result.ts
11415
12409
  function rawElement(value, consumed) {
11416
12410
  return {
@@ -11458,7 +12452,7 @@ function parseAngleRaw(ctx) {
11458
12452
  if (ctx.tokens[pos]?.type === "RAW_BLOCK_CLOSE") {
11459
12453
  consumed++;
11460
12454
  }
11461
- return rawElement(value, consumed);
12455
+ return rawElement(import_entities.decodeHTML(value), consumed);
11462
12456
  }
11463
12457
 
11464
12458
  // packages/parser/src/parser/rules/inline/raw/double-at.ts
@@ -11681,7 +12675,7 @@ function consumeSpanNewline(ctx, pos, paragraphStrip, afterBlankLine, children,
11681
12675
  return { consumed: consumed2, afterBlankLine: paragraphStrip };
11682
12676
  }
11683
12677
  const targetChildren = paragraphStrip && afterBlankLine ? escapedChildren : children;
11684
- targetChildren.push({ element: "line-break" });
12678
+ targetChildren.push(createAutomaticLineBreak(ctx.tokens[pos]));
11685
12679
  let consumed = 1;
11686
12680
  let nextPos = pos + 1;
11687
12681
  while (ctx.tokens[nextPos]?.type === "WHITESPACE" && ctx.tokens[nextPos]?.lineStart) {
@@ -11769,7 +12763,9 @@ function parseOneSpanChild(ctx, pos, targetChildren) {
11769
12763
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
11770
12764
  const result = rule.parse(inlineCtx);
11771
12765
  if (result.success) {
11772
- targetChildren.push(...result.elements);
12766
+ stripAutomaticLineBreak(targetChildren, result.stripLeadingLineBreak);
12767
+ for (const element of result.elements)
12768
+ targetChildren.push(element);
11773
12769
  return { consumed: result.consumed };
11774
12770
  }
11775
12771
  }
@@ -11837,6 +12833,48 @@ var closeSpanRule = {
11837
12833
  }
11838
12834
  };
11839
12835
 
12836
+ // packages/parser/src/parser/rules/inline/button/index.ts
12837
+ var buttonRule = {
12838
+ name: "button",
12839
+ startTokens: ["BLOCK_OPEN"],
12840
+ parse(ctx) {
12841
+ const result = parseButtonSyntax(ctx, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
12842
+ return result ? {
12843
+ success: true,
12844
+ consumed: result.end - ctx.pos,
12845
+ elements: [{ element: "button", data: result.data }]
12846
+ } : { success: false };
12847
+ }
12848
+ };
12849
+
12850
+ // packages/parser/src/parser/rules/inline/social/index.ts
12851
+ var socialRule = {
12852
+ name: "social",
12853
+ startTokens: ["BLOCK_OPEN"],
12854
+ parse(ctx) {
12855
+ const result = parseSocialSyntax(ctx, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
12856
+ return result ? {
12857
+ success: true,
12858
+ consumed: result.end - ctx.pos,
12859
+ elements: [{ element: "social", data: result.data }]
12860
+ } : { success: false };
12861
+ }
12862
+ };
12863
+
12864
+ // packages/parser/src/parser/rules/inline/date/index.ts
12865
+ var dateRule = {
12866
+ name: "date",
12867
+ startTokens: ["BLOCK_OPEN"],
12868
+ parse(ctx) {
12869
+ const parsed = parseDateSyntax(ctx, ctx.pos, ctx.scope.inlineEnd ?? ctx.tokens.length);
12870
+ return parsed ? {
12871
+ success: true,
12872
+ consumed: parsed.end - ctx.pos,
12873
+ elements: [{ element: "date", data: parsed.data }]
12874
+ } : { success: false };
12875
+ }
12876
+ };
12877
+
11840
12878
  // packages/parser/src/parser/rules/inline/size/content.ts
11841
12879
  function parseSizeContent(ctx, startPos) {
11842
12880
  const children = [];
@@ -11858,7 +12896,8 @@ function parseSizeContent(ctx, startPos) {
11858
12896
  const inlineCtx = { ...ctx, pos };
11859
12897
  const inlineResult = parseInlineUntil(inlineCtx, "BLOCK_END_OPEN");
11860
12898
  if (inlineResult.elements.length > 0) {
11861
- children.push(...inlineResult.elements);
12899
+ for (const element of inlineResult.elements)
12900
+ children.push(element);
11862
12901
  pos += inlineResult.consumed;
11863
12902
  consumed += inlineResult.consumed;
11864
12903
  } else {
@@ -11887,6 +12926,16 @@ function tryConsumeSizeClose(ctx, pos) {
11887
12926
 
11888
12927
  // packages/parser/src/parser/rules/inline/size/value.ts
11889
12928
  var VALID_SIZE_UNITS = ["px", "em", "rem", "ex", "%", "cm", "mm", "in", "pc"];
12929
+ var VALID_SIZE_KEYWORDS = [
12930
+ "smaller",
12931
+ "larger",
12932
+ "xx-small",
12933
+ "x-small",
12934
+ "small",
12935
+ "large",
12936
+ "x-large",
12937
+ "xx-large"
12938
+ ];
11890
12939
  function parseSizeValue(ctx, startPos) {
11891
12940
  let pos = startPos;
11892
12941
  let consumed = 0;
@@ -11911,6 +12960,8 @@ function parseSizeValue(ctx, startPos) {
11911
12960
  return isValidSizeValue(size) ? { size, consumed } : null;
11912
12961
  }
11913
12962
  function isValidSizeValue(size) {
12963
+ if (VALID_SIZE_KEYWORDS.includes(size))
12964
+ return true;
11914
12965
  const unitPattern = VALID_SIZE_UNITS.join("|");
11915
12966
  return new RegExp(`^(\\d+(?:\\.\\d+)?)(${unitPattern})$`, "i").test(size);
11916
12967
  }
@@ -11982,19 +13033,6 @@ var sizeRule = {
11982
13033
  }
11983
13034
  };
11984
13035
 
11985
- // packages/parser/src/parser/rules/inline/footnote/child.ts
11986
- function parseFootnoteChild(ctx, pos) {
11987
- const token5 = ctx.tokens[pos];
11988
- if (!token5) {
11989
- return { elements: [], consumed: 0 };
11990
- }
11991
- const inlineResult = parseInlineUntil({ ...ctx, pos }, "BLOCK_END_OPEN");
11992
- if (inlineResult.elements.length > 0) {
11993
- return { elements: inlineResult.elements, consumed: inlineResult.consumed };
11994
- }
11995
- return { elements: [{ element: "text", data: token5.value }], consumed: 1 };
11996
- }
11997
-
11998
13036
  // packages/parser/src/parser/rules/inline/footnote/close.ts
11999
13037
  function tryConsumeFootnoteClose(ctx, pos) {
12000
13038
  if (ctx.tokens[pos]?.type !== "BLOCK_END_OPEN") {
@@ -12016,96 +13054,73 @@ function tryConsumeFootnoteClose(ctx, pos) {
12016
13054
  return { consumed };
12017
13055
  }
12018
13056
 
12019
- // packages/parser/src/parser/rules/inline/footnote/newline.ts
12020
- function consumeFootnoteNewline(ctx, pos) {
12021
- let nextPos = pos + 1;
12022
- let consumed = 1;
12023
- while (ctx.tokens[nextPos]?.type === "WHITESPACE") {
12024
- nextPos++;
12025
- consumed++;
12026
- }
12027
- if (ctx.tokens[nextPos]?.type !== "NEWLINE") {
12028
- return { consumed, paragraphBreak: false };
13057
+ // packages/parser/src/parser/rules/inline/math-inline/syntax.ts
13058
+ function parseInlineMathSource(ctx, startPos) {
13059
+ let pos = startPos;
13060
+ let consumed = 0;
13061
+ if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
13062
+ return null;
12029
13063
  }
12030
- while (ctx.tokens[nextPos]?.type === "NEWLINE") {
12031
- nextPos++;
13064
+ pos++;
13065
+ consumed++;
13066
+ while (ctx.tokens[pos]?.type === "WHITESPACE") {
13067
+ pos++;
12032
13068
  consumed++;
12033
13069
  }
12034
- return { consumed, paragraphBreak: true };
12035
- }
12036
-
12037
- // packages/parser/src/parser/rules/inline/footnote/content.ts
12038
- function parseFootnoteContent(ctx, startPos) {
12039
- const paragraphs = [[]];
12040
- let currentParagraph = 0;
12041
- let pos = startPos;
12042
- let consumed = 0;
13070
+ let latexSource = "";
12043
13071
  while (pos < ctx.tokens.length) {
12044
13072
  const token5 = ctx.tokens[pos];
12045
- if (!token5 || token5.type === "EOF") {
12046
- break;
12047
- }
12048
- const closeResult = tryConsumeFootnoteClose(ctx, pos);
12049
- if (closeResult) {
12050
- return {
12051
- paragraphs,
12052
- consumed: consumed + closeResult.consumed,
12053
- foundClose: true
12054
- };
13073
+ if (!token5 || token5.type === "NEWLINE") {
13074
+ return null;
12055
13075
  }
12056
- if (token5.type === "NEWLINE") {
12057
- const newlineResult = consumeFootnoteNewline(ctx, pos);
12058
- pos += newlineResult.consumed;
12059
- consumed += newlineResult.consumed;
12060
- if (newlineResult.paragraphBreak) {
12061
- currentParagraph++;
12062
- paragraphs[currentParagraph] = [];
12063
- } else {
12064
- paragraphs[currentParagraph].push({ element: "line-break" });
12065
- }
12066
- continue;
13076
+ if (token5.type === "TEXT" && token5.value === "$" && ctx.tokens[pos + 1]?.type === "BLOCK_CLOSE") {
13077
+ break;
12067
13078
  }
12068
- const child = parseFootnoteChild(ctx, pos);
12069
- paragraphs[currentParagraph].push(...child.elements);
12070
- pos += child.consumed;
12071
- consumed += child.consumed;
13079
+ latexSource += token5.value;
13080
+ pos++;
13081
+ consumed++;
13082
+ }
13083
+ if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
13084
+ return null;
13085
+ }
13086
+ pos++;
13087
+ consumed++;
13088
+ if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
13089
+ return null;
12072
13090
  }
12073
- return { paragraphs, consumed, foundClose: false };
13091
+ return {
13092
+ latexSource: latexSource.trim(),
13093
+ consumed: consumed + 1
13094
+ };
12074
13095
  }
12075
13096
 
12076
- // packages/parser/src/parser/rules/inline/footnote/elements.ts
12077
- function buildFootnoteChildren(paragraphs) {
12078
- const children = [];
12079
- for (let i = 0;i < paragraphs.length; i++) {
12080
- const para = trimLineBreaks(paragraphs[i] ?? []);
12081
- if (para.length === 0) {
12082
- continue;
13097
+ // packages/parser/src/parser/rules/inline/math-inline/index.ts
13098
+ var mathInlineRule = {
13099
+ name: "math-inline",
13100
+ startTokens: ["BLOCK_OPEN"],
13101
+ parse(ctx) {
13102
+ const openToken = currentToken(ctx);
13103
+ if (openToken.type !== "BLOCK_OPEN") {
13104
+ return { success: false };
12083
13105
  }
12084
- if (i === 0) {
12085
- children.push(...para);
12086
- } else {
12087
- children.push({
12088
- element: "container",
12089
- data: {
12090
- type: "paragraph",
12091
- attributes: {},
12092
- elements: para
12093
- }
12094
- });
13106
+ const parsed = parseInlineMathSource(ctx, ctx.pos + 1);
13107
+ if (!parsed) {
13108
+ return { success: false };
12095
13109
  }
13110
+ return {
13111
+ success: true,
13112
+ elements: [
13113
+ {
13114
+ element: "math-inline",
13115
+ data: {
13116
+ "latex-source": parsed.latexSource
13117
+ }
13118
+ }
13119
+ ],
13120
+ consumed: 1 + parsed.consumed
13121
+ };
12096
13122
  }
12097
- return children;
12098
- }
12099
- function trimLineBreaks(elements) {
12100
- const result = [...elements];
12101
- while (result.length > 0 && result[0]?.element === "line-break") {
12102
- result.shift();
12103
- }
12104
- while (result.length > 0 && result[result.length - 1]?.element === "line-break") {
12105
- result.pop();
12106
- }
12107
- return result;
12108
- }
13123
+ };
12109
13124
 
12110
13125
  // packages/parser/src/parser/rules/inline/footnote/open.ts
12111
13126
  function parseFootnoteOpen(ctx) {
@@ -12132,6 +13147,91 @@ function parseFootnoteOpen(ctx) {
12132
13147
  return { bodyStart: pos, consumed };
12133
13148
  }
12134
13149
 
13150
+ // packages/parser/src/parser/rules/inline/footnote/boundary.ts
13151
+ var opaqueRules2 = [
13152
+ codeBlockRule,
13153
+ htmlBlockRule,
13154
+ mathBlockRule,
13155
+ mathInlineRule,
13156
+ linkTripleRule,
13157
+ linkSingleRule,
13158
+ linkStarRule,
13159
+ linkAnchorRule
13160
+ ];
13161
+ function findFootnoteEnd(ctx, start) {
13162
+ const probe = createOpaqueProbe(ctx);
13163
+ let depth = 0;
13164
+ for (let pos = start;pos < ctx.tokens.length; pos++) {
13165
+ if (ctx.tokens[pos]?.type === "EOF")
13166
+ return pos;
13167
+ const protectedEnd = protectedInlineRegionEnd(ctx.tokens, pos, ctx.tokens.length);
13168
+ if (protectedEnd > pos) {
13169
+ pos = protectedEnd - 1;
13170
+ continue;
13171
+ }
13172
+ probe.pos = pos;
13173
+ const close = tryConsumeFootnoteClose(probe, pos);
13174
+ if (close) {
13175
+ if (depth === 0)
13176
+ return pos;
13177
+ depth--;
13178
+ pos += close.consumed - 1;
13179
+ continue;
13180
+ }
13181
+ const open = parseFootnoteOpen(probe);
13182
+ if (open) {
13183
+ depth++;
13184
+ pos += open.consumed - 1;
13185
+ continue;
13186
+ }
13187
+ const opaqueEnd = opaqueRuleEnd(probe, pos, opaqueRules2);
13188
+ if (opaqueEnd > pos)
13189
+ pos = opaqueEnd - 1;
13190
+ }
13191
+ return ctx.tokens.length;
13192
+ }
13193
+
13194
+ // packages/parser/src/parser/rules/inline/footnote/content.ts
13195
+ function parseFootnoteContent(ctx, startPos) {
13196
+ const end = findFootnoteEnd(ctx, startPos);
13197
+ let leadingNewlines = 0;
13198
+ for (let pos = startPos;pos < end; pos++) {
13199
+ const type = ctx.tokens[pos]?.type;
13200
+ if (type === "NEWLINE")
13201
+ leadingNewlines++;
13202
+ else if (type !== "WHITESPACE")
13203
+ break;
13204
+ }
13205
+ const bodyCtx = {
13206
+ ...ctx,
13207
+ tokens: ctx.tokens.slice(startPos, end),
13208
+ pos: 0,
13209
+ scope: {
13210
+ ...ctx.scope,
13211
+ inlineEnd: undefined,
13212
+ tableFormatting: undefined,
13213
+ blockCloseCondition: undefined
13214
+ }
13215
+ };
13216
+ const result = parseBlocksUntil(bodyCtx, () => false);
13217
+ const close = tryConsumeFootnoteClose(ctx, end);
13218
+ return {
13219
+ elements: result.elements,
13220
+ consumed: end - startPos + (close?.consumed ?? 0),
13221
+ foundClose: close !== null,
13222
+ leadingParagraphBreak: leadingNewlines >= 2
13223
+ };
13224
+ }
13225
+
13226
+ // packages/parser/src/parser/rules/inline/footnote/elements.ts
13227
+ function buildFootnoteChildren(elements, leadingParagraphBreak) {
13228
+ const first = elements[0];
13229
+ if (!leadingParagraphBreak && first?.element === "container" && first.data.type === "paragraph") {
13230
+ return [...first.data.elements, ...elements.slice(1)];
13231
+ }
13232
+ return elements;
13233
+ }
13234
+
12135
13235
  // packages/parser/src/parser/rules/inline/footnote/index.ts
12136
13236
  var footnoteRule = {
12137
13237
  name: "footnote",
@@ -12147,7 +13247,7 @@ var footnoteRule = {
12147
13247
  }
12148
13248
  const contentResult = parseFootnoteContent(ctx, openResult.bodyStart);
12149
13249
  const consumed = openResult.consumed + contentResult.consumed;
12150
- const children = buildFootnoteChildren(contentResult.paragraphs);
13250
+ const children = buildFootnoteChildren(contentResult.elements, contentResult.leadingParagraphBreak);
12151
13251
  if (!contentResult.foundClose) {
12152
13252
  ctx.diagnostics.push({
12153
13253
  severity: "warning",
@@ -12507,11 +13607,15 @@ function parseAnchorChild(ctx, pos) {
12507
13607
  if (!token5) {
12508
13608
  return { elements: [], consumed: 0 };
12509
13609
  }
12510
- const inlineCtx = { ...ctx, pos };
13610
+ const inlineCtx = {
13611
+ ...ctx,
13612
+ pos,
13613
+ scope: { ...ctx.scope, suppressEmailLinks: true }
13614
+ };
12511
13615
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
12512
13616
  const result = rule.parse(inlineCtx);
12513
13617
  if (result.success) {
12514
- return { elements: result.elements, consumed: result.consumed };
13618
+ return result;
12515
13619
  }
12516
13620
  }
12517
13621
  return { elements: [{ element: "text", data: token5.value }], consumed: 1 };
@@ -12571,7 +13675,7 @@ function consumeAnchorNewline(ctx, pos, paragraphStrip, children) {
12571
13675
  let consumed = 1;
12572
13676
  let nextPos = pos + 1;
12573
13677
  if (!paragraphStrip) {
12574
- children.push({ element: "line-break" });
13678
+ children.push(createAutomaticLineBreak(ctx.tokens[pos]));
12575
13679
  while (ctx.tokens[nextPos]?.type === "WHITESPACE" && ctx.tokens[nextPos]?.lineStart) {
12576
13680
  nextPos++;
12577
13681
  consumed++;
@@ -12624,7 +13728,9 @@ function parseAnchorContent(ctx, startPos, paragraphStrip) {
12624
13728
  continue;
12625
13729
  }
12626
13730
  const child = parseAnchorChild(ctx, pos);
12627
- children.push(...child.elements);
13731
+ stripAutomaticLineBreak(children, child.stripLeadingLineBreak);
13732
+ for (const element of child.elements)
13733
+ children.push(element);
12628
13734
  pos += child.consumed;
12629
13735
  consumed += child.consumed;
12630
13736
  }
@@ -12698,75 +13804,8 @@ var anchorRule = {
12698
13804
  }
12699
13805
  }
12700
13806
  ],
12701
- consumed
12702
- };
12703
- }
12704
- };
12705
-
12706
- // packages/parser/src/parser/rules/inline/math-inline/syntax.ts
12707
- function parseInlineMathSource(ctx, startPos) {
12708
- let pos = startPos;
12709
- let consumed = 0;
12710
- if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
12711
- return null;
12712
- }
12713
- pos++;
12714
- consumed++;
12715
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
12716
- pos++;
12717
- consumed++;
12718
- }
12719
- let latexSource = "";
12720
- while (pos < ctx.tokens.length) {
12721
- const token5 = ctx.tokens[pos];
12722
- if (!token5 || token5.type === "NEWLINE") {
12723
- return null;
12724
- }
12725
- if (token5.type === "TEXT" && token5.value === "$" && ctx.tokens[pos + 1]?.type === "BLOCK_CLOSE") {
12726
- break;
12727
- }
12728
- latexSource += token5.value;
12729
- pos++;
12730
- consumed++;
12731
- }
12732
- if (ctx.tokens[pos]?.type !== "TEXT" || ctx.tokens[pos]?.value !== "$") {
12733
- return null;
12734
- }
12735
- pos++;
12736
- consumed++;
12737
- if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
12738
- return null;
12739
- }
12740
- return {
12741
- latexSource: latexSource.trim(),
12742
- consumed: consumed + 1
12743
- };
12744
- }
12745
-
12746
- // packages/parser/src/parser/rules/inline/math-inline/index.ts
12747
- var mathInlineRule = {
12748
- name: "math-inline",
12749
- startTokens: ["BLOCK_OPEN"],
12750
- parse(ctx) {
12751
- const openToken = currentToken(ctx);
12752
- if (openToken.type !== "BLOCK_OPEN") {
12753
- return { success: false };
12754
- }
12755
- const parsed = parseInlineMathSource(ctx, ctx.pos + 1);
12756
- if (!parsed) {
12757
- return { success: false };
12758
- }
12759
- return {
12760
- success: true,
12761
- elements: [
12762
- {
12763
- element: "math-inline",
12764
- data: {
12765
- "latex-source": parsed.latexSource
12766
- }
12767
- }
12768
- ],
12769
- consumed: 1 + parsed.consumed
13807
+ consumed,
13808
+ stripLeadingLineBreak: openResult.paragraphStrip ? precedingSingleNewline(ctx) : undefined
12770
13809
  };
12771
13810
  }
12772
13811
  };
@@ -12877,7 +13916,9 @@ function parseInlineBranch(ctx, startPos) {
12877
13916
  }
12878
13917
  const result = rule.parse(inlineCtx);
12879
13918
  if (result.success) {
12880
- elements.push(...result.elements);
13919
+ stripAutomaticLineBreak(elements, result.stripLeadingLineBreak);
13920
+ for (const element of result.elements)
13921
+ elements.push(element);
12881
13922
  consumed += result.consumed;
12882
13923
  pos += result.consumed;
12883
13924
  matched = true;
@@ -13200,6 +14241,7 @@ var textRule = {
13200
14241
 
13201
14242
  // packages/parser/src/parser/rules/inline/index.ts
13202
14243
  var inlineRules = [
14244
+ emailRule,
13203
14245
  boldRule,
13204
14246
  italicRule,
13205
14247
  underlineRule,
@@ -13211,6 +14253,7 @@ var inlineRules = [
13211
14253
  linkSingleRule,
13212
14254
  linkAnchorRule,
13213
14255
  linkStarRule,
14256
+ autolinkRule,
13214
14257
  colorRule,
13215
14258
  backslashLineBreakRule,
13216
14259
  underscoreLineBreakRule,
@@ -13220,6 +14263,9 @@ var inlineRules = [
13220
14263
  rawRule,
13221
14264
  imageRule,
13222
14265
  embedBlockRule,
14266
+ buttonRule,
14267
+ socialRule,
14268
+ dateRule,
13223
14269
  sizeRule,
13224
14270
  footnoteRule,
13225
14271
  spanRule,
@@ -13475,6 +14521,8 @@ function mergeSpanStripParagraphs(children) {
13475
14521
  i++;
13476
14522
  continue;
13477
14523
  }
14524
+ const firstMeaningful = paraData.elements.find((child) => child.element !== "line-break" && !isWhitespaceText(child));
14525
+ const keepParagraph = firstMeaningful && !isSpanStripMarker(firstMeaningful);
13478
14526
  const mergedChildren = [...paraData.elements];
13479
14527
  i++;
13480
14528
  while (i < expandedChildren.length) {
@@ -13487,7 +14535,8 @@ function mergeSpanStripParagraphs(children) {
13487
14535
  break;
13488
14536
  }
13489
14537
  const hasSpanStrip = hasParagraphStripSpan(nextPara);
13490
- mergedChildren.push(...nextParaData.elements);
14538
+ for (const element of nextParaData.elements)
14539
+ mergedChildren.push(element);
13491
14540
  i++;
13492
14541
  if (!hasSpanStrip) {
13493
14542
  const peekNext = expandedChildren[i];
@@ -13498,7 +14547,7 @@ function mergeSpanStripParagraphs(children) {
13498
14547
  }
13499
14548
  const escapedSpans = extractEscapedSpans(mergedChildren);
13500
14549
  removeLineBreaksAroundSpanStrip(mergedChildren);
13501
- if (escapedSpans.length > 0) {
14550
+ if (escapedSpans.length > 0 || keepParagraph) {
13502
14551
  if (mergedChildren.length > 0) {
13503
14552
  result.push(paragraphElement(mergedChildren));
13504
14553
  }
@@ -13719,50 +14768,6 @@ function cleanInternalFlags(elements) {
13719
14768
  }
13720
14769
  return removeEmptySpansAndAdjacentWhitespace(cleaned ?? elements);
13721
14770
  }
13722
- // packages/parser/src/parser/postprocess/divAdjacentParagraph.ts
13723
- function isParagraphContainer2(el) {
13724
- if (!el || el.element !== "container")
13725
- return false;
13726
- return el.data.type === "paragraph";
13727
- }
13728
- function isDivContainer(el) {
13729
- if (!el || el.element !== "container")
13730
- return false;
13731
- return el.data.type === "div";
13732
- }
13733
- function suppressAtLevel(elements) {
13734
- if (elements.length <= 1)
13735
- return elements;
13736
- const unwrap = Array.from({ length: elements.length }, () => false);
13737
- for (let i = 0;i < elements.length; i++) {
13738
- if (!isParagraphContainer2(elements[i]))
13739
- continue;
13740
- const prevIsDiv = i > 0 && isDivContainer(elements[i - 1]);
13741
- const nextIsDiv = i < elements.length - 1 && isDivContainer(elements[i + 1]);
13742
- if (prevIsDiv || nextIsDiv) {
13743
- unwrap[i] = true;
13744
- }
13745
- }
13746
- const result = [];
13747
- for (let i = 0;i < elements.length; i++) {
13748
- const el = elements[i];
13749
- if (!el)
13750
- continue;
13751
- if (unwrap[i] && el.element === "container") {
13752
- const inner = el.data.elements;
13753
- if (i > 0 && isDivContainer(elements[i - 1])) {
13754
- result.push({ element: "line-break" });
13755
- }
13756
- result.push(...inner);
13757
- } else {
13758
- result.push(el);
13759
- }
13760
- }
13761
- return result;
13762
- }
13763
- function suppressDivAdjacentParagraphs(elements) {
13764
- return suppressAtLevel(elements);
13765
- }
13766
14771
  // packages/parser/src/parser/parse/footnotes.ts
13767
14772
  function containsFootnoteBlock(elements) {
13768
14773
  let found = false;
@@ -14040,7 +15045,9 @@ function substitute(text2) {
14040
15045
  result = result.replace(WHITESPACE_ONLY_LINE, "");
14041
15046
  }
14042
15047
  if (result.indexOf("\\\n") !== -1) {
14043
- result = result.replace(CONCAT_LINES, String.fromCharCode(57344));
15048
+ const sentinels = makeUniqueSentinels(result);
15049
+ const { masked, placeholders } = maskRawRegions(result, sentinels);
15050
+ result = restorePlaceholders(masked.replace(CONCAT_LINES, String.fromCharCode(57344)), placeholders, sentinels);
14044
15051
  }
14045
15052
  if (result.indexOf("\t") !== -1) {
14046
15053
  result = result.replace(TABS, " ");
@@ -14107,14 +15114,26 @@ function replaceDelimitedTypography(text2, opener, closer, leftQuote, rightQuote
14107
15114
  let searchFrom = 0;
14108
15115
  let result = "";
14109
15116
  let lastCopied = 0;
15117
+ let closeIndex = -1;
15118
+ let newlineIndex = -1;
14110
15119
  while (searchFrom < text2.length) {
14111
15120
  const openIndex = text2.indexOf(opener, searchFrom);
14112
15121
  if (openIndex === -1)
14113
15122
  break;
14114
15123
  const contentStart = openIndex + opener.length;
14115
- const closeIndex = text2.indexOf(closer, contentStart);
15124
+ if (closeIndex < contentStart)
15125
+ closeIndex = text2.indexOf(closer, contentStart);
14116
15126
  if (closeIndex === -1)
14117
15127
  break;
15128
+ if (newlineIndex < contentStart) {
15129
+ const nextNewline = text2.indexOf(`
15130
+ `, contentStart);
15131
+ newlineIndex = nextNewline === -1 ? text2.length : nextNewline;
15132
+ }
15133
+ if (newlineIndex < closeIndex) {
15134
+ searchFrom = newlineIndex + 1;
15135
+ continue;
15136
+ }
14118
15137
  result += text2.slice(lastCopied, openIndex);
14119
15138
  result += leftQuote;
14120
15139
  result += text2.slice(contentStart, closeIndex);
@@ -14125,7 +15144,12 @@ function replaceDelimitedTypography(text2, opener, closer, leftQuote, rightQuote
14125
15144
  return lastCopied === 0 ? text2 : result + text2.slice(lastCopied);
14126
15145
  }
14127
15146
  function substitute2(text2) {
14128
- let result = text2;
15147
+ if (!text2.includes("`") && !text2.includes(",,") && !text2.includes("...") && !text2.includes(". . .")) {
15148
+ return text2;
15149
+ }
15150
+ const sentinels = makeUniqueSentinels(text2);
15151
+ const { masked, placeholders } = maskRawRegions(text2, sentinels);
15152
+ let result = masked;
14129
15153
  if (result.includes("``") && result.includes("''")) {
14130
15154
  result = replaceDelimitedTypography(result, "``", "''", LEFT_DOUBLE_QUOTE, RIGHT_DOUBLE_QUOTE);
14131
15155
  }
@@ -14141,7 +15165,7 @@ function substitute2(text2) {
14141
15165
  if (result.includes(". . .")) {
14142
15166
  result = replaceExactEllipsisPattern(result, ". . .");
14143
15167
  }
14144
- return result;
15168
+ return restorePlaceholders(result, placeholders, sentinels);
14145
15169
  }
14146
15170
 
14147
15171
  // packages/parser/src/parser/preprocess/index.ts
@@ -14388,7 +15412,7 @@ async function resolveModulesWithAsyncParse(ast, dataProvider, options) {
14388
15412
  registry.register(ast);
14389
15413
  const parse2 = async (source) => registry.register(await options.parse(source));
14390
15414
  const [listPagesData, listUsersData, tagCloudData] = await Promise.all([
14391
- buildListPagesDataMap(dataProvider, options.requirements.listPages ?? [], options.urlPath),
15415
+ buildListPagesDataMap(dataProvider, options.requirements.listPages ?? [], options.urlPath, options.paginationState),
14392
15416
  buildListUsersDataMap(dataProvider, options.requirements.listUsers ?? []),
14393
15417
  buildTagCloudDataMap(dataProvider, options.requirements.tagCloud ?? [])
14394
15418
  ]);
@@ -14476,9 +15500,9 @@ async function resolveListPagesAsync(module2, data, template, parse2) {
14476
15500
  continue;
14477
15501
  const variableContext = {
14478
15502
  page,
14479
- index: i + 1,
15503
+ index: (data.pagination?.offset ?? 0) + i + 1,
14480
15504
  total: data.totalCount,
14481
- limit: module2.limit,
15505
+ limit: data.pagination ? data.pagination.limit : module2.limit,
14482
15506
  site: data.site
14483
15507
  };
14484
15508
  const parsed = await parse2(template(variableContext));
@@ -14498,6 +15522,7 @@ async function resolveListPagesAsync(module2, data, template, parse2) {
14498
15522
  if (module2["append-line"] && !module2.separate) {
14499
15523
  result.push(...(await parse2(module2["append-line"])).elements);
14500
15524
  }
15525
+ result.push(...createListPagesPager(data));
14501
15526
  return module2.wrapper ? [
14502
15527
  {
14503
15528
  element: "container",
@@ -14598,6 +15623,9 @@ async function processWikitext(source, options) {
14598
15623
  const dataProvider = createModuleDataProvider(options, callbackContext);
14599
15624
  const parseFragment = parseSource2;
14600
15625
  let ast = initial.ast;
15626
+ const paginationState = { nextUnprefixed: 1 };
15627
+ const requestedPath = options.page.urlPath;
15628
+ const urlPath = !requestedPath || /^\/(?:[?#]|$)/.test(requestedPath) ? `/${options.page.fullName}${requestedPath?.slice(1) ?? ""}` : requestedPath;
14601
15629
  for (let pass = 0;pass < DEFAULT_MODULE_MAX_PASSES; pass++) {
14602
15630
  const extraction = extractDataRequirements(ast);
14603
15631
  if (pass > 0 && !hasResolvableRequirements(extraction.requirements, options.dataProvider)) {
@@ -14608,7 +15636,8 @@ async function processWikitext(source, options) {
14608
15636
  compiledListPagesTemplates: extraction.compiledListPagesTemplates,
14609
15637
  compiledListUsersTemplates: extraction.compiledListUsersTemplates,
14610
15638
  requirements: extraction.requirements,
14611
- urlPath: options.page.urlPath,
15639
+ urlPath,
15640
+ paginationState,
14612
15641
  pageTags: options.page.tags
14613
15642
  });
14614
15643
  ast = resolved.ast;