stream-markdown-parser 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10898,6 +10898,18 @@ function parseLooseInlineAttrs(input) {
10898
10898
  }
10899
10899
  return out;
10900
10900
  }
10901
+ function offsetTokenMaps(tokens, lineOffset, maxEndLine) {
10902
+ for (const token of tokens) {
10903
+ const mappedToken = token;
10904
+ const map$1 = mappedToken.map;
10905
+ if (Array.isArray(map$1) && map$1.length >= 2) {
10906
+ const startLine = Number(map$1[0]);
10907
+ const endLine = Number(map$1[1]);
10908
+ if (Number.isFinite(startLine) && Number.isFinite(endLine)) mappedToken.map = [startLine + lineOffset, Math.min(endLine + lineOffset, maxEndLine)];
10909
+ }
10910
+ if (Array.isArray(mappedToken.children)) offsetTokenMaps(mappedToken.children, lineOffset, maxEndLine);
10911
+ }
10912
+ }
10901
10913
  function applyContainers(md) {
10902
10914
  [
10903
10915
  "admonition",
@@ -10962,6 +10974,7 @@ function applyContainers(md) {
10962
10974
  if (!found) nextLine = endLine;
10963
10975
  const tokenOpen = s.push("vmr_container_open", "div", 1);
10964
10976
  tokenOpen.attrSet("class", `vmr-container vmr-container-${name}`);
10977
+ tokenOpen.map = [startLine, found ? nextLine : endLine];
10965
10978
  tokenOpen.meta = {
10966
10979
  ...tokenOpen.meta ?? {},
10967
10980
  unclosed: !found && !envFinal
@@ -10995,6 +11008,7 @@ function applyContainers(md) {
10995
11008
  if (prevToken) prevToken.raw = innerSrc;
10996
11009
  const innerTokens = [];
10997
11010
  s.md.block.parse(innerSrc, s.md, s.env, innerTokens);
11011
+ offsetTokenMaps(innerTokens, startLine + 1, startLine + 1 + contentLines.length);
10998
11012
  s.tokens.push(...innerTokens);
10999
11013
  }
11000
11014
  if (found) s.push("vmr_container_close", "div", -1);
@@ -13507,6 +13521,7 @@ const SPAN_CURLY_RE = /span\{([^}]+)\}/;
13507
13521
  const OPERATORNAME_SPAN_RE = /\\operatorname\{span\}\{((?:[^{}]|\{[^}]*\})+)\}/;
13508
13522
  const SINGLE_BACKSLASH_NEWLINE_RE = /(^|[^\\])\\\r?\n/g;
13509
13523
  const ENDING_SINGLE_BACKSLASH_RE = /(^|[^\\])\\$/g;
13524
+ const FACTORIAL_PRECEDING_RE = /[\p{L}\p{M}\p{N}\p{Pe}\p{Pf}'′″‴|‖]/u;
13510
13525
  const DEFAULT_MATH_RE = new RegExp(`(${CONTROL_CHARS_CLASS})|(${ESCAPED_KATEX_COMMANDS})\\b`, "g");
13511
13526
  const MATH_RE_CACHE = /* @__PURE__ */ new Map();
13512
13527
  const BRACE_CMD_RE_CACHE = /* @__PURE__ */ new Map();
@@ -13546,6 +13561,13 @@ function countUnescapedStrong(s) {
13546
13561
  while (re.exec(s) !== null) c++;
13547
13562
  return c;
13548
13563
  }
13564
+ function escapeStandaloneExclamation(value) {
13565
+ return value.replace(/(^|[^\\])!+/gu, (match, prefix) => {
13566
+ if (prefix && FACTORIAL_PRECEDING_RE.test(prefix)) return match;
13567
+ const marks = prefix ? match.slice(prefix.length) : match;
13568
+ return `${prefix}${"\\!".repeat(marks.length)}`;
13569
+ });
13570
+ }
13549
13571
  function findLastUnescapedStrongMarker(s) {
13550
13572
  const re = /(^|[^\\])(__|\*\*)/g;
13551
13573
  let m;
@@ -13570,7 +13592,7 @@ function normalizeStandaloneBackslashT(s, opts) {
13570
13592
  }
13571
13593
  return m;
13572
13594
  });
13573
- if (escapeExclamation) out = out.replace(/(^|[^\\])!/g, "$1\\!");
13595
+ if (escapeExclamation) out = escapeStandaloneExclamation(out);
13574
13596
  let result = out;
13575
13597
  const braceCmdRe = getBraceCmdRegex(useDefault, useDefault ? void 0 : commands);
13576
13598
  result = result.replace(braceCmdRe, (_m, p1, p2) => `${p1}\\${p2}{`);
@@ -16862,6 +16884,74 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
16862
16884
  return result;
16863
16885
  }
16864
16886
 
16887
+ //#endregion
16888
+ //#region src/parser/node-source-map.ts
16889
+ function mapSourceLineRange(startLine, endLine, options) {
16890
+ const mapper = options?.__sourceLineMapper;
16891
+ if (!mapper) return {
16892
+ startLine,
16893
+ endLine
16894
+ };
16895
+ const mappedStartRange = mapper(startLine);
16896
+ const mappedEndLine = endLine > startLine ? mapper(endLine - 1).endLine : mapper(endLine).startLine;
16897
+ return {
16898
+ startLine: mappedStartRange.startLine,
16899
+ endLine: Math.max(mappedStartRange.startLine, mappedEndLine)
16900
+ };
16901
+ }
16902
+ function lineAtOffset(source, offset) {
16903
+ const target = Math.max(0, Math.min(source.length, Math.trunc(offset)));
16904
+ let line = 0;
16905
+ for (let i = 0; i < target; i++) if (source[i] === "\n") line++;
16906
+ return line;
16907
+ }
16908
+ function sourceLineRangeFromOffsets(source, start, end) {
16909
+ const startIndex = Math.max(0, Math.min(source.length, Math.trunc(start)));
16910
+ const endIndex = Math.max(startIndex, Math.min(source.length, Math.trunc(end)));
16911
+ const startLine = lineAtOffset(source, startIndex);
16912
+ let endLine = lineAtOffset(source, endIndex);
16913
+ if (endIndex > startIndex && source[endIndex - 1] !== "\n") endLine++;
16914
+ return {
16915
+ startLine,
16916
+ endLine
16917
+ };
16918
+ }
16919
+ function createSourceMapFromOffsets(source, start, end, options) {
16920
+ const range = sourceLineRangeFromOffsets(source, start, end);
16921
+ return mapSourceLineRange(range.startLine, range.endLine, options);
16922
+ }
16923
+ function readSourceMap(token, options) {
16924
+ const map$1 = token?.map;
16925
+ if (!Array.isArray(map$1) || map$1.length < 2) return null;
16926
+ const startLine = Number(map$1[0]);
16927
+ const endLine = Number(map$1[1]);
16928
+ if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) return null;
16929
+ return mapSourceLineRange(startLine, endLine, options);
16930
+ }
16931
+ function applyNodeSourceMap(node, token, options) {
16932
+ if (!options?.includeSourceMap) return node;
16933
+ const sourceMap = readSourceMap(token, options);
16934
+ if (!sourceMap) return node;
16935
+ node.sourceMap = sourceMap;
16936
+ if (node.type === "code_block") {
16937
+ const codeNode = node;
16938
+ codeNode.startLine = sourceMap.startLine;
16939
+ codeNode.endLine = sourceMap.endLine;
16940
+ }
16941
+ return node;
16942
+ }
16943
+ function applyNodeSourceMapRange(node, token, endLine, options) {
16944
+ if (!options?.includeSourceMap) return node;
16945
+ const map$1 = token?.map;
16946
+ if (!Array.isArray(map$1) || map$1.length < 2) return node;
16947
+ const startLine = Number(map$1[0]);
16948
+ const tokenEndLine = Number(map$1[1]);
16949
+ const rangeEndLine = Number(endLine);
16950
+ if (!Number.isFinite(startLine) || !Number.isFinite(tokenEndLine) || !Number.isFinite(rangeEndLine)) return node;
16951
+ node.sourceMap = mapSourceLineRange(startLine, Math.max(tokenEndLine, rangeEndLine), options);
16952
+ return node;
16953
+ }
16954
+
16865
16955
  //#endregion
16866
16956
  //#region src/parser/node-parsers/list-parser.ts
16867
16957
  function trimInlineTokenTail(token) {
@@ -16946,11 +17036,13 @@ function parseList(tokens, index, options) {
16946
17036
  trimInlineTokenTail(contentToken);
16947
17037
  }
16948
17038
  const paragraphRaw = String(contentToken.content ?? "");
16949
- itemChildren.push({
17039
+ const paragraphNode = {
16950
17040
  type: "paragraph",
16951
17041
  children: parseInlineTokens(contentToken.children || [], paragraphRaw, preToken, linkifyContext.options()),
16952
17042
  raw: paragraphRaw
16953
- });
17043
+ };
17044
+ if (options?.includeSourceMap) applyNodeSourceMap(paragraphNode, tokens[k], options);
17045
+ itemChildren.push(paragraphNode);
16954
17046
  linkifyContext.remember(paragraphRaw);
16955
17047
  k += 3;
16956
17048
  } else if (tokens[k].type === "blockquote_open") {
@@ -16971,14 +17063,16 @@ function parseList(tokens, index, options) {
16971
17063
  k = handled[1];
16972
17064
  } else k += 1;
16973
17065
  }
16974
- listItems.push({
17066
+ const listItemNode = {
16975
17067
  type: "list_item",
16976
17068
  children: itemChildren,
16977
17069
  raw: itemChildren.map((child) => child.raw).join("")
16978
- });
17070
+ };
17071
+ if (options?.includeSourceMap) applyNodeSourceMap(listItemNode, tokens[j], options);
17072
+ listItems.push(listItemNode);
16979
17073
  j = k + 1;
16980
17074
  } else j += 1;
16981
- return [{
17075
+ const listNode = {
16982
17076
  type: "list",
16983
17077
  ordered: token.type === "ordered_list_open",
16984
17078
  start: (() => {
@@ -16992,7 +17086,9 @@ function parseList(tokens, index, options) {
16992
17086
  })(),
16993
17087
  items: listItems,
16994
17088
  raw: listItems.map((item) => item.raw).join("\n")
16995
- }, j + 1];
17089
+ };
17090
+ if (options?.includeSourceMap) applyNodeSourceMap(listNode, token, options);
17091
+ return [listNode, j + 1];
16996
17092
  }
16997
17093
 
16998
17094
  //#endregion
@@ -17011,17 +17107,20 @@ function parseAdmonition(tokens, index, match, options) {
17011
17107
  children: parseInlineTokens(contentToken.children || [], String(contentToken.content ?? ""), void 0, linkifyContext.options()),
17012
17108
  raw: String(contentToken.content ?? "")
17013
17109
  };
17110
+ if (options?.includeSourceMap) applyNodeSourceMap(paragraphNode, tokens[j], options);
17014
17111
  admonitionChildren.push(paragraphNode);
17015
17112
  linkifyContext.remember(paragraphNode.raw);
17016
17113
  }
17017
17114
  j += 3;
17018
17115
  } else if (tokens[j].type === "bullet_list_open" || tokens[j].type === "ordered_list_open") {
17019
17116
  const [listNode, newIndex] = parseList(tokens, j, linkifyContext.options());
17117
+ if (options?.includeSourceMap) applyNodeSourceMap(listNode, tokens[j], options);
17020
17118
  admonitionChildren.push(listNode);
17021
17119
  linkifyContext.remember(listNode.raw);
17022
17120
  j = newIndex;
17023
17121
  } else if (tokens[j].type === "blockquote_open") {
17024
17122
  const [blockquoteNode, newIndex] = parseBlockquote(tokens, j, linkifyContext.options());
17123
+ if (options?.includeSourceMap) applyNodeSourceMap(blockquoteNode, tokens[j], options);
17025
17124
  admonitionChildren.push(blockquoteNode);
17026
17125
  linkifyContext.remember(blockquoteNode.raw);
17027
17126
  j = newIndex;
@@ -17109,17 +17208,20 @@ function parseContainer(tokens, index, options) {
17109
17208
  children: parseInlineTokens((i !== -1 ? childrenArr.slice(0, i) : childrenArr) || [], void 0, void 0, linkifyContext.options()),
17110
17209
  raw: String(contentToken.content ?? "").replace(/\n:+$/, "").replace(/\n\s*:::\s*$/, "")
17111
17210
  };
17211
+ if (options?.includeSourceMap) applyNodeSourceMap(paragraphNode, tokens[j], options);
17112
17212
  children.push(paragraphNode);
17113
17213
  linkifyContext.remember(paragraphNode.raw);
17114
17214
  }
17115
17215
  j += 3;
17116
17216
  } else if (tokens[j].type === "bullet_list_open" || tokens[j].type === "ordered_list_open") {
17117
17217
  const [listNode, newIndex] = parseList(tokens, j, linkifyContext.options());
17218
+ if (options?.includeSourceMap) applyNodeSourceMap(listNode, tokens[j], options);
17118
17219
  children.push(listNode);
17119
17220
  linkifyContext.remember(listNode.raw);
17120
17221
  j = newIndex;
17121
17222
  } else if (tokens[j].type === "blockquote_open") {
17122
17223
  const [blockquoteNode, newIndex] = parseBlockquote(tokens, j, linkifyContext.options());
17224
+ if (options?.includeSourceMap) applyNodeSourceMap(blockquoteNode, tokens[j], options);
17123
17225
  children.push(blockquoteNode);
17124
17226
  linkifyContext.remember(blockquoteNode.raw);
17125
17227
  j = newIndex;
@@ -17161,49 +17263,55 @@ function parseBlockquote(tokens, index, options) {
17161
17263
  const blockquoteChildren = [];
17162
17264
  const linkifyContext = createLinkifyDemotionContextTracker(options, true);
17163
17265
  let j = index + 1;
17164
- while (j < tokens.length && tokens[j].type !== "blockquote_close") switch (tokens[j].type) {
17165
- case "paragraph_open": {
17166
- const contentToken = tokens[j + 1];
17167
- const paragraphNode = {
17168
- type: "paragraph",
17169
- children: parseInlineTokens(contentToken.children || [], String(contentToken.content ?? ""), void 0, linkifyContext.options()),
17170
- raw: String(contentToken.content ?? "")
17171
- };
17172
- blockquoteChildren.push(paragraphNode);
17173
- linkifyContext.remember(paragraphNode.raw);
17174
- j += 3;
17175
- break;
17176
- }
17177
- case "bullet_list_open":
17178
- case "ordered_list_open": {
17179
- const [listNode, newIndex] = parseList(tokens, j, linkifyContext.options());
17180
- blockquoteChildren.push(listNode);
17181
- linkifyContext.remember(listNode.raw);
17182
- j = newIndex;
17183
- break;
17184
- }
17185
- case "blockquote_open": {
17186
- const [nestedBlockquote, newIndex] = parseBlockquote(tokens, j, linkifyContext.options());
17187
- blockquoteChildren.push(nestedBlockquote);
17188
- linkifyContext.remember(nestedBlockquote.raw);
17189
- j = newIndex;
17190
- break;
17191
- }
17192
- default: {
17193
- const handled = parseCommonBlockToken(tokens, j, linkifyContext.options(), containerTokenHandlers);
17194
- if (handled) {
17195
- blockquoteChildren.push(handled[0]);
17196
- linkifyContext.remember(handled[0].raw);
17197
- j = handled[1];
17198
- } else j++;
17199
- break;
17266
+ while (j < tokens.length && tokens[j].type !== "blockquote_close") {
17267
+ const token = tokens[j];
17268
+ switch (token.type) {
17269
+ case "paragraph_open": {
17270
+ const contentToken = tokens[j + 1];
17271
+ const paragraphNode = {
17272
+ type: "paragraph",
17273
+ children: parseInlineTokens(contentToken.children || [], String(contentToken.content ?? ""), void 0, linkifyContext.options()),
17274
+ raw: String(contentToken.content ?? "")
17275
+ };
17276
+ if (options?.includeSourceMap) applyNodeSourceMap(paragraphNode, token, options);
17277
+ blockquoteChildren.push(paragraphNode);
17278
+ linkifyContext.remember(paragraphNode.raw);
17279
+ j += 3;
17280
+ break;
17281
+ }
17282
+ case "bullet_list_open":
17283
+ case "ordered_list_open": {
17284
+ const [listNode, newIndex] = parseList(tokens, j, linkifyContext.options());
17285
+ blockquoteChildren.push(listNode);
17286
+ linkifyContext.remember(listNode.raw);
17287
+ j = newIndex;
17288
+ break;
17289
+ }
17290
+ case "blockquote_open": {
17291
+ const [nestedBlockquote, newIndex] = parseBlockquote(tokens, j, linkifyContext.options());
17292
+ blockquoteChildren.push(nestedBlockquote);
17293
+ linkifyContext.remember(nestedBlockquote.raw);
17294
+ j = newIndex;
17295
+ break;
17296
+ }
17297
+ default: {
17298
+ const handled = parseCommonBlockToken(tokens, j, linkifyContext.options(), containerTokenHandlers);
17299
+ if (handled) {
17300
+ blockquoteChildren.push(handled[0]);
17301
+ linkifyContext.remember(handled[0].raw);
17302
+ j = handled[1];
17303
+ } else j++;
17304
+ break;
17305
+ }
17200
17306
  }
17201
17307
  }
17202
- return [{
17308
+ const blockquoteNode = {
17203
17309
  type: "blockquote",
17204
17310
  children: blockquoteChildren,
17205
17311
  raw: blockquoteChildren.map((child) => child.raw).join("\n")
17206
- }, j + 1];
17312
+ };
17313
+ if (options?.includeSourceMap) applyNodeSourceMap(blockquoteNode, tokens[index], options);
17314
+ return [blockquoteNode, j + 1];
17207
17315
  }
17208
17316
 
17209
17317
  //#endregion
@@ -17561,17 +17669,20 @@ function parseVmrContainer(tokens, index, options) {
17561
17669
  children: parseInlineTokens(contentToken.children || [], void 0, void 0, linkifyContext.options()),
17562
17670
  raw: String(contentToken.content ?? "")
17563
17671
  };
17672
+ if (options?.includeSourceMap) applyNodeSourceMap(paragraphNode, tokens[j], options);
17564
17673
  children.push(paragraphNode);
17565
17674
  linkifyContext.remember(paragraphNode.raw);
17566
17675
  }
17567
17676
  j += 3;
17568
17677
  } else if (tokens[j].type === "bullet_list_open" || tokens[j].type === "ordered_list_open") {
17569
17678
  const [listNode, newIndex] = parseList(tokens, j, linkifyContext.options());
17679
+ if (options?.includeSourceMap) applyNodeSourceMap(listNode, tokens[j], options);
17570
17680
  children.push(listNode);
17571
17681
  linkifyContext.remember(listNode.raw);
17572
17682
  j = newIndex;
17573
17683
  } else if (tokens[j].type === "blockquote_open") {
17574
17684
  const [blockquoteNode, newIndex] = parseBlockquote(tokens, j, linkifyContext.options());
17685
+ if (options?.includeSourceMap) applyNodeSourceMap(blockquoteNode, tokens[j], options);
17575
17686
  children.push(blockquoteNode);
17576
17687
  linkifyContext.remember(blockquoteNode.raw);
17577
17688
  j = newIndex;
@@ -17602,6 +17713,14 @@ function parseVmrContainer(tokens, index, options) {
17602
17713
  raw
17603
17714
  }, hasCloseToken ? j + 1 : j];
17604
17715
  }
17716
+ function applyPairedBlockSourceMap(node, openToken, closeToken, options) {
17717
+ if (closeToken?.type.endsWith("_close")) {
17718
+ let endLine = Array.isArray(closeToken.map) ? Number(closeToken.map[1]) : NaN;
17719
+ if (!Number.isFinite(endLine)) endLine = Array.isArray(openToken.map) ? Number(openToken.map[1]) + 1 : NaN;
17720
+ return applyNodeSourceMapRange(node, openToken, endLine, options);
17721
+ }
17722
+ return applyNodeSourceMap(node, openToken, options);
17723
+ }
17605
17724
  function stripWrapperNewlines(s) {
17606
17725
  return s.replace(/^\r?\n/, "").replace(/\r?\n$/, "");
17607
17726
  }
@@ -17660,6 +17779,7 @@ function findNextCustomHtmlBlockFromSource(source, tag, startIndex) {
17660
17779
  const end = openEnd + 1;
17661
17780
  return {
17662
17781
  raw: source.slice(openStart, end),
17782
+ start: openStart,
17663
17783
  end
17664
17784
  };
17665
17785
  }
@@ -17677,6 +17797,7 @@ function findNextCustomHtmlBlockFromSource(source, tag, startIndex) {
17677
17797
  const lt = source.indexOf("<", i);
17678
17798
  if (lt === -1) return {
17679
17799
  raw: source.slice(openStart),
17800
+ start: openStart,
17680
17801
  end: source.length
17681
17802
  };
17682
17803
  if (isCloseAt(lt)) {
@@ -17687,6 +17808,7 @@ function findNextCustomHtmlBlockFromSource(source, tag, startIndex) {
17687
17808
  const end = gt + 1;
17688
17809
  return {
17689
17810
  raw: source.slice(openStart, end),
17811
+ start: openStart,
17690
17812
  end
17691
17813
  };
17692
17814
  }
@@ -17704,6 +17826,7 @@ function findNextCustomHtmlBlockFromSource(source, tag, startIndex) {
17704
17826
  }
17705
17827
  return {
17706
17828
  raw: source.slice(openStart),
17829
+ start: openStart,
17707
17830
  end: source.length
17708
17831
  };
17709
17832
  }
@@ -17722,17 +17845,34 @@ function lineToIndex(source, line) {
17722
17845
  }
17723
17846
  function parseBasicBlockToken(tokens, index, options) {
17724
17847
  const token = tokens[index];
17848
+ const includeSourceMap = options?.includeSourceMap === true;
17725
17849
  switch (token.type) {
17726
- case "heading_open": return [parseHeading(tokens, index, options), index + 3];
17727
- case "code_block": return [parseCodeBlock(token), index + 1];
17728
- case "fence": return [parseFenceToken(token), index + 1];
17729
- case "math_block": return [parseMathBlock(token), index + 1];
17850
+ case "heading_open": {
17851
+ const node = parseHeading(tokens, index, options);
17852
+ if (includeSourceMap) applyNodeSourceMap(node, token, options);
17853
+ return [node, index + 3];
17854
+ }
17855
+ case "code_block": {
17856
+ const node = parseCodeBlock(token);
17857
+ if (includeSourceMap) applyNodeSourceMap(node, token, options);
17858
+ return [node, index + 1];
17859
+ }
17860
+ case "fence": {
17861
+ const node = parseFenceToken(token);
17862
+ if (includeSourceMap) applyNodeSourceMap(node, token, options);
17863
+ return [node, index + 1];
17864
+ }
17865
+ case "math_block": {
17866
+ const node = parseMathBlock(token);
17867
+ if (includeSourceMap) applyNodeSourceMap(node, token, options);
17868
+ return [node, index + 1];
17869
+ }
17730
17870
  case "html_block": {
17731
17871
  const htmlBlockNode = parseHtmlBlock(token);
17732
17872
  const tagSets = htmlBlockNode.tag ? getHtmlTagSets(options?.customHtmlTags) : null;
17733
17873
  if (htmlBlockNode.tag && htmlBlockNode.loading && tagSets && !tagSets.allowedTagSet.has(htmlBlockNode.tag)) {
17734
17874
  const content = String(token.content ?? "").replace(/\n+$/, "");
17735
- return [{
17875
+ const paragraphNode = {
17736
17876
  type: "paragraph",
17737
17877
  children: content ? [{
17738
17878
  type: "text",
@@ -17740,7 +17880,9 @@ function parseBasicBlockToken(tokens, index, options) {
17740
17880
  raw: content
17741
17881
  }] : [],
17742
17882
  raw: content
17743
- }, index + 1];
17883
+ };
17884
+ if (includeSourceMap) applyNodeSourceMap(paragraphNode, token, options);
17885
+ return [paragraphNode, index + 1];
17744
17886
  }
17745
17887
  if (htmlBlockNode.tag && tagSets?.customTagSet?.has(htmlBlockNode.tag)) {
17746
17888
  const tag = htmlBlockNode.tag;
@@ -17769,30 +17911,41 @@ function parseBasicBlockToken(tokens, index, options) {
17769
17911
  attrs.push([name, value]);
17770
17912
  }
17771
17913
  const loading = !options?.final && !selfClosing && closeRange == null;
17772
- return [{
17914
+ const customNode = {
17773
17915
  type: tag,
17774
17916
  tag,
17775
17917
  content: stripWrapperNewlines(inner),
17776
17918
  raw: String(fromSource?.raw ?? htmlBlockNode.raw ?? rawHtml),
17777
17919
  loading,
17778
17920
  attrs: attrs.length ? attrs : void 0
17779
- }, index + 1];
17921
+ };
17922
+ if (includeSourceMap) if (fromSource) customNode.sourceMap = createSourceMapFromOffsets(source, fromSource.start, fromSource.end, options);
17923
+ else applyNodeSourceMap(customNode, token, options);
17924
+ return [customNode, index + 1];
17780
17925
  }
17926
+ if (includeSourceMap) applyNodeSourceMap(htmlBlockNode, token, options);
17781
17927
  return [htmlBlockNode, index + 1];
17782
17928
  }
17783
17929
  case "table_open": {
17784
17930
  const [tableNode, newIndex] = parseTable(tokens, index, options);
17931
+ if (includeSourceMap) applyNodeSourceMap(tableNode, token, options);
17785
17932
  return [tableNode, newIndex];
17786
17933
  }
17787
17934
  case "dl_open": {
17788
17935
  const [definitionListNode, newIndex] = parseDefinitionList(tokens, index, options);
17936
+ if (includeSourceMap) applyNodeSourceMap(definitionListNode, token, options);
17789
17937
  return [definitionListNode, newIndex];
17790
17938
  }
17791
17939
  case "footnote_open": {
17792
17940
  const [footnoteNode, newIndex] = parseFootnote(tokens, index, options);
17941
+ if (includeSourceMap) applyNodeSourceMap(footnoteNode, token, options);
17793
17942
  return [footnoteNode, newIndex];
17794
17943
  }
17795
- case "hr": return [parseThematicBreak(), index + 1];
17944
+ case "hr": {
17945
+ const node = parseThematicBreak();
17946
+ if (includeSourceMap) applyNodeSourceMap(node, token, options);
17947
+ return [node, index + 1];
17948
+ }
17796
17949
  default: break;
17797
17950
  }
17798
17951
  return null;
@@ -17800,7 +17953,9 @@ function parseBasicBlockToken(tokens, index, options) {
17800
17953
  function parseCommonBlockToken(tokens, index, options, handlers) {
17801
17954
  const basicResult = parseBasicBlockToken(tokens, index, options);
17802
17955
  if (basicResult) return basicResult;
17803
- switch (tokens[index].type) {
17956
+ const token = tokens[index];
17957
+ const includeSourceMap = options?.includeSourceMap === true;
17958
+ switch (token.type) {
17804
17959
  case "container_warning_open":
17805
17960
  case "container_info_open":
17806
17961
  case "container_note_open":
@@ -17808,15 +17963,26 @@ function parseCommonBlockToken(tokens, index, options, handlers) {
17808
17963
  case "container_danger_open":
17809
17964
  case "container_caution_open":
17810
17965
  case "container_error_open":
17811
- if (handlers?.parseContainer) return handlers.parseContainer(tokens, index, options);
17966
+ if (handlers?.parseContainer) {
17967
+ const result = handlers.parseContainer(tokens, index, options);
17968
+ if (includeSourceMap) applyPairedBlockSourceMap(result[0], token, tokens[result[1] - 1], options);
17969
+ return result;
17970
+ }
17812
17971
  break;
17813
17972
  case "container_open":
17814
17973
  if (handlers?.matchAdmonition) {
17815
17974
  const result = handlers.matchAdmonition(tokens, index, options);
17816
- if (result) return result;
17975
+ if (result) {
17976
+ if (includeSourceMap) applyPairedBlockSourceMap(result[0], token, tokens[result[1] - 1], options);
17977
+ return result;
17978
+ }
17817
17979
  }
17818
17980
  break;
17819
- case "vmr_container_open": return parseVmrContainer(tokens, index, options);
17981
+ case "vmr_container_open": {
17982
+ const result = parseVmrContainer(tokens, index, options);
17983
+ if (includeSourceMap) applyPairedBlockSourceMap(result[0], token, tokens[result[1] - 1], options);
17984
+ return result;
17985
+ }
17820
17986
  default: break;
17821
17987
  }
17822
17988
  return null;
@@ -17865,6 +18031,15 @@ function finishTimedParse(result, timing, startedAt) {
17865
18031
  if (timing) addTiming(timing, "parseMarkdownToStructureTotalMs", getParserNow() - startedAt);
17866
18032
  return result;
17867
18033
  }
18034
+ function applyPostTransformNodes(nodes, options) {
18035
+ const transform = options.postTransformNodes;
18036
+ if (typeof transform !== "function") return nodes;
18037
+ const transformed = transform(nodes);
18038
+ return Array.isArray(transformed) ? transformed : nodes;
18039
+ }
18040
+ function finishParsedNodes(result, options, timing, startedAt) {
18041
+ return finishTimedParse(applyPostTransformNodes(result, options), timing, startedAt);
18042
+ }
17868
18043
  function processTokensWithTiming(tokens, options, timing) {
17869
18044
  if (!timing) return processTokens(tokens, options);
17870
18045
  const startedAt = getParserNow();
@@ -18030,8 +18205,24 @@ function shouldResetTopLevelStreamCacheForFinalAutoParse(md, options) {
18030
18205
  function clearTolerantMathBoundaryStreamCache(md) {
18031
18206
  tolerantMathBoundaryStreamCache.delete(md);
18032
18207
  }
18033
- function setTolerantMathBoundaryStreamCache(md, source, key) {
18208
+ function createExplicitBracketMathContext() {
18209
+ return {
18210
+ fenceChar: "",
18211
+ fenceInBlockquote: false,
18212
+ fenceInList: false,
18213
+ fenceLen: 0,
18214
+ fenceListIndent: 0,
18215
+ inFence: false,
18216
+ inMath: false,
18217
+ listContentIndent: null
18218
+ };
18219
+ }
18220
+ function cloneExplicitBracketMathContext(context) {
18221
+ return { ...context };
18222
+ }
18223
+ function setTolerantMathBoundaryStreamCache(md, source, key, explicitBracketMath = scanExplicitBracketMathStreamState(source).state) {
18034
18224
  tolerantMathBoundaryStreamCache.set(md, {
18225
+ explicitBracketMath,
18035
18226
  source,
18036
18227
  key,
18037
18228
  pendingCandidate: key === null && mayContainTolerantMathBlockBoundaryOpener(source)
@@ -18047,12 +18238,235 @@ function sourceEndsWithCompleteTolerantBoundaryOpener(source) {
18047
18238
  }
18048
18239
  function appendedChunkMayAffectTolerantMathBoundary(previousSource, appended) {
18049
18240
  if (!appended) return false;
18050
- if (appended.includes("$$") || appended.includes("\\[") || appended.includes("\\]")) return true;
18241
+ if (appended.includes("$$") || appended.includes("\\[")) return true;
18051
18242
  if (previousSource.endsWith("$") && appended[0] === "$") return true;
18052
- if (previousSource.endsWith("\\") && (appended[0] === "[" || appended[0] === "]")) return true;
18243
+ if (previousSource.endsWith("\\") && appended[0] === "[") return true;
18053
18244
  if (sourceEndsWithCompleteTolerantBoundaryOpener(previousSource) && /[\r\n]/.test(appended)) return true;
18054
18245
  return false;
18055
18246
  }
18247
+ function isEscapedDelimiterAt(source, index) {
18248
+ let cursor = index - 1;
18249
+ let backslashes = 0;
18250
+ while (cursor >= 0 && source[cursor] === "\\") {
18251
+ backslashes++;
18252
+ cursor--;
18253
+ }
18254
+ return backslashes % 2 === 1;
18255
+ }
18256
+ function isIndentWhitespace(ch) {
18257
+ return ch === " " || ch === " ";
18258
+ }
18259
+ function advanceMarkdownIndentColumn(column, ch) {
18260
+ return ch === " " ? column + 1 : column + 4 - column % 4;
18261
+ }
18262
+ function getMarkdownIndent(line) {
18263
+ let index = 0;
18264
+ let column = 0;
18265
+ while (index < line.length && isIndentWhitespace(line[index])) {
18266
+ column = advanceMarkdownIndentColumn(column, line[index]);
18267
+ index++;
18268
+ }
18269
+ return {
18270
+ index,
18271
+ column
18272
+ };
18273
+ }
18274
+ function consumeMarkdownIndent(line) {
18275
+ const indent = getMarkdownIndent(line);
18276
+ return indent.column > 3 ? null : indent;
18277
+ }
18278
+ function parseMarkdownFenceMarker(line) {
18279
+ const indent = consumeMarkdownIndent(line);
18280
+ if (!indent) return null;
18281
+ const index = indent.index;
18282
+ const markerChar = line[index];
18283
+ if (markerChar !== "`" && markerChar !== "~") return null;
18284
+ let markerEnd = index;
18285
+ while (markerEnd < line.length && line[markerEnd] === markerChar) markerEnd++;
18286
+ const markerLen = markerEnd - index;
18287
+ if (markerLen < 3) return null;
18288
+ const rest = line.slice(markerEnd);
18289
+ if (markerChar === "`" && rest.includes("`")) return null;
18290
+ return {
18291
+ markerChar,
18292
+ markerLen,
18293
+ rest
18294
+ };
18295
+ }
18296
+ function stripMarkdownListPrefix(line) {
18297
+ const indent = consumeMarkdownIndent(line);
18298
+ if (!indent) return null;
18299
+ const rest = line.slice(indent.index);
18300
+ const marker = /^(?:[-+*]|\d{1,9}[.)])(?=[\t ]|$)/.exec(rest)?.[0];
18301
+ if (!marker) return null;
18302
+ let index = indent.index + marker.length;
18303
+ let column = indent.column + marker.length;
18304
+ if (!isIndentWhitespace(line[index])) return null;
18305
+ while (index < line.length && isIndentWhitespace(line[index])) {
18306
+ column = advanceMarkdownIndentColumn(column, line[index]);
18307
+ index++;
18308
+ }
18309
+ return {
18310
+ content: line.slice(index),
18311
+ contentIndent: column
18312
+ };
18313
+ }
18314
+ function stripMarkdownBlockquotePrefix(line) {
18315
+ let rest = line;
18316
+ let saw = false;
18317
+ while (true) {
18318
+ const indent = consumeMarkdownIndent(rest);
18319
+ if (!indent) return saw ? rest : null;
18320
+ let index = indent.index;
18321
+ if (rest[index] !== ">") return saw ? rest : null;
18322
+ saw = true;
18323
+ index++;
18324
+ if (rest[index] === " " || rest[index] === " ") index++;
18325
+ rest = rest.slice(index);
18326
+ }
18327
+ }
18328
+ function matchMarkdownFenceMarker(line) {
18329
+ const direct = parseMarkdownFenceMarker(line);
18330
+ if (direct) return {
18331
+ ...direct,
18332
+ inBlockquote: false,
18333
+ inList: false,
18334
+ listIndent: 0
18335
+ };
18336
+ const quoted = stripMarkdownBlockquotePrefix(line);
18337
+ const quotedMarker = quoted == null ? null : parseMarkdownFenceMarker(quoted);
18338
+ if (quotedMarker) return {
18339
+ ...quotedMarker,
18340
+ inBlockquote: true,
18341
+ inList: false,
18342
+ listIndent: 0
18343
+ };
18344
+ const listed = stripMarkdownListPrefix(line);
18345
+ if (!listed) return null;
18346
+ const listedMarker = parseMarkdownFenceMarker(listed.content);
18347
+ return listedMarker == null ? null : {
18348
+ ...listedMarker,
18349
+ inBlockquote: false,
18350
+ inList: true,
18351
+ listIndent: listed.contentIndent
18352
+ };
18353
+ }
18354
+ function countRepeatedChar(source, index, ch) {
18355
+ let end = index;
18356
+ while (end < source.length && source[end] === ch) end++;
18357
+ return end - index;
18358
+ }
18359
+ function findCodeSpanCloseIndex(line, start, markerLen) {
18360
+ let index = start;
18361
+ while (index < line.length) {
18362
+ const next = line.indexOf("`", index);
18363
+ if (next === -1) return -1;
18364
+ const runLen = countRepeatedChar(line, next, "`");
18365
+ if (runLen === markerLen) return next;
18366
+ index = next + runLen;
18367
+ }
18368
+ return -1;
18369
+ }
18370
+ function resetExplicitBracketFenceContext(context) {
18371
+ context.inFence = false;
18372
+ context.fenceChar = "";
18373
+ context.fenceLen = 0;
18374
+ context.fenceInBlockquote = false;
18375
+ context.fenceInList = false;
18376
+ context.fenceListIndent = 0;
18377
+ }
18378
+ function scanLineForExplicitBracketMathState(line, context, lineStart, appendStart, openAtAppendStart) {
18379
+ let index = 0;
18380
+ let closedOpenMath = false;
18381
+ while (index < line.length) {
18382
+ const sourceIndex = index;
18383
+ if (context.inMath) {
18384
+ if (line.startsWith("\\]", index) && !isEscapedDelimiterAt(line, sourceIndex)) {
18385
+ if (appendStart != null && openAtAppendStart && lineStart + index + 2 > appendStart) closedOpenMath = true;
18386
+ context.inMath = false;
18387
+ index += 2;
18388
+ continue;
18389
+ }
18390
+ index++;
18391
+ continue;
18392
+ }
18393
+ if (line[index] === "`" && !isEscapedDelimiterAt(line, sourceIndex)) {
18394
+ const markerLen = countRepeatedChar(line, index, "`");
18395
+ const closeIndex = findCodeSpanCloseIndex(line, index + markerLen, markerLen);
18396
+ if (closeIndex === -1) break;
18397
+ index = closeIndex + markerLen;
18398
+ continue;
18399
+ }
18400
+ if (line.startsWith("\\[", index) && !isEscapedDelimiterAt(line, sourceIndex)) {
18401
+ context.inMath = true;
18402
+ index += 2;
18403
+ continue;
18404
+ }
18405
+ index++;
18406
+ }
18407
+ return closedOpenMath;
18408
+ }
18409
+ function scanExplicitBracketMathLine(line, context, lineStart, appendStart, openAtAppendStart) {
18410
+ const lineIndent = getMarkdownIndent(line);
18411
+ const listPrefix = stripMarkdownListPrefix(line);
18412
+ if (context.inFence && context.fenceInBlockquote && line.trim() && stripMarkdownBlockquotePrefix(line) == null) resetExplicitBracketFenceContext(context);
18413
+ if (context.inFence && context.fenceInList && line.trim() && lineIndent.column < context.fenceListIndent && !listPrefix) resetExplicitBracketFenceContext(context);
18414
+ if (listPrefix) context.listContentIndent = listPrefix.contentIndent;
18415
+ else if (line.trim() && context.listContentIndent != null && lineIndent.column < context.listContentIndent && !context.inFence) context.listContentIndent = null;
18416
+ if (!context.inMath) {
18417
+ const fenceMatch = matchMarkdownFenceMarker(line);
18418
+ if (fenceMatch) if (context.inFence) {
18419
+ if (fenceMatch.markerChar === context.fenceChar && fenceMatch.markerLen >= context.fenceLen && /^\s*$/.test(fenceMatch.rest)) resetExplicitBracketFenceContext(context);
18420
+ } else {
18421
+ context.inFence = true;
18422
+ context.fenceChar = fenceMatch.markerChar;
18423
+ context.fenceLen = fenceMatch.markerLen;
18424
+ context.fenceInBlockquote = fenceMatch.inBlockquote;
18425
+ context.fenceInList = fenceMatch.inList || context.listContentIndent != null && !fenceMatch.inBlockquote && lineIndent.column >= context.listContentIndent;
18426
+ context.fenceListIndent = fenceMatch.listIndent || context.listContentIndent || 0;
18427
+ }
18428
+ else if (!context.inFence) return scanLineForExplicitBracketMathState(line, context, lineStart, appendStart, openAtAppendStart);
18429
+ } else return scanLineForExplicitBracketMathState(line, context, lineStart, appendStart, openAtAppendStart);
18430
+ return false;
18431
+ }
18432
+ function scanExplicitBracketMathStreamState(source, initialContext = createExplicitBracketMathContext(), appendStart = null, openAtAppendStart = false) {
18433
+ const context = cloneExplicitBracketMathContext(initialContext);
18434
+ let committedContext = cloneExplicitBracketMathContext(initialContext);
18435
+ let lineBuffer = "";
18436
+ let closedOpenMath = false;
18437
+ let index = 0;
18438
+ while (index < source.length) {
18439
+ const newlineIndex = source.indexOf("\n", index);
18440
+ const hasNewline = newlineIndex !== -1;
18441
+ const lineEnd$2 = hasNewline && newlineIndex > index && source[newlineIndex - 1] === "\r" ? newlineIndex - 1 : hasNewline ? newlineIndex : source.length;
18442
+ const line = source.slice(index, lineEnd$2);
18443
+ if (scanExplicitBracketMathLine(line, context, index, appendStart, openAtAppendStart)) closedOpenMath = true;
18444
+ if (hasNewline) {
18445
+ committedContext = cloneExplicitBracketMathContext(context);
18446
+ lineBuffer = "";
18447
+ } else lineBuffer = line;
18448
+ index = hasNewline ? newlineIndex + 1 : source.length;
18449
+ }
18450
+ return {
18451
+ closedOpenMath,
18452
+ state: {
18453
+ committedContext,
18454
+ context,
18455
+ lineBuffer
18456
+ }
18457
+ };
18458
+ }
18459
+ function updateExplicitBracketMathStreamState(previous, appended) {
18460
+ if (appended && !previous.context.inMath && !previous.context.inFence && !previous.committedContext.inFence && !/[\\`~\r\n]/.test(appended) && !(previous.lineBuffer.endsWith("\\") && (appended[0] === "[" || appended[0] === "]"))) return {
18461
+ closedOpenMath: false,
18462
+ state: {
18463
+ committedContext: cloneExplicitBracketMathContext(previous.committedContext),
18464
+ context: cloneExplicitBracketMathContext(previous.context),
18465
+ lineBuffer: previous.lineBuffer + appended
18466
+ }
18467
+ };
18468
+ return scanExplicitBracketMathStreamState(previous.lineBuffer + appended, previous.committedContext, previous.lineBuffer.length, previous.context.inMath);
18469
+ }
18056
18470
  function syncTolerantMathBoundaryStreamCache(md, source) {
18057
18471
  if (!hasMarkstreamMathPlugin(md)) return;
18058
18472
  const stream = md.stream;
@@ -18060,18 +18474,22 @@ function syncTolerantMathBoundaryStreamCache(md, source) {
18060
18474
  const owner = md;
18061
18475
  const previous = tolerantMathBoundaryStreamCache.get(owner);
18062
18476
  if (previous?.source === source) return;
18063
- if (previous && source.startsWith(previous.source)) {
18064
- const appended = source.slice(previous.source.length);
18065
- if (previous.key === null && previous.pendingCandidate === false && !appendedChunkMayAffectTolerantMathBoundary(previous.source, appended) && !sourceEndsWithSplitTolerantBoundaryPrefix(source)) {
18477
+ const sourceExtendsPrevious = previous ? source.startsWith(previous.source) : false;
18478
+ const appended = sourceExtendsPrevious && previous ? source.slice(previous.source.length) : "";
18479
+ const explicitBracketMathUpdate = sourceExtendsPrevious && previous ? updateExplicitBracketMathStreamState(previous.explicitBracketMath, appended) : scanExplicitBracketMathStreamState(source);
18480
+ const nextExplicitBracketMath = explicitBracketMathUpdate.state;
18481
+ const completesExplicitBracketMathClose = sourceExtendsPrevious && previous ? explicitBracketMathUpdate.closedOpenMath : false;
18482
+ if (previous && sourceExtendsPrevious) {
18483
+ if (previous.key === null && previous.pendingCandidate === false && !completesExplicitBracketMathClose && !appendedChunkMayAffectTolerantMathBoundary(previous.source, appended) && !sourceEndsWithSplitTolerantBoundaryPrefix(source)) {
18066
18484
  previous.source = source;
18485
+ previous.explicitBracketMath = nextExplicitBracketMath;
18067
18486
  return;
18068
18487
  }
18069
18488
  }
18070
18489
  const nextKey = getTolerantMathBlockBoundaryStreamKey(source);
18071
- const sourceWasReplaced = previous ? !source.startsWith(previous.source) : false;
18072
- if (previous && (sourceWasReplaced || previous.key !== nextKey)) stream.reset();
18490
+ if (previous && ((previous ? !sourceExtendsPrevious : false) || previous.key !== nextKey || completesExplicitBracketMathClose)) stream.reset();
18073
18491
  else if (!previous && nextKey) stream.reset();
18074
- setTolerantMathBoundaryStreamCache(md, source, nextKey);
18492
+ setTolerantMathBoundaryStreamCache(md, source, nextKey, nextExplicitBracketMath);
18075
18493
  }
18076
18494
  function shouldCloneTopLevelStreamTokens(options) {
18077
18495
  return typeof options.preTransformTokens === "function" || typeof options.postTransformTokens === "function";
@@ -18140,6 +18558,10 @@ function buildParagraphFromInlineChildren(children) {
18140
18558
  raw: children.map(stringifyInlineNodeRaw).join("")
18141
18559
  };
18142
18560
  }
18561
+ function inheritSourceMap(nodes, sourceNode) {
18562
+ if (!sourceNode.sourceMap) return;
18563
+ for (const node of nodes) if (!node.sourceMap) node.sourceMap = sourceNode.sourceMap;
18564
+ }
18143
18565
  function maybePromoteCustomNodeFromParagraph(node, options) {
18144
18566
  if (node.type !== "paragraph") return null;
18145
18567
  const nodeChildren = getNodeFields(node).children;
@@ -18479,7 +18901,7 @@ function combineStructuredDetailsHtmlBlocks(nodes, source, md, options, final, s
18479
18901
  const renderedCloseRaw = explicitClose ? source.slice(closeStart, closeSliceEnd) : closeRaw;
18480
18902
  const mergedRaw = explicitClose ? source.slice(openStart, closeSliceEnd) : source.slice(openStart);
18481
18903
  const contentPrefix = selfContained && openTagEndIndex !== -1 ? openRaw.slice(0, openTagEndIndex + 1) : openRaw;
18482
- merged.push({
18904
+ const detailsNode = {
18483
18905
  ...node,
18484
18906
  tag: "details",
18485
18907
  attrs: parseTagAttrs(openRaw.slice(0, openTagEndIndex + 1)),
@@ -18487,14 +18909,16 @@ function combineStructuredDetailsHtmlBlocks(nodes, source, md, options, final, s
18487
18909
  content: `${contentPrefix}${renderedMiddle}${renderedCloseRaw}`,
18488
18910
  children: [...prefixChildren, ...children],
18489
18911
  loading: !final && !explicitClose
18490
- });
18912
+ };
18913
+ if (options.includeSourceMap) detailsNode.sourceMap = createSourceMapFromOffsets(source, openStart, explicitClose ? closeSliceEnd : source.length, options);
18914
+ merged.push(detailsNode);
18491
18915
  cursor = explicitClose ? closeSliceEnd : source.length;
18492
18916
  if (closeIndex === -1 && !selfContained) break;
18493
18917
  if (closeIndex !== -1) i = closeIndex;
18494
18918
  }
18495
18919
  return [merged, cursor];
18496
18920
  }
18497
- function mergeSplitTopLevelHtmlBlocks(nodes, final, source) {
18921
+ function mergeSplitTopLevelHtmlBlocks(nodes, final, source, options) {
18498
18922
  if (!source) return nodes;
18499
18923
  const merged = nodes.slice();
18500
18924
  let sourceHtmlCursor = 0;
@@ -18527,6 +18951,7 @@ function mergeSplitTopLevelHtmlBlocks(nodes, final, source) {
18527
18951
  node.raw = exact.raw;
18528
18952
  node.loading = desiredLoading;
18529
18953
  node.attrs = exactAttrs.length ? exactAttrs : void 0;
18954
+ if (options?.includeSourceMap) node.sourceMap = createSourceMapFromOffsets(source, exact.start, exact.end, options);
18530
18955
  if (!needsExpansion) continue;
18531
18956
  let tailCursor = findApproximateConsumedPrefixEnd(exact.raw, currentRaw);
18532
18957
  if (tailCursor === -1) tailCursor = 0;
@@ -18693,6 +19118,106 @@ function stripDanglingHtmlLikeTail(markdown) {
18693
19118
  if (!isLikelyHtmlTagPrefix(tail)) return s;
18694
19119
  return s.slice(0, lastLt);
18695
19120
  }
19121
+ function createSourceLineMapper(source, parsedSource) {
19122
+ if (source === parsedSource) return void 0;
19123
+ const sourceLines = source.split(/\r?\n/);
19124
+ const parsedLines = parsedSource.split(/\r?\n/);
19125
+ const mappedLines = [];
19126
+ let sourceCursor = 0;
19127
+ for (let parsedLine = 0; parsedLine < parsedLines.length; parsedLine++) {
19128
+ const line = parsedLines[parsedLine] ?? "";
19129
+ if (sourceLines[sourceCursor] === line) {
19130
+ mappedLines[parsedLine] = {
19131
+ startLine: sourceCursor,
19132
+ endLine: sourceCursor + 1
19133
+ };
19134
+ sourceCursor++;
19135
+ continue;
19136
+ }
19137
+ const sourceLine = sourceLines[sourceCursor] ?? "";
19138
+ if (line !== "" && sourceLine !== line && sourceLine.startsWith(line)) {
19139
+ let joinedLine = line;
19140
+ let splitEnd = -1;
19141
+ for (let nextParsedLine = parsedLine + 1; nextParsedLine < parsedLines.length; nextParsedLine++) {
19142
+ joinedLine += parsedLines[nextParsedLine] ?? "";
19143
+ if (joinedLine === sourceLine) {
19144
+ splitEnd = nextParsedLine;
19145
+ break;
19146
+ }
19147
+ if (!sourceLine.startsWith(joinedLine)) break;
19148
+ }
19149
+ if (splitEnd !== -1) {
19150
+ for (let mappedLine = parsedLine; mappedLine <= splitEnd; mappedLine++) mappedLines[mappedLine] = {
19151
+ startLine: sourceCursor,
19152
+ endLine: sourceCursor + 1
19153
+ };
19154
+ sourceCursor++;
19155
+ parsedLine = splitEnd;
19156
+ continue;
19157
+ }
19158
+ mappedLines[parsedLine] = {
19159
+ startLine: sourceCursor,
19160
+ endLine: sourceCursor + 1
19161
+ };
19162
+ continue;
19163
+ }
19164
+ let collapsedLine = sourceLines[sourceCursor] ?? "";
19165
+ let collapsedEnd = -1;
19166
+ for (let sourceLine$1 = sourceCursor + 1; sourceLine$1 < sourceLines.length; sourceLine$1++) {
19167
+ collapsedLine += `\\n${sourceLines[sourceLine$1] ?? ""}`;
19168
+ if (collapsedLine === line) {
19169
+ collapsedEnd = sourceLine$1 + 1;
19170
+ break;
19171
+ }
19172
+ if (!line.startsWith(collapsedLine)) break;
19173
+ }
19174
+ if (collapsedEnd !== -1) {
19175
+ mappedLines[parsedLine] = {
19176
+ startLine: sourceCursor,
19177
+ endLine: collapsedEnd
19178
+ };
19179
+ sourceCursor = collapsedEnd;
19180
+ continue;
19181
+ }
19182
+ let found = -1;
19183
+ if (line !== "") {
19184
+ const searchEnd = Math.min(sourceLines.length, sourceCursor + 80);
19185
+ for (let sourceLine$1 = sourceCursor; sourceLine$1 < searchEnd; sourceLine$1++) if (sourceLines[sourceLine$1] === line) {
19186
+ found = sourceLine$1;
19187
+ break;
19188
+ }
19189
+ }
19190
+ if (found !== -1) {
19191
+ mappedLines[parsedLine] = {
19192
+ startLine: found,
19193
+ endLine: found + 1
19194
+ };
19195
+ sourceCursor = found + 1;
19196
+ continue;
19197
+ }
19198
+ const fallbackLine = Math.min(Math.max(0, sourceLines.length - 1), Math.max(0, sourceCursor - 1));
19199
+ mappedLines[parsedLine] = {
19200
+ startLine: fallbackLine,
19201
+ endLine: fallbackLine + 1
19202
+ };
19203
+ }
19204
+ return (line) => {
19205
+ const index = Number.isFinite(line) ? Math.max(0, Math.trunc(line)) : 0;
19206
+ if (index < mappedLines.length) return mappedLines[index] ?? {
19207
+ startLine: 0,
19208
+ endLine: 0
19209
+ };
19210
+ const lastMapped = mappedLines[mappedLines.length - 1] ?? {
19211
+ startLine: Math.max(0, sourceLines.length - 1),
19212
+ endLine: sourceLines.length
19213
+ };
19214
+ const startLine = Math.min(sourceLines.length, lastMapped.endLine + index - mappedLines.length);
19215
+ return {
19216
+ startLine,
19217
+ endLine: Math.min(sourceLines.length, startLine + 1)
19218
+ };
19219
+ };
19220
+ }
18696
19221
  function ensureBlankLineBeforeInlineMultilineCustomHtmlBlocks(markdown, tags) {
18697
19222
  if (!markdown || !tags.length) return markdown;
18698
19223
  const tagSet = new Set(tags.map((t) => String(t ?? "").toLowerCase()).filter(Boolean));
@@ -19314,7 +19839,8 @@ function parseMarkdownToStructure(markdown, md, options = {}) {
19314
19839
  const timing = getParseTiming(options);
19315
19840
  const parseStartedAt = timing ? getParserNow() : 0;
19316
19841
  const isFinal = !!options.final;
19317
- let safeMarkdown = (markdown ?? "").toString().replace(/([^\\])\r(ight|ho)/g, "$1\\r$2").replace(/([^\\])\n(abla|eq|ot|exists)/g, "$1\\n$2");
19842
+ const sourceMarkdown = (markdown ?? "").toString();
19843
+ let safeMarkdown = sourceMarkdown.replace(/([^\\])\r(ight|ho)/g, "$1\\r$2").replace(/([^\\])\r?\n(abla|eq|ot|exists)/g, "$1\\n$2");
19318
19844
  if (shouldResetTopLevelStreamCacheForFinalAutoParse(md, options)) {
19319
19845
  md.stream.reset();
19320
19846
  clearTolerantMathBoundaryStreamCache(md);
@@ -19357,6 +19883,13 @@ function parseMarkdownToStructure(markdown, md, options = {}) {
19357
19883
  if (!isFinal) safeMarkdown = stripDanglingHtmlLikeTail(safeMarkdown);
19358
19884
  const standaloneHtmlDocument = parseStandaloneHtmlDocument(safeMarkdown);
19359
19885
  if (standaloneHtmlDocument) {
19886
+ if (options.includeSourceMap) {
19887
+ const sourceMapOptions = {
19888
+ ...options,
19889
+ __sourceLineMapper: createSourceLineMapper(sourceMarkdown, safeMarkdown)
19890
+ };
19891
+ standaloneHtmlDocument[0].sourceMap = createSourceMapFromOffsets(safeMarkdown, 0, safeMarkdown.length, sourceMapOptions);
19892
+ }
19360
19893
  const preHook = options.preTransformTokens;
19361
19894
  const postHook = options.postTransformTokens;
19362
19895
  if (shouldUseTopLevelStreamParse(md, options) || typeof preHook === "function" || typeof postHook === "function") {
@@ -19364,10 +19897,10 @@ function parseMarkdownToStructure(markdown, md, options = {}) {
19364
19897
  const hookedTokens = typeof preHook === "function" ? preHook(rawTokens) || rawTokens : rawTokens;
19365
19898
  if (typeof postHook === "function") postHook(hookedTokens);
19366
19899
  }
19367
- return finishTimedParse(standaloneHtmlDocument, timing, parseStartedAt);
19900
+ return finishParsedNodes(standaloneHtmlDocument, options, timing, parseStartedAt);
19368
19901
  }
19369
19902
  const tokens = parseTopLevelTokens(md, safeMarkdown, { __markstreamFinal: isFinal }, options);
19370
- if (!tokens || !Array.isArray(tokens)) return finishTimedParse([], timing, parseStartedAt);
19903
+ if (!tokens || !Array.isArray(tokens)) return finishParsedNodes([], options, timing, parseStartedAt);
19371
19904
  const pre = options.preTransformTokens;
19372
19905
  const post = options.postTransformTokens;
19373
19906
  let transformedTokens = tokens;
@@ -19378,6 +19911,7 @@ function parseMarkdownToStructure(markdown, md, options = {}) {
19378
19911
  ...options,
19379
19912
  validateLink: validateLink$1,
19380
19913
  __markdownIt: md,
19914
+ __sourceLineMapper: options.includeSourceMap === true ? createSourceLineMapper(sourceMarkdown, safeMarkdown) : void 0,
19381
19915
  __sourceMarkdown: safeMarkdown,
19382
19916
  __customHtmlBlockCursor: 0
19383
19917
  };
@@ -19387,13 +19921,16 @@ function parseMarkdownToStructure(markdown, md, options = {}) {
19387
19921
  if (Array.isArray(postResult)) {
19388
19922
  const first = postResult[0];
19389
19923
  const firstType = first?.type;
19390
- if (first && typeof firstType === "string") result = processTokensWithTiming(postResult, void 0, timing);
19924
+ if (first && typeof firstType === "string") result = processTokensWithTiming(postResult, {
19925
+ ...internalOptions,
19926
+ __customHtmlBlockCursor: 0
19927
+ }, timing);
19391
19928
  else result = postResult;
19392
19929
  }
19393
19930
  }
19394
- result = mergeSplitTopLevelHtmlBlocks(result, isFinal, safeMarkdown);
19395
- result = combineStructuredDetailsHtmlBlocks(result, safeMarkdown, md, options, isFinal)[0];
19396
- result = structureGenericHtmlBlockChildren(result, md, options, isFinal);
19931
+ result = mergeSplitTopLevelHtmlBlocks(result, isFinal, safeMarkdown, internalOptions);
19932
+ result = combineStructuredDetailsHtmlBlocks(result, safeMarkdown, md, internalOptions, isFinal)[0];
19933
+ result = structureGenericHtmlBlockChildren(result, md, internalOptions, isFinal);
19397
19934
  if (isFinal) {
19398
19935
  const seen = /* @__PURE__ */ new WeakSet();
19399
19936
  const finalizeHtmlBlockLoading = (value) => {
@@ -19410,6 +19947,7 @@ function parseMarkdownToStructure(markdown, md, options = {}) {
19410
19947
  };
19411
19948
  finalizeHtmlBlockLoading(result);
19412
19949
  }
19950
+ result = applyPostTransformNodes(result, options);
19413
19951
  if (options.debug) console.log("Parsed Markdown Tree Structure:", result);
19414
19952
  return finishTimedParse(result, timing, parseStartedAt);
19415
19953
  }
@@ -19417,6 +19955,7 @@ function processTokens(tokens, options) {
19417
19955
  if (!tokens || !Array.isArray(tokens)) return [];
19418
19956
  const result = [];
19419
19957
  const linkifyContext = createLinkifyDemotionContextTracker(options);
19958
+ const includeSourceMap = options?.includeSourceMap === true;
19420
19959
  let i = 0;
19421
19960
  while (i < tokens.length) {
19422
19961
  const handled = parseCommonBlockToken(tokens, i, linkifyContext.options(), containerTokenHandlers);
@@ -19431,9 +19970,12 @@ function processTokens(tokens, options) {
19431
19970
  case "paragraph_open": {
19432
19971
  const paragraphRaw = String(tokens[i + 1]?.content ?? "");
19433
19972
  const paragraphNode = parseParagraph(tokens, i, linkifyContext.options(paragraphRaw));
19973
+ if (includeSourceMap) applyNodeSourceMap(paragraphNode, token, options);
19434
19974
  const promoted = maybePromoteCustomNodeFromParagraph(paragraphNode, options);
19435
- if (promoted) result.push(...promoted);
19436
- else result.push(paragraphNode);
19975
+ if (promoted) {
19976
+ if (includeSourceMap) inheritSourceMap(promoted, paragraphNode);
19977
+ result.push(...promoted);
19978
+ } else result.push(paragraphNode);
19437
19979
  linkifyContext.remember(paragraphNode.raw);
19438
19980
  i += 3;
19439
19981
  break;
@@ -19441,6 +19983,7 @@ function processTokens(tokens, options) {
19441
19983
  case "bullet_list_open":
19442
19984
  case "ordered_list_open": {
19443
19985
  const [listNode, newIndex] = parseList(tokens, i, linkifyContext.options());
19986
+ if (includeSourceMap) applyNodeSourceMap(listNode, token, options);
19444
19987
  result.push(listNode);
19445
19988
  linkifyContext.remember(listNode.raw);
19446
19989
  i = newIndex;
@@ -19448,6 +19991,7 @@ function processTokens(tokens, options) {
19448
19991
  }
19449
19992
  case "blockquote_open": {
19450
19993
  const [blockquoteNode, newIndex] = parseBlockquote(tokens, i, linkifyContext.options());
19994
+ if (includeSourceMap) applyNodeSourceMap(blockquoteNode, token, options);
19451
19995
  result.push(blockquoteNode);
19452
19996
  linkifyContext.remember(blockquoteNode.raw);
19453
19997
  i = newIndex;
@@ -19455,12 +19999,13 @@ function processTokens(tokens, options) {
19455
19999
  }
19456
20000
  case "footnote_anchor": {
19457
20001
  const meta = token.meta ?? {};
19458
- const id = String(meta.label ?? token.content ?? "");
19459
- result.push({
20002
+ const footnoteAnchorNode = {
19460
20003
  type: "footnote_anchor",
19461
- id,
20004
+ id: String(meta.label ?? token.content ?? ""),
19462
20005
  raw: String(token.content ?? "")
19463
- });
20006
+ };
20007
+ if (includeSourceMap) applyNodeSourceMap(footnoteAnchorNode, token, options);
20008
+ result.push(footnoteAnchorNode);
19464
20009
  linkifyContext.remember(String(token.content ?? ""));
19465
20010
  i++;
19466
20011
  break;
@@ -19472,7 +20017,7 @@ function processTokens(tokens, options) {
19472
20017
  break;
19473
20018
  case "text": {
19474
20019
  const content = String(token.content ?? "");
19475
- result.push({
20020
+ const paragraphNode = {
19476
20021
  type: "paragraph",
19477
20022
  raw: content,
19478
20023
  children: content ? [{
@@ -19480,7 +20025,9 @@ function processTokens(tokens, options) {
19480
20025
  content,
19481
20026
  raw: content
19482
20027
  }] : []
19483
- });
20028
+ };
20029
+ if (includeSourceMap) applyNodeSourceMap(paragraphNode, token, options);
20030
+ result.push(paragraphNode);
19484
20031
  linkifyContext.remember(content);
19485
20032
  i++;
19486
20033
  break;
@@ -19489,16 +20036,21 @@ function processTokens(tokens, options) {
19489
20036
  {
19490
20037
  const raw = String(token.content ?? "");
19491
20038
  const parsed = parseInlineTokens(token.children || [], raw, void 0, linkifyContext.options(raw));
19492
- if (parsed.length === 0) {} else if (parsed.every((n) => n.type === "html_block")) result.push(...parsed);
19493
- else {
20039
+ if (parsed.length === 0) {} else if (parsed.every((n) => n.type === "html_block")) {
20040
+ if (includeSourceMap) for (const node of parsed) applyNodeSourceMap(node, token, options);
20041
+ result.push(...parsed);
20042
+ } else {
19494
20043
  const paragraphNode = {
19495
20044
  type: "paragraph",
19496
20045
  raw,
19497
20046
  children: parsed
19498
20047
  };
20048
+ if (includeSourceMap) applyNodeSourceMap(paragraphNode, token, options);
19499
20049
  const promoted = maybePromoteCustomNodeFromParagraph(paragraphNode, options);
19500
- if (promoted) result.push(...promoted);
19501
- else result.push(paragraphNode);
20050
+ if (promoted) {
20051
+ if (includeSourceMap) inheritSourceMap(promoted, paragraphNode);
20052
+ result.push(...promoted);
20053
+ } else result.push(paragraphNode);
19502
20054
  }
19503
20055
  linkifyContext.remember(raw);
19504
20056
  }
@@ -19514,6 +20066,7 @@ function processTokens(tokens, options) {
19514
20066
 
19515
20067
  //#endregion
19516
20068
  //#region src/htmlRenderUtils.ts
20069
+ const HTML_TOKEN_TAG_NAME_RE = /^([a-z][\w-]*)(?=[\t\n\f\r />]|$)/i;
19517
20070
  const SAFE_BLOCKED_HTML_TAGS = new Set([
19518
20071
  ...BLOCKED_HTML_TAGS,
19519
20072
  "base",
@@ -19587,7 +20140,6 @@ const SAFE_ALLOWED_HTML_TAGS = new Set([
19587
20140
  "tr",
19588
20141
  "ul"
19589
20142
  ]);
19590
- const CUSTOM_TAG_REGEX = /<([a-z][a-z0-9-]*)\b[^>]*>/gi;
19591
20143
  function hasOwn(obj, key) {
19592
20144
  return Object.prototype.hasOwnProperty.call(obj, key);
19593
20145
  }
@@ -19625,6 +20177,18 @@ function serializeAttrs(attrs) {
19625
20177
  if (pairs.length === 0) return "";
19626
20178
  return pairs.map(([name, value]) => value === "" ? ` ${name}` : ` ${name}="${escapeAttr(value)}"`).join("");
19627
20179
  }
20180
+ function parseHtmlTokenTag(rawContent) {
20181
+ const isClosing = rawContent.startsWith("/");
20182
+ const source = isClosing ? rawContent.slice(1) : rawContent;
20183
+ const match = source.match(HTML_TOKEN_TAG_NAME_RE);
20184
+ if (!match) return null;
20185
+ return {
20186
+ attrsStr: isClosing ? "" : source.slice(match[0].length).trimStart(),
20187
+ isClosing,
20188
+ isSelfClosing: !isClosing && rawContent.trimEnd().endsWith("/"),
20189
+ tagName: match[1]
20190
+ };
20191
+ }
19628
20192
  function isUnsafeSrcset(value, tagName) {
19629
20193
  const candidates = value.split(",").map((candidate) => candidate.trim()).filter(Boolean);
19630
20194
  if (candidates.length === 0) return false;
@@ -19797,37 +20361,34 @@ function tokenizeHtml(html) {
19797
20361
  }
19798
20362
  const tagEnd = html.indexOf(">", tagStart);
19799
20363
  if (tagEnd === -1) break;
19800
- const tagContent = html.slice(tagStart + 1, tagEnd).trim();
19801
- const isClosingTag$1 = tagContent.startsWith("/");
19802
- const isSelfClosing$1 = tagContent.endsWith("/");
19803
- if (isClosingTag$1) {
19804
- const tagName = tagContent.slice(1).trim();
19805
- tokens.push({
19806
- type: "tag_close",
19807
- tagName
20364
+ const parsedTag = parseHtmlTokenTag(html.slice(tagStart + 1, tagEnd));
20365
+ if (!parsedTag) {
20366
+ const rawTag = html.slice(tagStart, tagEnd + 1);
20367
+ if (isMeaningfulText(rawTag)) tokens.push({
20368
+ type: "text",
20369
+ content: rawTag
19808
20370
  });
19809
- } else {
19810
- const spaceIndex = tagContent.indexOf(" ");
19811
- let tagName;
19812
- let attrsStr = "";
19813
- if (spaceIndex === -1) tagName = isSelfClosing$1 ? tagContent.slice(0, -1).trim() : tagContent.trim();
19814
- else {
19815
- tagName = tagContent.slice(0, spaceIndex).trim();
19816
- attrsStr = tagContent.slice(spaceIndex + 1);
19817
- }
20371
+ pos = tagEnd + 1;
20372
+ continue;
20373
+ }
20374
+ if (parsedTag.isClosing) tokens.push({
20375
+ type: "tag_close",
20376
+ tagName: parsedTag.tagName
20377
+ });
20378
+ else {
19818
20379
  const attrs = {};
19819
- if (attrsStr) {
20380
+ if (parsedTag.attrsStr) {
19820
20381
  const attrRegex = /([^\s=]+)(?:=(?:"([^"]*)"|'([^']*)'|(\S*)))?/g;
19821
20382
  let attrMatch;
19822
- while ((attrMatch = attrRegex.exec(attrsStr)) !== null) {
20383
+ while ((attrMatch = attrRegex.exec(parsedTag.attrsStr)) !== null) {
19823
20384
  const name = attrMatch[1];
19824
20385
  const value = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? "";
19825
20386
  if (name && !name.endsWith("/")) attrs[name] = value;
19826
20387
  }
19827
20388
  }
19828
20389
  tokens.push({
19829
- type: isSelfClosing$1 || VOID_HTML_TAGS.has(tagName.toLowerCase()) ? "self_closing" : "tag_open",
19830
- tagName,
20390
+ type: parsedTag.isSelfClosing || VOID_HTML_TAGS.has(parsedTag.tagName.toLowerCase()) ? "self_closing" : "tag_open",
20391
+ tagName: parsedTag.tagName,
19831
20392
  attrs
19832
20393
  });
19833
20394
  }
@@ -19881,43 +20442,36 @@ function tokenizeHtmlPreservingText(html) {
19881
20442
  }
19882
20443
  const tagEnd = html.indexOf(">", tagStart);
19883
20444
  if (tagEnd === -1) break;
19884
- const tagContent = html.slice(tagStart + 1, tagEnd).trim();
19885
- if (!tagContent) {
20445
+ const parsedTag = parseHtmlTokenTag(html.slice(tagStart + 1, tagEnd));
20446
+ if (!parsedTag) {
20447
+ tokens.push({
20448
+ type: "text",
20449
+ content: html.slice(tagStart, tagEnd + 1)
20450
+ });
19886
20451
  pos = tagEnd + 1;
19887
20452
  continue;
19888
20453
  }
19889
- const isClosingTag$1 = tagContent.startsWith("/");
19890
- const isSelfClosing$1 = tagContent.endsWith("/");
19891
- if (isClosingTag$1) {
19892
- const tagName$1 = tagContent.slice(1).trim();
20454
+ if (parsedTag.isClosing) {
19893
20455
  tokens.push({
19894
20456
  type: "tag_close",
19895
- tagName: tagName$1
20457
+ tagName: parsedTag.tagName
19896
20458
  });
19897
20459
  pos = tagEnd + 1;
19898
20460
  continue;
19899
20461
  }
19900
- const spaceIndex = tagContent.indexOf(" ");
19901
- let tagName = "";
19902
- let attrsStr = "";
19903
- if (spaceIndex === -1) tagName = isSelfClosing$1 ? tagContent.slice(0, -1).trim() : tagContent.trim();
19904
- else {
19905
- tagName = tagContent.slice(0, spaceIndex).trim();
19906
- attrsStr = tagContent.slice(spaceIndex + 1);
19907
- }
19908
20462
  const attrs = {};
19909
- if (attrsStr) {
20463
+ if (parsedTag.attrsStr) {
19910
20464
  const attrRegex = /([^\s=]+)(?:=(?:"([^"]*)"|'([^']*)'|(\S*)))?/g;
19911
20465
  let attrMatch;
19912
- while ((attrMatch = attrRegex.exec(attrsStr)) !== null) {
20466
+ while ((attrMatch = attrRegex.exec(parsedTag.attrsStr)) !== null) {
19913
20467
  const name = attrMatch[1];
19914
20468
  const value = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? "";
19915
20469
  if (name && !name.endsWith("/")) attrs[name] = value;
19916
20470
  }
19917
20471
  }
19918
20472
  tokens.push({
19919
- type: isSelfClosing$1 || VOID_HTML_TAGS.has(tagName.toLowerCase()) ? "self_closing" : "tag_open",
19920
- tagName,
20473
+ type: parsedTag.isSelfClosing || VOID_HTML_TAGS.has(parsedTag.tagName.toLowerCase()) ? "self_closing" : "tag_open",
20474
+ tagName: parsedTag.tagName,
19921
20475
  attrs
19922
20476
  });
19923
20477
  pos = tagEnd + 1;
@@ -19934,9 +20488,7 @@ function serializeLiteralHtmlTag(token) {
19934
20488
  function hasCustomHtmlComponents(content, customComponents) {
19935
20489
  if (!content || !content.includes("<")) return false;
19936
20490
  if (!customComponents || Object.keys(customComponents).length === 0) return false;
19937
- CUSTOM_TAG_REGEX.lastIndex = 0;
19938
- let match;
19939
- while ((match = CUSTOM_TAG_REGEX.exec(content)) !== null) if (isCustomHtmlComponentTag(match[1], customComponents)) return true;
20491
+ for (const token of tokenizeHtml(content)) if ((token.type === "tag_open" || token.type === "self_closing") && isCustomHtmlComponentTag(token.tagName ?? "", customComponents)) return true;
19940
20492
  return false;
19941
20493
  }
19942
20494
  function sanitizeHtmlContent(content, policy = "safe") {