stream-markdown-parser 0.0.79 → 0.0.81
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 +2 -1
- package/README.zh-CN.md +2 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +548 -239
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6489,7 +6489,7 @@ const DEFAULT_RENDERER_OPTIONS = {
|
|
|
6489
6489
|
xhtmlOut: false,
|
|
6490
6490
|
breaks: false
|
|
6491
6491
|
};
|
|
6492
|
-
const hasOwn = Object.prototype.hasOwnProperty;
|
|
6492
|
+
const hasOwn$1 = Object.prototype.hasOwnProperty;
|
|
6493
6493
|
const defaultRules = {
|
|
6494
6494
|
code_inline(tokens, idx) {
|
|
6495
6495
|
return renderCodeInlineToken(tokens[idx]);
|
|
@@ -6889,16 +6889,16 @@ var Renderer = class {
|
|
|
6889
6889
|
if (!merged) merged = { ...base$1 };
|
|
6890
6890
|
return merged;
|
|
6891
6891
|
};
|
|
6892
|
-
if (hasOwn.call(overrides, "highlight") && overrides.highlight !== base$1.highlight) ensureMerged().highlight = overrides.highlight;
|
|
6893
|
-
if (hasOwn.call(overrides, "langPrefix")) {
|
|
6892
|
+
if (hasOwn$1.call(overrides, "highlight") && overrides.highlight !== base$1.highlight) ensureMerged().highlight = overrides.highlight;
|
|
6893
|
+
if (hasOwn$1.call(overrides, "langPrefix")) {
|
|
6894
6894
|
const value = overrides.langPrefix;
|
|
6895
6895
|
if (value !== base$1.langPrefix) ensureMerged().langPrefix = value;
|
|
6896
6896
|
}
|
|
6897
|
-
if (hasOwn.call(overrides, "xhtmlOut")) {
|
|
6897
|
+
if (hasOwn$1.call(overrides, "xhtmlOut")) {
|
|
6898
6898
|
const value = overrides.xhtmlOut;
|
|
6899
6899
|
if (value !== base$1.xhtmlOut) ensureMerged().xhtmlOut = value;
|
|
6900
6900
|
}
|
|
6901
|
-
if (hasOwn.call(overrides, "breaks")) {
|
|
6901
|
+
if (hasOwn$1.call(overrides, "breaks")) {
|
|
6902
6902
|
const value = overrides.breaks;
|
|
6903
6903
|
if (value !== base$1.breaks) ensureMerged().breaks = value;
|
|
6904
6904
|
}
|
|
@@ -8733,10 +8733,10 @@ const config = {
|
|
|
8733
8733
|
commonmark: commonmark_default
|
|
8734
8734
|
};
|
|
8735
8735
|
function hasExplicitChunkOverride(presetOptions, userOptions, keys) {
|
|
8736
|
-
const hasOwn$1 = (obj, key) => !!obj && Object.prototype.hasOwnProperty.call(obj, key) && obj[key] !== void 0;
|
|
8736
|
+
const hasOwn$1$1 = (obj, key) => !!obj && Object.prototype.hasOwnProperty.call(obj, key) && obj[key] !== void 0;
|
|
8737
8737
|
for (let i = 0; i < keys.length; i++) {
|
|
8738
8738
|
const key = keys[i];
|
|
8739
|
-
if (hasOwn$1(userOptions, key) || hasOwn$1(presetOptions, key)) return true;
|
|
8739
|
+
if (hasOwn$1$1(userOptions, key) || hasOwn$1$1(presetOptions, key)) return true;
|
|
8740
8740
|
}
|
|
8741
8741
|
return false;
|
|
8742
8742
|
}
|
|
@@ -9602,6 +9602,124 @@ function isUnsafeHtmlUrl(value) {
|
|
|
9602
9602
|
return false;
|
|
9603
9603
|
}
|
|
9604
9604
|
|
|
9605
|
+
//#endregion
|
|
9606
|
+
//#region src/htmlTagUtils.ts
|
|
9607
|
+
function escapeTagForRegExp(tag) {
|
|
9608
|
+
return tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9609
|
+
}
|
|
9610
|
+
function findTagCloseIndexOutsideQuotes(input) {
|
|
9611
|
+
let inSingle = false;
|
|
9612
|
+
let inDouble = false;
|
|
9613
|
+
for (let i = 0; i < input.length; i++) {
|
|
9614
|
+
const ch = input[i];
|
|
9615
|
+
if (ch === "\\") {
|
|
9616
|
+
i++;
|
|
9617
|
+
continue;
|
|
9618
|
+
}
|
|
9619
|
+
if (!inDouble && ch === "'") {
|
|
9620
|
+
inSingle = !inSingle;
|
|
9621
|
+
continue;
|
|
9622
|
+
}
|
|
9623
|
+
if (!inSingle && ch === "\"") {
|
|
9624
|
+
inDouble = !inDouble;
|
|
9625
|
+
continue;
|
|
9626
|
+
}
|
|
9627
|
+
if (!inSingle && !inDouble && ch === ">") return i;
|
|
9628
|
+
}
|
|
9629
|
+
return -1;
|
|
9630
|
+
}
|
|
9631
|
+
function parseTagAttrs(openTag) {
|
|
9632
|
+
const attrs = [];
|
|
9633
|
+
const attrRegex = /\s([\w:-]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/g;
|
|
9634
|
+
let match;
|
|
9635
|
+
while ((match = attrRegex.exec(openTag)) !== null) {
|
|
9636
|
+
const attrName = match[1];
|
|
9637
|
+
if (!attrName) continue;
|
|
9638
|
+
const attrValue = match[2] || match[3] || match[4] || "";
|
|
9639
|
+
attrs.push([attrName, attrValue]);
|
|
9640
|
+
}
|
|
9641
|
+
return attrs;
|
|
9642
|
+
}
|
|
9643
|
+
|
|
9644
|
+
//#endregion
|
|
9645
|
+
//#region src/customHtmlTags.ts
|
|
9646
|
+
const HTML_LIKE_TAG_NAME_RE = /^[a-z][a-z0-9_-]*$/;
|
|
9647
|
+
function isHtmlLikeTagName(tag) {
|
|
9648
|
+
return HTML_LIKE_TAG_NAME_RE.test(String(tag ?? "").trim().toLowerCase());
|
|
9649
|
+
}
|
|
9650
|
+
function normalizeCustomHtmlTagName(value) {
|
|
9651
|
+
const raw = String(value ?? "").trim();
|
|
9652
|
+
if (!raw) return "";
|
|
9653
|
+
if (!raw.startsWith("<")) return isHtmlLikeTagName(raw) ? raw.toLowerCase() : "";
|
|
9654
|
+
let index = 1;
|
|
9655
|
+
while (index < raw.length && /\s/.test(raw[index])) index++;
|
|
9656
|
+
if (raw[index] === "/") {
|
|
9657
|
+
index++;
|
|
9658
|
+
while (index < raw.length && /\s/.test(raw[index])) index++;
|
|
9659
|
+
}
|
|
9660
|
+
const start = index;
|
|
9661
|
+
while (index < raw.length && /[\w-]/.test(raw[index])) index++;
|
|
9662
|
+
const normalized = raw.slice(start, index).toLowerCase();
|
|
9663
|
+
const next = raw[index] ?? "";
|
|
9664
|
+
if (next && !/[\s/>]/.test(next)) return "";
|
|
9665
|
+
return isHtmlLikeTagName(normalized) ? normalized : "";
|
|
9666
|
+
}
|
|
9667
|
+
function normalizeCustomHtmlTags(tags) {
|
|
9668
|
+
if (!tags || tags.length === 0) return [];
|
|
9669
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9670
|
+
const normalized = [];
|
|
9671
|
+
for (const tag of tags) {
|
|
9672
|
+
const value = normalizeCustomHtmlTagName(tag);
|
|
9673
|
+
if (!value || seen.has(value)) continue;
|
|
9674
|
+
seen.add(value);
|
|
9675
|
+
normalized.push(value);
|
|
9676
|
+
}
|
|
9677
|
+
return normalized;
|
|
9678
|
+
}
|
|
9679
|
+
function mergeCustomHtmlTags(...lists) {
|
|
9680
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9681
|
+
const normalized = [];
|
|
9682
|
+
for (const list$1 of lists) for (const tag of normalizeCustomHtmlTags(list$1)) {
|
|
9683
|
+
if (seen.has(tag)) continue;
|
|
9684
|
+
seen.add(tag);
|
|
9685
|
+
normalized.push(tag);
|
|
9686
|
+
}
|
|
9687
|
+
return normalized;
|
|
9688
|
+
}
|
|
9689
|
+
function resolveCustomHtmlTags(tags) {
|
|
9690
|
+
const normalized = normalizeCustomHtmlTags(tags);
|
|
9691
|
+
return {
|
|
9692
|
+
key: normalized.join(","),
|
|
9693
|
+
tags: normalized
|
|
9694
|
+
};
|
|
9695
|
+
}
|
|
9696
|
+
function getHtmlTagFromContent(html) {
|
|
9697
|
+
return normalizeCustomHtmlTagName(html);
|
|
9698
|
+
}
|
|
9699
|
+
function hasCompleteHtmlTagContent(html, tag) {
|
|
9700
|
+
const raw = String(html ?? "");
|
|
9701
|
+
const normalizedTag = normalizeCustomHtmlTagName(tag);
|
|
9702
|
+
if (!normalizedTag) return false;
|
|
9703
|
+
const escaped = escapeTagForRegExp(normalizedTag);
|
|
9704
|
+
const openMatch = raw.match(new RegExp(String.raw`^\s*<\s*${escaped}(?:\s[^>]*)?(\s*\/)?>`, "i"));
|
|
9705
|
+
if (!openMatch) return false;
|
|
9706
|
+
if (openMatch[1]) return true;
|
|
9707
|
+
return new RegExp(String.raw`<\s*\/\s*${escaped}\s*>`, "i").test(raw);
|
|
9708
|
+
}
|
|
9709
|
+
function shouldRenderUnknownHtmlTagAsText(html, tag) {
|
|
9710
|
+
const normalizedTag = normalizeCustomHtmlTagName(tag);
|
|
9711
|
+
return Boolean(normalizedTag) && !STANDARD_HTML_TAGS.has(normalizedTag) && !hasCompleteHtmlTagContent(html, normalizedTag);
|
|
9712
|
+
}
|
|
9713
|
+
function stripCustomHtmlWrapper(html, tag) {
|
|
9714
|
+
const raw = String(html ?? "");
|
|
9715
|
+
const normalizedTag = normalizeCustomHtmlTagName(tag);
|
|
9716
|
+
if (!normalizedTag) return raw;
|
|
9717
|
+
const escaped = escapeTagForRegExp(normalizedTag);
|
|
9718
|
+
const openRe = new RegExp(String.raw`^\s*<\s*${escaped}(?:\s[^>]*)?>\s*`, "i");
|
|
9719
|
+
const closeRe = new RegExp(String.raw`\s*<\s*\/\s*${escaped}\s*>\s*$`, "i");
|
|
9720
|
+
return raw.replace(openRe, "").replace(closeRe, "");
|
|
9721
|
+
}
|
|
9722
|
+
|
|
9605
9723
|
//#endregion
|
|
9606
9724
|
//#region src/plugins/fixHtmlInline.ts
|
|
9607
9725
|
const VOID_TAGS = VOID_HTML_TAGS;
|
|
@@ -9621,9 +9739,6 @@ function isHtmlInlineClosingTag(content) {
|
|
|
9621
9739
|
function isSelfClosingHtmlInline(content, tag) {
|
|
9622
9740
|
return VOID_TAGS.has(tag) || /\/\s*>\s*$/.test(content);
|
|
9623
9741
|
}
|
|
9624
|
-
function escapeRegex$2(value) {
|
|
9625
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9626
|
-
}
|
|
9627
9742
|
function findMatchingCloseChildIndex(children, tag) {
|
|
9628
9743
|
let depth = 0;
|
|
9629
9744
|
for (let index = 0; index < children.length; index++) {
|
|
@@ -9657,7 +9772,7 @@ function getTrailingOpenDepth(children, tag) {
|
|
|
9657
9772
|
return depth;
|
|
9658
9773
|
}
|
|
9659
9774
|
function findMatchingCloseRangeInHtml(content, tag, startIndex = 0) {
|
|
9660
|
-
const tokenRe = new RegExp(String.raw`<\s*(\/?)\s*${
|
|
9775
|
+
const tokenRe = new RegExp(String.raw`<\s*(\/?)\s*${escapeTagForRegExp(tag)}(?=[\s>/])[^>]*>`, "gi");
|
|
9661
9776
|
tokenRe.lastIndex = Math.max(0, startIndex);
|
|
9662
9777
|
let depth = 0;
|
|
9663
9778
|
let match;
|
|
@@ -9678,7 +9793,7 @@ function findMatchingCloseRangeInHtml(content, tag, startIndex = 0) {
|
|
|
9678
9793
|
return null;
|
|
9679
9794
|
}
|
|
9680
9795
|
function getTrailingCustomTagDepthInHtml(content, tag) {
|
|
9681
|
-
const tokenRe = new RegExp(String.raw`<\s*(\/?)\s*${
|
|
9796
|
+
const tokenRe = new RegExp(String.raw`<\s*(\/?)\s*${escapeTagForRegExp(tag)}(?=[\s>/])[^>]*>`, "gi");
|
|
9682
9797
|
let depth = 0;
|
|
9683
9798
|
let match;
|
|
9684
9799
|
while ((match = tokenRe.exec(content)) !== null) {
|
|
@@ -9693,23 +9808,6 @@ function getTrailingCustomTagDepthInHtml(content, tag) {
|
|
|
9693
9808
|
}
|
|
9694
9809
|
return depth;
|
|
9695
9810
|
}
|
|
9696
|
-
function findTagCloseIndexOutsideQuotes$3(html) {
|
|
9697
|
-
let inSingle = false;
|
|
9698
|
-
let inDouble = false;
|
|
9699
|
-
for (let i = 0; i < html.length; i++) {
|
|
9700
|
-
const ch = html[i];
|
|
9701
|
-
if (ch === "\"" && !inSingle) {
|
|
9702
|
-
inDouble = !inDouble;
|
|
9703
|
-
continue;
|
|
9704
|
-
}
|
|
9705
|
-
if (ch === "'" && !inDouble) {
|
|
9706
|
-
inSingle = !inSingle;
|
|
9707
|
-
continue;
|
|
9708
|
-
}
|
|
9709
|
-
if (ch === ">" && !inSingle && !inDouble) return i;
|
|
9710
|
-
}
|
|
9711
|
-
return -1;
|
|
9712
|
-
}
|
|
9713
9811
|
function tokenToRaw$1(token) {
|
|
9714
9812
|
const shape = token;
|
|
9715
9813
|
return String(shape.raw ?? shape.content ?? shape.markup ?? "");
|
|
@@ -9737,7 +9835,7 @@ function findFirstIncompleteTag(content, tagSet) {
|
|
|
9737
9835
|
if (idx < 0) continue;
|
|
9738
9836
|
const tag = (m[1] ?? "").toLowerCase();
|
|
9739
9837
|
if (!isCommonHtmlTagOrPrefix(tag, tagSet)) continue;
|
|
9740
|
-
if (findTagCloseIndexOutsideQuotes
|
|
9838
|
+
if (findTagCloseIndexOutsideQuotes(content.slice(idx)) !== -1) continue;
|
|
9741
9839
|
if (!first || idx < first.index) first = {
|
|
9742
9840
|
index: idx,
|
|
9743
9841
|
tag,
|
|
@@ -9749,7 +9847,7 @@ function findFirstIncompleteTag(content, tagSet) {
|
|
|
9749
9847
|
if (idx < 0) continue;
|
|
9750
9848
|
const tag = (m[1] ?? "").toLowerCase();
|
|
9751
9849
|
if (!isCommonHtmlTagOrPrefix(tag, tagSet)) continue;
|
|
9752
|
-
if (findTagCloseIndexOutsideQuotes
|
|
9850
|
+
if (findTagCloseIndexOutsideQuotes(content.slice(idx)) !== -1) continue;
|
|
9753
9851
|
if (!first || idx < first.index) first = {
|
|
9754
9852
|
index: idx,
|
|
9755
9853
|
tag,
|
|
@@ -9815,7 +9913,7 @@ function fixStreamingHtmlInlineChildren(children, tagSet) {
|
|
|
9815
9913
|
cursor = lt + 1;
|
|
9816
9914
|
continue;
|
|
9817
9915
|
}
|
|
9818
|
-
const closeIdx = findTagCloseIndexOutsideQuotes
|
|
9916
|
+
const closeIdx = findTagCloseIndexOutsideQuotes(sub);
|
|
9819
9917
|
if (closeIdx === -1) {
|
|
9820
9918
|
pushTextPart("<", baseToken);
|
|
9821
9919
|
cursor = lt + 1;
|
|
@@ -9853,7 +9951,7 @@ function fixStreamingHtmlInlineChildren(children, tagSet) {
|
|
|
9853
9951
|
if (pending) {
|
|
9854
9952
|
pending.buffer += tokenToRaw$1(child);
|
|
9855
9953
|
pendingAtEnd = pending.buffer;
|
|
9856
|
-
const closeIdx = findTagCloseIndexOutsideQuotes
|
|
9954
|
+
const closeIdx = findTagCloseIndexOutsideQuotes(pending.buffer);
|
|
9857
9955
|
if (closeIdx === -1) continue;
|
|
9858
9956
|
const tagChunk = pending.buffer.slice(0, closeIdx + 1);
|
|
9859
9957
|
const afterChunk = pending.buffer.slice(closeIdx + 1);
|
|
@@ -9871,7 +9969,7 @@ function fixStreamingHtmlInlineChildren(children, tagSet) {
|
|
|
9871
9969
|
if (child.type === "html_inline") {
|
|
9872
9970
|
const content = tokenToRaw$1(child);
|
|
9873
9971
|
const tagName = (content.match(TAG_NAME_AT_START_RE)?.[1] ?? "").toLowerCase();
|
|
9874
|
-
if (tagName && tagSet.has(tagName) && findTagCloseIndexOutsideQuotes
|
|
9972
|
+
if (tagName && tagSet.has(tagName) && findTagCloseIndexOutsideQuotes(content) === -1) {
|
|
9875
9973
|
pending = {
|
|
9876
9974
|
tag: tagName,
|
|
9877
9975
|
buffer: content,
|
|
@@ -9910,11 +10008,8 @@ function applyFixHtmlInlineTokens(md, options = {}) {
|
|
|
9910
10008
|
]);
|
|
9911
10009
|
const customTagSet = /* @__PURE__ */ new Set();
|
|
9912
10010
|
if (options.customHtmlTags?.length) for (const t of options.customHtmlTags) {
|
|
9913
|
-
const
|
|
9914
|
-
if (!
|
|
9915
|
-
const m = raw.match(/^[<\s/]*([A-Z][\w-]*)/i);
|
|
9916
|
-
if (!m) continue;
|
|
9917
|
-
const name = m[1].toLowerCase();
|
|
10011
|
+
const name = normalizeCustomHtmlTagName(t);
|
|
10012
|
+
if (!name) continue;
|
|
9918
10013
|
customTagSet.add(name);
|
|
9919
10014
|
autoCloseInlineTagSet.add(name);
|
|
9920
10015
|
}
|
|
@@ -9977,7 +10072,7 @@ function applyFixHtmlInlineTokens(md, options = {}) {
|
|
|
9977
10072
|
if (chunk) {
|
|
9978
10073
|
const openToken = toks[openIndex];
|
|
9979
10074
|
const mergedContent = `${String(openToken.content || "")}\n${chunk}`;
|
|
9980
|
-
const openEnd = findTagCloseIndexOutsideQuotes
|
|
10075
|
+
const openEnd = findTagCloseIndexOutsideQuotes(mergedContent);
|
|
9981
10076
|
const closeRange = openEnd === -1 ? null : findMatchingCloseRangeInHtml(mergedContent, openTag, openEnd + 1);
|
|
9982
10077
|
if (closeRange) {
|
|
9983
10078
|
const before = mergedContent.slice(0, closeRange.end);
|
|
@@ -10176,7 +10271,7 @@ function applyFixHtmlInlineTokens(md, options = {}) {
|
|
|
10176
10271
|
}
|
|
10177
10272
|
if (customTagSet.has(tag)) {
|
|
10178
10273
|
const raw$2 = String(t.content ?? "");
|
|
10179
|
-
const openEnd = findTagCloseIndexOutsideQuotes
|
|
10274
|
+
const openEnd = findTagCloseIndexOutsideQuotes(raw$2);
|
|
10180
10275
|
const closeRange = openEnd === -1 ? null : findMatchingCloseRangeInHtml(raw$2, tag, openEnd + 1);
|
|
10181
10276
|
t.loading = !!closeRange ? false : t.loading !== void 0 ? t.loading : true;
|
|
10182
10277
|
const endTagIndex$1 = closeRange?.start ?? -1;
|
|
@@ -12340,17 +12435,11 @@ function isClosingTag(html) {
|
|
|
12340
12435
|
function isSelfClosing(tag, html) {
|
|
12341
12436
|
return /\/\s*>\s*$/.test(html) || VOID_HTML_TAGS.has(tag);
|
|
12342
12437
|
}
|
|
12343
|
-
function normalizeCustomTag$1(t) {
|
|
12344
|
-
const raw = String(t ?? "").trim();
|
|
12345
|
-
if (!raw) return "";
|
|
12346
|
-
const m = raw.match(/^[<\s/]*([A-Z][\w-]*)/i);
|
|
12347
|
-
return m ? m[1].toLowerCase() : "";
|
|
12348
|
-
}
|
|
12349
12438
|
function getTagSets(customTags) {
|
|
12350
12439
|
if (!customTags || customTags.length === 0) return getEmptyTagSets();
|
|
12351
12440
|
const cached = TAG_SET_CACHE.get(customTags);
|
|
12352
12441
|
if (cached) return cached;
|
|
12353
|
-
const normalized = customTags.map(
|
|
12442
|
+
const normalized = customTags.map(normalizeCustomHtmlTagName).filter(Boolean);
|
|
12354
12443
|
if (!normalized.length) {
|
|
12355
12444
|
const entry$1 = getEmptyTagSets();
|
|
12356
12445
|
TAG_SET_CACHE.set(customTags, entry$1);
|
|
@@ -12368,18 +12457,6 @@ function tokenToRaw(token) {
|
|
|
12368
12457
|
const raw = shape.raw ?? shape.content ?? shape.markup ?? "";
|
|
12369
12458
|
return String(raw ?? "");
|
|
12370
12459
|
}
|
|
12371
|
-
function parseTagAttrs$1(openTag) {
|
|
12372
|
-
const attrs = [];
|
|
12373
|
-
const attrRegex = /\s([\w:-]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/g;
|
|
12374
|
-
let match;
|
|
12375
|
-
while ((match = attrRegex.exec(openTag)) !== null) {
|
|
12376
|
-
const attrName = match[1];
|
|
12377
|
-
if (!attrName) continue;
|
|
12378
|
-
const attrValue = match[2] || match[3] || match[4] || "";
|
|
12379
|
-
attrs.push([attrName, attrValue]);
|
|
12380
|
-
}
|
|
12381
|
-
return attrs;
|
|
12382
|
-
}
|
|
12383
12460
|
function getAttrValue$1(attrs, name) {
|
|
12384
12461
|
const lowerName = name.toLowerCase();
|
|
12385
12462
|
for (let i = attrs.length - 1; i >= 0; i--) {
|
|
@@ -12474,7 +12551,7 @@ function parseHtmlInlineCodeToken(token, tokens, i, parseInlineTokens$1, raw, pP
|
|
|
12474
12551
|
}, i + 1];
|
|
12475
12552
|
if (tag === "a") {
|
|
12476
12553
|
const fragment$1 = collectHtmlFragment(tokens, i, tag);
|
|
12477
|
-
const attrs$1 = parseTagAttrs
|
|
12554
|
+
const attrs$1 = parseTagAttrs(code$1);
|
|
12478
12555
|
const innerTokens = fragment$1.innerTokens;
|
|
12479
12556
|
const href = String(getAttrValue$1(attrs$1, "href") ?? "");
|
|
12480
12557
|
const titleAttr = getAttrValue$1(attrs$1, "title");
|
|
@@ -12914,6 +12991,30 @@ function decodeVisibleTextFromRaw(rawText) {
|
|
|
12914
12991
|
}
|
|
12915
12992
|
return output;
|
|
12916
12993
|
}
|
|
12994
|
+
function getRawIndexForVisibleIndex(rawText, visibleIndex) {
|
|
12995
|
+
let outputIndex = 0;
|
|
12996
|
+
for (let rawIndex = 0; rawIndex < rawText.length; rawIndex++) {
|
|
12997
|
+
const char = rawText[rawIndex];
|
|
12998
|
+
const nextChar = rawText[rawIndex + 1];
|
|
12999
|
+
if (char === "\\" && nextChar && ESCAPABLE_PUNCTUATION.has(nextChar)) {
|
|
13000
|
+
if (outputIndex === visibleIndex) return rawIndex + 1;
|
|
13001
|
+
outputIndex++;
|
|
13002
|
+
rawIndex++;
|
|
13003
|
+
continue;
|
|
13004
|
+
}
|
|
13005
|
+
if (outputIndex === visibleIndex) return rawIndex;
|
|
13006
|
+
outputIndex++;
|
|
13007
|
+
}
|
|
13008
|
+
return -1;
|
|
13009
|
+
}
|
|
13010
|
+
function isEscapedVisibleChar(rawText, visibleIndex, expectedChar) {
|
|
13011
|
+
const rawIndex = getRawIndexForVisibleIndex(rawText, visibleIndex);
|
|
13012
|
+
if (rawIndex === -1) return false;
|
|
13013
|
+
if (expectedChar && rawText[rawIndex] !== expectedChar) return false;
|
|
13014
|
+
let slashCount = 0;
|
|
13015
|
+
for (let i = rawIndex - 1; i >= 0 && rawText[i] === "\\"; i--) slashCount++;
|
|
13016
|
+
return slashCount % 2 === 1;
|
|
13017
|
+
}
|
|
12917
13018
|
const WORD_CHAR_RE = /[\p{L}\p{N}]/u;
|
|
12918
13019
|
const WORD_ONLY_RE = /^[\p{L}\p{N}]+$/u;
|
|
12919
13020
|
function isWordChar(ch) {
|
|
@@ -13359,9 +13460,11 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
|
|
|
13359
13460
|
handleLinkOpen(token);
|
|
13360
13461
|
break;
|
|
13361
13462
|
case "image":
|
|
13362
|
-
|
|
13363
|
-
|
|
13364
|
-
|
|
13463
|
+
if (!recoverOuterImageLinkStartFromImageToken(token)) {
|
|
13464
|
+
resetCurrentTextNode();
|
|
13465
|
+
pushNode(parseImageToken(token));
|
|
13466
|
+
i++;
|
|
13467
|
+
}
|
|
13365
13468
|
break;
|
|
13366
13469
|
case "strong_open": {
|
|
13367
13470
|
resetCurrentTextNode();
|
|
@@ -13506,7 +13609,9 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
|
|
|
13506
13609
|
const displayText = String(token.text ?? "");
|
|
13507
13610
|
pushText(displayText, displayText);
|
|
13508
13611
|
i++;
|
|
13509
|
-
} else if (
|
|
13612
|
+
} else if (recoverOuterImageLinkFromSyntheticLinkToken(token)) i++;
|
|
13613
|
+
else if (recoverMarkdownImageFromTrailingBang(token)) i++;
|
|
13614
|
+
else if (recoverMarkdownLinkFromTrailingText(token)) i++;
|
|
13510
13615
|
else {
|
|
13511
13616
|
pushToken(token);
|
|
13512
13617
|
i++;
|
|
@@ -13567,6 +13672,8 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
|
|
|
13567
13672
|
i++;
|
|
13568
13673
|
return;
|
|
13569
13674
|
}
|
|
13675
|
+
if (recoverOuterImageLinkFromRawText(content)) return;
|
|
13676
|
+
if (recoverOuterImageLinkMidStateFromText(content)) return;
|
|
13570
13677
|
if (!(content.includes("*") || content.includes("_") || content.includes("~") || content.includes("`") || content.includes("[") || content.includes("!") || content.includes("$") || content.includes("|") || content.includes("("))) {
|
|
13571
13678
|
commitTextNode(content, token, tokens[i - 1], nextToken);
|
|
13572
13679
|
i++;
|
|
@@ -13574,12 +13681,12 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
|
|
|
13574
13681
|
}
|
|
13575
13682
|
if (handleCheckboxLike(content)) return;
|
|
13576
13683
|
const preToken = tokens[i - 1];
|
|
13577
|
-
if (content === "[" && !nextToken?.markup?.includes("*") || content === "]" && !preToken?.markup?.includes("*")) {
|
|
13684
|
+
if (content === "[" && !nextToken?.markup?.includes("*") && !hasEscapedMarkup(token, "\\[") || content === "]" && !preToken?.markup?.includes("*") && !hasEscapedMarkup(token, "\\]")) {
|
|
13578
13685
|
i++;
|
|
13579
13686
|
return;
|
|
13580
13687
|
}
|
|
13581
13688
|
if (handleInlineCodeContent(rawContent, token)) return;
|
|
13582
|
-
if (handleInlineImageContent(content
|
|
13689
|
+
if (handleInlineImageContent(content)) return;
|
|
13583
13690
|
if (tokens[i + 1]?.type !== "link_open" && handleInlineLinkContent(content, token)) return;
|
|
13584
13691
|
const reparsedNodes = tryReparseCollapsedInlineText(rawContent);
|
|
13585
13692
|
if (reparsedNodes) {
|
|
@@ -13593,6 +13700,13 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
|
|
|
13593
13700
|
i++;
|
|
13594
13701
|
}
|
|
13595
13702
|
function handleLinkOpen(token) {
|
|
13703
|
+
if (shouldTreatLinkOpenAsTextInEscapedOuterImageTail()) {
|
|
13704
|
+
const { node: node$1, nextIndex: nextIndex$1 } = parseLinkToken(tokens, i, options);
|
|
13705
|
+
const text$1 = String(node$1.text || node$1.href || "");
|
|
13706
|
+
pushText(text$1, text$1);
|
|
13707
|
+
i = nextIndex$1;
|
|
13708
|
+
return;
|
|
13709
|
+
}
|
|
13596
13710
|
resetCurrentTextNode();
|
|
13597
13711
|
const { node, nextIndex } = parseLinkToken(tokens, i, options);
|
|
13598
13712
|
i = nextIndex;
|
|
@@ -13662,6 +13776,149 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
|
|
|
13662
13776
|
});
|
|
13663
13777
|
return true;
|
|
13664
13778
|
}
|
|
13779
|
+
function recoverMarkdownImageFromTrailingBang(token) {
|
|
13780
|
+
if (token.type !== "link") return false;
|
|
13781
|
+
const previous = result[result.length - 1];
|
|
13782
|
+
const previousToken = tokens[i - 1];
|
|
13783
|
+
if (!previous || previous.type !== "text" || previousToken?.type !== "text") return false;
|
|
13784
|
+
const previousContent = String(previous.content ?? "");
|
|
13785
|
+
const previousTokenContent = String(previousToken.content ?? "");
|
|
13786
|
+
if (!previousContent.endsWith("!") || !previousTokenContent.endsWith("!")) return false;
|
|
13787
|
+
if (hasEscapedMarkup(previousToken, "\\!")) return false;
|
|
13788
|
+
const before = previousContent.slice(0, -1);
|
|
13789
|
+
if (before) {
|
|
13790
|
+
previous.content = before;
|
|
13791
|
+
previous.raw = before;
|
|
13792
|
+
currentTextNode = previous;
|
|
13793
|
+
} else {
|
|
13794
|
+
result.pop();
|
|
13795
|
+
currentTextNode = null;
|
|
13796
|
+
}
|
|
13797
|
+
const linkToken = token;
|
|
13798
|
+
const alt = String(linkToken.text ?? linkToken.children?.map((child) => String(child?.content ?? child?.raw ?? "")).join("") ?? "");
|
|
13799
|
+
const href = String(linkToken.href ?? "");
|
|
13800
|
+
const title = linkToken.title == null || linkToken.title === "" ? null : String(linkToken.title);
|
|
13801
|
+
pushParsed({
|
|
13802
|
+
type: "image",
|
|
13803
|
+
src: href,
|
|
13804
|
+
alt,
|
|
13805
|
+
title,
|
|
13806
|
+
raw: String(``),
|
|
13807
|
+
loading: Boolean(linkToken.loading)
|
|
13808
|
+
});
|
|
13809
|
+
return true;
|
|
13810
|
+
}
|
|
13811
|
+
function buildLoadingOuterImageLinkNode(imageNode, href = "", title = null) {
|
|
13812
|
+
const text$1 = String(imageNode.alt ?? imageNode.raw ?? "");
|
|
13813
|
+
return {
|
|
13814
|
+
type: "link",
|
|
13815
|
+
href,
|
|
13816
|
+
title,
|
|
13817
|
+
text: text$1,
|
|
13818
|
+
children: [imageNode],
|
|
13819
|
+
raw: String(`[${text$1}](${href}${title ? ` "${title}"` : ""})`),
|
|
13820
|
+
loading: true
|
|
13821
|
+
};
|
|
13822
|
+
}
|
|
13823
|
+
function buildLoadingImageNodeFromRaw(raw$1) {
|
|
13824
|
+
const normalizedRaw = raw$1.startsWith(";
|
|
13827
|
+
return {
|
|
13828
|
+
type: "image",
|
|
13829
|
+
src: "",
|
|
13830
|
+
alt: closeIdx === -1 ? innerRaw.replace(/\]$/, "") : innerRaw.slice(0, closeIdx),
|
|
13831
|
+
title: null,
|
|
13832
|
+
raw: normalizedRaw,
|
|
13833
|
+
loading: true
|
|
13834
|
+
};
|
|
13835
|
+
}
|
|
13836
|
+
function recoverOuterImageLinkFromRawText(content) {
|
|
13837
|
+
const outerStart = content.indexOf("[![");
|
|
13838
|
+
if (outerStart === -1) return false;
|
|
13839
|
+
if (typeof raw === "string" && tokens.length === 1 && isEscapedVisibleChar(raw, outerStart, "[")) return false;
|
|
13840
|
+
const before = content.slice(0, outerStart);
|
|
13841
|
+
if (before) pushText(before, before);
|
|
13842
|
+
pushParsed(buildLoadingOuterImageLinkNode(buildLoadingImageNodeFromRaw(content.slice(outerStart + 1))));
|
|
13843
|
+
i++;
|
|
13844
|
+
return true;
|
|
13845
|
+
}
|
|
13846
|
+
function recoverOuterImageLinkStartFromImageToken(token) {
|
|
13847
|
+
if (options?.final) return false;
|
|
13848
|
+
const previousToken = tokens[i - 1];
|
|
13849
|
+
if (previousToken?.type !== "text") return false;
|
|
13850
|
+
if (!String(previousToken.content ?? "").endsWith("[")) return false;
|
|
13851
|
+
if (hasEscapedMarkup(previousToken, "\\[")) return false;
|
|
13852
|
+
const previous = result[result.length - 1];
|
|
13853
|
+
if (previous?.type === "text" && previous.content.endsWith("[")) {
|
|
13854
|
+
const before = previous.content.slice(0, -1);
|
|
13855
|
+
if (before) {
|
|
13856
|
+
previous.content = before;
|
|
13857
|
+
previous.raw = before;
|
|
13858
|
+
currentTextNode = previous;
|
|
13859
|
+
} else {
|
|
13860
|
+
result.pop();
|
|
13861
|
+
currentTextNode = null;
|
|
13862
|
+
}
|
|
13863
|
+
}
|
|
13864
|
+
pushParsed(buildLoadingOuterImageLinkNode(parseImageToken(token)));
|
|
13865
|
+
i++;
|
|
13866
|
+
return true;
|
|
13867
|
+
}
|
|
13868
|
+
function recoverOuterImageLinkFromSyntheticLinkToken(token) {
|
|
13869
|
+
if (token.type !== "link") return false;
|
|
13870
|
+
const linkToken = token;
|
|
13871
|
+
const raw$1 = String(linkToken.raw ?? "");
|
|
13872
|
+
const text$1 = String(linkToken.text ?? "");
|
|
13873
|
+
if (!raw$1.startsWith("[![") && !text$1.startsWith("![")) return false;
|
|
13874
|
+
const imageTitle = linkToken.title == null || linkToken.title === "" ? null : String(linkToken.title);
|
|
13875
|
+
pushParsed(buildLoadingOuterImageLinkNode({
|
|
13876
|
+
type: "image",
|
|
13877
|
+
src: String(linkToken.href ?? ""),
|
|
13878
|
+
alt: text$1.replace(/^!\[/, "").replace(/\]$/, ""),
|
|
13879
|
+
title: imageTitle,
|
|
13880
|
+
raw: raw$1.startsWith("[) return false;
|
|
13887
|
+
const outerOpenToken = tokens[i - 2];
|
|
13888
|
+
if (outerOpenToken?.type === "text" && String(outerOpenToken.content ?? "").endsWith("[") && hasEscapedMarkup(outerOpenToken, "\\[")) return false;
|
|
13889
|
+
const previous = result[result.length - 1];
|
|
13890
|
+
if (previous?.type !== "image" && previous?.type !== "link") return false;
|
|
13891
|
+
const previousLink = previous?.type === "link" && Array.isArray(previous.children) && previous.children.length === 1 && previous.children[0]?.type === "image" ? result.pop() : null;
|
|
13892
|
+
const imageNode = previousLink ? previousLink.children[0] : result.pop();
|
|
13893
|
+
if (!imageNode || imageNode.type !== "image") return false;
|
|
13894
|
+
const nextToken = tokens[i + 1];
|
|
13895
|
+
let href = String(previousLink?.href ?? "");
|
|
13896
|
+
let title = previousLink?.title == null ? null : String(previousLink.title);
|
|
13897
|
+
let loading = true;
|
|
13898
|
+
if (nextToken?.type === "link_open") {
|
|
13899
|
+
const { node, nextIndex } = parseLinkToken(tokens, i + 1, options);
|
|
13900
|
+
href = node.href;
|
|
13901
|
+
title = node.title;
|
|
13902
|
+
loading = true;
|
|
13903
|
+
i = nextIndex;
|
|
13904
|
+
} else {
|
|
13905
|
+
href = content.slice(2);
|
|
13906
|
+
if (href.includes("\"")) {
|
|
13907
|
+
const parts = href.split("\"");
|
|
13908
|
+
href = String(parts[0] ?? "").trim();
|
|
13909
|
+
title = parts[1] == null ? null : String(parts[1]).trim();
|
|
13910
|
+
}
|
|
13911
|
+
i++;
|
|
13912
|
+
}
|
|
13913
|
+
const linkNode = buildLoadingOuterImageLinkNode(imageNode, href, title);
|
|
13914
|
+
linkNode.loading = loading;
|
|
13915
|
+
pushParsed(linkNode);
|
|
13916
|
+
return true;
|
|
13917
|
+
}
|
|
13918
|
+
function shouldTreatLinkOpenAsTextInEscapedOuterImageTail() {
|
|
13919
|
+
const outerOpenToken = tokens[i - 3];
|
|
13920
|
+
return tokens[i - 2]?.type === "image" && tokens[i - 1]?.type === "text" && String(tokens[i - 1].content ?? "") === "](" && outerOpenToken?.type === "text" && String(outerOpenToken.content ?? "").endsWith("[") && hasEscapedMarkup(outerOpenToken, "\\[");
|
|
13921
|
+
}
|
|
13665
13922
|
function handleInlineLinkContent(content, _token) {
|
|
13666
13923
|
const linkStart = content.indexOf("[");
|
|
13667
13924
|
if (linkStart === -1) return false;
|
|
@@ -13808,19 +14065,21 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
|
|
|
13808
14065
|
}
|
|
13809
14066
|
return false;
|
|
13810
14067
|
}
|
|
13811
|
-
function handleInlineImageContent(content
|
|
14068
|
+
function handleInlineImageContent(content) {
|
|
13812
14069
|
const imageStart = content.indexOf("![");
|
|
13813
14070
|
if (imageStart === -1) return false;
|
|
13814
14071
|
const textNodeContent = content.slice(0, imageStart);
|
|
13815
|
-
if (!currentTextNode) currentTextNode = {
|
|
14072
|
+
if (textNodeContent && !currentTextNode) currentTextNode = {
|
|
13816
14073
|
type: "text",
|
|
13817
14074
|
content: textNodeContent,
|
|
13818
14075
|
raw: textNodeContent
|
|
13819
14076
|
};
|
|
13820
|
-
else currentTextNode.content += textNodeContent;
|
|
13821
|
-
|
|
13822
|
-
|
|
13823
|
-
|
|
14077
|
+
else if (textNodeContent && currentTextNode) currentTextNode.content += textNodeContent;
|
|
14078
|
+
if (currentTextNode) {
|
|
14079
|
+
result.push(currentTextNode);
|
|
14080
|
+
currentTextNode = null;
|
|
14081
|
+
}
|
|
14082
|
+
pushParsed(buildLoadingImageNodeFromRaw(content.slice(imageStart)));
|
|
13824
14083
|
i++;
|
|
13825
14084
|
return true;
|
|
13826
14085
|
}
|
|
@@ -14264,27 +14523,6 @@ function parseHeading(tokens, index, options) {
|
|
|
14264
14523
|
|
|
14265
14524
|
//#endregion
|
|
14266
14525
|
//#region src/parser/node-parsers/html-block-parser.ts
|
|
14267
|
-
function findTagCloseIndexOutsideQuotes$2(input) {
|
|
14268
|
-
let inSingle = false;
|
|
14269
|
-
let inDouble = false;
|
|
14270
|
-
for (let i = 0; i < input.length; i++) {
|
|
14271
|
-
const ch = input[i];
|
|
14272
|
-
if (ch === "\\") {
|
|
14273
|
-
i++;
|
|
14274
|
-
continue;
|
|
14275
|
-
}
|
|
14276
|
-
if (!inDouble && ch === "'") {
|
|
14277
|
-
inSingle = !inSingle;
|
|
14278
|
-
continue;
|
|
14279
|
-
}
|
|
14280
|
-
if (!inSingle && ch === "\"") {
|
|
14281
|
-
inDouble = !inDouble;
|
|
14282
|
-
continue;
|
|
14283
|
-
}
|
|
14284
|
-
if (!inSingle && !inDouble && ch === ">") return i;
|
|
14285
|
-
}
|
|
14286
|
-
return -1;
|
|
14287
|
-
}
|
|
14288
14526
|
function findMatchingCloseTagEnd(rawHtml, tag, startIndex) {
|
|
14289
14527
|
const lowerTag = tag.toLowerCase();
|
|
14290
14528
|
const openTagRe = new RegExp(String.raw`^<\s*${lowerTag}(?=\s|>|/)`, "i");
|
|
@@ -14296,7 +14534,7 @@ function findMatchingCloseTagEnd(rawHtml, tag, startIndex) {
|
|
|
14296
14534
|
if (lt === -1) return -1;
|
|
14297
14535
|
const slice = rawHtml.slice(lt);
|
|
14298
14536
|
if (closeTagRe.test(slice)) {
|
|
14299
|
-
const endRel = findTagCloseIndexOutsideQuotes
|
|
14537
|
+
const endRel = findTagCloseIndexOutsideQuotes(slice);
|
|
14300
14538
|
if (endRel === -1) return -1;
|
|
14301
14539
|
if (depth === 0) return lt + endRel + 1;
|
|
14302
14540
|
depth--;
|
|
@@ -14304,7 +14542,7 @@ function findMatchingCloseTagEnd(rawHtml, tag, startIndex) {
|
|
|
14304
14542
|
continue;
|
|
14305
14543
|
}
|
|
14306
14544
|
if (openTagRe.test(slice)) {
|
|
14307
|
-
const endRel = findTagCloseIndexOutsideQuotes
|
|
14545
|
+
const endRel = findTagCloseIndexOutsideQuotes(slice);
|
|
14308
14546
|
if (endRel === -1) return -1;
|
|
14309
14547
|
const rawTag = slice.slice(0, endRel + 1);
|
|
14310
14548
|
if (!/\/\s*>$/.test(rawTag)) depth++;
|
|
@@ -14315,18 +14553,6 @@ function findMatchingCloseTagEnd(rawHtml, tag, startIndex) {
|
|
|
14315
14553
|
}
|
|
14316
14554
|
return -1;
|
|
14317
14555
|
}
|
|
14318
|
-
function parseTagAttrs(openTag) {
|
|
14319
|
-
const attrs = [];
|
|
14320
|
-
const attrRegex = /\s([\w:-]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/g;
|
|
14321
|
-
let match;
|
|
14322
|
-
while ((match = attrRegex.exec(openTag)) !== null) {
|
|
14323
|
-
const attrName = match[1];
|
|
14324
|
-
if (!attrName) continue;
|
|
14325
|
-
const attrValue = match[2] || match[3] || match[4] || "";
|
|
14326
|
-
attrs.push([attrName, attrValue]);
|
|
14327
|
-
}
|
|
14328
|
-
return attrs;
|
|
14329
|
-
}
|
|
14330
14556
|
function parseHtmlBlock(token) {
|
|
14331
14557
|
const raw = String(token.content ?? "");
|
|
14332
14558
|
if (/^\s*<!--/.test(raw) || /^\s*<!/.test(raw) || /^\s*<\?/.test(raw)) return {
|
|
@@ -14344,7 +14570,7 @@ function parseHtmlBlock(token) {
|
|
|
14344
14570
|
tag: "",
|
|
14345
14571
|
loading: false
|
|
14346
14572
|
};
|
|
14347
|
-
const openEnd = findTagCloseIndexOutsideQuotes
|
|
14573
|
+
const openEnd = findTagCloseIndexOutsideQuotes(raw);
|
|
14348
14574
|
const openTag = openEnd === -1 ? raw : raw.slice(0, openEnd + 1);
|
|
14349
14575
|
const selfClosing = openEnd !== -1 && /\/\s*>$/.test(openTag);
|
|
14350
14576
|
const isVoid = VOID_HTML_TAGS.has(tag);
|
|
@@ -14463,17 +14689,11 @@ function getEmptyHtmlTagSets() {
|
|
|
14463
14689
|
};
|
|
14464
14690
|
return emptyHtmlTagSets;
|
|
14465
14691
|
}
|
|
14466
|
-
function normalizeCustomTag(t) {
|
|
14467
|
-
const raw = String(t ?? "").trim();
|
|
14468
|
-
if (!raw) return "";
|
|
14469
|
-
const m = raw.match(/^[<\s/]*([A-Z][\w-]*)/i);
|
|
14470
|
-
return m ? m[1].toLowerCase() : "";
|
|
14471
|
-
}
|
|
14472
14692
|
function getHtmlTagSets(customTags) {
|
|
14473
14693
|
if (!customTags || customTags.length === 0) return getEmptyHtmlTagSets();
|
|
14474
14694
|
const cached = HTML_TAG_SET_CACHE.get(customTags);
|
|
14475
14695
|
if (cached) return cached;
|
|
14476
|
-
const normalized = customTags.map(
|
|
14696
|
+
const normalized = customTags.map(normalizeCustomHtmlTagName).filter(Boolean);
|
|
14477
14697
|
if (!normalized.length) {
|
|
14478
14698
|
const entry$1 = getEmptyHtmlTagSets();
|
|
14479
14699
|
HTML_TAG_SET_CACHE.set(customTags, entry$1);
|
|
@@ -14551,27 +14771,6 @@ function parseVmrContainer(tokens, index, options) {
|
|
|
14551
14771
|
raw
|
|
14552
14772
|
}, hasCloseToken ? j + 1 : j];
|
|
14553
14773
|
}
|
|
14554
|
-
function findTagCloseIndexOutsideQuotes$1(input) {
|
|
14555
|
-
let inSingle = false;
|
|
14556
|
-
let inDouble = false;
|
|
14557
|
-
for (let i = 0; i < input.length; i++) {
|
|
14558
|
-
const ch = input[i];
|
|
14559
|
-
if (ch === "\\") {
|
|
14560
|
-
i++;
|
|
14561
|
-
continue;
|
|
14562
|
-
}
|
|
14563
|
-
if (!inDouble && ch === "'") {
|
|
14564
|
-
inSingle = !inSingle;
|
|
14565
|
-
continue;
|
|
14566
|
-
}
|
|
14567
|
-
if (!inSingle && ch === "\"") {
|
|
14568
|
-
inDouble = !inDouble;
|
|
14569
|
-
continue;
|
|
14570
|
-
}
|
|
14571
|
-
if (!inSingle && !inDouble && ch === ">") return i;
|
|
14572
|
-
}
|
|
14573
|
-
return -1;
|
|
14574
|
-
}
|
|
14575
14774
|
function stripWrapperNewlines(s) {
|
|
14576
14775
|
return s.replace(/^\r?\n/, "").replace(/\r?\n$/, "");
|
|
14577
14776
|
}
|
|
@@ -14580,14 +14779,11 @@ function stripTrailingPartialClosingTag(inner, tag) {
|
|
|
14580
14779
|
const re = new RegExp(String.raw`[\t ]*<\s*\/\s*${tag}[^>]*$`, "i");
|
|
14581
14780
|
return inner.replace(re, "");
|
|
14582
14781
|
}
|
|
14583
|
-
function escapeRegex$1(value) {
|
|
14584
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14585
|
-
}
|
|
14586
14782
|
function findMatchingCloseTagRange(rawHtml, tag, startIndex) {
|
|
14587
14783
|
if (!rawHtml || !tag) return null;
|
|
14588
14784
|
const lowerTag = tag.toLowerCase();
|
|
14589
|
-
const openTagRe = new RegExp(String.raw`^<\s*${
|
|
14590
|
-
const closeTagRe = new RegExp(String.raw`^<\s*\/\s*${
|
|
14785
|
+
const openTagRe = new RegExp(String.raw`^<\s*${escapeTagForRegExp(lowerTag)}(?=\s|>|/)`, "i");
|
|
14786
|
+
const closeTagRe = new RegExp(String.raw`^<\s*\/\s*${escapeTagForRegExp(lowerTag)}(?=\s|>)`, "i");
|
|
14591
14787
|
let depth = 0;
|
|
14592
14788
|
let index = Math.max(0, startIndex);
|
|
14593
14789
|
while (index < rawHtml.length) {
|
|
@@ -14595,7 +14791,7 @@ function findMatchingCloseTagRange(rawHtml, tag, startIndex) {
|
|
|
14595
14791
|
if (lt === -1) break;
|
|
14596
14792
|
const slice = rawHtml.slice(lt);
|
|
14597
14793
|
if (closeTagRe.test(slice)) {
|
|
14598
|
-
const endRel = findTagCloseIndexOutsideQuotes
|
|
14794
|
+
const endRel = findTagCloseIndexOutsideQuotes(slice);
|
|
14599
14795
|
if (endRel === -1) return null;
|
|
14600
14796
|
if (depth === 0) return {
|
|
14601
14797
|
start: lt,
|
|
@@ -14606,7 +14802,7 @@ function findMatchingCloseTagRange(rawHtml, tag, startIndex) {
|
|
|
14606
14802
|
continue;
|
|
14607
14803
|
}
|
|
14608
14804
|
if (openTagRe.test(slice)) {
|
|
14609
|
-
const endRel = findTagCloseIndexOutsideQuotes
|
|
14805
|
+
const endRel = findTagCloseIndexOutsideQuotes(slice);
|
|
14610
14806
|
if (endRel === -1) return null;
|
|
14611
14807
|
const raw = slice.slice(0, endRel + 1);
|
|
14612
14808
|
if (!/\/\s*>$/.test(raw)) depth++;
|
|
@@ -14626,7 +14822,7 @@ function findNextCustomHtmlBlockFromSource(source, tag, startIndex) {
|
|
|
14626
14822
|
if (!openMatch || openMatch.index == null) return null;
|
|
14627
14823
|
const openStart = openMatch.index;
|
|
14628
14824
|
const openSlice = source.slice(openStart);
|
|
14629
|
-
const openEndRel = findTagCloseIndexOutsideQuotes
|
|
14825
|
+
const openEndRel = findTagCloseIndexOutsideQuotes(openSlice);
|
|
14630
14826
|
if (openEndRel === -1) return null;
|
|
14631
14827
|
const openEnd = openStart + openEndRel;
|
|
14632
14828
|
if (/\/\s*>\s*$/.test(openSlice.slice(0, openEndRel + 1))) {
|
|
@@ -14667,7 +14863,7 @@ function findNextCustomHtmlBlockFromSource(source, tag, startIndex) {
|
|
|
14667
14863
|
continue;
|
|
14668
14864
|
}
|
|
14669
14865
|
if (isOpenAt(lt)) {
|
|
14670
|
-
const rel = findTagCloseIndexOutsideQuotes
|
|
14866
|
+
const rel = findTagCloseIndexOutsideQuotes(source.slice(lt));
|
|
14671
14867
|
if (rel === -1) return null;
|
|
14672
14868
|
depth++;
|
|
14673
14869
|
i = lt + rel + 1;
|
|
@@ -14723,7 +14919,7 @@ function parseBasicBlockToken(tokens, index, options) {
|
|
|
14723
14919
|
const fromSource = findNextCustomHtmlBlockFromSource(source, tag, Math.max(clampNonNegative(cursor), clampNonNegative(mappedLineStart)));
|
|
14724
14920
|
if (fromSource) options.__customHtmlBlockCursor = fromSource.end;
|
|
14725
14921
|
const rawHtml = String(fromSource?.raw ?? htmlBlockNode.raw ?? "");
|
|
14726
|
-
const openEnd = findTagCloseIndexOutsideQuotes
|
|
14922
|
+
const openEnd = findTagCloseIndexOutsideQuotes(rawHtml);
|
|
14727
14923
|
const openTag = openEnd !== -1 ? rawHtml.slice(0, openEnd + 1) : rawHtml;
|
|
14728
14924
|
const selfClosing = openEnd !== -1 && /\/\s*>\s*$/.test(openTag);
|
|
14729
14925
|
const closeRange = openEnd === -1 ? null : findMatchingCloseTagRange(rawHtml, tag, openEnd + 1);
|
|
@@ -14818,26 +15014,17 @@ function parseParagraph(tokens, index, options) {
|
|
|
14818
15014
|
|
|
14819
15015
|
//#endregion
|
|
14820
15016
|
//#region src/parser/index.ts
|
|
14821
|
-
function normalizeTagName(t) {
|
|
14822
|
-
const raw = String(t ?? "").trim();
|
|
14823
|
-
if (!raw) return "";
|
|
14824
|
-
const m = raw.match(/^[<\s/]*([A-Z][\w-]*)/i);
|
|
14825
|
-
return m ? m[1].toLowerCase() : "";
|
|
14826
|
-
}
|
|
14827
15017
|
function getCustomHtmlTagSet(options) {
|
|
14828
15018
|
const custom = options?.customHtmlTags;
|
|
14829
15019
|
if (!Array.isArray(custom) || custom.length === 0) return null;
|
|
14830
|
-
const normalized = custom
|
|
15020
|
+
const normalized = normalizeCustomHtmlTags(custom);
|
|
14831
15021
|
return normalized.length ? new Set(normalized) : null;
|
|
14832
15022
|
}
|
|
14833
15023
|
function buildAllowedHtmlTagSet(options) {
|
|
14834
15024
|
const custom = options?.customHtmlTags;
|
|
14835
15025
|
if (!Array.isArray(custom) || custom.length === 0) return STANDARD_HTML_TAGS;
|
|
14836
15026
|
const set = new Set(STANDARD_HTML_TAGS);
|
|
14837
|
-
for (const
|
|
14838
|
-
const name = normalizeTagName(t);
|
|
14839
|
-
if (name) set.add(name);
|
|
14840
|
-
}
|
|
15027
|
+
for (const name of normalizeCustomHtmlTags(custom)) if (name) set.add(name);
|
|
14841
15028
|
return set;
|
|
14842
15029
|
}
|
|
14843
15030
|
function stringifyInlineNodeRaw(node) {
|
|
@@ -14896,30 +15083,6 @@ function parseStandaloneHtmlDocument(markdown) {
|
|
|
14896
15083
|
loading: false
|
|
14897
15084
|
}];
|
|
14898
15085
|
}
|
|
14899
|
-
function escapeRegex(value) {
|
|
14900
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14901
|
-
}
|
|
14902
|
-
function findTagCloseIndexOutsideQuotes(input) {
|
|
14903
|
-
let inSingle = false;
|
|
14904
|
-
let inDouble = false;
|
|
14905
|
-
for (let i = 0; i < input.length; i++) {
|
|
14906
|
-
const ch = input[i];
|
|
14907
|
-
if (ch === "\\") {
|
|
14908
|
-
i++;
|
|
14909
|
-
continue;
|
|
14910
|
-
}
|
|
14911
|
-
if (!inDouble && ch === "'") {
|
|
14912
|
-
inSingle = !inSingle;
|
|
14913
|
-
continue;
|
|
14914
|
-
}
|
|
14915
|
-
if (!inSingle && ch === "\"") {
|
|
14916
|
-
inDouble = !inDouble;
|
|
14917
|
-
continue;
|
|
14918
|
-
}
|
|
14919
|
-
if (!inSingle && !inDouble && ch === ">") return i;
|
|
14920
|
-
}
|
|
14921
|
-
return -1;
|
|
14922
|
-
}
|
|
14923
15086
|
function getMergeableNodeRaw(node) {
|
|
14924
15087
|
const raw = node?.raw;
|
|
14925
15088
|
if (typeof raw === "string") return raw;
|
|
@@ -14927,41 +15090,15 @@ function getMergeableNodeRaw(node) {
|
|
|
14927
15090
|
if (typeof content === "string") return content;
|
|
14928
15091
|
return "";
|
|
14929
15092
|
}
|
|
14930
|
-
|
|
14931
|
-
"
|
|
14932
|
-
"
|
|
14933
|
-
"
|
|
14934
|
-
|
|
14935
|
-
"figcaption",
|
|
14936
|
-
"figure",
|
|
14937
|
-
"footer",
|
|
14938
|
-
"h1",
|
|
14939
|
-
"h2",
|
|
14940
|
-
"h3",
|
|
14941
|
-
"h4",
|
|
14942
|
-
"h5",
|
|
14943
|
-
"h6",
|
|
14944
|
-
"header",
|
|
14945
|
-
"li",
|
|
14946
|
-
"main",
|
|
14947
|
-
"nav",
|
|
14948
|
-
"ol",
|
|
14949
|
-
"p",
|
|
14950
|
-
"pre",
|
|
14951
|
-
"section",
|
|
14952
|
-
"summary",
|
|
14953
|
-
"table",
|
|
14954
|
-
"tbody",
|
|
14955
|
-
"td",
|
|
14956
|
-
"th",
|
|
14957
|
-
"thead",
|
|
14958
|
-
"tr",
|
|
14959
|
-
"ul"
|
|
14960
|
-
]);
|
|
15093
|
+
function isCloseOnlyHtmlBlockForTag(node, tag) {
|
|
15094
|
+
if (node.type !== "html_block" || !tag) return false;
|
|
15095
|
+
const raw = String(node?.raw ?? node?.content ?? "");
|
|
15096
|
+
return new RegExp(String.raw`^\s*<\s*\/\s*${escapeTagForRegExp(tag)}\s*>\s*$`, "i").test(raw);
|
|
15097
|
+
}
|
|
14961
15098
|
function findNextHtmlBlockFromSource(source, tag, startIndex) {
|
|
14962
15099
|
if (!source || !tag) return null;
|
|
14963
15100
|
const lowerTag = tag.toLowerCase();
|
|
14964
|
-
const openRe = new RegExp(String.raw`<\s*${
|
|
15101
|
+
const openRe = new RegExp(String.raw`<\s*${escapeTagForRegExp(lowerTag)}(?=\s|>|/)`, "gi");
|
|
14965
15102
|
openRe.lastIndex = Math.max(0, startIndex);
|
|
14966
15103
|
const openMatch = openRe.exec(source);
|
|
14967
15104
|
if (!openMatch || openMatch.index == null) return null;
|
|
@@ -14980,11 +15117,11 @@ function findNextHtmlBlockFromSource(source, tag, startIndex) {
|
|
|
14980
15117
|
let index = openEnd + 1;
|
|
14981
15118
|
const isOpenAt = (pos) => {
|
|
14982
15119
|
const slice = source.slice(pos);
|
|
14983
|
-
return new RegExp(String.raw`^<\s*${
|
|
15120
|
+
return new RegExp(String.raw`^<\s*${escapeTagForRegExp(lowerTag)}(?=\s|>|/)`, "i").test(slice);
|
|
14984
15121
|
};
|
|
14985
15122
|
const isCloseAt = (pos) => {
|
|
14986
15123
|
const slice = source.slice(pos);
|
|
14987
|
-
return new RegExp(String.raw`^<\s*\/\s*${
|
|
15124
|
+
return new RegExp(String.raw`^<\s*\/\s*${escapeTagForRegExp(lowerTag)}(?=\s|>)`, "i").test(slice);
|
|
14988
15125
|
};
|
|
14989
15126
|
while (index < source.length) {
|
|
14990
15127
|
const lt = source.indexOf("<", index);
|
|
@@ -15070,7 +15207,7 @@ function isDetailsCloseHtmlBlock(node) {
|
|
|
15070
15207
|
return /^\s*<\/details\b/i.test(raw);
|
|
15071
15208
|
}
|
|
15072
15209
|
function findLastClosingTagStart(raw, tag) {
|
|
15073
|
-
const closeRe = new RegExp(String.raw`<\s*\/\s*${
|
|
15210
|
+
const closeRe = new RegExp(String.raw`<\s*\/\s*${escapeTagForRegExp(tag)}(?=\s|>)`, "gi");
|
|
15074
15211
|
let last = -1;
|
|
15075
15212
|
let match;
|
|
15076
15213
|
while ((match = closeRe.exec(raw)) !== null) last = match.index;
|
|
@@ -15150,35 +15287,43 @@ function combineStructuredDetailsHtmlBlocks(nodes, source, md, options, final, s
|
|
|
15150
15287
|
break;
|
|
15151
15288
|
}
|
|
15152
15289
|
}
|
|
15153
|
-
const [children] = combineStructuredDetailsHtmlBlocks(closeIndex === -1 ? nodes.slice(i + 1) : nodes.slice(i + 1, closeIndex), source, md, options, final, openStart + openRaw.length);
|
|
15154
|
-
const prefixChildren = buildDetailsPrefixChildren(openRaw, md, buildDetailsChildParseOptions(options, final));
|
|
15155
15290
|
const exact = findNextHtmlBlockFromSource(source, "details", openStart);
|
|
15291
|
+
const selfContained = closeIndex === -1 && exact?.closed === true;
|
|
15292
|
+
const effectiveOpenRaw = selfContained ? (() => {
|
|
15293
|
+
const ct = findLastClosingTagStart(openRaw, "details");
|
|
15294
|
+
return ct !== -1 ? openRaw.slice(0, ct) : openRaw;
|
|
15295
|
+
})() : openRaw;
|
|
15296
|
+
const [children] = combineStructuredDetailsHtmlBlocks(selfContained ? [] : closeIndex === -1 ? nodes.slice(i + 1) : nodes.slice(i + 1, closeIndex), source, md, options, final, openStart + openRaw.length);
|
|
15297
|
+
const prefixChildren = buildDetailsPrefixChildren(effectiveOpenRaw, md, buildDetailsChildParseOptions(options, final));
|
|
15156
15298
|
const closeRaw = closeIndex === -1 ? "</details>" : String(nodes[closeIndex]?.raw ?? getMergeableNodeRaw(nodes[closeIndex]) ?? "</details>");
|
|
15157
|
-
const explicitClose = closeIndex !== -1 && exact?.closed === true;
|
|
15299
|
+
const explicitClose = selfContained || closeIndex !== -1 && exact?.closed === true;
|
|
15158
15300
|
const trimmedCloseRaw = closeRaw.replace(/[\t\r\n ]+$/, "");
|
|
15159
15301
|
const closeStart = explicitClose ? (() => {
|
|
15160
|
-
const closeOffset = exact
|
|
15302
|
+
const closeOffset = (exact?.raw ?? "").lastIndexOf(trimmedCloseRaw);
|
|
15161
15303
|
return closeOffset === -1 ? source.length : openStart + closeOffset;
|
|
15162
15304
|
})() : source.length;
|
|
15163
|
-
const
|
|
15305
|
+
const openTagEndIndex = findTagCloseIndexOutsideQuotes(openRaw);
|
|
15306
|
+
const middleSourceStart = selfContained && openTagEndIndex !== -1 ? openStart + openTagEndIndex + 1 : openStart + openRaw.length;
|
|
15307
|
+
const middleSource = source.slice(middleSourceStart, closeStart === -1 ? source.length : closeStart);
|
|
15164
15308
|
const middleTokens = md.parse(middleSource, { __markstreamFinal: final });
|
|
15165
15309
|
const renderedMiddle = md.renderer.render(middleTokens, md.options, { __markstreamFinal: final });
|
|
15166
15310
|
const closeMarkupEnd = closeStart + trimmedCloseRaw.length;
|
|
15167
15311
|
const closeSliceEnd = explicitClose ? Math.max(closeStart + closeRaw.length, extendHtmlBlockCloseToLineEnding(source, closeMarkupEnd)) : source.length;
|
|
15168
15312
|
const renderedCloseRaw = explicitClose ? source.slice(closeStart, closeSliceEnd) : closeRaw;
|
|
15169
15313
|
const mergedRaw = explicitClose ? source.slice(openStart, closeSliceEnd) : source.slice(openStart);
|
|
15314
|
+
const contentPrefix = selfContained && openTagEndIndex !== -1 ? openRaw.slice(0, openTagEndIndex + 1) : openRaw;
|
|
15170
15315
|
merged.push({
|
|
15171
15316
|
...node,
|
|
15172
15317
|
tag: "details",
|
|
15173
|
-
attrs: parseTagAttrs(openRaw.slice(0,
|
|
15318
|
+
attrs: parseTagAttrs(openRaw.slice(0, openTagEndIndex + 1)),
|
|
15174
15319
|
raw: mergedRaw,
|
|
15175
|
-
content: `${
|
|
15320
|
+
content: `${contentPrefix}${renderedMiddle}${renderedCloseRaw}`,
|
|
15176
15321
|
children: [...prefixChildren, ...children],
|
|
15177
15322
|
loading: !final && !explicitClose
|
|
15178
15323
|
});
|
|
15179
15324
|
cursor = explicitClose ? closeSliceEnd : source.length;
|
|
15180
|
-
if (closeIndex === -1) break;
|
|
15181
|
-
i = closeIndex;
|
|
15325
|
+
if (closeIndex === -1 && !selfContained) break;
|
|
15326
|
+
if (closeIndex !== -1) i = closeIndex;
|
|
15182
15327
|
}
|
|
15183
15328
|
return [merged, cursor];
|
|
15184
15329
|
}
|
|
@@ -15188,14 +15333,22 @@ function mergeSplitTopLevelHtmlBlocks(nodes, final, source) {
|
|
|
15188
15333
|
let sourceHtmlCursor = 0;
|
|
15189
15334
|
for (let i = 0; i < merged.length; i++) {
|
|
15190
15335
|
const node = merged[i];
|
|
15191
|
-
|
|
15336
|
+
const nodeRaw = getMergeableNodeRaw(node);
|
|
15337
|
+
const nodePos = nodeRaw ? source.indexOf(nodeRaw, sourceHtmlCursor) : -1;
|
|
15338
|
+
if (node?.type !== "html_block") {
|
|
15339
|
+
if (nodePos !== -1) sourceHtmlCursor = nodePos + nodeRaw.length;
|
|
15340
|
+
continue;
|
|
15341
|
+
}
|
|
15192
15342
|
const tag = String(node?.tag ?? "").toLowerCase();
|
|
15193
15343
|
if (!tag) continue;
|
|
15194
|
-
if (
|
|
15195
|
-
|
|
15344
|
+
if (tag === "details") {
|
|
15345
|
+
if (nodePos !== -1) sourceHtmlCursor = nodePos + nodeRaw.length;
|
|
15346
|
+
continue;
|
|
15347
|
+
}
|
|
15348
|
+
const exact = findNextHtmlBlockFromSource(source, tag, nodePos !== -1 ? nodePos : sourceHtmlCursor);
|
|
15196
15349
|
if (!exact) continue;
|
|
15197
15350
|
sourceHtmlCursor = exact.end;
|
|
15198
|
-
const currentContent = String(node?.content ??
|
|
15351
|
+
const currentContent = String(node?.content ?? nodeRaw);
|
|
15199
15352
|
const currentRaw = String(node?.raw ?? currentContent);
|
|
15200
15353
|
const nextContent = buildHtmlBlockContent(exact.raw, tag, exact.closed);
|
|
15201
15354
|
const desiredLoading = !final && !exact.closed;
|
|
@@ -15204,10 +15357,14 @@ function mergeSplitTopLevelHtmlBlocks(nodes, final, source) {
|
|
|
15204
15357
|
node.raw = exact.raw;
|
|
15205
15358
|
node.loading = desiredLoading;
|
|
15206
15359
|
if (!needsExpansion) continue;
|
|
15207
|
-
let tailCursor = findApproximateConsumedPrefixEnd(exact.raw,
|
|
15360
|
+
let tailCursor = findApproximateConsumedPrefixEnd(exact.raw, currentRaw);
|
|
15208
15361
|
if (tailCursor === -1) tailCursor = 0;
|
|
15209
15362
|
const j = i + 1;
|
|
15210
15363
|
while (j < merged.length) {
|
|
15364
|
+
if (exact.closed && isCloseOnlyHtmlBlockForTag(merged[j], tag)) {
|
|
15365
|
+
merged.splice(j, 1);
|
|
15366
|
+
continue;
|
|
15367
|
+
}
|
|
15211
15368
|
const nextRaw = getMergeableNodeRaw(merged[j]);
|
|
15212
15369
|
if (!nextRaw) break;
|
|
15213
15370
|
const nextPos = exact.raw.indexOf(nextRaw, tailCursor);
|
|
@@ -15386,7 +15543,7 @@ function ensureBlankLineBeforeInlineMultilineCustomHtmlBlocks(markdown, tags) {
|
|
|
15386
15543
|
}
|
|
15387
15544
|
return false;
|
|
15388
15545
|
};
|
|
15389
|
-
const findTagCloseIndexOutsideQuotes$
|
|
15546
|
+
const findTagCloseIndexOutsideQuotes$1 = (input) => {
|
|
15390
15547
|
let inSingle = false;
|
|
15391
15548
|
let inDouble = false;
|
|
15392
15549
|
for (let i = 0; i < input.length; i++) {
|
|
@@ -15435,7 +15592,7 @@ function ensureBlankLineBeforeInlineMultilineCustomHtmlBlocks(markdown, tags) {
|
|
|
15435
15592
|
i++;
|
|
15436
15593
|
continue;
|
|
15437
15594
|
}
|
|
15438
|
-
const closeIdxRel = findTagCloseIndexOutsideQuotes$
|
|
15595
|
+
const closeIdxRel = findTagCloseIndexOutsideQuotes$1(line.slice(i));
|
|
15439
15596
|
if (closeIdxRel === -1) {
|
|
15440
15597
|
hasRenderablePrefix = true;
|
|
15441
15598
|
i++;
|
|
@@ -15539,7 +15696,7 @@ function normalizeCustomHtmlOpeningTagSameLine(markdown, tags) {
|
|
|
15539
15696
|
while (i < s.length && isIndentWs(s[i])) i++;
|
|
15540
15697
|
return s.slice(i);
|
|
15541
15698
|
};
|
|
15542
|
-
const findTagCloseIndexOutsideQuotes$
|
|
15699
|
+
const findTagCloseIndexOutsideQuotes$1 = (input) => {
|
|
15543
15700
|
let inSingle = false;
|
|
15544
15701
|
let inDouble = false;
|
|
15545
15702
|
for (let i = 0; i < input.length; i++) {
|
|
@@ -15611,7 +15768,7 @@ function normalizeCustomHtmlOpeningTagSameLine(markdown, tags) {
|
|
|
15611
15768
|
if (i === nameStart) return line;
|
|
15612
15769
|
const tagName = line.slice(nameStart, i).toLowerCase();
|
|
15613
15770
|
if (!tagSet.has(tagName)) return line;
|
|
15614
|
-
const gtRel = findTagCloseIndexOutsideQuotes$
|
|
15771
|
+
const gtRel = findTagCloseIndexOutsideQuotes$1(line.slice(i));
|
|
15615
15772
|
if (gtRel === -1) return line;
|
|
15616
15773
|
const gt = i + gtRel;
|
|
15617
15774
|
if (hasClosingTagOnLine(line, gt + 1, tagName)) return line;
|
|
@@ -16004,9 +16161,7 @@ function parseMarkdownToStructure(markdown, md, options = {}) {
|
|
|
16004
16161
|
else if (/\n[[(]\n*$/.test(safeMarkdown)) safeMarkdown = safeMarkdown.replace(/(\n\[|\n\()+\n*$/g, "\n");
|
|
16005
16162
|
}
|
|
16006
16163
|
if (options.customHtmlTags?.length && safeMarkdown.includes("<")) {
|
|
16007
|
-
const tags = options.customHtmlTags
|
|
16008
|
-
return (t.match(/^[<\s/]*([A-Z][\w-]*)/i)?.[1] ?? "").toLowerCase();
|
|
16009
|
-
}).filter(Boolean);
|
|
16164
|
+
const tags = normalizeCustomHtmlTags(options.customHtmlTags);
|
|
16010
16165
|
if (tags.length) {
|
|
16011
16166
|
safeMarkdown = ensureBlankLineBeforeInlineMultilineCustomHtmlBlocks(safeMarkdown, tags);
|
|
16012
16167
|
safeMarkdown = normalizeCustomHtmlOpeningTagSameLine(safeMarkdown, tags);
|
|
@@ -16164,6 +16319,160 @@ function processTokens(tokens, options) {
|
|
|
16164
16319
|
return result;
|
|
16165
16320
|
}
|
|
16166
16321
|
|
|
16322
|
+
//#endregion
|
|
16323
|
+
//#region src/htmlRenderUtils.ts
|
|
16324
|
+
const CUSTOM_TAG_REGEX = /<([a-z][a-z0-9-]*)\b[^>]*>/gi;
|
|
16325
|
+
function hasOwn(obj, key) {
|
|
16326
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
16327
|
+
}
|
|
16328
|
+
function isCustomHtmlComponentTag(tagName, customComponents) {
|
|
16329
|
+
const lowerTag = tagName.toLowerCase();
|
|
16330
|
+
if (EXTENDED_STANDARD_HTML_TAGS.has(lowerTag)) return false;
|
|
16331
|
+
return hasOwn(customComponents, lowerTag) || hasOwn(customComponents, tagName);
|
|
16332
|
+
}
|
|
16333
|
+
function sanitizeHtmlAttrs(attrs) {
|
|
16334
|
+
const clean = {};
|
|
16335
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
16336
|
+
const lowerKey = key.toLowerCase();
|
|
16337
|
+
if (DANGEROUS_HTML_ATTRS.has(lowerKey)) continue;
|
|
16338
|
+
if (URL_HTML_ATTRS.has(lowerKey) && value && isUnsafeHtmlUrl(value)) continue;
|
|
16339
|
+
clean[key] = value;
|
|
16340
|
+
}
|
|
16341
|
+
return clean;
|
|
16342
|
+
}
|
|
16343
|
+
function convertHtmlPropValue(value, key) {
|
|
16344
|
+
const lowerKey = key.toLowerCase();
|
|
16345
|
+
if ([
|
|
16346
|
+
"checked",
|
|
16347
|
+
"disabled",
|
|
16348
|
+
"readonly",
|
|
16349
|
+
"required",
|
|
16350
|
+
"autofocus",
|
|
16351
|
+
"multiple",
|
|
16352
|
+
"hidden"
|
|
16353
|
+
].includes(lowerKey)) return value === "true" || value === "" || value === key;
|
|
16354
|
+
if ([
|
|
16355
|
+
"value",
|
|
16356
|
+
"min",
|
|
16357
|
+
"max",
|
|
16358
|
+
"step",
|
|
16359
|
+
"width",
|
|
16360
|
+
"height",
|
|
16361
|
+
"size",
|
|
16362
|
+
"maxlength"
|
|
16363
|
+
].includes(lowerKey)) {
|
|
16364
|
+
const num = Number(value);
|
|
16365
|
+
if (value !== "" && !Number.isNaN(num)) return num;
|
|
16366
|
+
}
|
|
16367
|
+
return value;
|
|
16368
|
+
}
|
|
16369
|
+
function convertHtmlAttrsToProps(attrs) {
|
|
16370
|
+
const result = {};
|
|
16371
|
+
for (const [key, value] of Object.entries(attrs)) result[key] = convertHtmlPropValue(value, key);
|
|
16372
|
+
return result;
|
|
16373
|
+
}
|
|
16374
|
+
function isMeaningfulText(text$1) {
|
|
16375
|
+
return text$1.trim().length > 0;
|
|
16376
|
+
}
|
|
16377
|
+
function tokenizeHtml(html) {
|
|
16378
|
+
const tokens = [];
|
|
16379
|
+
let pos = 0;
|
|
16380
|
+
while (pos < html.length) {
|
|
16381
|
+
if (html.startsWith("<!--", pos)) {
|
|
16382
|
+
const commentEnd = html.indexOf("-->", pos);
|
|
16383
|
+
if (commentEnd !== -1) {
|
|
16384
|
+
pos = commentEnd + 3;
|
|
16385
|
+
continue;
|
|
16386
|
+
}
|
|
16387
|
+
break;
|
|
16388
|
+
}
|
|
16389
|
+
const tagStart = html.indexOf("<", pos);
|
|
16390
|
+
if (tagStart === -1) {
|
|
16391
|
+
if (pos < html.length) {
|
|
16392
|
+
const remainingText = html.slice(pos);
|
|
16393
|
+
if (isMeaningfulText(remainingText)) tokens.push({
|
|
16394
|
+
type: "text",
|
|
16395
|
+
content: remainingText
|
|
16396
|
+
});
|
|
16397
|
+
}
|
|
16398
|
+
break;
|
|
16399
|
+
}
|
|
16400
|
+
if (tagStart > pos) {
|
|
16401
|
+
const textContent = html.slice(pos, tagStart);
|
|
16402
|
+
if (isMeaningfulText(textContent)) tokens.push({
|
|
16403
|
+
type: "text",
|
|
16404
|
+
content: textContent
|
|
16405
|
+
});
|
|
16406
|
+
}
|
|
16407
|
+
if (html.startsWith("![CDATA[", tagStart + 1)) {
|
|
16408
|
+
const cdataEnd = html.indexOf("]]>", tagStart);
|
|
16409
|
+
if (cdataEnd !== -1) {
|
|
16410
|
+
tokens.push({
|
|
16411
|
+
type: "text",
|
|
16412
|
+
content: html.slice(tagStart, cdataEnd + 3)
|
|
16413
|
+
});
|
|
16414
|
+
pos = cdataEnd + 3;
|
|
16415
|
+
continue;
|
|
16416
|
+
}
|
|
16417
|
+
break;
|
|
16418
|
+
}
|
|
16419
|
+
if (html.startsWith("!", tagStart + 1)) {
|
|
16420
|
+
const specialEnd = html.indexOf(">", tagStart);
|
|
16421
|
+
if (specialEnd !== -1) {
|
|
16422
|
+
pos = specialEnd + 1;
|
|
16423
|
+
continue;
|
|
16424
|
+
}
|
|
16425
|
+
break;
|
|
16426
|
+
}
|
|
16427
|
+
const tagEnd = html.indexOf(">", tagStart);
|
|
16428
|
+
if (tagEnd === -1) break;
|
|
16429
|
+
const tagContent = html.slice(tagStart + 1, tagEnd).trim();
|
|
16430
|
+
const isClosingTag$1 = tagContent.startsWith("/");
|
|
16431
|
+
const isSelfClosing$1 = tagContent.endsWith("/");
|
|
16432
|
+
if (isClosingTag$1) {
|
|
16433
|
+
const tagName = tagContent.slice(1).trim();
|
|
16434
|
+
tokens.push({
|
|
16435
|
+
type: "tag_close",
|
|
16436
|
+
tagName
|
|
16437
|
+
});
|
|
16438
|
+
} else {
|
|
16439
|
+
const spaceIndex = tagContent.indexOf(" ");
|
|
16440
|
+
let tagName;
|
|
16441
|
+
let attrsStr = "";
|
|
16442
|
+
if (spaceIndex === -1) tagName = isSelfClosing$1 ? tagContent.slice(0, -1).trim() : tagContent.trim();
|
|
16443
|
+
else {
|
|
16444
|
+
tagName = tagContent.slice(0, spaceIndex).trim();
|
|
16445
|
+
attrsStr = tagContent.slice(spaceIndex + 1);
|
|
16446
|
+
}
|
|
16447
|
+
const attrs = {};
|
|
16448
|
+
if (attrsStr) {
|
|
16449
|
+
const attrRegex = /([^\s=]+)(?:=(?:"([^"]*)"|'([^']*)'|(\S*)))?/g;
|
|
16450
|
+
let attrMatch;
|
|
16451
|
+
while ((attrMatch = attrRegex.exec(attrsStr)) !== null) {
|
|
16452
|
+
const name = attrMatch[1];
|
|
16453
|
+
const value = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? "";
|
|
16454
|
+
if (name && !name.endsWith("/")) attrs[name] = value;
|
|
16455
|
+
}
|
|
16456
|
+
}
|
|
16457
|
+
tokens.push({
|
|
16458
|
+
type: isSelfClosing$1 || VOID_HTML_TAGS.has(tagName.toLowerCase()) ? "self_closing" : "tag_open",
|
|
16459
|
+
tagName,
|
|
16460
|
+
attrs
|
|
16461
|
+
});
|
|
16462
|
+
}
|
|
16463
|
+
pos = tagEnd + 1;
|
|
16464
|
+
}
|
|
16465
|
+
return tokens;
|
|
16466
|
+
}
|
|
16467
|
+
function hasCustomHtmlComponents(content, customComponents) {
|
|
16468
|
+
if (!content || !content.includes("<")) return false;
|
|
16469
|
+
if (!customComponents || Object.keys(customComponents).length === 0) return false;
|
|
16470
|
+
CUSTOM_TAG_REGEX.lastIndex = 0;
|
|
16471
|
+
let match;
|
|
16472
|
+
while ((match = CUSTOM_TAG_REGEX.exec(content)) !== null) if (isCustomHtmlComponentTag(match[1], customComponents)) return true;
|
|
16473
|
+
return false;
|
|
16474
|
+
}
|
|
16475
|
+
|
|
16167
16476
|
//#endregion
|
|
16168
16477
|
//#region src/index.ts
|
|
16169
16478
|
const _registeredMarkdownPlugins = [];
|
|
@@ -16304,5 +16613,5 @@ function getMarkdown(msgId = `editor-${Date.now()}`, options = {}) {
|
|
|
16304
16613
|
}
|
|
16305
16614
|
|
|
16306
16615
|
//#endregion
|
|
16307
|
-
export { BLOCKED_HTML_TAGS, BLOCKED_HTML_TAG_NAMES, BLOCK_HTML_TAG_NAMES, DANGEROUS_HTML_ATTRS, DANGEROUS_HTML_ATTR_NAMES, ESCAPED_TEX_BRACE_COMMANDS, EXTENDED_STANDARD_HTML_TAGS, EXTENDED_STANDARD_HTML_TAG_NAMES, INLINE_HTML_TAG_NAMES, KATEX_COMMANDS, STANDARD_BLOCK_HTML_TAGS, STANDARD_HTML_TAGS, SVG_HTML_TAG_NAMES, TEX_BRACE_COMMANDS, URL_HTML_ATTRS, URL_HTML_ATTR_NAMES, VOID_HTML_TAGS, VOID_HTML_TAG_NAMES, applyContainers, applyMath, clearRegisteredMarkdownPlugins, findMatchingClose, getMarkdown, isMathLike, isUnsafeHtmlUrl, normalizeStandaloneBackslashT, parseFenceToken, parseInlineTokens, parseMarkdownToStructure, processTokens, registerMarkdownPlugin, setDefaultMathOptions, stripHtmlControlAndWhitespace };
|
|
16616
|
+
export { BLOCKED_HTML_TAGS, BLOCKED_HTML_TAG_NAMES, BLOCK_HTML_TAG_NAMES, DANGEROUS_HTML_ATTRS, DANGEROUS_HTML_ATTR_NAMES, ESCAPED_TEX_BRACE_COMMANDS, EXTENDED_STANDARD_HTML_TAGS, EXTENDED_STANDARD_HTML_TAG_NAMES, INLINE_HTML_TAG_NAMES, KATEX_COMMANDS, STANDARD_BLOCK_HTML_TAGS, STANDARD_HTML_TAGS, SVG_HTML_TAG_NAMES, TEX_BRACE_COMMANDS, URL_HTML_ATTRS, URL_HTML_ATTR_NAMES, VOID_HTML_TAGS, VOID_HTML_TAG_NAMES, applyContainers, applyMath, clearRegisteredMarkdownPlugins, convertHtmlAttrsToProps, convertHtmlPropValue, findMatchingClose, getHtmlTagFromContent, getMarkdown, hasCompleteHtmlTagContent, hasCustomHtmlComponents, isCustomHtmlComponentTag, isHtmlLikeTagName, isMathLike, isUnsafeHtmlUrl, mergeCustomHtmlTags, normalizeCustomHtmlTagName, normalizeCustomHtmlTags, normalizeStandaloneBackslashT, parseFenceToken, parseInlineTokens, parseMarkdownToStructure, processTokens, registerMarkdownPlugin, resolveCustomHtmlTags, sanitizeHtmlAttrs, setDefaultMathOptions, shouldRenderUnknownHtmlTagAsText, stripCustomHtmlWrapper, stripHtmlControlAndWhitespace, tokenizeHtml };
|
|
16308
16617
|
//# sourceMappingURL=index.js.map
|