stream-markdown-parser 1.0.3 → 1.0.6
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/README.md +5 -5
- package/README.zh-CN.md +5 -5
- package/dist/index.cjs +19869 -0
- package/dist/index.d.cts +623 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +411 -19
- package/dist/index.js.map +1 -1
- package/package.json +15 -13
package/dist/index.js
CHANGED
|
@@ -10191,10 +10191,19 @@ const URL_HTML_ATTR_NAMES = [
|
|
|
10191
10191
|
const BLOCKED_HTML_TAG_NAMES = ["script"];
|
|
10192
10192
|
const NON_STRUCTURING_HTML_TAG_NAMES = [
|
|
10193
10193
|
"pre",
|
|
10194
|
+
"iframe",
|
|
10194
10195
|
"script",
|
|
10195
10196
|
"style",
|
|
10197
|
+
"table",
|
|
10198
|
+
"tbody",
|
|
10199
|
+
"td",
|
|
10200
|
+
"tfoot",
|
|
10201
|
+
"th",
|
|
10202
|
+
"thead",
|
|
10196
10203
|
"textarea",
|
|
10197
|
-
"
|
|
10204
|
+
"tr",
|
|
10205
|
+
"title",
|
|
10206
|
+
"video"
|
|
10198
10207
|
];
|
|
10199
10208
|
const VOID_HTML_TAGS = new Set(VOID_HTML_TAG_NAMES);
|
|
10200
10209
|
const STANDARD_BLOCK_HTML_TAGS = new Set(BLOCK_HTML_TAG_NAMES);
|
|
@@ -10247,19 +10256,38 @@ const HREF_URL_PROTOCOLS = new Set([
|
|
|
10247
10256
|
"mailto",
|
|
10248
10257
|
"tel"
|
|
10249
10258
|
]);
|
|
10259
|
+
const BLOCKED_HREF_URL_PROTOCOLS = new Set([
|
|
10260
|
+
"javascript",
|
|
10261
|
+
"vbscript",
|
|
10262
|
+
"data",
|
|
10263
|
+
"file",
|
|
10264
|
+
"ftp",
|
|
10265
|
+
"blob",
|
|
10266
|
+
"filesystem",
|
|
10267
|
+
"intent",
|
|
10268
|
+
"chrome",
|
|
10269
|
+
"chrome-extension",
|
|
10270
|
+
"moz-extension",
|
|
10271
|
+
"ms-browser-extension",
|
|
10272
|
+
"view-source"
|
|
10273
|
+
]);
|
|
10250
10274
|
const RESOURCE_URL_PROTOCOLS = new Set(["http", "https"]);
|
|
10251
10275
|
function getUrlScheme(normalized) {
|
|
10252
10276
|
return normalized.match(/^([a-z][a-z0-9+.-]*):/i)?.[1]?.toLowerCase() ?? "";
|
|
10253
10277
|
}
|
|
10278
|
+
function isLinkHrefUrlContext(tagName, attrName) {
|
|
10279
|
+
if (!tagName) return !attrName || attrName === "href";
|
|
10280
|
+
return (tagName === "a" || tagName === "area") && (!attrName || attrName === "href" || attrName === "xlink:href");
|
|
10281
|
+
}
|
|
10254
10282
|
function getAllowedUrlProtocols(tagName, attrName) {
|
|
10255
|
-
if (attrName === "href") return HREF_URL_PROTOCOLS;
|
|
10256
|
-
if (attrName === "xlink:href") return HREF_URL_PROTOCOLS;
|
|
10283
|
+
if (attrName === "href") return isLinkHrefUrlContext(tagName, attrName) ? HREF_URL_PROTOCOLS : RESOURCE_URL_PROTOCOLS;
|
|
10284
|
+
if (attrName === "xlink:href") return isLinkHrefUrlContext(tagName, attrName) ? HREF_URL_PROTOCOLS : RESOURCE_URL_PROTOCOLS;
|
|
10257
10285
|
if (attrName === "src") return RESOURCE_URL_PROTOCOLS;
|
|
10258
10286
|
if (attrName === "srcset") return RESOURCE_URL_PROTOCOLS;
|
|
10259
10287
|
if (attrName === "poster") return RESOURCE_URL_PROTOCOLS;
|
|
10260
10288
|
if (attrName === "action" || attrName === "formaction") return RESOURCE_URL_PROTOCOLS;
|
|
10261
10289
|
if (attrName === "data") return RESOURCE_URL_PROTOCOLS;
|
|
10262
|
-
if (tagName
|
|
10290
|
+
if (isLinkHrefUrlContext(tagName, attrName)) return HREF_URL_PROTOCOLS;
|
|
10263
10291
|
return HREF_URL_PROTOCOLS;
|
|
10264
10292
|
}
|
|
10265
10293
|
function isUnsafeHtmlUrl(value, context = {}) {
|
|
@@ -10276,6 +10304,7 @@ function isUnsafeHtmlUrl(value, context = {}) {
|
|
|
10276
10304
|
if (normalized.startsWith("/") || normalized.startsWith("./") || normalized.startsWith("../") || normalized.startsWith("#") || normalized.startsWith("?")) return false;
|
|
10277
10305
|
const scheme = getUrlScheme(normalized);
|
|
10278
10306
|
if (!scheme) return false;
|
|
10307
|
+
if (isLinkHrefUrlContext(tagName, attrName)) return BLOCKED_HREF_URL_PROTOCOLS.has(scheme);
|
|
10279
10308
|
return !getAllowedUrlProtocols(tagName, attrName).has(scheme);
|
|
10280
10309
|
}
|
|
10281
10310
|
function shouldOpenLinkInNewTab(href) {
|
|
@@ -12712,6 +12741,16 @@ function isMathLike(s) {
|
|
|
12712
12741
|
|
|
12713
12742
|
//#endregion
|
|
12714
12743
|
//#region src/plugins/math.ts
|
|
12744
|
+
const MARKSTREAM_MATH_PLUGIN_APPLIED = "__markstreamMathPluginApplied";
|
|
12745
|
+
const TOLERANT_BOUNDARY_SCAN_MAX_LINES = 80;
|
|
12746
|
+
const TOLERANT_BOUNDARY_SCAN_MAX_CHARS = 2e4;
|
|
12747
|
+
const TOLERANT_BOUNDARY_SCAN_TAIL_CHARS = TOLERANT_BOUNDARY_SCAN_MAX_CHARS + 4096;
|
|
12748
|
+
function hasMarkstreamMathPlugin(md) {
|
|
12749
|
+
return !!md[MARKSTREAM_MATH_PLUGIN_APPLIED];
|
|
12750
|
+
}
|
|
12751
|
+
function markMarkstreamMathPluginApplied(md) {
|
|
12752
|
+
md[MARKSTREAM_MATH_PLUGIN_APPLIED] = true;
|
|
12753
|
+
}
|
|
12715
12754
|
const KATEX_COMMANDS = [
|
|
12716
12755
|
"ldots",
|
|
12717
12756
|
"cdots",
|
|
@@ -12985,6 +13024,233 @@ function findSingleDollarClose(src, startIdx) {
|
|
|
12985
13024
|
}
|
|
12986
13025
|
return -1;
|
|
12987
13026
|
}
|
|
13027
|
+
function findUnescapedDelimiter(src, delimiter$1, startIdx = 0) {
|
|
13028
|
+
let searchPos = Math.max(0, startIdx);
|
|
13029
|
+
while (searchPos < src.length) {
|
|
13030
|
+
const index = src.indexOf(delimiter$1, searchPos);
|
|
13031
|
+
if (index === -1) return -1;
|
|
13032
|
+
if (!isEscapedAt(src, index)) return index;
|
|
13033
|
+
searchPos = index + Math.max(1, delimiter$1.length);
|
|
13034
|
+
}
|
|
13035
|
+
return -1;
|
|
13036
|
+
}
|
|
13037
|
+
function countUnescapedDelimiter(src, delimiter$1, startIdx = 0, endIdx = src.length, excludedRanges = []) {
|
|
13038
|
+
let count = 0;
|
|
13039
|
+
let searchPos = Math.max(0, startIdx);
|
|
13040
|
+
const end = Math.min(src.length, Math.max(0, endIdx));
|
|
13041
|
+
while (searchPos < end) {
|
|
13042
|
+
const index = src.indexOf(delimiter$1, searchPos);
|
|
13043
|
+
if (index === -1 || index >= end) break;
|
|
13044
|
+
const excluded = findRangeAt(excludedRanges, index);
|
|
13045
|
+
if (excluded) {
|
|
13046
|
+
searchPos = Math.max(index + Math.max(1, delimiter$1.length), excluded[1]);
|
|
13047
|
+
continue;
|
|
13048
|
+
}
|
|
13049
|
+
if (!isEscapedAt(src, index)) count++;
|
|
13050
|
+
searchPos = index + Math.max(1, delimiter$1.length);
|
|
13051
|
+
}
|
|
13052
|
+
return count;
|
|
13053
|
+
}
|
|
13054
|
+
function getTolerantBoundaryLineEndOpenIndex(line, openDelim, closeDelim) {
|
|
13055
|
+
const source = trimRightSpaceOrTab(String(line ?? ""));
|
|
13056
|
+
if (!source.endsWith(openDelim)) return -1;
|
|
13057
|
+
const openIndex = source.length - openDelim.length;
|
|
13058
|
+
if (openIndex <= 0) return -1;
|
|
13059
|
+
if (!trimRightSpaceOrTab(source.slice(0, openIndex)).trim()) return -1;
|
|
13060
|
+
if (isEscapedAt(source, openIndex)) return -1;
|
|
13061
|
+
const codeSpanRanges = buildCodeSpanRanges(source);
|
|
13062
|
+
if (findRangeAt(codeSpanRanges, openIndex)) return -1;
|
|
13063
|
+
const previousOpenCount = countUnescapedDelimiter(source, openDelim, 0, openIndex, codeSpanRanges);
|
|
13064
|
+
if (openDelim === "$$") {
|
|
13065
|
+
if (previousOpenCount % 2 === 1) return -1;
|
|
13066
|
+
} else if (previousOpenCount > countUnescapedDelimiter(source, closeDelim, 0, openIndex, codeSpanRanges)) return -1;
|
|
13067
|
+
return openIndex;
|
|
13068
|
+
}
|
|
13069
|
+
function isSpaceOrTab(ch) {
|
|
13070
|
+
return ch === " " || ch === " ";
|
|
13071
|
+
}
|
|
13072
|
+
function trimRightSpaceOrTab(value) {
|
|
13073
|
+
let end = value.length;
|
|
13074
|
+
while (end > 0 && isSpaceOrTab(value[end - 1])) end--;
|
|
13075
|
+
return value.slice(0, end);
|
|
13076
|
+
}
|
|
13077
|
+
function countLineBreaks(value) {
|
|
13078
|
+
let count = 0;
|
|
13079
|
+
for (let index = 0; index < value.length; index++) if (value[index] === "\n") count++;
|
|
13080
|
+
return count;
|
|
13081
|
+
}
|
|
13082
|
+
function isAsciiDigit(ch) {
|
|
13083
|
+
if (!ch) return false;
|
|
13084
|
+
const code$1 = ch.charCodeAt(0);
|
|
13085
|
+
return code$1 >= 48 && code$1 <= 57;
|
|
13086
|
+
}
|
|
13087
|
+
function isThematicLikeLine(trimmed) {
|
|
13088
|
+
if (trimmed.length < 3) return false;
|
|
13089
|
+
const marker = trimmed[0];
|
|
13090
|
+
if (marker !== "-" && marker !== "*" && marker !== "_" && marker !== "=") return false;
|
|
13091
|
+
let markerCount = 0;
|
|
13092
|
+
for (let index = 0; index < trimmed.length; index++) {
|
|
13093
|
+
const ch = trimmed[index];
|
|
13094
|
+
if (ch === marker) {
|
|
13095
|
+
markerCount++;
|
|
13096
|
+
continue;
|
|
13097
|
+
}
|
|
13098
|
+
if (isSpaceOrTab(ch)) continue;
|
|
13099
|
+
return false;
|
|
13100
|
+
}
|
|
13101
|
+
return markerCount >= 3;
|
|
13102
|
+
}
|
|
13103
|
+
function isMarkdownTableDelimiterCell(cell) {
|
|
13104
|
+
const value = cell.trim();
|
|
13105
|
+
if (!value) return false;
|
|
13106
|
+
let index = 0;
|
|
13107
|
+
if (value[index] === ":") index++;
|
|
13108
|
+
let dashCount = 0;
|
|
13109
|
+
while (value[index] === "-") {
|
|
13110
|
+
dashCount++;
|
|
13111
|
+
index++;
|
|
13112
|
+
}
|
|
13113
|
+
if (dashCount < 3) return false;
|
|
13114
|
+
if (value[index] === ":") index++;
|
|
13115
|
+
return index === value.length;
|
|
13116
|
+
}
|
|
13117
|
+
function isMarkdownTableDelimiterLine(trimmed) {
|
|
13118
|
+
if (!trimmed.includes("|")) return false;
|
|
13119
|
+
const withoutLeadingPipe = trimmed[0] === "|" ? trimmed.slice(1) : trimmed;
|
|
13120
|
+
return (withoutLeadingPipe.endsWith("|") ? withoutLeadingPipe.slice(0, -1) : withoutLeadingPipe).split("|").every(isMarkdownTableDelimiterCell);
|
|
13121
|
+
}
|
|
13122
|
+
function isOrderedListBoundaryLine(trimmed) {
|
|
13123
|
+
let index = 0;
|
|
13124
|
+
if (!isAsciiDigit(trimmed[index])) return false;
|
|
13125
|
+
while (isAsciiDigit(trimmed[index])) index++;
|
|
13126
|
+
if (trimmed[index] !== "." && trimmed[index] !== ")") return false;
|
|
13127
|
+
return isSpaceOrTab(trimmed[index + 1]);
|
|
13128
|
+
}
|
|
13129
|
+
function isTolerantBoundaryStopLine(line) {
|
|
13130
|
+
const trimmed = line.trimStart();
|
|
13131
|
+
if (!trimmed) return true;
|
|
13132
|
+
if (trimmed.startsWith("```") || trimmed.startsWith("~~~") || trimmed.startsWith(":::")) return true;
|
|
13133
|
+
if (trimmed[0] === ">" || trimmed[0] === "<") return true;
|
|
13134
|
+
if (trimmed[0] === "#") {
|
|
13135
|
+
let level = 0;
|
|
13136
|
+
while (trimmed[level] === "#") level++;
|
|
13137
|
+
if (level >= 1 && level <= 6 && isSpaceOrTab(trimmed[level])) return true;
|
|
13138
|
+
}
|
|
13139
|
+
if ((trimmed[0] === "-" || trimmed[0] === "+" || trimmed[0] === "*") && isSpaceOrTab(trimmed[1])) return true;
|
|
13140
|
+
if (isOrderedListBoundaryLine(trimmed)) return true;
|
|
13141
|
+
if (isThematicLikeLine(trimmed)) return true;
|
|
13142
|
+
if (isMarkdownTableDelimiterLine(trimmed)) return true;
|
|
13143
|
+
return false;
|
|
13144
|
+
}
|
|
13145
|
+
function appendTolerantBoundaryContent(content, line) {
|
|
13146
|
+
if (!content) return line;
|
|
13147
|
+
if (!line) return content;
|
|
13148
|
+
return `${content}\n${line}`;
|
|
13149
|
+
}
|
|
13150
|
+
function isTolerantMathBlockContent(content) {
|
|
13151
|
+
const stripped = String(content ?? "").trim();
|
|
13152
|
+
if (!stripped) return false;
|
|
13153
|
+
return isMathLike(stripped);
|
|
13154
|
+
}
|
|
13155
|
+
function hashTolerantBoundaryContent(content) {
|
|
13156
|
+
let hash = 0;
|
|
13157
|
+
for (let index = 0; index < content.length; index++) hash = hash * 31 + content.charCodeAt(index) | 0;
|
|
13158
|
+
return hash.toString(36);
|
|
13159
|
+
}
|
|
13160
|
+
function getTolerantBoundaryScanWindow(source) {
|
|
13161
|
+
if (source.length <= TOLERANT_BOUNDARY_SCAN_TAIL_CHARS) return {
|
|
13162
|
+
source,
|
|
13163
|
+
lineOffset: 0
|
|
13164
|
+
};
|
|
13165
|
+
let start = source.length - TOLERANT_BOUNDARY_SCAN_TAIL_CHARS;
|
|
13166
|
+
const nextLineBreak = source.indexOf("\n", start);
|
|
13167
|
+
if (nextLineBreak === -1) return {
|
|
13168
|
+
source: "",
|
|
13169
|
+
lineOffset: countLineBreaks(source)
|
|
13170
|
+
};
|
|
13171
|
+
start = nextLineBreak + 1;
|
|
13172
|
+
return {
|
|
13173
|
+
source: source.slice(start),
|
|
13174
|
+
lineOffset: countLineBreaks(source.slice(0, start))
|
|
13175
|
+
};
|
|
13176
|
+
}
|
|
13177
|
+
function mayContainTolerantMathBlockBoundaryOpener(markdown) {
|
|
13178
|
+
const fullSource = String(markdown ?? "");
|
|
13179
|
+
if (!fullSource || !fullSource.includes("$$") && !fullSource.includes("\\[")) return false;
|
|
13180
|
+
const { source } = getTolerantBoundaryScanWindow(fullSource);
|
|
13181
|
+
if (!source) return false;
|
|
13182
|
+
const lines = source.split(/\r?\n/);
|
|
13183
|
+
const startLine = Math.max(0, lines.length - TOLERANT_BOUNDARY_SCAN_MAX_LINES - 2);
|
|
13184
|
+
const delimiters = [["$$", "$$"], ["\\[", "\\]"]];
|
|
13185
|
+
for (let line = startLine; line < lines.length; line++) {
|
|
13186
|
+
const openingLine = trimRightSpaceOrTab(lines[line]);
|
|
13187
|
+
if (!openingLine) continue;
|
|
13188
|
+
if (isTolerantBoundaryStopLine(openingLine)) continue;
|
|
13189
|
+
for (const [openDelim, closeDelim] of delimiters) if (getTolerantBoundaryLineEndOpenIndex(openingLine, openDelim, closeDelim) !== -1) return true;
|
|
13190
|
+
}
|
|
13191
|
+
return false;
|
|
13192
|
+
}
|
|
13193
|
+
function getTolerantMathBlockBoundaryStreamKey(markdown) {
|
|
13194
|
+
const fullSource = String(markdown ?? "");
|
|
13195
|
+
if (!fullSource || !fullSource.includes("$$") && !fullSource.includes("\\[")) return null;
|
|
13196
|
+
const { source, lineOffset } = getTolerantBoundaryScanWindow(fullSource);
|
|
13197
|
+
if (!source) return null;
|
|
13198
|
+
const lines = source.split(/\r?\n/);
|
|
13199
|
+
const startLine = Math.max(0, lines.length - TOLERANT_BOUNDARY_SCAN_MAX_LINES - 2);
|
|
13200
|
+
const delimiters = [["$$", "$$"], ["\\[", "\\]"]];
|
|
13201
|
+
for (let line = startLine; line < lines.length - 1; line++) {
|
|
13202
|
+
const openingLine = trimRightSpaceOrTab(lines[line]);
|
|
13203
|
+
for (const [openDelim, closeDelim] of delimiters) {
|
|
13204
|
+
const openIndex = getTolerantBoundaryLineEndOpenIndex(openingLine, openDelim, closeDelim);
|
|
13205
|
+
if (openIndex === -1) continue;
|
|
13206
|
+
let content = "";
|
|
13207
|
+
let stopped = false;
|
|
13208
|
+
for (let currentLine = line + 1; currentLine < lines.length; currentLine++) {
|
|
13209
|
+
if (currentLine - line > TOLERANT_BOUNDARY_SCAN_MAX_LINES) {
|
|
13210
|
+
stopped = true;
|
|
13211
|
+
break;
|
|
13212
|
+
}
|
|
13213
|
+
const current = lines[currentLine];
|
|
13214
|
+
const closeIndex = findUnescapedDelimiter(current, closeDelim);
|
|
13215
|
+
if (closeIndex !== -1) {
|
|
13216
|
+
const nextContent = appendTolerantBoundaryContent(content, current.slice(0, closeIndex));
|
|
13217
|
+
if (!isTolerantMathBlockContent(nextContent)) {
|
|
13218
|
+
stopped = true;
|
|
13219
|
+
break;
|
|
13220
|
+
}
|
|
13221
|
+
const suffix = current.slice(closeIndex + closeDelim.length);
|
|
13222
|
+
const suffixKey = suffix.trim() ? `suffix:${hashTolerantBoundaryContent(suffix)}` : "nosuffix";
|
|
13223
|
+
return [
|
|
13224
|
+
"closed",
|
|
13225
|
+
openDelim,
|
|
13226
|
+
lineOffset + line,
|
|
13227
|
+
openIndex,
|
|
13228
|
+
lineOffset + currentLine,
|
|
13229
|
+
closeIndex,
|
|
13230
|
+
hashTolerantBoundaryContent(nextContent),
|
|
13231
|
+
suffixKey
|
|
13232
|
+
].join(":");
|
|
13233
|
+
}
|
|
13234
|
+
if (isTolerantBoundaryStopLine(current)) {
|
|
13235
|
+
stopped = true;
|
|
13236
|
+
break;
|
|
13237
|
+
}
|
|
13238
|
+
content = appendTolerantBoundaryContent(content, current);
|
|
13239
|
+
if (content.length > TOLERANT_BOUNDARY_SCAN_MAX_CHARS) {
|
|
13240
|
+
stopped = true;
|
|
13241
|
+
break;
|
|
13242
|
+
}
|
|
13243
|
+
}
|
|
13244
|
+
if (!stopped && isTolerantMathBlockContent(content)) return [
|
|
13245
|
+
"pending",
|
|
13246
|
+
openDelim,
|
|
13247
|
+
lineOffset + line,
|
|
13248
|
+
openIndex
|
|
13249
|
+
].join(":");
|
|
13250
|
+
}
|
|
13251
|
+
}
|
|
13252
|
+
return null;
|
|
13253
|
+
}
|
|
12988
13254
|
function isLikelyCurrencyRangeDollar(content, nextChar) {
|
|
12989
13255
|
const stripped = String(content ?? "").trim();
|
|
12990
13256
|
if (!stripped) return false;
|
|
@@ -13005,6 +13271,18 @@ function isLikelyPlaceholderDollar(content) {
|
|
|
13005
13271
|
return /^(?:\.{3,}|…+)$/.test(stripped);
|
|
13006
13272
|
}
|
|
13007
13273
|
function applyMath(md, mathOpts) {
|
|
13274
|
+
markMarkstreamMathPluginApplied(md);
|
|
13275
|
+
const pushInlineParagraph = (s, content, line) => {
|
|
13276
|
+
const paragraphContent = String(content ?? "").replace(/^[\t ]+/, "").replace(/[\t ]+$/, "");
|
|
13277
|
+
if (!paragraphContent) return;
|
|
13278
|
+
const paragraphOpen = s.push("paragraph_open", "p", 1);
|
|
13279
|
+
paragraphOpen.map = [line, line + 1];
|
|
13280
|
+
const inlineToken = s.push("inline", "", 0);
|
|
13281
|
+
inlineToken.content = paragraphContent;
|
|
13282
|
+
inlineToken.map = [line, line + 1];
|
|
13283
|
+
inlineToken.children = [];
|
|
13284
|
+
s.push("paragraph_close", "p", -1);
|
|
13285
|
+
};
|
|
13008
13286
|
const mathInline = (state, silent) => {
|
|
13009
13287
|
const s = state;
|
|
13010
13288
|
const strict = !!mathOpts?.strictDelimiters;
|
|
@@ -13020,6 +13298,18 @@ function applyMath(md, mathOpts) {
|
|
|
13020
13298
|
return end;
|
|
13021
13299
|
};
|
|
13022
13300
|
if (/^\*[^*]+/.test(s.src)) return false;
|
|
13301
|
+
if (s.src[s.pos] === "$") {
|
|
13302
|
+
let dollarRunEnd = s.pos + 1;
|
|
13303
|
+
while (s.src[dollarRunEnd] === "$") dollarRunEnd++;
|
|
13304
|
+
const dollarRunLength = dollarRunEnd - s.pos;
|
|
13305
|
+
const nextChar = s.src[dollarRunEnd];
|
|
13306
|
+
if (dollarRunLength >= 3 && (!nextChar || /\s/.test(nextChar))) {
|
|
13307
|
+
const token = s.push("text", "", 0);
|
|
13308
|
+
token.content = s.src.slice(s.pos, dollarRunEnd);
|
|
13309
|
+
s.pos = dollarRunEnd;
|
|
13310
|
+
return true;
|
|
13311
|
+
}
|
|
13312
|
+
}
|
|
13023
13313
|
const delimiters = [
|
|
13024
13314
|
["$$", "$$"],
|
|
13025
13315
|
["$", "$"],
|
|
@@ -13383,6 +13673,8 @@ function applyMath(md, mathOpts) {
|
|
|
13383
13673
|
let openDelim = "";
|
|
13384
13674
|
let closeDelim = "";
|
|
13385
13675
|
let skipFirstLine = false;
|
|
13676
|
+
let prefixBeforeOpen = "";
|
|
13677
|
+
let tolerantBoundary = false;
|
|
13386
13678
|
for (const [open, close] of delimiters) if (lineText.startsWith(open)) if (open.includes("[")) if (mathOpts?.strictDelimiters) {
|
|
13387
13679
|
if (lineText.replace("\\", "") === "[") {
|
|
13388
13680
|
if (startLine + 1 < endLine) {
|
|
@@ -13423,8 +13715,11 @@ function applyMath(md, mathOpts) {
|
|
|
13423
13715
|
closeDelim = close;
|
|
13424
13716
|
break;
|
|
13425
13717
|
}
|
|
13426
|
-
else if (open === "$$"
|
|
13427
|
-
|
|
13718
|
+
else if ((open === "$$" || open === "\\[") && lineText.endsWith(open) && startLine + 1 < endLine) {
|
|
13719
|
+
const openIndex = getTolerantBoundaryLineEndOpenIndex(lineText, open, close);
|
|
13720
|
+
if (openIndex === -1) continue;
|
|
13721
|
+
prefixBeforeOpen = trimRightSpaceOrTab(lineText.slice(0, openIndex));
|
|
13722
|
+
tolerantBoundary = true;
|
|
13428
13723
|
const nextLineStartPos = s.bMarks[startLine + 1] + s.tShift[startLine + 1];
|
|
13429
13724
|
lineText = s.src.slice(nextLineStartPos, s.eMarks[startLine + 1]).trim();
|
|
13430
13725
|
skipFirstLine = true;
|
|
@@ -13434,13 +13729,13 @@ function applyMath(md, mathOpts) {
|
|
|
13434
13729
|
break;
|
|
13435
13730
|
}
|
|
13436
13731
|
if (!matched) return false;
|
|
13437
|
-
if (silent) return true;
|
|
13732
|
+
if (silent && !tolerantBoundary) return true;
|
|
13438
13733
|
const startDelimIndex = lineText.indexOf(openDelim);
|
|
13439
13734
|
const closeSearchStart = startDelimIndex + openDelim.length;
|
|
13440
13735
|
const escapedPlainBracketCloseIndex = !strict && openDelim === "[" ? lineText.indexOf("\\]", closeSearchStart) : -1;
|
|
13441
13736
|
const sameLineCloseDelim = escapedPlainBracketCloseIndex >= 0 ? "\\]" : closeDelim;
|
|
13442
|
-
const sameLineCloseIndex = escapedPlainBracketCloseIndex >= 0 ? escapedPlainBracketCloseIndex : lineText
|
|
13443
|
-
if (sameLineCloseIndex > openDelim.length) {
|
|
13737
|
+
const sameLineCloseIndex = escapedPlainBracketCloseIndex >= 0 ? escapedPlainBracketCloseIndex : findUnescapedDelimiter(lineText, closeDelim, closeSearchStart);
|
|
13738
|
+
if (!skipFirstLine && sameLineCloseIndex > openDelim.length) {
|
|
13444
13739
|
const content$1 = lineText.slice(startDelimIndex + openDelim.length, sameLineCloseIndex);
|
|
13445
13740
|
const token$1 = s.push("math_block", "math", 0);
|
|
13446
13741
|
token$1.content = normalizeStandaloneBackslashT(content$1);
|
|
@@ -13450,18 +13745,25 @@ function applyMath(md, mathOpts) {
|
|
|
13450
13745
|
token$1.block = true;
|
|
13451
13746
|
token$1.loading = false;
|
|
13452
13747
|
s.line = startLine + 1;
|
|
13748
|
+
const trailingAfterClose$1 = lineText.slice(sameLineCloseIndex + sameLineCloseDelim.length);
|
|
13749
|
+
if (trailingAfterClose$1.trim()) pushInlineParagraph(s, trailingAfterClose$1, startLine);
|
|
13453
13750
|
return true;
|
|
13454
13751
|
}
|
|
13455
13752
|
let nextLine = startLine;
|
|
13456
13753
|
let content = "";
|
|
13457
13754
|
let found = false;
|
|
13458
|
-
|
|
13755
|
+
let trailingAfterClose = "";
|
|
13756
|
+
let trailingAfterCloseLine = startLine;
|
|
13757
|
+
const firstLineContent = skipFirstLine ? lineText : lineText === openDelim ? "" : lineText.slice(openDelim.length);
|
|
13459
13758
|
const fallbackPlainBracketClose = !strict && openDelim === "\\[" ? "]" : "";
|
|
13460
|
-
|
|
13461
|
-
|
|
13759
|
+
const firstLineCloseIndex = findUnescapedDelimiter(firstLineContent, closeDelim);
|
|
13760
|
+
if (firstLineCloseIndex !== -1) {
|
|
13761
|
+
const endIndex = firstLineCloseIndex;
|
|
13462
13762
|
content = firstLineContent.slice(0, endIndex);
|
|
13763
|
+
trailingAfterClose = firstLineContent.slice(endIndex + closeDelim.length);
|
|
13764
|
+
trailingAfterCloseLine = skipFirstLine ? startLine + 1 : startLine;
|
|
13463
13765
|
found = true;
|
|
13464
|
-
nextLine =
|
|
13766
|
+
nextLine = trailingAfterCloseLine;
|
|
13465
13767
|
} else {
|
|
13466
13768
|
if (firstLineContent && !skipFirstLine) content = firstLineContent;
|
|
13467
13769
|
for (nextLine = startLine + 1; nextLine < endLine; nextLine++) {
|
|
@@ -13486,12 +13788,18 @@ function applyMath(md, mathOpts) {
|
|
|
13486
13788
|
found = true;
|
|
13487
13789
|
const endIndex = currentLine.indexOf("\\]");
|
|
13488
13790
|
closeDelim = "\\]";
|
|
13489
|
-
|
|
13791
|
+
const beforeClose = currentLine.slice(0, endIndex);
|
|
13792
|
+
if (beforeClose) content += (content ? "\n" : "") + beforeClose;
|
|
13793
|
+
trailingAfterClose = currentLine.slice(endIndex + closeDelim.length);
|
|
13794
|
+
trailingAfterCloseLine = nextLine;
|
|
13490
13795
|
break;
|
|
13491
|
-
} else if (currentLine
|
|
13796
|
+
} else if (findUnescapedDelimiter(currentLine, closeDelim) !== -1) {
|
|
13492
13797
|
found = true;
|
|
13493
|
-
const endIndex = currentLine
|
|
13494
|
-
|
|
13798
|
+
const endIndex = findUnescapedDelimiter(currentLine, closeDelim);
|
|
13799
|
+
const beforeClose = currentLine.slice(0, endIndex);
|
|
13800
|
+
if (beforeClose) content += (content ? "\n" : "") + beforeClose;
|
|
13801
|
+
trailingAfterClose = currentLine.slice(endIndex + closeDelim.length);
|
|
13802
|
+
trailingAfterCloseLine = nextLine;
|
|
13495
13803
|
break;
|
|
13496
13804
|
}
|
|
13497
13805
|
content += (content ? "\n" : "") + currentLine;
|
|
@@ -13499,7 +13807,9 @@ function applyMath(md, mathOpts) {
|
|
|
13499
13807
|
}
|
|
13500
13808
|
if ((!allowLoading || strict) && !found) return false;
|
|
13501
13809
|
const hasMarkdownPrefix = /^\s*!\[/.test(content);
|
|
13502
|
-
if (!(openDelim === "$$" ? !hasMarkdownPrefix : openDelim === "[" ? isPlainBracketMathLike(content) : isMathLike(content))) return false;
|
|
13810
|
+
if (!(tolerantBoundary ? !hasMarkdownPrefix && isTolerantMathBlockContent(content) : openDelim === "$$" ? !hasMarkdownPrefix : openDelim === "[" ? isPlainBracketMathLike(content) : isMathLike(content))) return false;
|
|
13811
|
+
if (silent) return true;
|
|
13812
|
+
if (prefixBeforeOpen) pushInlineParagraph(s, prefixBeforeOpen, startLine);
|
|
13503
13813
|
const token = s.push("math_block", "math", 0);
|
|
13504
13814
|
token.content = normalizeStandaloneBackslashT(content);
|
|
13505
13815
|
token.markup = openDelim === "$$" ? "$$" : openDelim === "[" ? "[]" : "\\[\\]";
|
|
@@ -13508,6 +13818,7 @@ function applyMath(md, mathOpts) {
|
|
|
13508
13818
|
token.block = true;
|
|
13509
13819
|
token.loading = !found;
|
|
13510
13820
|
s.line = nextLine + 1;
|
|
13821
|
+
if (trailingAfterClose.trim()) pushInlineParagraph(s, trailingAfterClose, trailingAfterCloseLine);
|
|
13511
13822
|
return true;
|
|
13512
13823
|
};
|
|
13513
13824
|
const explicitMathBlockBeforeSetext = (state, startLine, endLine, silent) => {
|
|
@@ -16848,6 +17159,8 @@ function parseParagraph(tokens, index, options) {
|
|
|
16848
17159
|
//#endregion
|
|
16849
17160
|
//#region src/parser/index.ts
|
|
16850
17161
|
const streamParseEnvCache = /* @__PURE__ */ new WeakMap();
|
|
17162
|
+
const tolerantMathBoundaryStreamCache = /* @__PURE__ */ new WeakMap();
|
|
17163
|
+
const TOLERANT_BOUNDARY_SPLIT_OPENERS = ["$$", "\\["];
|
|
16851
17164
|
function getNodeFields(node) {
|
|
16852
17165
|
return node;
|
|
16853
17166
|
}
|
|
@@ -17027,13 +17340,89 @@ function shouldResetTopLevelStreamCacheForFinalAutoParse(md, options) {
|
|
|
17027
17340
|
const stream = md.stream;
|
|
17028
17341
|
return options.final === true && streamParse === "auto" && internalOptions.__disableStreamParse !== true && stream?.enabled === true && typeof stream.reset === "function";
|
|
17029
17342
|
}
|
|
17343
|
+
function clearTolerantMathBoundaryStreamCache(md) {
|
|
17344
|
+
tolerantMathBoundaryStreamCache.delete(md);
|
|
17345
|
+
}
|
|
17346
|
+
function setTolerantMathBoundaryStreamCache(md, source, key) {
|
|
17347
|
+
tolerantMathBoundaryStreamCache.set(md, {
|
|
17348
|
+
source,
|
|
17349
|
+
key,
|
|
17350
|
+
pendingCandidate: key === null && mayContainTolerantMathBlockBoundaryOpener(source)
|
|
17351
|
+
});
|
|
17352
|
+
}
|
|
17353
|
+
function sourceEndsWithSplitTolerantBoundaryPrefix(source) {
|
|
17354
|
+
return source.endsWith("$") || source.endsWith("\\");
|
|
17355
|
+
}
|
|
17356
|
+
function sourceEndsWithCompleteTolerantBoundaryOpener(source) {
|
|
17357
|
+
const lastLineStart = Math.max(source.lastIndexOf("\n") + 1, 0);
|
|
17358
|
+
const lastLine = source.slice(lastLineStart).replace(/[\t ]+$/, "");
|
|
17359
|
+
return TOLERANT_BOUNDARY_SPLIT_OPENERS.some((open) => lastLine.endsWith(open));
|
|
17360
|
+
}
|
|
17361
|
+
function appendedChunkMayAffectTolerantMathBoundary(previousSource, appended) {
|
|
17362
|
+
if (!appended) return false;
|
|
17363
|
+
if (appended.includes("$$") || appended.includes("\\[") || appended.includes("\\]")) return true;
|
|
17364
|
+
if (previousSource.endsWith("$") && appended[0] === "$") return true;
|
|
17365
|
+
if (previousSource.endsWith("\\") && (appended[0] === "[" || appended[0] === "]")) return true;
|
|
17366
|
+
if (sourceEndsWithCompleteTolerantBoundaryOpener(previousSource) && /[\r\n]/.test(appended)) return true;
|
|
17367
|
+
return false;
|
|
17368
|
+
}
|
|
17369
|
+
function syncTolerantMathBoundaryStreamCache(md, source) {
|
|
17370
|
+
if (!hasMarkstreamMathPlugin(md)) return;
|
|
17371
|
+
const stream = md.stream;
|
|
17372
|
+
if (typeof stream?.reset !== "function") return;
|
|
17373
|
+
const owner = md;
|
|
17374
|
+
const previous = tolerantMathBoundaryStreamCache.get(owner);
|
|
17375
|
+
if (previous?.source === source) return;
|
|
17376
|
+
if (previous && source.startsWith(previous.source)) {
|
|
17377
|
+
const appended = source.slice(previous.source.length);
|
|
17378
|
+
if (previous.key === null && previous.pendingCandidate === false && !appendedChunkMayAffectTolerantMathBoundary(previous.source, appended) && !sourceEndsWithSplitTolerantBoundaryPrefix(source)) {
|
|
17379
|
+
previous.source = source;
|
|
17380
|
+
return;
|
|
17381
|
+
}
|
|
17382
|
+
}
|
|
17383
|
+
const nextKey = getTolerantMathBlockBoundaryStreamKey(source);
|
|
17384
|
+
const sourceWasReplaced = previous ? !source.startsWith(previous.source) : false;
|
|
17385
|
+
if (previous && (sourceWasReplaced || previous.key !== nextKey)) stream.reset();
|
|
17386
|
+
else if (!previous && nextKey) stream.reset();
|
|
17387
|
+
setTolerantMathBoundaryStreamCache(md, source, nextKey);
|
|
17388
|
+
}
|
|
17030
17389
|
function shouldCloneTopLevelStreamTokens(options) {
|
|
17031
17390
|
return typeof options.preTransformTokens === "function" || typeof options.postTransformTokens === "function";
|
|
17032
17391
|
}
|
|
17392
|
+
function sameTokenMap(left, right) {
|
|
17393
|
+
const leftMap = left?.map;
|
|
17394
|
+
const rightMap = right?.map;
|
|
17395
|
+
if (leftMap === rightMap) return true;
|
|
17396
|
+
if (!Array.isArray(leftMap) || !Array.isArray(rightMap)) return false;
|
|
17397
|
+
return leftMap.length === rightMap.length && leftMap.every((value, index) => value === rightMap[index]);
|
|
17398
|
+
}
|
|
17399
|
+
function isSameTokenShape(left, right) {
|
|
17400
|
+
return !!left && !!right && left.type === right.type && left.tag === right.tag && left.nesting === right.nesting && left.markup === right.markup && left.content === right.content && sameTokenMap(left, right);
|
|
17401
|
+
}
|
|
17402
|
+
function isParagraphTokenTriplet(tokens, index) {
|
|
17403
|
+
return tokens[index]?.type === "paragraph_open" && tokens[index + 1]?.type === "inline" && tokens[index + 2]?.type === "paragraph_close";
|
|
17404
|
+
}
|
|
17405
|
+
function hasAdjacentDuplicateParagraphTokenTriplet(tokens) {
|
|
17406
|
+
for (let index = 0; index + 5 < tokens.length; index++) if (isParagraphTokenTriplet(tokens, index) && isParagraphTokenTriplet(tokens, index + 3) && isSameTokenShape(tokens[index], tokens[index + 3]) && isSameTokenShape(tokens[index + 1], tokens[index + 4]) && isSameTokenShape(tokens[index + 2], tokens[index + 5])) return true;
|
|
17407
|
+
return false;
|
|
17408
|
+
}
|
|
17409
|
+
function shouldFallbackDuplicateTolerantMathStreamTokens(md, source, tokens) {
|
|
17410
|
+
return hasMarkstreamMathPlugin(md) && mayContainTolerantMathBlockBoundaryOpener(source) && hasAdjacentDuplicateParagraphTokenTriplet(tokens);
|
|
17411
|
+
}
|
|
17412
|
+
function shouldUseSyncParseForPendingTolerantMathBoundary(md) {
|
|
17413
|
+
const cache = tolerantMathBoundaryStreamCache.get(md);
|
|
17414
|
+
return typeof cache?.key === "string" && cache.key.startsWith("pending:");
|
|
17415
|
+
}
|
|
17033
17416
|
function parseTopLevelTokens(md, source, env, options) {
|
|
17034
17417
|
if (options.customHtmlTags?.length) env.__markstreamCustomHtmlTags = options.customHtmlTags;
|
|
17035
17418
|
if (!shouldUseTopLevelStreamParse(md, options)) return md.parse(source, env);
|
|
17419
|
+
syncTolerantMathBoundaryStreamCache(md, source);
|
|
17420
|
+
if (shouldUseSyncParseForPendingTolerantMathBoundary(md)) return md.parse(source, env);
|
|
17036
17421
|
const tokens = md.stream.parse(source, getStableStreamEnv(md, env));
|
|
17422
|
+
if (shouldFallbackDuplicateTolerantMathStreamTokens(md, source, tokens)) {
|
|
17423
|
+
md.stream?.reset?.();
|
|
17424
|
+
return md.parse(source, env);
|
|
17425
|
+
}
|
|
17037
17426
|
if (!shouldCloneTopLevelStreamTokens(options)) return tokens;
|
|
17038
17427
|
const timing = getParseTiming(options);
|
|
17039
17428
|
if (!timing) return cloneMarkdownTokens(tokens, true);
|
|
@@ -18239,7 +18628,10 @@ function parseMarkdownToStructure(markdown, md, options = {}) {
|
|
|
18239
18628
|
const parseStartedAt = timing ? getParserNow() : 0;
|
|
18240
18629
|
const isFinal = !!options.final;
|
|
18241
18630
|
let safeMarkdown = (markdown ?? "").toString().replace(/([^\\])\r(ight|ho)/g, "$1\\r$2").replace(/([^\\])\n(abla|eq|ot|exists)/g, "$1\\n$2");
|
|
18242
|
-
if (shouldResetTopLevelStreamCacheForFinalAutoParse(md, options))
|
|
18631
|
+
if (shouldResetTopLevelStreamCacheForFinalAutoParse(md, options)) {
|
|
18632
|
+
md.stream.reset();
|
|
18633
|
+
clearTolerantMathBoundaryStreamCache(md);
|
|
18634
|
+
}
|
|
18243
18635
|
if (!isFinal) {
|
|
18244
18636
|
if (safeMarkdown.endsWith("- *")) safeMarkdown = safeMarkdown.replace(/- \*$/, "- \\*");
|
|
18245
18637
|
if (/(?:^|\n)\s*-\s*$/.test(safeMarkdown)) safeMarkdown = safeMarkdown.replace(/(?:^|\n)\s*-\s*$/, (m) => {
|