@wdprlib/parser 5.1.6 → 5.3.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 (61) hide show
  1. package/dist/index.cjs +764 -452
  2. package/dist/index.d.cts +24 -4
  3. package/dist/index.d.ts +24 -4
  4. package/dist/index.js +762 -452
  5. package/package.json +2 -2
  6. package/src/build-info.generated.ts +11 -0
  7. package/src/index.ts +2 -0
  8. package/src/lexer/lexer.ts +6 -4
  9. package/src/lexer/punctuation.ts +2 -1
  10. package/src/lexer/spacing-actions.ts +15 -0
  11. package/src/lexer/state.ts +30 -4
  12. package/src/lexer/token-factory.ts +15 -2
  13. package/src/lexer/tokens.ts +4 -3
  14. package/src/parser/rules/block/blockquote/build.ts +81 -37
  15. package/src/parser/rules/block/blockquote/index.ts +5 -6
  16. package/src/parser/rules/block/blockquote/line.ts +11 -10
  17. package/src/parser/rules/block/blockquote/lines.ts +102 -4
  18. package/src/parser/rules/block/code/content.ts +0 -3
  19. package/src/parser/rules/block/code/index.ts +14 -10
  20. package/src/parser/rules/block/definition-list/index.ts +4 -10
  21. package/src/parser/rules/block/definition-list/item-value.ts +2 -11
  22. package/src/parser/rules/block/definition-list/items.ts +1 -2
  23. package/src/parser/rules/block/embed-block/index.ts +30 -30
  24. package/src/parser/rules/block/heading/index.ts +7 -0
  25. package/src/parser/rules/block/module/listpages/resolution/items.ts +2 -2
  26. package/src/parser/rules/block/module/listpages/resolution/pager.ts +43 -0
  27. package/src/parser/rules/block/module/listpages/resolution/wrapper.ts +6 -1
  28. package/src/parser/rules/block/module/listpages/resolve.ts +1 -1
  29. package/src/parser/rules/block/module/listpages/types/external-data.ts +13 -0
  30. package/src/parser/rules/block/module/listpages/url-resolution/params.ts +5 -4
  31. package/src/parser/rules/block/module/resolution/data-maps.ts +75 -3
  32. package/src/parser/rules/block/module/resolution/resolve-async.ts +12 -3
  33. package/src/parser/rules/block/paragraph/index.ts +41 -10
  34. package/src/parser/rules/block/parsing/content.ts +18 -1
  35. package/src/parser/rules/block/table/pipe/cell.ts +36 -1
  36. package/src/parser/rules/block/table/pipe/row.ts +21 -1
  37. package/src/parser/rules/block/toc/element.ts +2 -2
  38. package/src/parser/rules/block/toc/index.ts +2 -2
  39. package/src/parser/rules/block/toc/open.ts +5 -18
  40. package/src/parser/rules/contracts/scope.ts +4 -0
  41. package/src/parser/rules/inline/bold.ts +5 -5
  42. package/src/parser/rules/inline/color/syntax.ts +5 -8
  43. package/src/parser/rules/inline/formatting/close.ts +39 -0
  44. package/src/parser/rules/inline/formatting/container.ts +7 -4
  45. package/src/parser/rules/inline/index.ts +2 -0
  46. package/src/parser/rules/inline/italic.ts +5 -5
  47. package/src/parser/rules/inline/monospace.ts +5 -5
  48. package/src/parser/rules/inline/parsing/block-start-predicates.ts +2 -0
  49. package/src/parser/rules/inline/parsing/inline-content.ts +58 -4
  50. package/src/parser/rules/inline/raw/end.ts +28 -0
  51. package/src/parser/rules/inline/span/content.ts +32 -1
  52. package/src/parser/rules/inline/strikethrough/index.ts +1 -1
  53. package/src/parser/rules/inline/strikethrough/parse.ts +2 -9
  54. package/src/parser/rules/inline/strikethrough/syntax.ts +3 -20
  55. package/src/parser/rules/inline/subscript.ts +5 -5
  56. package/src/parser/rules/inline/superscript.ts +5 -5
  57. package/src/parser/rules/inline/underline/index.ts +5 -78
  58. package/src/parser/rules/tokens.ts +6 -15
  59. package/src/pipeline/process.ts +8 -1
  60. package/src/parser/rules/inline/underline/child.ts +0 -26
  61. package/src/parser/rules/inline/underline/content.ts +0 -29
package/dist/index.js CHANGED
@@ -1,3 +1,10 @@
1
+ // packages/parser/src/build-info.generated.ts
2
+ var buildInfo = Object.freeze({
3
+ version: "5.3.0",
4
+ sha: "b71b84c705e05717f5ea12fd89684a91b669dbff",
5
+ dirty: false
6
+ });
7
+
1
8
  // packages/parser/src/index.ts
2
9
  import {
3
10
  createPoint,
@@ -21,49 +28,6 @@ import { createSettings, DEFAULT_SETTINGS as DEFAULT_SETTINGS3 } from "@wdprlib/
21
28
  function createToken(type, value, position, lineStart = false) {
22
29
  return { type, value, position, lineStart };
23
30
  }
24
- // packages/parser/src/lexer/token-factory.ts
25
- var ZERO_POSITION = {
26
- start: { line: 0, column: 0, offset: 0 },
27
- end: { line: 0, column: 0, offset: 0 }
28
- };
29
- function createLexerToken(state, type, value, trackPositions) {
30
- return {
31
- type,
32
- value,
33
- position: trackPositions ? currentTokenPosition(state, value) : ZERO_POSITION,
34
- lineStart: isTokenAtLineStart(state)
35
- };
36
- }
37
- function updateLastNonWhitespaceType(current, type) {
38
- return type === "WHITESPACE" ? current : type;
39
- }
40
- function nextBlockOpenerDepth(current, type) {
41
- if (type === "BLOCK_OPEN" || type === "BLOCK_END_OPEN") {
42
- return current + 1;
43
- }
44
- if (type === "BLOCK_CLOSE" && current > 0) {
45
- return current - 1;
46
- }
47
- return current;
48
- }
49
- function currentTokenPosition(state, value) {
50
- return {
51
- start: {
52
- line: state.line,
53
- column: state.column - value.length,
54
- offset: state.pos - value.length
55
- },
56
- end: {
57
- line: state.line,
58
- column: state.column,
59
- offset: state.pos
60
- }
61
- };
62
- }
63
- function isTokenAtLineStart(state) {
64
- return state.tokens.length === 0 || state.tokens[state.tokens.length - 1]?.type === "NEWLINE";
65
- }
66
-
67
31
  // packages/parser/src/lexer/state.ts
68
32
  function createInitialLexerState(source) {
69
33
  return {
@@ -72,9 +36,16 @@ function createInitialLexerState(source) {
72
36
  line: 1,
73
37
  column: 1,
74
38
  lineStart: true,
39
+ quoteContentStart: false,
75
40
  tokens: []
76
41
  };
77
42
  }
43
+ function isSyntaxLineStart(state) {
44
+ return state.lineStart || state.quoteContentStart;
45
+ }
46
+ function isLineStartQuoteMarker(token) {
47
+ return token?.type === "BLOCKQUOTE_MARKER" && token.lineStart;
48
+ }
78
49
  function isAtEnd(state) {
79
50
  return state.pos >= state.source.length;
80
51
  }
@@ -88,18 +59,25 @@ function advance(state, n = 1) {
88
59
  updatePosition(state, start, end);
89
60
  return value;
90
61
  }
91
- function advanceByToken(state, type, length) {
62
+ function advanceByToken(state, type, length, value = "") {
63
+ const afterQuoteMarker = isLineStartQuoteMarker(state.tokens[state.tokens.length - 1]);
92
64
  state.pos += length;
93
65
  if (type === "NEWLINE") {
94
66
  state.line++;
95
67
  state.column = 1;
96
68
  state.lineStart = true;
69
+ state.quoteContentStart = false;
97
70
  return;
98
71
  }
99
72
  state.column += length;
100
- if (type !== "WHITESPACE") {
101
- state.lineStart = false;
73
+ if (type === "WHITESPACE") {
74
+ if (afterQuoteMarker && value === " ") {
75
+ state.quoteContentStart = true;
76
+ }
77
+ return;
102
78
  }
79
+ state.lineStart = false;
80
+ state.quoteContentStart = false;
103
81
  }
104
82
  function updatePosition(state, start, end) {
105
83
  state.pos = end;
@@ -110,8 +88,9 @@ function updatePositionFromValue(state, value) {
110
88
  `);
111
89
  if (firstNewline === -1) {
112
90
  state.column += value.length;
113
- if (state.lineStart && hasNonLineStartSpacing(value, 0)) {
91
+ if (hasNonLineStartSpacing(value, 0)) {
114
92
  state.lineStart = false;
93
+ state.quoteContentStart = false;
115
94
  }
116
95
  return;
117
96
  }
@@ -130,6 +109,7 @@ function updatePositionFromValue(state, value) {
130
109
  state.line += newlineCount;
131
110
  state.column = value.length - lastNewline;
132
111
  state.lineStart = !hasNonLineStartSpacing(value, lastNewline + 1);
112
+ state.quoteContentStart = false;
133
113
  }
134
114
  function hasNonLineStartSpacing(value, start) {
135
115
  for (let i = start;i < value.length; i++) {
@@ -141,6 +121,56 @@ function hasNonLineStartSpacing(value, start) {
141
121
  return false;
142
122
  }
143
123
 
124
+ // packages/parser/src/lexer/token-factory.ts
125
+ var ZERO_POSITION = {
126
+ start: { line: 0, column: 0, offset: 0 },
127
+ end: { line: 0, column: 0, offset: 0 }
128
+ };
129
+ function createLexerToken(state, type, value, trackPositions) {
130
+ return {
131
+ type,
132
+ value,
133
+ position: trackPositions ? currentTokenPosition(state, value) : ZERO_POSITION,
134
+ lineStart: isTokenAtLineStart(state)
135
+ };
136
+ }
137
+ function updateLastNonWhitespaceType(current2, type) {
138
+ return type === "WHITESPACE" ? current2 : type;
139
+ }
140
+ function nextBlockOpenerDepth(current2, type) {
141
+ if (type === "BLOCK_OPEN" || type === "BLOCK_END_OPEN") {
142
+ return current2 + 1;
143
+ }
144
+ if (type === "BLOCK_CLOSE" && current2 > 0) {
145
+ return current2 - 1;
146
+ }
147
+ return current2;
148
+ }
149
+ function currentTokenPosition(state, value) {
150
+ return {
151
+ start: {
152
+ line: state.line,
153
+ column: state.column - value.length,
154
+ offset: state.pos - value.length
155
+ },
156
+ end: {
157
+ line: state.line,
158
+ column: state.column,
159
+ offset: state.pos
160
+ }
161
+ };
162
+ }
163
+ function isTokenAtLineStart(state) {
164
+ if (state.tokens.length === 0) {
165
+ return true;
166
+ }
167
+ const last = state.tokens[state.tokens.length - 1];
168
+ if (last?.type === "NEWLINE") {
169
+ return true;
170
+ }
171
+ return last?.type === "WHITESPACE" && last.value === " " && isLineStartQuoteMarker(state.tokens[state.tokens.length - 2]);
172
+ }
173
+
144
174
  // packages/parser/src/lexer/anchor.ts
145
175
  function findInvalidAnchorNameEnd(src, pos) {
146
176
  if (src[pos] !== "[" || src[pos + 1] !== "[" || src[pos + 2] !== "#") {
@@ -457,7 +487,7 @@ function scanPunctuationToken(input) {
457
487
  case "@":
458
488
  return { handled: true, actions: scanAtToken(source, pos) };
459
489
  case ">":
460
- return { handled: true, actions: scanGreaterToken(source, pos, lineStart) };
490
+ return { handled: true, actions: scanGreaterToken(source, pos, input.physicalLineStart) };
461
491
  case "-":
462
492
  return { handled: true, actions: scanDashToken(source, pos, lineStart) };
463
493
  case "~": {
@@ -499,6 +529,15 @@ function runToken3(src, pos, end, type) {
499
529
  }
500
530
 
501
531
  // packages/parser/src/lexer/spacing-actions.ts
532
+ function limitBlockquotePrefixSpace(action, previous) {
533
+ if (!isLineStartQuoteMarker(previous) || action.type !== "WHITESPACE") {
534
+ return action;
535
+ }
536
+ if (action.length <= 1 || !action.value.startsWith(" ")) {
537
+ return action;
538
+ }
539
+ return { type: "WHITESPACE", value: " ", length: 1 };
540
+ }
502
541
  function scanSpacingToken(src, pos) {
503
542
  const char = src[pos];
504
543
  if (char === `
@@ -560,7 +599,7 @@ class Lexer {
560
599
  this.blockOpenerDepth = nextBlockOpenerDepth(this.blockOpenerDepth, type);
561
600
  }
562
601
  emitTokenAction(action) {
563
- advanceByToken(this.state, action.type, action.length);
602
+ advanceByToken(this.state, action.type, action.length, action.value);
564
603
  this.addToken(action.type, action.value);
565
604
  }
566
605
  emitTokenActions(actions) {
@@ -574,11 +613,11 @@ class Lexer {
574
613
  }
575
614
  scanToken() {
576
615
  const char = this.current();
577
- const isLineStart = this.state.lineStart;
616
+ const isLineStart = isSyntaxLineStart(this.state);
578
617
  const src = this.state.source;
579
618
  const spacingAction = scanSpacingToken(src, this.state.pos);
580
619
  if (spacingAction) {
581
- this.emitTokenAction(spacingAction);
620
+ this.emitTokenAction(limitBlockquotePrefixSpace(spacingAction, this.state.tokens.at(-1)));
582
621
  return;
583
622
  }
584
623
  const punctuation = scanPunctuationToken({
@@ -586,6 +625,7 @@ class Lexer {
586
625
  source: src,
587
626
  pos: this.state.pos,
588
627
  lineStart: isLineStart,
628
+ physicalLineStart: this.state.lineStart,
589
629
  splitBlockClose: this.splitBlockClosePositions.has(this.state.pos),
590
630
  findInvalidAnchorNameEnd: () => this.findInvalidAnchorNameEnd()
591
631
  });
@@ -802,7 +842,7 @@ function parseBlocksUntil(ctx, closeCondition, options) {
802
842
  const elements = [];
803
843
  let consumed = 0;
804
844
  let pos = ctx.pos;
805
- const excluded = options?.excludedBlockNames;
845
+ const excluded = mergeExcludedBlockNames(ctx.scope.excludedBlockNames, options?.excludedBlockNames);
806
846
  const blockRules = excluded ? getExcludedBlockRules(ctx.blockRules, excluded) : ctx.blockRules;
807
847
  const blockScope = {
808
848
  ...ctx.scope,
@@ -844,6 +884,13 @@ function parseBlocksUntil(ctx, closeCondition, options) {
844
884
  }
845
885
  return { elements, consumed };
846
886
  }
887
+ function mergeExcludedBlockNames(inherited, added) {
888
+ if (!inherited?.size)
889
+ return added;
890
+ if (!added?.size)
891
+ return inherited;
892
+ return new Set([...inherited, ...added]);
893
+ }
847
894
  function getExcludedBlockRules(blockRules, excluded) {
848
895
  let byExcluded = excludedBlockRulesCache.get(blockRules);
849
896
  if (!byExcluded) {
@@ -880,6 +927,31 @@ function getCandidateInlineRules(inlineRules, tokenType) {
880
927
  byType.set(tokenType, candidates);
881
928
  return candidates;
882
929
  }
930
+ // packages/parser/src/parser/rules/inline/raw/end.ts
931
+ function rawRegionEnd(tokens, start, end) {
932
+ const type = tokens[start]?.type;
933
+ const close = type === "RAW_OPEN" ? "RAW_OPEN" : type === "RAW_BLOCK_OPEN" ? "RAW_BLOCK_CLOSE" : null;
934
+ if (!close)
935
+ return start;
936
+ for (let pos = start + 1;pos < end; pos++) {
937
+ if (tokens[pos]?.type === "NEWLINE" || tokens[pos]?.type === "EOF")
938
+ break;
939
+ if (tokens[pos]?.type === close)
940
+ return pos + 1;
941
+ }
942
+ return start;
943
+ }
944
+ function protectedInlineRegionEnd(tokens, start, end) {
945
+ const rawEnd = rawRegionEnd(tokens, start, end);
946
+ if (rawEnd > start || tokens[start]?.type !== "COMMENT_OPEN")
947
+ return rawEnd;
948
+ for (let pos = start + 1;pos < end; pos++) {
949
+ if (tokens[pos]?.type === "COMMENT_CLOSE")
950
+ return pos + 1;
951
+ }
952
+ return start;
953
+ }
954
+
883
955
  // packages/parser/src/parser/rules/inline/parsing/plain-text.ts
884
956
  var MIN_INLINE_TEXT_RUN_LENGTH = 32;
885
957
  var MIN_INLINE_TEXT_RUN_DOCUMENT_TOKENS = 1e5;
@@ -922,6 +994,135 @@ function isPlainTextRunToken(ctx, pos) {
922
994
  return token5.value !== "(";
923
995
  }
924
996
 
997
+ // packages/parser/src/parser/rules/inline/image/attributes.ts
998
+ function parseImageAttributes(ctx, startPos) {
999
+ const result = parseAttributesRaw(ctx, startPos, false);
1000
+ const link = result.attrs.link ?? null;
1001
+ const { link: _link, ...htmlAttributes } = result.attrs;
1002
+ return {
1003
+ attributes: filterUnsafeAttributes(htmlAttributes),
1004
+ link,
1005
+ consumed: result.consumed
1006
+ };
1007
+ }
1008
+
1009
+ // packages/parser/src/parser/rules/inline/image/body.ts
1010
+ function parseImageSourceText(ctx, startPos) {
1011
+ let pos = startPos;
1012
+ let consumed = 0;
1013
+ while (ctx.tokens[pos]?.type === "WHITESPACE") {
1014
+ pos++;
1015
+ consumed++;
1016
+ }
1017
+ let sourceText = "";
1018
+ while (pos < ctx.tokens.length) {
1019
+ const token5 = ctx.tokens[pos];
1020
+ if (!token5 || token5.type === "WHITESPACE" || token5.type === "BLOCK_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF") {
1021
+ break;
1022
+ }
1023
+ sourceText += token5.value;
1024
+ pos++;
1025
+ consumed++;
1026
+ }
1027
+ return { sourceText, consumed };
1028
+ }
1029
+
1030
+ // packages/parser/src/parser/rules/inline/image/syntax.ts
1031
+ var IMAGE_BLOCK_NAMES = new Set([
1032
+ "image",
1033
+ "=image",
1034
+ "<image",
1035
+ ">image",
1036
+ "f<image",
1037
+ "f>image",
1038
+ "f=image"
1039
+ ]);
1040
+ function parseImageBlockName(ctx, startPos) {
1041
+ let pos = startPos;
1042
+ let consumed = 0;
1043
+ while (ctx.tokens[pos]?.type === "WHITESPACE") {
1044
+ pos++;
1045
+ consumed++;
1046
+ }
1047
+ const prefixResult = parseImagePrefix(ctx, pos);
1048
+ const prefix = prefixResult.prefix;
1049
+ pos += prefixResult.consumed;
1050
+ consumed += prefixResult.consumed;
1051
+ const nameToken = ctx.tokens[pos];
1052
+ if (!nameToken || nameToken.type !== "TEXT" && nameToken.type !== "IDENTIFIER") {
1053
+ return null;
1054
+ }
1055
+ return { name: prefix + nameToken.value.toLowerCase(), consumed: consumed + 1 };
1056
+ }
1057
+ function isImageBlockName(blockName) {
1058
+ return IMAGE_BLOCK_NAMES.has(blockName);
1059
+ }
1060
+ function parseImagePrefix(ctx, pos) {
1061
+ const token5 = ctx.tokens[pos];
1062
+ if (token5?.type === "EQUALS") {
1063
+ return { prefix: "=", consumed: 1 };
1064
+ }
1065
+ if (token5?.type === "TEXT" && token5.value === "<") {
1066
+ return { prefix: "<", consumed: 1 };
1067
+ }
1068
+ if ((token5?.type === "TEXT" || token5?.type === "BLOCKQUOTE_MARKER") && token5.value === ">") {
1069
+ return { prefix: ">", consumed: 1 };
1070
+ }
1071
+ if (token5?.type === "IDENTIFIER" && token5.value.toLowerCase() === "f") {
1072
+ return parseFloatPrefix(ctx, pos + 1);
1073
+ }
1074
+ return { prefix: "", consumed: 0 };
1075
+ }
1076
+ function parseFloatPrefix(ctx, pos) {
1077
+ const token5 = ctx.tokens[pos];
1078
+ if (token5?.type === "TEXT" && token5.value === "<") {
1079
+ return { prefix: "f<", consumed: 2 };
1080
+ }
1081
+ if ((token5?.type === "TEXT" || token5?.type === "BLOCKQUOTE_MARKER") && token5.value === ">") {
1082
+ return { prefix: "f>", consumed: 2 };
1083
+ }
1084
+ if (token5?.type === "EQUALS") {
1085
+ return { prefix: "f=", consumed: 2 };
1086
+ }
1087
+ return { prefix: "", consumed: 0 };
1088
+ }
1089
+
1090
+ // packages/parser/src/parser/rules/inline/image/open.ts
1091
+ function parseImageOpen(ctx) {
1092
+ if (ctx.tokens[ctx.pos]?.type !== "BLOCK_OPEN") {
1093
+ return null;
1094
+ }
1095
+ let pos = ctx.pos + 1;
1096
+ let consumed = 1;
1097
+ const nameResult = parseImageBlockName(ctx, pos);
1098
+ if (!nameResult || !isImageBlockName(nameResult.name)) {
1099
+ return null;
1100
+ }
1101
+ pos += nameResult.consumed;
1102
+ consumed += nameResult.consumed;
1103
+ const sourceText = parseImageSourceText(ctx, pos);
1104
+ pos += sourceText.consumed;
1105
+ consumed += sourceText.consumed;
1106
+ const attrs = parseImageAttributes(ctx, pos);
1107
+ pos += attrs.consumed;
1108
+ consumed += attrs.consumed;
1109
+ if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
1110
+ return null;
1111
+ }
1112
+ pos++;
1113
+ consumed++;
1114
+ if (!sourceText.sourceText) {
1115
+ return null;
1116
+ }
1117
+ return {
1118
+ blockName: nameResult.name,
1119
+ sourceText: sourceText.sourceText,
1120
+ link: attrs.link,
1121
+ attributes: attrs.attributes,
1122
+ consumed
1123
+ };
1124
+ }
1125
+
925
1126
  // packages/parser/src/parser/constants.ts
926
1127
  var BLOCK_START_TOKENS = [
927
1128
  "BLOCKQUOTE_MARKER",
@@ -1052,7 +1253,7 @@ function isParagraphBreakingBlockStart(ctx, newlinePos, lookAhead) {
1052
1253
  if (!nextMeaningfulToken.lineStart && !isIndentedBlockOpener) {
1053
1254
  return false;
1054
1255
  }
1055
- return !isOrphanCloseSpan(ctx, nextPos) && !isAnchorName(ctx, nextPos) && !isInvalidBlockOpen(ctx, nextPos) && !isInvalidHeading(ctx, nextPos) && !isExcludedBlockStart(ctx, nextPos) && !isUnknownBlockStart(ctx, nextPos);
1256
+ return !parseImageOpen({ ...ctx, pos: nextPos }) && !isOrphanCloseSpan(ctx, nextPos) && !isAnchorName(ctx, nextPos) && !isInvalidBlockOpen(ctx, nextPos) && !isInvalidHeading(ctx, nextPos) && !isExcludedBlockStart(ctx, nextPos) && !isUnknownBlockStart(ctx, nextPos);
1056
1257
  }
1057
1258
  function isOrphanCloseSpan(ctx, blockEndOpenPos) {
1058
1259
  const token5 = ctx.tokens[blockEndOpenPos];
@@ -1162,27 +1363,45 @@ function parseInlineUntil(ctx, endType) {
1162
1363
  let consumed = 0;
1163
1364
  let pos = ctx.pos;
1164
1365
  const paragraphMode = endType === "PARAGRAPH_BREAK";
1366
+ const multiline = paragraphMode || FORMATTING_CLOSE_TOKENS.has(endType);
1367
+ let inlineEnd = ctx.scope.inlineEnd ?? ctx.tokens.length;
1368
+ if (!multiline) {
1369
+ for (let end = ctx.pos;end < inlineEnd; end++) {
1370
+ const protectedEnd = protectedInlineRegionEnd(ctx.tokens, end, inlineEnd);
1371
+ if (protectedEnd > end) {
1372
+ end = protectedEnd - 1;
1373
+ continue;
1374
+ }
1375
+ if (ctx.tokens[end]?.type === "NEWLINE" && ctx.tokens[end - 1]?.type === "UNDERSCORE" && ctx.tokens[end - 2]?.type === "WHITESPACE")
1376
+ continue;
1377
+ if (ctx.tokens[end]?.type === "NEWLINE" || ctx.tokens[end]?.type === endType) {
1378
+ inlineEnd = end;
1379
+ break;
1380
+ }
1381
+ }
1382
+ }
1165
1383
  const { inlineRules } = ctx;
1166
1384
  const inlineCtx = {
1167
1385
  ...ctx,
1168
- pos
1386
+ pos,
1387
+ scope: { ...ctx.scope, inlineEnd }
1169
1388
  };
1170
1389
  const canCollectLongPlainTextRuns = ctx.tokens.length >= MIN_INLINE_TEXT_RUN_DOCUMENT_TOKENS;
1171
- while (pos < ctx.tokens.length) {
1390
+ while (pos < inlineEnd) {
1172
1391
  const token5 = ctx.tokens[pos];
1173
1392
  if (!token5 || token5.type === "EOF") {
1174
1393
  break;
1175
1394
  }
1176
- if (paragraphMode && ctx.scope.blockCloseCondition) {
1395
+ if (ctx.scope.blockCloseCondition) {
1177
1396
  const checkCtx = { ...ctx, pos };
1178
1397
  if (ctx.scope.blockCloseCondition(checkCtx)) {
1179
1398
  break;
1180
1399
  }
1181
1400
  }
1182
- if (!paragraphMode && token5.type === "NEWLINE") {
1401
+ if (!multiline && token5.type === "NEWLINE") {
1183
1402
  break;
1184
1403
  }
1185
- if (paragraphMode && token5.type === "NEWLINE") {
1404
+ if (multiline && token5.type === "NEWLINE" && !ctx.scope.tableFormatting) {
1186
1405
  const boundary = getParagraphNewlineBoundary(ctx, pos, nodes.length > 0);
1187
1406
  if (boundary.shouldBreak) {
1188
1407
  if (boundary.preservePrecedingLineBreak) {
@@ -1192,6 +1411,11 @@ function parseInlineUntil(ctx, endType) {
1192
1411
  break;
1193
1412
  }
1194
1413
  }
1414
+ if (ctx.scope.tableFormatting?.suppressedClosers.has(pos)) {
1415
+ pos++;
1416
+ consumed++;
1417
+ continue;
1418
+ }
1195
1419
  if (token5.type === endType) {
1196
1420
  break;
1197
1421
  }
@@ -1216,6 +1440,24 @@ function parseInlineUntil(ctx, endType) {
1216
1440
  for (const rule of getCandidateInlineRules(inlineRules, token5.type)) {
1217
1441
  const result = rule.parse(inlineCtx);
1218
1442
  if (result.success) {
1443
+ if (rule.name === "comment") {
1444
+ let after = pos + result.consumed;
1445
+ while (ctx.tokens[after]?.type === "WHITESPACE")
1446
+ after++;
1447
+ if (ctx.tokens[after]?.type === "NEWLINE" || ctx.tokens[after]?.type === "EOF") {
1448
+ while (nodes.at(-1)?.element === "text") {
1449
+ const last = nodes.at(-1);
1450
+ if (last.element !== "text")
1451
+ break;
1452
+ last.data = last.data.trimEnd();
1453
+ if (last.data)
1454
+ break;
1455
+ nodes.pop();
1456
+ }
1457
+ if (nodes.at(-1)?.element === "line-break")
1458
+ nodes.pop();
1459
+ }
1460
+ }
1219
1461
  nodes.push(...result.elements);
1220
1462
  consumed += result.consumed;
1221
1463
  pos += result.consumed;
@@ -1231,6 +1473,16 @@ function parseInlineUntil(ctx, endType) {
1231
1473
  }
1232
1474
  return { elements: nodes, consumed };
1233
1475
  }
1476
+ var FORMATTING_CLOSE_TOKENS = new Set([
1477
+ "BOLD_MARKER",
1478
+ "ITALIC_MARKER",
1479
+ "UNDERLINE_MARKER",
1480
+ "STRIKE_MARKER",
1481
+ "SUPER_MARKER",
1482
+ "SUB_MARKER",
1483
+ "MONO_CLOSE",
1484
+ "COLOR_MARKER"
1485
+ ]);
1234
1486
  // packages/parser/src/parser/rules/block/parsing/attributes/names.ts
1235
1487
  function consumeAttributeName(ctx, startPos, startConsumed, startName, options) {
1236
1488
  if (startName === "_" && isAttributeWordToken(ctx.tokens[startPos])) {
@@ -1406,7 +1658,7 @@ function eofToken() {
1406
1658
  }
1407
1659
  function hasClosingMarkerBeforeNewline(ctx, markerType, markerValue) {
1408
1660
  let pos = ctx.pos;
1409
- while (pos < ctx.tokens.length) {
1661
+ while (pos < (ctx.scope.inlineEnd ?? ctx.tokens.length)) {
1410
1662
  const token5 = ctx.tokens[pos];
1411
1663
  if (!token5 || token5.type === "NEWLINE" || token5.type === "EOF") {
1412
1664
  return false;
@@ -1420,31 +1672,6 @@ function hasClosingMarkerBeforeNewline(ctx, markerType, markerValue) {
1420
1672
  }
1421
1673
  return false;
1422
1674
  }
1423
- function hasClosingMarkerBeforeParagraphBreak(ctx, markerType, markerValue) {
1424
- let pos = ctx.pos;
1425
- while (pos < ctx.tokens.length) {
1426
- const token5 = ctx.tokens[pos];
1427
- if (!token5 || token5.type === "EOF") {
1428
- return false;
1429
- }
1430
- if (token5.type === "NEWLINE") {
1431
- let lookAhead = 1;
1432
- while (ctx.tokens[pos + lookAhead]?.type === "WHITESPACE") {
1433
- lookAhead++;
1434
- }
1435
- if (ctx.tokens[pos + lookAhead]?.type === "NEWLINE" || ctx.tokens[pos + lookAhead]?.type === "EOF" || !ctx.tokens[pos + lookAhead]) {
1436
- return false;
1437
- }
1438
- }
1439
- if (token5.type === markerType) {
1440
- if (markerValue === undefined || token5.value === markerValue) {
1441
- return true;
1442
- }
1443
- }
1444
- pos++;
1445
- }
1446
- return false;
1447
- }
1448
1675
  // packages/parser/src/parser/rules/block/heading/open.ts
1449
1676
  function parseHeadingOpen(ctx) {
1450
1677
  const marker = ctx.tokens[ctx.pos];
@@ -1511,6 +1738,15 @@ var headingRule = {
1511
1738
  const inlineCtx = { ...ctx, pos };
1512
1739
  const inlineResult = parseInlineUntil(inlineCtx, "NEWLINE");
1513
1740
  const children = inlineResult.elements;
1741
+ while (children.at(-1)?.element === "text") {
1742
+ const last = children.at(-1);
1743
+ if (last.element !== "text")
1744
+ break;
1745
+ last.data = last.data.trimEnd();
1746
+ if (last.data)
1747
+ break;
1748
+ children.pop();
1749
+ }
1514
1750
  consumed += inlineResult.consumed;
1515
1751
  pos += inlineResult.consumed;
1516
1752
  if (ctx.tokens[pos]?.type === "NEWLINE") {
@@ -2310,17 +2546,15 @@ function parseBlockquoteLine(ctx, startPos) {
2310
2546
  if (ctx.tokens[pos]?.type !== "WHITESPACE") {
2311
2547
  return { kind: "skipped", consumed: consumeLineRemainder(ctx, pos) + consumed };
2312
2548
  }
2313
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
2549
+ pos++;
2550
+ consumed++;
2551
+ const contentStart = pos;
2552
+ while (pos < ctx.tokens.length && ctx.tokens[pos]?.type !== "NEWLINE" && ctx.tokens[pos]?.type !== "EOF") {
2314
2553
  pos++;
2315
2554
  consumed++;
2316
2555
  }
2317
- const inlineCtx = { ...ctx, pos };
2318
- const inlineResult = parseInlineUntil(inlineCtx, "NEWLINE");
2319
- consumed += inlineResult.consumed;
2320
- pos += inlineResult.consumed;
2321
- let hasLineBreak = false;
2322
2556
  if (ctx.tokens[pos]?.type === "NEWLINE") {
2323
- hasLineBreak = true;
2557
+ pos++;
2324
2558
  consumed++;
2325
2559
  }
2326
2560
  return {
@@ -2328,7 +2562,7 @@ function parseBlockquoteLine(ctx, startPos) {
2328
2562
  line: {
2329
2563
  depth: depth - 1,
2330
2564
  ltype: null,
2331
- value: { elements: inlineResult.elements, hasLineBreak }
2565
+ value: { start: contentStart, end: pos }
2332
2566
  },
2333
2567
  consumed
2334
2568
  };
@@ -2347,47 +2581,43 @@ function consumeLineRemainder(ctx, startPos) {
2347
2581
  }
2348
2582
 
2349
2583
  // packages/parser/src/parser/rules/block/blockquote/build.ts
2350
- function buildBlockquoteElements(lines) {
2584
+ var EXCLUDED_BLOCK_NAMES = new Set([
2585
+ "bibliography",
2586
+ "code",
2587
+ "html",
2588
+ "include",
2589
+ "math",
2590
+ "module"
2591
+ ]);
2592
+ var NEVER_CLOSES = () => false;
2593
+ function buildBlockquoteElements(ctx, lines) {
2351
2594
  const depthTrees = processDepths(null, lines);
2352
- return depthTrees.map(({ list }) => buildBlockquoteElement(list));
2595
+ return depthTrees.map(({ list }) => buildBlockquoteElement(ctx, list)).filter((element) => element !== null);
2353
2596
  }
2354
- function buildBlockquoteElement(list) {
2597
+ function buildBlockquoteElement(ctx, list) {
2355
2598
  const children = [];
2356
- let currentParagraphChildren = [];
2357
- function flushParagraph() {
2358
- if (currentParagraphChildren.length > 0) {
2359
- while (currentParagraphChildren.length > 0 && currentParagraphChildren[currentParagraphChildren.length - 1]?.element === "line-break") {
2360
- currentParagraphChildren.pop();
2361
- }
2362
- if (currentParagraphChildren.length > 0) {
2363
- children.push({
2364
- element: "container",
2365
- data: {
2366
- type: "paragraph",
2367
- attributes: {},
2368
- elements: currentParagraphChildren
2369
- }
2370
- });
2371
- }
2372
- currentParagraphChildren = [];
2373
- }
2599
+ let pending = [];
2600
+ function flushPending() {
2601
+ if (pending.length === 0)
2602
+ return;
2603
+ children.push(...parseLines(ctx, pending));
2604
+ pending = [];
2374
2605
  }
2375
2606
  for (const item of list) {
2376
2607
  if (item.kind === "item") {
2377
- if (item.value.elements.length === 0) {
2378
- flushParagraph();
2379
- continue;
2380
- }
2381
- currentParagraphChildren.push(...item.value.elements);
2382
- if (item.value.hasLineBreak) {
2383
- currentParagraphChildren.push({ element: "line-break" });
2384
- }
2385
- } else {
2386
- flushParagraph();
2387
- children.push(buildBlockquoteElement(item.children));
2608
+ pending.push(item.value);
2609
+ continue;
2610
+ }
2611
+ flushPending();
2612
+ const nested = buildBlockquoteElement(ctx, item.children);
2613
+ if (nested) {
2614
+ children.push(nested);
2388
2615
  }
2389
2616
  }
2390
- flushParagraph();
2617
+ flushPending();
2618
+ if (children.length === 0) {
2619
+ return null;
2620
+ }
2391
2621
  return {
2392
2622
  element: "container",
2393
2623
  data: {
@@ -2397,6 +2627,31 @@ function buildBlockquoteElement(list) {
2397
2627
  }
2398
2628
  };
2399
2629
  }
2630
+ function parseLines(ctx, lines) {
2631
+ const tokens = sliceLineTokens(ctx, lines);
2632
+ const lineCtx = { ...ctx, tokens, pos: 0 };
2633
+ return parseBlocksUntil(lineCtx, NEVER_CLOSES, {
2634
+ excludedBlockNames: EXCLUDED_BLOCK_NAMES
2635
+ }).elements;
2636
+ }
2637
+ function sliceLineTokens(ctx, lines) {
2638
+ const tokens = [];
2639
+ for (const { start, end } of lines) {
2640
+ for (let pos = start;pos < end; pos++) {
2641
+ const token5 = ctx.tokens[pos];
2642
+ if (token5) {
2643
+ tokens.push(token5);
2644
+ }
2645
+ }
2646
+ }
2647
+ const last = tokens[tokens.length - 1];
2648
+ tokens.push(createToken("EOF", "", last?.position ?? ZERO_POSITION2));
2649
+ return tokens;
2650
+ }
2651
+ var ZERO_POSITION2 = {
2652
+ start: { line: 0, column: 0, offset: 0 },
2653
+ end: { line: 0, column: 0, offset: 0 }
2654
+ };
2400
2655
 
2401
2656
  // packages/parser/src/parser/rules/block/blockquote/lines.ts
2402
2657
  function collectBlockquoteLines(ctx) {
@@ -2405,16 +2660,90 @@ function collectBlockquoteLines(ctx) {
2405
2660
  let consumed = 0;
2406
2661
  while (pos < ctx.tokens.length) {
2407
2662
  const result = parseBlockquoteLine(ctx, pos);
2408
- if (result.kind === "stop")
2409
- break;
2663
+ if (result.kind === "stop") {
2664
+ const resumed = extendToCommentClose(ctx, lines, pos);
2665
+ if (resumed === pos)
2666
+ break;
2667
+ consumed += resumed - pos;
2668
+ pos = resumed;
2669
+ continue;
2670
+ }
2410
2671
  pos += result.consumed;
2411
2672
  consumed += result.consumed;
2412
2673
  if (result.kind === "parsed") {
2413
2674
  lines.push(result.line);
2414
2675
  }
2415
2676
  }
2677
+ blankCommentOnlyLines(ctx, lines);
2416
2678
  return { lines, consumed };
2417
2679
  }
2680
+ function extendToCommentClose(ctx, lines, stopPos) {
2681
+ const last = lines[lines.length - 1];
2682
+ if (!last || !endsInsideComment(ctx, lines)) {
2683
+ return stopPos;
2684
+ }
2685
+ let pos = stopPos;
2686
+ while (pos < ctx.tokens.length && ctx.tokens[pos]?.type !== "COMMENT_CLOSE") {
2687
+ if (ctx.tokens[pos]?.type === "EOF")
2688
+ return stopPos;
2689
+ pos++;
2690
+ }
2691
+ while (pos < ctx.tokens.length && ctx.tokens[pos]?.type !== "EOF") {
2692
+ pos++;
2693
+ if (ctx.tokens[pos - 1]?.type === "NEWLINE")
2694
+ break;
2695
+ }
2696
+ last.value.end = pos;
2697
+ return pos;
2698
+ }
2699
+ function endsInsideComment(ctx, lines) {
2700
+ let open = false;
2701
+ for (const { value } of lines) {
2702
+ for (let pos = value.start;pos < value.end; pos++) {
2703
+ const type = ctx.tokens[pos]?.type;
2704
+ if (type === "COMMENT_OPEN")
2705
+ open = true;
2706
+ else if (type === "COMMENT_CLOSE")
2707
+ open = false;
2708
+ }
2709
+ }
2710
+ return open;
2711
+ }
2712
+ function blankCommentOnlyLines(ctx, lines) {
2713
+ const positions = [];
2714
+ lines.forEach((line, index) => {
2715
+ for (let pos = line.value.start;pos < line.value.end; pos++) {
2716
+ if (ctx.tokens[pos]?.type !== "NEWLINE") {
2717
+ positions.push({ line: index, pos });
2718
+ }
2719
+ }
2720
+ });
2721
+ const commented = Array.from({ length: positions.length }, () => false);
2722
+ for (let open = 0;open < positions.length; open++) {
2723
+ if (ctx.tokens[positions[open].pos]?.type !== "COMMENT_OPEN")
2724
+ continue;
2725
+ let close = open + 1;
2726
+ while (close < positions.length && ctx.tokens[positions[close].pos]?.type !== "COMMENT_CLOSE") {
2727
+ close++;
2728
+ }
2729
+ if (close === positions.length)
2730
+ break;
2731
+ commented.fill(true, open, close + 1);
2732
+ open = close;
2733
+ }
2734
+ const hasContent = Array.from({ length: lines.length }, () => false);
2735
+ positions.forEach(({ line, pos }, index) => {
2736
+ if (commented[index] || ctx.tokens[pos]?.type === "WHITESPACE")
2737
+ return;
2738
+ hasContent[line] = true;
2739
+ });
2740
+ lines.forEach((line, index) => {
2741
+ if (hasContent[index])
2742
+ return;
2743
+ const { end } = line.value;
2744
+ line.value.start = ctx.tokens[end - 1]?.type === "NEWLINE" ? end - 1 : end;
2745
+ });
2746
+ }
2418
2747
 
2419
2748
  // packages/parser/src/parser/rules/block/blockquote/index.ts
2420
2749
  var blockquoteRule = {
@@ -2433,9 +2762,9 @@ var blockquoteRule = {
2433
2762
  }
2434
2763
  return { success: false };
2435
2764
  }
2436
- const blockquotes = buildBlockquoteElements(blockquoteLines.lines);
2765
+ const blockquotes = buildBlockquoteElements(ctx, blockquoteLines.lines);
2437
2766
  if (blockquotes.length === 0) {
2438
- return { success: false };
2767
+ return { success: true, elements: [], consumed: blockquoteLines.consumed };
2439
2768
  }
2440
2769
  return {
2441
2770
  success: true,
@@ -2530,17 +2859,8 @@ function parseDefinitionItemValue(ctx, startPos) {
2530
2859
  break;
2531
2860
  }
2532
2861
  if (token5.type === "NEWLINE") {
2533
- const nextToken = ctx.tokens[pos + 1];
2534
- if (nextToken?.type === "COLON" && nextToken.lineStart) {
2535
- pos++;
2536
- consumed++;
2537
- break;
2538
- }
2539
- if (nextToken?.type === "NEWLINE" || !nextToken || nextToken.type === "EOF") {
2540
- pos++;
2541
- consumed++;
2542
- break;
2543
- }
2862
+ consumed++;
2863
+ break;
2544
2864
  }
2545
2865
  const inlineCtx = { ...ctx, pos };
2546
2866
  const result = parseInlineUntil(inlineCtx, "NEWLINE");
@@ -2618,14 +2938,10 @@ var definitionListRule = {
2618
2938
  if (result.items.length === 0) {
2619
2939
  return { success: false };
2620
2940
  }
2941
+ const items = toDefinitionListItems(result.items.filter((item) => item.value.length > 0));
2621
2942
  return {
2622
2943
  success: true,
2623
- elements: [
2624
- {
2625
- element: "definition-list",
2626
- data: toDefinitionListItems(result.items)
2627
- }
2628
- ],
2944
+ elements: items.length > 0 ? [{ element: "definition-list", data: items }] : [],
2629
2945
  consumed: result.consumed
2630
2946
  };
2631
2947
  }
@@ -2730,20 +3046,49 @@ var paragraphRule = {
2730
3046
  }
2731
3047
  return {
2732
3048
  success: true,
2733
- elements: [
2734
- {
2735
- element: "container",
2736
- data: {
2737
- type: "paragraph",
2738
- attributes: {},
2739
- elements
2740
- }
2741
- }
2742
- ],
3049
+ elements: wrapParagraphElements(elements),
2743
3050
  consumed: result.consumed
2744
3051
  };
2745
3052
  }
2746
3053
  };
3054
+ function wrapParagraphElements(elements) {
3055
+ const output = [];
3056
+ let group = [];
3057
+ let bare = false;
3058
+ const flush = (trimBreaks = false) => {
3059
+ const content = trimBreaks ? normalizeParagraphElements(group) : group;
3060
+ while (content[0]?.element === "line-break")
3061
+ content.shift();
3062
+ while (content.length) {
3063
+ const last = content.at(-1);
3064
+ if (last.element !== "text" || last.data.trim() !== "")
3065
+ break;
3066
+ content.pop();
3067
+ }
3068
+ while (content[0]?.element === "text" && content[0].data.trim() === "")
3069
+ content.shift();
3070
+ if (content[0]?.element === "text")
3071
+ content[0] = { element: "text", data: content[0].data.trimStart() };
3072
+ if (content.length)
3073
+ output.push(...bare || content.some((el) => el.element === "image") ? content : [
3074
+ {
3075
+ element: "container",
3076
+ data: { type: "paragraph", attributes: {}, elements: content }
3077
+ }
3078
+ ]);
3079
+ group = [];
3080
+ };
3081
+ for (const el of elements) {
3082
+ if (el.element === "image" && el.data.alignment !== null || el.element === "embed-block") {
3083
+ flush(el.element === "image");
3084
+ output.push(el);
3085
+ bare = el.element === "embed-block";
3086
+ } else
3087
+ group.push(el);
3088
+ }
3089
+ flush();
3090
+ return output;
3091
+ }
2747
3092
 
2748
3093
  // packages/parser/src/parser/rules/block/div/close.ts
2749
3094
  function isDivClose(ctx) {
@@ -3109,9 +3454,6 @@ function consumeCodeClose(ctx, startPos, closeNameConsumed) {
3109
3454
  pos++;
3110
3455
  consumed++;
3111
3456
  }
3112
- if (ctx.tokens[pos]?.type === "NEWLINE") {
3113
- consumed++;
3114
- }
3115
3457
  return consumed;
3116
3458
  }
3117
3459
 
@@ -3168,16 +3510,13 @@ var codeBlockRule = {
3168
3510
  name: attrResult.attrs.name ?? null
3169
3511
  };
3170
3512
  ctx.codeBlocks.push(codeBlockData);
3171
- return {
3172
- success: true,
3173
- elements: [
3174
- {
3175
- element: "code",
3176
- data: codeBlockData
3177
- }
3178
- ],
3179
- consumed
3180
- };
3513
+ const elements = [{ element: "code", data: codeBlockData }];
3514
+ if (ctx.tokens[pos]?.type === "NEWLINE" && !getParagraphNewlineBoundary(ctx, pos, false).shouldBreak && !ctx.scope.blockCloseCondition?.({ ...ctx, pos: pos + 1 })) {
3515
+ const after = parseInlineUntil({ ...ctx, pos: pos + 1 }, "PARAGRAPH_BREAK");
3516
+ elements.push({ element: "line-break" }, ...normalizeParagraphElements(after.elements));
3517
+ consumed += 1 + after.consumed;
3518
+ }
3519
+ return { success: true, elements, consumed };
3181
3520
  }
3182
3521
  };
3183
3522
 
@@ -3556,7 +3895,30 @@ function parseTableCell(ctx, startPos, cellStart) {
3556
3895
  consumed++;
3557
3896
  }
3558
3897
  const { inlineRules } = ctx;
3559
- const inlineCtx = { ...ctx, pos };
3898
+ let inlineEnd = pos;
3899
+ while (inlineEnd < ctx.tokens.length) {
3900
+ const rawEnd = protectedInlineRegionEnd(ctx.tokens, inlineEnd, ctx.tokens.length);
3901
+ if (rawEnd > inlineEnd) {
3902
+ inlineEnd = rawEnd;
3903
+ continue;
3904
+ }
3905
+ const token5 = ctx.tokens[inlineEnd];
3906
+ if (!token5 || token5.type === "EOF" || token5.type === "NEWLINE" || isPipeTableToken(token5.type))
3907
+ break;
3908
+ if (token5.type === "WHITESPACE" && ctx.tokens[inlineEnd + 1]?.type === "UNDERSCORE" && ctx.tokens[inlineEnd + 2]?.type === "NEWLINE")
3909
+ inlineEnd += 3;
3910
+ else
3911
+ inlineEnd++;
3912
+ }
3913
+ const inlineCtx = {
3914
+ ...ctx,
3915
+ pos,
3916
+ scope: {
3917
+ ...ctx.scope,
3918
+ inlineEnd,
3919
+ tableFormatting: isPipeTableToken(ctx.tokens[inlineEnd]?.type ?? "EOF") ? ctx.scope.tableFormatting : undefined
3920
+ }
3921
+ };
3560
3922
  while (pos < ctx.tokens.length) {
3561
3923
  const token5 = ctx.tokens[pos];
3562
3924
  if (!token5 || token5.type === "NEWLINE" || token5.type === "EOF") {
@@ -3565,6 +3927,11 @@ function parseTableCell(ctx, startPos, cellStart) {
3565
3927
  if (isPipeTableToken(token5.type)) {
3566
3928
  break;
3567
3929
  }
3930
+ if (ctx.scope.tableFormatting?.suppressedClosers.has(pos)) {
3931
+ pos++;
3932
+ consumed++;
3933
+ continue;
3934
+ }
3568
3935
  if (tryConsumeUnderscoreLineBreak(ctx, pos, children)) {
3569
3936
  pos += 3;
3570
3937
  consumed += 3;
@@ -3624,6 +3991,21 @@ function tryConsumeUnderscoreLineBreak(ctx, pos, children) {
3624
3991
 
3625
3992
  // packages/parser/src/parser/rules/block/table/pipe/row.ts
3626
3993
  function parsePipeTableRows(ctx, startPos) {
3994
+ let end = startPos;
3995
+ while (end < ctx.tokens.length && ctx.tokens[end]?.type !== "EOF") {
3996
+ const protectedEnd = protectedInlineRegionEnd(ctx.tokens, end, ctx.tokens.length);
3997
+ if (protectedEnd > end) {
3998
+ end = protectedEnd;
3999
+ continue;
4000
+ }
4001
+ if (ctx.tokens[end]?.type === "NEWLINE" && !isPipeTableToken(ctx.tokens[end + 1]?.type ?? "EOF") && !(ctx.tokens[end - 1]?.type === "UNDERSCORE" && ctx.tokens[end - 2]?.type === "WHITESPACE"))
4002
+ break;
4003
+ end++;
4004
+ }
4005
+ const tableCtx = {
4006
+ ...ctx,
4007
+ scope: { ...ctx.scope, tableFormatting: { end, suppressedClosers: new Set } }
4008
+ };
3627
4009
  const rows = [];
3628
4010
  let pos = startPos;
3629
4011
  let consumed = 0;
@@ -3632,7 +4014,7 @@ function parsePipeTableRows(ctx, startPos) {
3632
4014
  if (!token5 || !token5.lineStart || !isPipeTableToken(token5.type)) {
3633
4015
  break;
3634
4016
  }
3635
- const rowResult = parseTableRow(ctx, pos);
4017
+ const rowResult = parseTableRow(tableCtx, pos);
3636
4018
  rows.push(rowResult.row);
3637
4019
  pos += rowResult.consumed;
3638
4020
  consumed += rowResult.consumed;
@@ -5804,9 +6186,9 @@ function renderListPagesItems(module, data, compiledTemplate, parse) {
5804
6186
  continue;
5805
6187
  const ctx = {
5806
6188
  page,
5807
- index: i + 1,
6189
+ index: (data.pagination?.offset ?? 0) + i + 1,
5808
6190
  total: data.totalCount,
5809
- limit: module.limit,
6191
+ limit: data.pagination ? data.pagination.limit : module.limit,
5810
6192
  site: data.site
5811
6193
  };
5812
6194
  const itemAst = getModuleParseAst(parse(compiledTemplate(ctx)));
@@ -5823,12 +6205,49 @@ function renderListPagesItems(module, data, compiledTemplate, parse) {
5823
6205
  items.push(...itemAst.elements);
5824
6206
  }
5825
6207
  }
5826
- return items;
6208
+ return items;
6209
+ }
6210
+
6211
+ // packages/parser/src/parser/rules/block/module/listpages/resolution/pager.ts
6212
+ function createListPagesPager(data) {
6213
+ const pagination = data.pagination;
6214
+ if (!pagination || pagination.totalPages <= 1)
6215
+ return [];
6216
+ const { currentPage, totalPages, urlPath, parameter } = pagination;
6217
+ const pages = new Set([1, totalPages]);
6218
+ for (let page = Math.max(1, currentPage - 2);page <= Math.min(totalPages, currentPage + 2); page++) {
6219
+ pages.add(page);
6220
+ }
6221
+ return [
6222
+ {
6223
+ element: "pager",
6224
+ data: {
6225
+ currentPage,
6226
+ totalPages,
6227
+ pages: [...pages].sort((a, b) => a - b).map((page) => ({
6228
+ page,
6229
+ href: pageUrl(urlPath, parameter, page)
6230
+ }))
6231
+ }
6232
+ }
6233
+ ];
6234
+ }
6235
+ function pageUrl(urlPath, parameter, page) {
6236
+ const suffixStart = urlPath.search(/[?#]/);
6237
+ const path = suffixStart === -1 ? urlPath : urlPath.slice(0, suffixStart);
6238
+ const suffix = suffixStart === -1 ? "" : urlPath.slice(suffixStart);
6239
+ const parts = path.split("/");
6240
+ const result = parts.slice(0, 2);
6241
+ for (let index = 2;index < parts.length; index += 2) {
6242
+ if (parts[index] !== parameter)
6243
+ result.push(...parts.slice(index, index + 2));
6244
+ }
6245
+ return `${result.join("/").replace(/\/$/, "")}/${parameter}/${page}${suffix}`;
5827
6246
  }
5828
6247
 
5829
6248
  // packages/parser/src/parser/rules/block/module/listpages/resolution/wrapper.ts
5830
- function wrapListPagesResult(module, items, parse) {
5831
- if (items.length === 0) {
6249
+ function wrapListPagesResult(module, items, parse, data) {
6250
+ if (items.length === 0 && (!data || data.pages.length === 0)) {
5832
6251
  return [];
5833
6252
  }
5834
6253
  const result = [];
@@ -5841,6 +6260,8 @@ function wrapListPagesResult(module, items, parse) {
5841
6260
  const appendAst = getModuleParseAst(parse(module["append-line"]));
5842
6261
  result.push(...appendAst.elements);
5843
6262
  }
6263
+ if (data)
6264
+ result.push(...createListPagesPager(data));
5844
6265
  if (module.wrapper) {
5845
6266
  return [
5846
6267
  {
@@ -5862,7 +6283,7 @@ function isListPagesModule2(module) {
5862
6283
  }
5863
6284
  function resolveListPages(module, data, compiledTemplate, parse) {
5864
6285
  const items = renderListPagesItems(module, data, compiledTemplate, parse);
5865
- return wrapListPagesResult(module, items, parse);
6286
+ return wrapListPagesResult(module, items, parse, data);
5866
6287
  }
5867
6288
  // packages/parser/src/parser/rules/block/module/listpages/types/external-data.ts
5868
6289
  function definePageData(input) {
@@ -5959,10 +6380,10 @@ var URL_RESOLVABLE_FIELDS = [
5959
6380
  ];
5960
6381
 
5961
6382
  // packages/parser/src/parser/rules/block/module/listpages/url-resolution/params.ts
5962
- function parseUrlParams(url) {
6383
+ function parseUrlParams(url, hasPageName = false) {
5963
6384
  const params = new Map;
5964
- const parts = url.split("/").filter(Boolean);
5965
- const pairStart = isUrlParameter(parts[0]) ? 0 : 1;
6385
+ const parts = url.split(/[?#]/, 1)[0].split("/").filter(Boolean);
6386
+ const pairStart = !hasPageName && isUrlParameter(parts[0]) ? 0 : 1;
5966
6387
  for (let i = pairStart;i < parts.length - 1; i += 2) {
5967
6388
  const key = parts[i];
5968
6389
  const value = parts[i + 1];
@@ -7060,18 +7481,66 @@ function resolveListUsers(_module, data, compiledTemplate, parse) {
7060
7481
  return itemAst.elements;
7061
7482
  }
7062
7483
  // packages/parser/src/parser/rules/block/module/resolution/data-maps.ts
7063
- async function buildListPagesDataMap(dataProvider, requirements, urlPath) {
7484
+ async function buildListPagesDataMap(dataProvider, requirements, urlPath, paginationState = { nextUnprefixed: 1 }) {
7064
7485
  const dataMap = new Map;
7065
- const urlParams = parseUrlParams(urlPath ?? "");
7486
+ const urlParams = parseUrlParams(urlPath ?? "", true);
7066
7487
  for (const req of requirements) {
7067
7488
  const normalizedQuery = resolveAndNormalizeQuery(req, urlParams);
7068
- const data = await dataProvider.fetchListPages?.(normalizedQuery, req);
7489
+ if (!urlPath || !urlPath.startsWith("/") || urlPath.startsWith("//")) {
7490
+ const data2 = await dataProvider.fetchListPages?.(normalizedQuery, req);
7491
+ if (data2)
7492
+ dataMap.set(req.id, data2);
7493
+ continue;
7494
+ }
7495
+ const ordinal = req.urlAttrPrefix ? 0 : paginationState.nextUnprefixed++;
7496
+ const parameter = req.urlAttrPrefix ? `${encodeURIComponent(req.urlAttrPrefix)}_p` : ordinal === 1 ? "p" : `p${ordinal}`;
7497
+ const perPage = Math.min(250, positiveInteger(normalizedQuery.perPage, 20));
7498
+ const baseOffset = positiveInteger(normalizedQuery.offset, 0);
7499
+ const limit = normalizedQuery.limit !== undefined && normalizedQuery.limit >= 0 ? Math.floor(normalizedQuery.limit) : undefined;
7500
+ const rawPage = urlParams.get(parameter) ?? "1";
7501
+ let currentPage = /^\d+$/.test(rawPage) ? positiveInteger(Number(rawPage), 1) : 1;
7502
+ currentPage = Math.min(currentPage, Math.floor((Number.MAX_SAFE_INTEGER - baseOffset) / perPage) + 1);
7503
+ if (limit !== undefined)
7504
+ currentPage = Math.min(currentPage, Math.max(1, Math.ceil(limit / perPage)));
7505
+ const fetchPage = () => {
7506
+ const relativeOffset = (currentPage - 1) * perPage;
7507
+ return dataProvider.fetchListPages?.({
7508
+ ...normalizedQuery,
7509
+ offset: baseOffset + relativeOffset,
7510
+ limit: Math.min(perPage, limit === undefined ? perPage : Math.max(0, limit - relativeOffset)),
7511
+ perPage
7512
+ }, req);
7513
+ };
7514
+ let data = await fetchPage();
7069
7515
  if (data) {
7070
- dataMap.set(req.id, data);
7516
+ const count = Math.min(Math.max(0, data.totalCount - baseOffset), limit ?? Infinity);
7517
+ const totalPages = Math.ceil(count / perPage);
7518
+ const lastPage = Math.max(1, totalPages);
7519
+ if (currentPage > lastPage) {
7520
+ currentPage = lastPage;
7521
+ data = await fetchPage();
7522
+ }
7523
+ if (data)
7524
+ dataMap.set(req.id, {
7525
+ ...data,
7526
+ pages: data.pages.slice(0, Math.min(perPage, Math.max(0, count - (currentPage - 1) * perPage))),
7527
+ pagination: {
7528
+ currentPage,
7529
+ perPage,
7530
+ totalPages,
7531
+ offset: baseOffset + (currentPage - 1) * perPage,
7532
+ limit,
7533
+ urlPath,
7534
+ parameter
7535
+ }
7536
+ });
7071
7537
  }
7072
7538
  }
7073
7539
  return dataMap;
7074
7540
  }
7541
+ function positiveInteger(value, fallback) {
7542
+ return value !== undefined && Number.isSafeInteger(value) && value > 0 ? value : fallback;
7543
+ }
7075
7544
  async function buildListUsersDataMap(dataProvider, requirements) {
7076
7545
  const dataMap = new Map;
7077
7546
  for (const req of requirements) {
@@ -9063,6 +9532,8 @@ var embedBlockRule = {
9063
9532
  name: "embed-block",
9064
9533
  startTokens: ["BLOCK_OPEN"],
9065
9534
  requiresLineStart: false,
9535
+ preservesPrecedingLineBreak: true,
9536
+ isStartPattern: (ctx, pos) => parseEmbedBlockOpen(ctx, pos) !== null,
9066
9537
  parse(ctx) {
9067
9538
  const openToken = currentToken(ctx);
9068
9539
  if (openToken.type !== "BLOCK_OPEN") {
@@ -9078,38 +9549,30 @@ var embedBlockRule = {
9078
9549
  pos += contentResult.consumed;
9079
9550
  consumed += contentResult.consumed;
9080
9551
  if (!contentResult.foundClose) {
9081
- ctx.diagnostics.push({
9082
- severity: "warning",
9083
- code: "unclosed-block",
9084
- message: `Missing closing tag [[/${openResult.blockName}]] for [[${openResult.blockName}]]`,
9085
- position: openToken.position
9086
- });
9552
+ if (!ctx.diagnostics.some((d) => d.code === "unclosed-block" && d.position === openToken.position))
9553
+ ctx.diagnostics.push({
9554
+ severity: "warning",
9555
+ code: "unclosed-block",
9556
+ message: `Missing closing tag [[/${openResult.blockName}]] for [[${openResult.blockName}]]`,
9557
+ position: openToken.position
9558
+ });
9087
9559
  return { success: false };
9088
9560
  }
9089
9561
  const closeConsumed = consumeEmbedClose(ctx, pos);
9090
9562
  pos += closeConsumed;
9091
9563
  consumed += closeConsumed;
9092
- return {
9093
- success: true,
9094
- elements: [
9095
- {
9096
- element: "container",
9097
- data: {
9098
- type: "paragraph",
9099
- attributes: {},
9100
- elements: [
9101
- {
9102
- element: "embed-block",
9103
- data: {
9104
- contents: contentResult.contents.trim()
9105
- }
9106
- }
9107
- ]
9108
- }
9109
- }
9110
- ],
9111
- consumed
9112
- };
9564
+ const elements = [
9565
+ { element: "embed-block", data: { contents: contentResult.contents.trim() } }
9566
+ ];
9567
+ if (ctx.scope.inlineEnd === undefined && ctx.tokens[pos]?.type !== "NEWLINE" && ctx.tokens[pos]?.type !== "EOF") {
9568
+ const after = parseInlineUntil({ ...ctx, pos }, "PARAGRAPH_BREAK");
9569
+ const tail = normalizeParagraphElements(after.elements);
9570
+ if (tail[0]?.element === "text")
9571
+ tail[0].data = tail[0].data.trimStart();
9572
+ elements.push(...tail);
9573
+ consumed += after.consumed;
9574
+ }
9575
+ return { success: true, elements, consumed };
9113
9576
  }
9114
9577
  };
9115
9578
 
@@ -9423,11 +9886,11 @@ var iftagsRule = {
9423
9886
  };
9424
9887
 
9425
9888
  // packages/parser/src/parser/rules/block/toc/element.ts
9426
- function createTocElement(align) {
9889
+ function createTocElement(align, title) {
9427
9890
  return {
9428
9891
  element: "table-of-contents",
9429
9892
  data: {
9430
- attributes: {},
9893
+ attributes: title === undefined ? {} : { title },
9431
9894
  align
9432
9895
  }
9433
9896
  };
@@ -9470,26 +9933,17 @@ function parseTocOpen(ctx, startPos) {
9470
9933
  } else {
9471
9934
  return null;
9472
9935
  }
9473
- pos = skipUntilClose(ctx, pos);
9936
+ const attributes = parseAttributes(ctx, pos);
9937
+ pos += attributes.consumed;
9474
9938
  if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
9475
9939
  return null;
9476
9940
  }
9477
9941
  return {
9478
9942
  align,
9943
+ title: attributes.attrs.title,
9479
9944
  consumed: pos + 1 - startPos
9480
9945
  };
9481
9946
  }
9482
- function skipUntilClose(ctx, startPos) {
9483
- let pos = startPos;
9484
- while (pos < ctx.tokens.length) {
9485
- const token5 = ctx.tokens[pos];
9486
- if (!token5 || token5.type === "BLOCK_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF") {
9487
- break;
9488
- }
9489
- pos++;
9490
- }
9491
- return pos;
9492
- }
9493
9947
  function isNameToken(token5) {
9494
9948
  return token5?.type === "TEXT" || token5?.type === "IDENTIFIER";
9495
9949
  }
@@ -9513,7 +9967,7 @@ var tocRule = {
9513
9967
  }
9514
9968
  return {
9515
9969
  success: true,
9516
- elements: [createTocElement(openResult.align)],
9970
+ elements: [createTocElement(openResult.align, openResult.title)],
9517
9971
  consumed: openResult.consumed
9518
9972
  };
9519
9973
  }
@@ -10114,6 +10568,31 @@ var blockRules = [
10114
10568
  galleryRule,
10115
10569
  divRule
10116
10570
  ];
10571
+ // packages/parser/src/parser/rules/inline/formatting/close.ts
10572
+ function findFormattingClose(ctx, start, marker) {
10573
+ const table = ctx.scope.tableFormatting;
10574
+ const end = table?.end ?? ctx.scope.inlineEnd ?? ctx.tokens.length;
10575
+ for (let pos = start;pos < end; pos++) {
10576
+ const token5 = ctx.tokens[pos];
10577
+ if (!token5 || token5.type === "EOF" || ctx.scope.blockCloseCondition?.({ ...ctx, pos }))
10578
+ return null;
10579
+ if (!table && token5.type === "NEWLINE" && getParagraphNewlineBoundary(ctx, pos, true).shouldBreak)
10580
+ return null;
10581
+ if (token5.type === marker && !table?.suppressedClosers.has(pos))
10582
+ return pos;
10583
+ const protectedEnd = protectedInlineRegionEnd(ctx.tokens, pos, end);
10584
+ if (protectedEnd > pos)
10585
+ pos = protectedEnd - 1;
10586
+ }
10587
+ return null;
10588
+ }
10589
+ function consumeFormattingClose(ctx, close, contentEnd) {
10590
+ if (close === contentEnd)
10591
+ return 1;
10592
+ ctx.scope.tableFormatting?.suppressedClosers.add(close);
10593
+ return 0;
10594
+ }
10595
+
10117
10596
  // packages/parser/src/parser/rules/inline/formatting/container.ts
10118
10597
  function createInlineContainer(type, elements) {
10119
10598
  return {
@@ -10125,9 +10604,10 @@ function createInlineContainer(type, elements) {
10125
10604
  }
10126
10605
  };
10127
10606
  }
10128
- function parseSameLineDelimitedContainer(ctx, closeToken, type, options = {}) {
10607
+ function parseDelimitedContainer(ctx, closeToken, type, options = {}) {
10129
10608
  const startToken = currentToken(ctx);
10130
- if (!hasClosingMarkerBeforeNewline({ ...ctx, pos: ctx.pos + 1 }, closeToken)) {
10609
+ const close = findFormattingClose(ctx, ctx.pos + 1, closeToken);
10610
+ if (close === null) {
10131
10611
  return {
10132
10612
  success: true,
10133
10613
  elements: [{ element: "text", data: startToken.value }],
@@ -10135,7 +10615,7 @@ function parseSameLineDelimitedContainer(ctx, closeToken, type, options = {}) {
10135
10615
  };
10136
10616
  }
10137
10617
  const result = parseInlineUntil({ ...ctx, pos: ctx.pos + 1 }, closeToken);
10138
- const consumed = 1 + result.consumed + 1;
10618
+ const consumed = 1 + result.consumed + consumeFormattingClose(ctx, close, ctx.pos + 1 + result.consumed);
10139
10619
  if (options.discardEmpty === true && result.elements.length === 0) {
10140
10620
  return {
10141
10621
  success: true,
@@ -10155,7 +10635,7 @@ var boldRule = {
10155
10635
  name: "bold",
10156
10636
  startTokens: ["BOLD_MARKER"],
10157
10637
  parse(ctx) {
10158
- return parseSameLineDelimitedContainer(ctx, "BOLD_MARKER", "bold", { discardEmpty: true });
10638
+ return parseDelimitedContainer(ctx, "BOLD_MARKER", "bold", { discardEmpty: true });
10159
10639
  }
10160
10640
  };
10161
10641
 
@@ -10164,102 +10644,28 @@ var italicRule = {
10164
10644
  name: "italic",
10165
10645
  startTokens: ["ITALIC_MARKER"],
10166
10646
  parse(ctx) {
10167
- return parseSameLineDelimitedContainer(ctx, "ITALIC_MARKER", "italics");
10647
+ return parseDelimitedContainer(ctx, "ITALIC_MARKER", "italics");
10168
10648
  }
10169
10649
  };
10170
10650
 
10171
- // packages/parser/src/parser/rules/inline/underline/child.ts
10172
- function parseUnderlineChild(ctx, pos) {
10173
- const token5 = ctx.tokens[pos];
10174
- if (!token5) {
10175
- return { elements: [], consumed: 0 };
10176
- }
10177
- if (token5.type === "NEWLINE") {
10178
- return { elements: [{ element: "line-break" }], consumed: 1 };
10179
- }
10180
- const result = parseInlineUntil({ ...ctx, pos }, "UNDERLINE_MARKER");
10181
- if (result.elements.length > 0) {
10182
- return { elements: result.elements, consumed: result.consumed };
10183
- }
10184
- return { elements: [{ element: "text", data: token5.value }], consumed: 1 };
10185
- }
10186
-
10187
- // packages/parser/src/parser/rules/inline/underline/content.ts
10188
- function parseUnderlineContent(ctx, startPos) {
10189
- const children = [];
10190
- let pos = startPos;
10191
- let consumed = 1;
10192
- while (pos < ctx.tokens.length) {
10193
- const token5 = ctx.tokens[pos];
10194
- if (!token5 || token5.type === "EOF")
10195
- break;
10196
- if (token5.type === "UNDERLINE_MARKER") {
10197
- consumed++;
10198
- break;
10199
- }
10200
- const child = parseUnderlineChild(ctx, pos);
10201
- children.push(...child.elements);
10202
- pos += child.consumed;
10203
- consumed += child.consumed;
10204
- }
10205
- return { children, consumed };
10206
- }
10207
-
10208
10651
  // packages/parser/src/parser/rules/inline/underline/index.ts
10209
10652
  var underlineRule = {
10210
10653
  name: "underline",
10211
10654
  startTokens: ["UNDERLINE_MARKER"],
10212
10655
  parse(ctx) {
10213
- const startToken = currentToken(ctx);
10214
- if (!hasClosingMarkerBeforeParagraphBreak({ ...ctx, pos: ctx.pos + 1 }, "UNDERLINE_MARKER")) {
10215
- return {
10216
- success: true,
10217
- elements: [{ element: "text", data: startToken.value }],
10218
- consumed: 1
10219
- };
10220
- }
10221
- const { children, consumed } = parseUnderlineContent(ctx, ctx.pos + 1);
10222
- if (children.length === 0) {
10223
- return {
10224
- success: true,
10225
- elements: [],
10226
- consumed
10227
- };
10228
- }
10229
- return {
10230
- success: true,
10231
- elements: [createInlineContainer("underline", children)],
10232
- consumed
10233
- };
10656
+ return parseDelimitedContainer(ctx, "UNDERLINE_MARKER", "underline", { discardEmpty: true });
10234
10657
  }
10235
10658
  };
10236
10659
 
10237
10660
  // packages/parser/src/parser/rules/inline/strikethrough/parse.ts
10238
10661
  function parseStrikethroughContent(ctx) {
10239
- const result = parseInlineUntil({ ...ctx, pos: ctx.pos + 1 }, "STRIKE_MARKER");
10240
- return {
10241
- success: true,
10242
- elements: [createInlineContainer("strikethrough", result.elements)],
10243
- consumed: 1 + result.consumed + 1
10244
- };
10662
+ return parseDelimitedContainer(ctx, "STRIKE_MARKER", "strikethrough");
10245
10663
  }
10246
10664
 
10247
10665
  // packages/parser/src/parser/rules/inline/strikethrough/syntax.ts
10248
10666
  function hasValidStrikethroughClose(ctx) {
10249
- let pos = ctx.pos + 1;
10250
- let prevWasWhitespace = false;
10251
- while (pos < ctx.tokens.length) {
10252
- const token5 = ctx.tokens[pos];
10253
- if (!token5 || token5.type === "NEWLINE" || token5.type === "EOF") {
10254
- return false;
10255
- }
10256
- if (token5.type === "STRIKE_MARKER") {
10257
- return !prevWasWhitespace;
10258
- }
10259
- prevWasWhitespace = token5.type === "WHITESPACE";
10260
- pos++;
10261
- }
10262
- return false;
10667
+ const close = findFormattingClose(ctx, ctx.pos + 1, "STRIKE_MARKER");
10668
+ return close !== null && close > ctx.pos + 1 && ctx.tokens[close - 1]?.type !== "WHITESPACE";
10263
10669
  }
10264
10670
 
10265
10671
  // packages/parser/src/parser/rules/inline/strikethrough/index.ts
@@ -10283,7 +10689,7 @@ var superscriptRule = {
10283
10689
  name: "superscript",
10284
10690
  startTokens: ["SUPER_MARKER"],
10285
10691
  parse(ctx) {
10286
- return parseSameLineDelimitedContainer(ctx, "SUPER_MARKER", "superscript", {
10692
+ return parseDelimitedContainer(ctx, "SUPER_MARKER", "superscript", {
10287
10693
  discardEmpty: true
10288
10694
  });
10289
10695
  }
@@ -10294,7 +10700,7 @@ var subscriptRule = {
10294
10700
  name: "subscript",
10295
10701
  startTokens: ["SUB_MARKER"],
10296
10702
  parse(ctx) {
10297
- return parseSameLineDelimitedContainer(ctx, "SUB_MARKER", "subscript", { discardEmpty: true });
10703
+ return parseDelimitedContainer(ctx, "SUB_MARKER", "subscript", { discardEmpty: true });
10298
10704
  }
10299
10705
  };
10300
10706
 
@@ -10303,7 +10709,7 @@ var monospaceRule = {
10303
10709
  name: "monospace",
10304
10710
  startTokens: ["MONO_MARKER"],
10305
10711
  parse(ctx) {
10306
- return parseSameLineDelimitedContainer(ctx, "MONO_CLOSE", "monospace");
10712
+ return parseDelimitedContainer(ctx, "MONO_CLOSE", "monospace");
10307
10713
  }
10308
10714
  };
10309
10715
 
@@ -10664,13 +11070,14 @@ var linkStarRule = {
10664
11070
 
10665
11071
  // packages/parser/src/parser/rules/inline/color/syntax.ts
10666
11072
  function parseColorContent(ctx) {
10667
- if (!hasClosingMarkerBeforeNewline({ ...ctx, pos: ctx.pos + 1 }, "COLOR_MARKER")) {
11073
+ const close = findFormattingClose(ctx, ctx.pos + 1, "COLOR_MARKER");
11074
+ if (close === null) {
10668
11075
  return null;
10669
11076
  }
10670
11077
  let pos = ctx.pos + 1;
10671
11078
  let consumed = 1;
10672
11079
  let colorSpec = "";
10673
- while (pos < ctx.tokens.length) {
11080
+ while (pos < (ctx.scope.inlineEnd ?? ctx.tokens.length)) {
10674
11081
  const token5 = ctx.tokens[pos];
10675
11082
  if (!token5 || token5.type === "PIPE" || token5.type === "COLOR_MARKER" || token5.type === "NEWLINE" || token5.type === "EOF") {
10676
11083
  break;
@@ -10687,14 +11094,11 @@ function parseColorContent(ctx) {
10687
11094
  const contentResult = parseInlineUntil({ ...ctx, pos }, "COLOR_MARKER");
10688
11095
  pos += contentResult.consumed;
10689
11096
  consumed += contentResult.consumed;
10690
- if (ctx.tokens[pos]?.type !== "COLOR_MARKER") {
10691
- return null;
10692
- }
10693
- consumed++;
10694
11097
  const color = colorSpec.trim();
10695
11098
  if (color === "" || contentResult.elements.length === 0) {
10696
11099
  return null;
10697
11100
  }
11101
+ consumed += consumeFormattingClose(ctx, close, pos);
10698
11102
  return {
10699
11103
  color: hexifyColor(color),
10700
11104
  elements: contentResult.elements,
@@ -11317,14 +11721,19 @@ function parseSpanContent(ctx, startPos, blockName) {
11317
11721
  const escapedChildren = [];
11318
11722
  const splitSpans = [];
11319
11723
  let foundClose = false;
11724
+ let forcedClose = false;
11320
11725
  let afterBlankLine = false;
11321
11726
  let consumed = 0;
11322
11727
  let pos = startPos;
11323
- while (pos < ctx.tokens.length) {
11728
+ while (pos < (ctx.scope.inlineEnd ?? ctx.tokens.length)) {
11324
11729
  const token5 = ctx.tokens[pos];
11325
11730
  if (!token5 || token5.type === "EOF") {
11326
11731
  break;
11327
11732
  }
11733
+ if (ctx.scope.tableFormatting?.suppressedClosers.has(pos)) {
11734
+ forcedClose = true;
11735
+ break;
11736
+ }
11328
11737
  const close = parseCloseSpan(ctx, pos);
11329
11738
  if (close.success) {
11330
11739
  pos += close.consumed;
@@ -11349,6 +11758,29 @@ function parseSpanContent(ctx, startPos, blockName) {
11349
11758
  pos += parsed.consumed;
11350
11759
  consumed += parsed.consumed;
11351
11760
  }
11761
+ if (!foundClose && ctx.scope.tableFormatting && (pos === ctx.scope.inlineEnd || forcedClose)) {
11762
+ let depth = 0;
11763
+ for (let next = pos;next < ctx.scope.tableFormatting.end; next++) {
11764
+ const rawEnd = rawRegionEnd(ctx.tokens, next, ctx.scope.tableFormatting.end);
11765
+ if (rawEnd > next) {
11766
+ next = rawEnd - 1;
11767
+ continue;
11768
+ }
11769
+ if (ctx.tokens[next]?.type === "BLOCK_OPEN" && /^span_?$/i.test(ctx.tokens[next + 1]?.value ?? ""))
11770
+ depth++;
11771
+ const close = parseCloseSpan(ctx, next);
11772
+ if (!close.success)
11773
+ continue;
11774
+ if (depth > 0) {
11775
+ depth--;
11776
+ continue;
11777
+ }
11778
+ for (let offset = 0;offset < close.consumed; offset++)
11779
+ ctx.scope.tableFormatting.suppressedClosers.add(next + offset);
11780
+ foundClose = true;
11781
+ break;
11782
+ }
11783
+ }
11352
11784
  return { children, escapedChildren, splitSpans, consumed, foundClose };
11353
11785
  }
11354
11786
  function parseOneSpanChild(ctx, pos, targetChildren) {
@@ -11760,135 +12192,6 @@ var footnoteRule = {
11760
12192
  }
11761
12193
  };
11762
12194
 
11763
- // packages/parser/src/parser/rules/inline/image/attributes.ts
11764
- function parseImageAttributes(ctx, startPos) {
11765
- const result = parseAttributesRaw(ctx, startPos, false);
11766
- const link = result.attrs.link ?? null;
11767
- const { link: _link, ...htmlAttributes } = result.attrs;
11768
- return {
11769
- attributes: filterUnsafeAttributes(htmlAttributes),
11770
- link,
11771
- consumed: result.consumed
11772
- };
11773
- }
11774
-
11775
- // packages/parser/src/parser/rules/inline/image/body.ts
11776
- function parseImageSourceText(ctx, startPos) {
11777
- let pos = startPos;
11778
- let consumed = 0;
11779
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
11780
- pos++;
11781
- consumed++;
11782
- }
11783
- let sourceText = "";
11784
- while (pos < ctx.tokens.length) {
11785
- const token5 = ctx.tokens[pos];
11786
- if (!token5 || token5.type === "WHITESPACE" || token5.type === "BLOCK_CLOSE" || token5.type === "NEWLINE" || token5.type === "EOF") {
11787
- break;
11788
- }
11789
- sourceText += token5.value;
11790
- pos++;
11791
- consumed++;
11792
- }
11793
- return { sourceText, consumed };
11794
- }
11795
-
11796
- // packages/parser/src/parser/rules/inline/image/syntax.ts
11797
- var IMAGE_BLOCK_NAMES = new Set([
11798
- "image",
11799
- "=image",
11800
- "<image",
11801
- ">image",
11802
- "f<image",
11803
- "f>image",
11804
- "f=image"
11805
- ]);
11806
- function parseImageBlockName(ctx, startPos) {
11807
- let pos = startPos;
11808
- let consumed = 0;
11809
- while (ctx.tokens[pos]?.type === "WHITESPACE") {
11810
- pos++;
11811
- consumed++;
11812
- }
11813
- const prefixResult = parseImagePrefix(ctx, pos);
11814
- const prefix = prefixResult.prefix;
11815
- pos += prefixResult.consumed;
11816
- consumed += prefixResult.consumed;
11817
- const nameToken = ctx.tokens[pos];
11818
- if (!nameToken || nameToken.type !== "TEXT" && nameToken.type !== "IDENTIFIER") {
11819
- return null;
11820
- }
11821
- return { name: prefix + nameToken.value.toLowerCase(), consumed: consumed + 1 };
11822
- }
11823
- function isImageBlockName(blockName) {
11824
- return IMAGE_BLOCK_NAMES.has(blockName);
11825
- }
11826
- function parseImagePrefix(ctx, pos) {
11827
- const token5 = ctx.tokens[pos];
11828
- if (token5?.type === "EQUALS") {
11829
- return { prefix: "=", consumed: 1 };
11830
- }
11831
- if (token5?.type === "TEXT" && token5.value === "<") {
11832
- return { prefix: "<", consumed: 1 };
11833
- }
11834
- if ((token5?.type === "TEXT" || token5?.type === "BLOCKQUOTE_MARKER") && token5.value === ">") {
11835
- return { prefix: ">", consumed: 1 };
11836
- }
11837
- if (token5?.type === "IDENTIFIER" && token5.value.toLowerCase() === "f") {
11838
- return parseFloatPrefix(ctx, pos + 1);
11839
- }
11840
- return { prefix: "", consumed: 0 };
11841
- }
11842
- function parseFloatPrefix(ctx, pos) {
11843
- const token5 = ctx.tokens[pos];
11844
- if (token5?.type === "TEXT" && token5.value === "<") {
11845
- return { prefix: "f<", consumed: 2 };
11846
- }
11847
- if ((token5?.type === "TEXT" || token5?.type === "BLOCKQUOTE_MARKER") && token5.value === ">") {
11848
- return { prefix: "f>", consumed: 2 };
11849
- }
11850
- if (token5?.type === "EQUALS") {
11851
- return { prefix: "f=", consumed: 2 };
11852
- }
11853
- return { prefix: "", consumed: 0 };
11854
- }
11855
-
11856
- // packages/parser/src/parser/rules/inline/image/open.ts
11857
- function parseImageOpen(ctx) {
11858
- if (ctx.tokens[ctx.pos]?.type !== "BLOCK_OPEN") {
11859
- return null;
11860
- }
11861
- let pos = ctx.pos + 1;
11862
- let consumed = 1;
11863
- const nameResult = parseImageBlockName(ctx, pos);
11864
- if (!nameResult || !isImageBlockName(nameResult.name)) {
11865
- return null;
11866
- }
11867
- pos += nameResult.consumed;
11868
- consumed += nameResult.consumed;
11869
- const sourceText = parseImageSourceText(ctx, pos);
11870
- pos += sourceText.consumed;
11871
- consumed += sourceText.consumed;
11872
- const attrs = parseImageAttributes(ctx, pos);
11873
- pos += attrs.consumed;
11874
- consumed += attrs.consumed;
11875
- if (ctx.tokens[pos]?.type !== "BLOCK_CLOSE") {
11876
- return null;
11877
- }
11878
- pos++;
11879
- consumed++;
11880
- if (!sourceText.sourceText) {
11881
- return null;
11882
- }
11883
- return {
11884
- blockName: nameResult.name,
11885
- sourceText: sourceText.sourceText,
11886
- link: attrs.link,
11887
- attributes: attrs.attributes,
11888
- consumed
11889
- };
11890
- }
11891
-
11892
12195
  // packages/parser/src/parser/rules/inline/image/source.ts
11893
12196
  function parseImageSource(src) {
11894
12197
  if (src.startsWith("http://") || src.startsWith("https://") || src.startsWith("/")) {
@@ -12939,6 +13242,7 @@ var inlineRules = [
12939
13242
  htmlInlineRule,
12940
13243
  rawRule,
12941
13244
  imageRule,
13245
+ embedBlockRule,
12942
13246
  sizeRule,
12943
13247
  footnoteRule,
12944
13248
  spanRule,
@@ -14109,7 +14413,7 @@ async function resolveModulesWithAsyncParse(ast, dataProvider, options) {
14109
14413
  registry.register(ast);
14110
14414
  const parse2 = async (source) => registry.register(await options.parse(source));
14111
14415
  const [listPagesData, listUsersData, tagCloudData] = await Promise.all([
14112
- buildListPagesDataMap(dataProvider, options.requirements.listPages ?? [], options.urlPath),
14416
+ buildListPagesDataMap(dataProvider, options.requirements.listPages ?? [], options.urlPath, options.paginationState),
14113
14417
  buildListUsersDataMap(dataProvider, options.requirements.listUsers ?? []),
14114
14418
  buildTagCloudDataMap(dataProvider, options.requirements.tagCloud ?? [])
14115
14419
  ]);
@@ -14197,9 +14501,9 @@ async function resolveListPagesAsync(module, data, template, parse2) {
14197
14501
  continue;
14198
14502
  const variableContext = {
14199
14503
  page,
14200
- index: i + 1,
14504
+ index: (data.pagination?.offset ?? 0) + i + 1,
14201
14505
  total: data.totalCount,
14202
- limit: module.limit,
14506
+ limit: data.pagination ? data.pagination.limit : module.limit,
14203
14507
  site: data.site
14204
14508
  };
14205
14509
  const parsed = await parse2(template(variableContext));
@@ -14219,6 +14523,7 @@ async function resolveListPagesAsync(module, data, template, parse2) {
14219
14523
  if (module["append-line"] && !module.separate) {
14220
14524
  result.push(...(await parse2(module["append-line"])).elements);
14221
14525
  }
14526
+ result.push(...createListPagesPager(data));
14222
14527
  return module.wrapper ? [
14223
14528
  {
14224
14529
  element: "container",
@@ -14319,6 +14624,9 @@ async function processWikitext(source, options) {
14319
14624
  const dataProvider = createModuleDataProvider(options, callbackContext);
14320
14625
  const parseFragment = parseSource2;
14321
14626
  let ast = initial.ast;
14627
+ const paginationState = { nextUnprefixed: 1 };
14628
+ const requestedPath = options.page.urlPath;
14629
+ const urlPath = !requestedPath || /^\/(?:[?#]|$)/.test(requestedPath) ? `/${options.page.fullName}${requestedPath?.slice(1) ?? ""}` : requestedPath;
14322
14630
  for (let pass = 0;pass < DEFAULT_MODULE_MAX_PASSES; pass++) {
14323
14631
  const extraction = extractDataRequirements(ast);
14324
14632
  if (pass > 0 && !hasResolvableRequirements(extraction.requirements, options.dataProvider)) {
@@ -14329,7 +14637,8 @@ async function processWikitext(source, options) {
14329
14637
  compiledListPagesTemplates: extraction.compiledListPagesTemplates,
14330
14638
  compiledListUsersTemplates: extraction.compiledListUsersTemplates,
14331
14639
  requirements: extraction.requirements,
14332
- urlPath: options.page.urlPath,
14640
+ urlPath,
14641
+ paginationState,
14333
14642
  pageTags: options.page.tags
14334
14643
  });
14335
14644
  ast = resolved.ast;
@@ -14431,6 +14740,7 @@ export {
14431
14740
  container,
14432
14741
  compileTemplate,
14433
14742
  compileListUsersTemplate,
14743
+ buildInfo,
14434
14744
  bold,
14435
14745
  STYLE_SLOT_PREFIX2 as STYLE_SLOT_PREFIX,
14436
14746
  Parser,