markstream-svelte 0.0.1-beta.1
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 +77 -0
- package/dist/components/AdmonitionNode.svelte +8 -0
- package/dist/components/AdmonitionNode.svelte.d.ts +23 -0
- package/dist/components/AdmonitionNode.svelte.d.ts.map +1 -0
- package/dist/components/BlockquoteNode.svelte +10 -0
- package/dist/components/BlockquoteNode.svelte.d.ts +24 -0
- package/dist/components/BlockquoteNode.svelte.d.ts.map +1 -0
- package/dist/components/CheckboxNode.svelte +5 -0
- package/dist/components/CheckboxNode.svelte.d.ts +22 -0
- package/dist/components/CheckboxNode.svelte.d.ts.map +1 -0
- package/dist/components/CodeBlockNode.svelte +944 -0
- package/dist/components/CodeBlockNode.svelte.d.ts +43 -0
- package/dist/components/CodeBlockNode.svelte.d.ts.map +1 -0
- package/dist/components/D2BlockNode.svelte +272 -0
- package/dist/components/D2BlockNode.svelte.d.ts +33 -0
- package/dist/components/D2BlockNode.svelte.d.ts.map +1 -0
- package/dist/components/DefinitionListNode.svelte +8 -0
- package/dist/components/DefinitionListNode.svelte.d.ts +23 -0
- package/dist/components/DefinitionListNode.svelte.d.ts.map +1 -0
- package/dist/components/EmojiNode.svelte +7 -0
- package/dist/components/EmojiNode.svelte.d.ts +22 -0
- package/dist/components/EmojiNode.svelte.d.ts.map +1 -0
- package/dist/components/EmphasisNode.svelte +8 -0
- package/dist/components/EmphasisNode.svelte.d.ts +24 -0
- package/dist/components/EmphasisNode.svelte.d.ts.map +1 -0
- package/dist/components/FallbackComponent.svelte +8 -0
- package/dist/components/FallbackComponent.svelte.d.ts +23 -0
- package/dist/components/FallbackComponent.svelte.d.ts.map +1 -0
- package/dist/components/FootnoteAnchorNode.svelte +21 -0
- package/dist/components/FootnoteAnchorNode.svelte.d.ts +22 -0
- package/dist/components/FootnoteAnchorNode.svelte.d.ts.map +1 -0
- package/dist/components/FootnoteNode.svelte +19 -0
- package/dist/components/FootnoteNode.svelte.d.ts +24 -0
- package/dist/components/FootnoteNode.svelte.d.ts.map +1 -0
- package/dist/components/FootnoteReferenceNode.svelte +26 -0
- package/dist/components/FootnoteReferenceNode.svelte.d.ts +22 -0
- package/dist/components/FootnoteReferenceNode.svelte.d.ts.map +1 -0
- package/dist/components/HardBreakNode.svelte +1 -0
- package/dist/components/HardBreakNode.svelte.d.ts +27 -0
- package/dist/components/HardBreakNode.svelte.d.ts.map +1 -0
- package/dist/components/HeadingNode.svelte +15 -0
- package/dist/components/HeadingNode.svelte.d.ts +24 -0
- package/dist/components/HeadingNode.svelte.d.ts.map +1 -0
- package/dist/components/HighlightNode.svelte +8 -0
- package/dist/components/HighlightNode.svelte.d.ts +24 -0
- package/dist/components/HighlightNode.svelte.d.ts.map +1 -0
- package/dist/components/HtmlBlockNode.svelte +8 -0
- package/dist/components/HtmlBlockNode.svelte.d.ts +23 -0
- package/dist/components/HtmlBlockNode.svelte.d.ts.map +1 -0
- package/dist/components/HtmlInlineNode.svelte +8 -0
- package/dist/components/HtmlInlineNode.svelte.d.ts +23 -0
- package/dist/components/HtmlInlineNode.svelte.d.ts.map +1 -0
- package/dist/components/HtmlPreviewFrame.svelte +13 -0
- package/dist/components/HtmlPreviewFrame.svelte.d.ts +26 -0
- package/dist/components/HtmlPreviewFrame.svelte.d.ts.map +1 -0
- package/dist/components/ImageNode.svelte +86 -0
- package/dist/components/ImageNode.svelte.d.ts +25 -0
- package/dist/components/ImageNode.svelte.d.ts.map +1 -0
- package/dist/components/InfographicBlockNode.svelte +429 -0
- package/dist/components/InfographicBlockNode.svelte.d.ts +34 -0
- package/dist/components/InfographicBlockNode.svelte.d.ts.map +1 -0
- package/dist/components/InlineCodeNode.svelte +7 -0
- package/dist/components/InlineCodeNode.svelte.d.ts +22 -0
- package/dist/components/InlineCodeNode.svelte.d.ts.map +1 -0
- package/dist/components/InlineWrapNode.svelte +10 -0
- package/dist/components/InlineWrapNode.svelte.d.ts +25 -0
- package/dist/components/InlineWrapNode.svelte.d.ts.map +1 -0
- package/dist/components/InsertNode.svelte +8 -0
- package/dist/components/InsertNode.svelte.d.ts +24 -0
- package/dist/components/InsertNode.svelte.d.ts.map +1 -0
- package/dist/components/LinkNode.svelte +41 -0
- package/dist/components/LinkNode.svelte.d.ts +25 -0
- package/dist/components/LinkNode.svelte.d.ts.map +1 -0
- package/dist/components/ListItemNode.svelte +9 -0
- package/dist/components/ListItemNode.svelte.d.ts +24 -0
- package/dist/components/ListItemNode.svelte.d.ts.map +1 -0
- package/dist/components/ListNode.svelte +16 -0
- package/dist/components/ListNode.svelte.d.ts +24 -0
- package/dist/components/ListNode.svelte.d.ts.map +1 -0
- package/dist/components/MarkdownCodeBlockNode.svelte +7 -0
- package/dist/components/MarkdownCodeBlockNode.svelte.d.ts +23 -0
- package/dist/components/MarkdownCodeBlockNode.svelte.d.ts.map +1 -0
- package/dist/components/MathBlockNode.svelte +96 -0
- package/dist/components/MathBlockNode.svelte.d.ts +22 -0
- package/dist/components/MathBlockNode.svelte.d.ts.map +1 -0
- package/dist/components/MathInlineNode.svelte +103 -0
- package/dist/components/MathInlineNode.svelte.d.ts +22 -0
- package/dist/components/MathInlineNode.svelte.d.ts.map +1 -0
- package/dist/components/MermaidBlockNode.svelte +557 -0
- package/dist/components/MermaidBlockNode.svelte.d.ts +40 -0
- package/dist/components/MermaidBlockNode.svelte.d.ts.map +1 -0
- package/dist/components/NodeOutlet.svelte +192 -0
- package/dist/components/NodeOutlet.svelte.d.ts +24 -0
- package/dist/components/NodeOutlet.svelte.d.ts.map +1 -0
- package/dist/components/NodeRenderer.svelte +349 -0
- package/dist/components/NodeRenderer.svelte.d.ts +64 -0
- package/dist/components/NodeRenderer.svelte.d.ts.map +1 -0
- package/dist/components/ParagraphNode.svelte +21 -0
- package/dist/components/ParagraphNode.svelte.d.ts +24 -0
- package/dist/components/ParagraphNode.svelte.d.ts.map +1 -0
- package/dist/components/PreCodeNode.svelte +13 -0
- package/dist/components/PreCodeNode.svelte.d.ts +22 -0
- package/dist/components/PreCodeNode.svelte.d.ts.map +1 -0
- package/dist/components/ReferenceNode.svelte +8 -0
- package/dist/components/ReferenceNode.svelte.d.ts +23 -0
- package/dist/components/ReferenceNode.svelte.d.ts.map +1 -0
- package/dist/components/RenderChildren.svelte +12 -0
- package/dist/components/RenderChildren.svelte.d.ts +24 -0
- package/dist/components/RenderChildren.svelte.d.ts.map +1 -0
- package/dist/components/StrikethroughNode.svelte +8 -0
- package/dist/components/StrikethroughNode.svelte.d.ts +24 -0
- package/dist/components/StrikethroughNode.svelte.d.ts.map +1 -0
- package/dist/components/StrongNode.svelte +8 -0
- package/dist/components/StrongNode.svelte.d.ts +24 -0
- package/dist/components/StrongNode.svelte.d.ts.map +1 -0
- package/dist/components/SubscriptNode.svelte +8 -0
- package/dist/components/SubscriptNode.svelte.d.ts +24 -0
- package/dist/components/SubscriptNode.svelte.d.ts.map +1 -0
- package/dist/components/SuperscriptNode.svelte +8 -0
- package/dist/components/SuperscriptNode.svelte.d.ts +24 -0
- package/dist/components/SuperscriptNode.svelte.d.ts.map +1 -0
- package/dist/components/TableNode.svelte +8 -0
- package/dist/components/TableNode.svelte.d.ts +23 -0
- package/dist/components/TableNode.svelte.d.ts.map +1 -0
- package/dist/components/TextNode.svelte +37 -0
- package/dist/components/TextNode.svelte.d.ts +25 -0
- package/dist/components/TextNode.svelte.d.ts.map +1 -0
- package/dist/components/ThematicBreakNode.svelte +1 -0
- package/dist/components/ThematicBreakNode.svelte.d.ts +27 -0
- package/dist/components/ThematicBreakNode.svelte.d.ts.map +1 -0
- package/dist/components/Tooltip.svelte +4 -0
- package/dist/components/Tooltip.svelte.d.ts +21 -0
- package/dist/components/Tooltip.svelte.d.ts.map +1 -0
- package/dist/components/VmrContainerNode.svelte +8 -0
- package/dist/components/VmrContainerNode.svelte.d.ts +23 -0
- package/dist/components/VmrContainerNode.svelte.d.ts.map +1 -0
- package/dist/components/shared/diagram-height.d.ts +10 -0
- package/dist/components/shared/diagram-height.d.ts.map +1 -0
- package/dist/components/shared/diagram-height.js +52 -0
- package/dist/components/shared/node-helpers.d.ts +108 -0
- package/dist/components/shared/node-helpers.d.ts.map +1 -0
- package/dist/components/shared/node-helpers.js +220 -0
- package/dist/components/shared/node-outlet-helpers.d.ts +9 -0
- package/dist/components/shared/node-outlet-helpers.d.ts.map +1 -0
- package/dist/components/shared/node-outlet-helpers.js +92 -0
- package/dist/components/shared/render-window.d.ts +19 -0
- package/dist/components/shared/render-window.d.ts.map +1 -0
- package/dist/components/shared/render-window.js +63 -0
- package/dist/components/shared/renderNodeHtml.d.ts +4 -0
- package/dist/components/shared/renderNodeHtml.d.ts.map +1 -0
- package/dist/components/shared/renderNodeHtml.js +19 -0
- package/dist/components/shared/rich-block-helpers.d.ts +7 -0
- package/dist/components/shared/rich-block-helpers.d.ts.map +1 -0
- package/dist/components/shared/rich-block-helpers.js +76 -0
- package/dist/customComponents.d.ts +10 -0
- package/dist/customComponents.d.ts.map +1 -0
- package/dist/customComponents.js +66 -0
- package/dist/enhanceRenderedHtml.d.ts +21 -0
- package/dist/enhanceRenderedHtml.d.ts.map +1 -0
- package/dist/enhanceRenderedHtml.js +768 -0
- package/dist/hydrateCustomTagContent.d.ts +3 -0
- package/dist/hydrateCustomTagContent.d.ts.map +1 -0
- package/dist/hydrateCustomTagContent.js +130 -0
- package/dist/i18n/useSafeI18n.d.ts +5 -0
- package/dist/i18n/useSafeI18n.d.ts.map +1 -0
- package/dist/i18n/useSafeI18n.js +42 -0
- package/dist/index.css +2009 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.px.css +2009 -0
- package/dist/index.tailwind.css +2009 -0
- package/dist/optional/d2.d.ts +7 -0
- package/dist/optional/d2.d.ts.map +1 -0
- package/dist/optional/d2.js +67 -0
- package/dist/optional/infographic.d.ts +2 -0
- package/dist/optional/infographic.d.ts.map +1 -0
- package/dist/optional/infographic.js +29 -0
- package/dist/optional/katex.d.ts +7 -0
- package/dist/optional/katex.d.ts.map +1 -0
- package/dist/optional/katex.js +93 -0
- package/dist/optional/mermaid.d.ts +7 -0
- package/dist/optional/mermaid.d.ts.map +1 -0
- package/dist/optional/mermaid.js +171 -0
- package/dist/optional/monaco.d.ts +5 -0
- package/dist/optional/monaco.d.ts.map +1 -0
- package/dist/optional/monaco.js +70 -0
- package/dist/parseNestedMarkdownToNodes.d.ts +15 -0
- package/dist/parseNestedMarkdownToNodes.d.ts.map +1 -0
- package/dist/parseNestedMarkdownToNodes.js +66 -0
- package/dist/renderMarkdownHtml.d.ts +31 -0
- package/dist/renderMarkdownHtml.d.ts.map +1 -0
- package/dist/renderMarkdownHtml.js +465 -0
- package/dist/sanitizeHtmlContent.d.ts +3 -0
- package/dist/sanitizeHtmlContent.d.ts.map +1 -0
- package/dist/sanitizeHtmlContent.js +175 -0
- package/dist/sanitizeSvg.d.ts +3 -0
- package/dist/sanitizeSvg.d.ts.map +1 -0
- package/dist/sanitizeSvg.js +76 -0
- package/dist/tailwind-entry.d.ts +2 -0
- package/dist/tailwind-entry.d.ts.map +1 -0
- package/dist/tailwind-entry.js +1 -0
- package/dist/tooltip/singletonTooltip.d.ts +8 -0
- package/dist/tooltip/singletonTooltip.d.ts.map +1 -0
- package/dist/tooltip/singletonTooltip.js +124 -0
- package/dist/types/monaco.d.ts +66 -0
- package/dist/types/monaco.d.ts.map +1 -0
- package/dist/types/monaco.js +1 -0
- package/dist/utils/languageIcon.d.ts +8 -0
- package/dist/utils/languageIcon.d.ts.map +1 -0
- package/dist/utils/languageIcon.js +182 -0
- package/dist/utils/normalizeKaTeXRenderInput.d.ts +2 -0
- package/dist/utils/normalizeKaTeXRenderInput.d.ts.map +1 -0
- package/dist/utils/normalizeKaTeXRenderInput.js +7 -0
- package/dist/workers/katexCdnWorker.d.ts +21 -0
- package/dist/workers/katexCdnWorker.d.ts.map +1 -0
- package/dist/workers/katexCdnWorker.js +193 -0
- package/dist/workers/katexRenderer.worker.d.ts +2 -0
- package/dist/workers/katexRenderer.worker.d.ts.map +1 -0
- package/dist/workers/katexRenderer.worker.js +55 -0
- package/dist/workers/katexWorkerClient.d.ts +36 -0
- package/dist/workers/katexWorkerClient.d.ts.map +1 -0
- package/dist/workers/katexWorkerClient.js +270 -0
- package/dist/workers/mermaidCdnWorker.d.ts +16 -0
- package/dist/workers/mermaidCdnWorker.d.ts.map +1 -0
- package/dist/workers/mermaidCdnWorker.js +235 -0
- package/dist/workers/mermaidParser.worker.d.ts +2 -0
- package/dist/workers/mermaidParser.worker.d.ts.map +1 -0
- package/dist/workers/mermaidParser.worker.js +85 -0
- package/dist/workers/mermaidWorkerClient.d.ts +16 -0
- package/dist/workers/mermaidWorkerClient.d.ts.map +1 -0
- package/dist/workers/mermaidWorkerClient.js +170 -0
- package/package.json +95 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { getMarkdown, isHtmlTagBlocked, isUnsafeHtmlUrl, NON_STRUCTURING_HTML_TAGS, normalizeCustomHtmlTagName, normalizeCustomHtmlTags, parseMarkdownToStructure, sanitizeHtmlAttrs, } from 'stream-markdown-parser';
|
|
2
|
+
import { hydrateCustomTagContent } from './hydrateCustomTagContent';
|
|
3
|
+
import { sanitizeHtmlContent } from './sanitizeHtmlContent';
|
|
4
|
+
const DEFAULT_CACHE_KEY = 'markstream-svelte-html';
|
|
5
|
+
const DEFAULT_CUSTOM_NODE_TAG = 'div';
|
|
6
|
+
const markdownCache = new Map();
|
|
7
|
+
const KNOWN_NODE_TYPES = new Set([
|
|
8
|
+
'admonition',
|
|
9
|
+
'blockquote',
|
|
10
|
+
'checkbox',
|
|
11
|
+
'checkbox_input',
|
|
12
|
+
'code_block',
|
|
13
|
+
'definition_item',
|
|
14
|
+
'definition_list',
|
|
15
|
+
'emoji',
|
|
16
|
+
'emphasis',
|
|
17
|
+
'footnote',
|
|
18
|
+
'footnote_reference',
|
|
19
|
+
'footnote_anchor',
|
|
20
|
+
'hardbreak',
|
|
21
|
+
'heading',
|
|
22
|
+
'highlight',
|
|
23
|
+
'html_block',
|
|
24
|
+
'html_inline',
|
|
25
|
+
'image',
|
|
26
|
+
'inline_code',
|
|
27
|
+
'insert',
|
|
28
|
+
'link',
|
|
29
|
+
'list',
|
|
30
|
+
'list_item',
|
|
31
|
+
'math_block',
|
|
32
|
+
'math_inline',
|
|
33
|
+
'paragraph',
|
|
34
|
+
'reference',
|
|
35
|
+
'strikethrough',
|
|
36
|
+
'strong',
|
|
37
|
+
'subscript',
|
|
38
|
+
'superscript',
|
|
39
|
+
'table',
|
|
40
|
+
'table_cell',
|
|
41
|
+
'table_row',
|
|
42
|
+
'text',
|
|
43
|
+
'thematic_break',
|
|
44
|
+
]);
|
|
45
|
+
export function renderMarkdownToHtml(input) {
|
|
46
|
+
var _a;
|
|
47
|
+
const normalizedTags = normalizeCustomHtmlTags([
|
|
48
|
+
...(input.customHtmlTags || []),
|
|
49
|
+
...(((_a = input.parseOptions) === null || _a === void 0 ? void 0 : _a.customHtmlTags) || []),
|
|
50
|
+
]);
|
|
51
|
+
const ctx = createRenderContext({
|
|
52
|
+
allowHtml: input.allowHtml,
|
|
53
|
+
customHtmlTags: normalizedTags,
|
|
54
|
+
htmlPolicy: input.htmlPolicy,
|
|
55
|
+
});
|
|
56
|
+
const nodes = resolveParsedNodes(input, ctx);
|
|
57
|
+
return renderNodesToHtml(nodes, ctx);
|
|
58
|
+
}
|
|
59
|
+
export function renderNestedMarkdownToHtml(input, options = {}) {
|
|
60
|
+
const ctx = createRenderContext(options);
|
|
61
|
+
return renderNestedInputToHtml(input, ctx);
|
|
62
|
+
}
|
|
63
|
+
export function renderMarkdownNodesToHtml(nodes, options = {}) {
|
|
64
|
+
const ctx = createRenderContext(options);
|
|
65
|
+
return renderNodesToHtml(nodes, ctx);
|
|
66
|
+
}
|
|
67
|
+
export function renderMarkdownNodeToHtml(node, options = {}) {
|
|
68
|
+
const ctx = createRenderContext(options);
|
|
69
|
+
return renderNodeToHtml(node, ctx);
|
|
70
|
+
}
|
|
71
|
+
function resolveParsedNodes(input, ctx) {
|
|
72
|
+
var _a, _b;
|
|
73
|
+
if (Array.isArray(input.nodes))
|
|
74
|
+
return input.nodes;
|
|
75
|
+
const content = getString(input.content);
|
|
76
|
+
if (!content)
|
|
77
|
+
return [];
|
|
78
|
+
const normalizedTags = normalizeCustomHtmlTags([
|
|
79
|
+
...(input.customHtmlTags || []),
|
|
80
|
+
...(((_a = input.parseOptions) === null || _a === void 0 ? void 0 : _a.customHtmlTags) || []),
|
|
81
|
+
]);
|
|
82
|
+
const mergedParseOptions = {
|
|
83
|
+
...((_b = input.parseOptions) !== null && _b !== void 0 ? _b : {}),
|
|
84
|
+
};
|
|
85
|
+
if (typeof input.final === 'boolean')
|
|
86
|
+
mergedParseOptions.final = input.final;
|
|
87
|
+
if (normalizedTags.length > 0)
|
|
88
|
+
mergedParseOptions.customHtmlTags = normalizedTags;
|
|
89
|
+
return hydrateCustomTagContent(parseMarkdownToStructure(content, ctx.markdown, mergedParseOptions), content, normalizedTags);
|
|
90
|
+
}
|
|
91
|
+
function createRenderContext(options) {
|
|
92
|
+
var _a;
|
|
93
|
+
const normalizedTags = normalizeCustomHtmlTags(options.customHtmlTags);
|
|
94
|
+
const cacheKey = `${options.cacheKey || DEFAULT_CACHE_KEY}::${normalizedTags.join(',')}`;
|
|
95
|
+
let markdown = markdownCache.get(cacheKey);
|
|
96
|
+
if (!markdown) {
|
|
97
|
+
markdown = getMarkdown(cacheKey, {
|
|
98
|
+
customHtmlTags: normalizedTags,
|
|
99
|
+
});
|
|
100
|
+
markdownCache.set(cacheKey, markdown);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
markdown,
|
|
104
|
+
options: {
|
|
105
|
+
allowHtml: options.allowHtml !== false,
|
|
106
|
+
htmlPolicy: (_a = options.htmlPolicy) !== null && _a !== void 0 ? _a : 'safe',
|
|
107
|
+
customNodeTag: normalizeCustomHtmlTagName(options.customNodeTag) || DEFAULT_CUSTOM_NODE_TAG,
|
|
108
|
+
customNodeClass: options.customNodeClass,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function renderNestedInputToHtml(input, ctx) {
|
|
113
|
+
if (Array.isArray(input.nodes) && input.nodes.length > 0)
|
|
114
|
+
return renderNodesToHtml(input.nodes, ctx);
|
|
115
|
+
const node = input.node;
|
|
116
|
+
if (node) {
|
|
117
|
+
const children = getNodeList(node.children);
|
|
118
|
+
if (children.length > 0)
|
|
119
|
+
return renderNodesToHtml(children, ctx);
|
|
120
|
+
const content = getString(node.content);
|
|
121
|
+
if (content)
|
|
122
|
+
return renderMarkdownFragment(content, ctx);
|
|
123
|
+
if (node.raw != null)
|
|
124
|
+
return escapeHtml(getString(node.raw));
|
|
125
|
+
}
|
|
126
|
+
return input.content ? renderMarkdownFragment(getString(input.content), ctx) : '';
|
|
127
|
+
}
|
|
128
|
+
function renderNodesToHtml(nodes, ctx) {
|
|
129
|
+
return (Array.isArray(nodes) ? nodes : []).map(node => renderNodeToHtml(node, ctx)).join('');
|
|
130
|
+
}
|
|
131
|
+
function renderNodeToHtml(node, ctx) {
|
|
132
|
+
if (!node || typeof node !== 'object')
|
|
133
|
+
return '';
|
|
134
|
+
switch (node.type) {
|
|
135
|
+
case 'text':
|
|
136
|
+
return renderTextNode(node);
|
|
137
|
+
case 'paragraph':
|
|
138
|
+
return `<p>${renderNodesToHtml(getNodeList(node.children), ctx)}</p>`;
|
|
139
|
+
case 'strong':
|
|
140
|
+
return `<strong>${renderNodesToHtml(getNodeList(node.children), ctx)}</strong>`;
|
|
141
|
+
case 'emphasis':
|
|
142
|
+
return `<em>${renderNodesToHtml(getNodeList(node.children), ctx)}</em>`;
|
|
143
|
+
case 'strikethrough':
|
|
144
|
+
return `<del>${renderNodesToHtml(getNodeList(node.children), ctx)}</del>`;
|
|
145
|
+
case 'highlight':
|
|
146
|
+
return `<mark>${renderNodesToHtml(getNodeList(node.children), ctx)}</mark>`;
|
|
147
|
+
case 'insert':
|
|
148
|
+
return `<ins>${renderNodesToHtml(getNodeList(node.children), ctx)}</ins>`;
|
|
149
|
+
case 'subscript':
|
|
150
|
+
return `<sub>${renderNodesToHtml(getNodeList(node.children), ctx)}</sub>`;
|
|
151
|
+
case 'superscript':
|
|
152
|
+
return `<sup>${renderNodesToHtml(getNodeList(node.children), ctx)}</sup>`;
|
|
153
|
+
case 'inline_code':
|
|
154
|
+
return `<code>${escapeHtml(getString(node.code))}</code>`;
|
|
155
|
+
case 'hardbreak':
|
|
156
|
+
return '<br>';
|
|
157
|
+
case 'link':
|
|
158
|
+
return renderLinkNode(node, ctx);
|
|
159
|
+
case 'image':
|
|
160
|
+
return renderImageNode(node);
|
|
161
|
+
case 'list':
|
|
162
|
+
return renderListNode(node, ctx);
|
|
163
|
+
case 'list_item':
|
|
164
|
+
return `<li>${renderNodesToHtml(getNodeList(node.children), ctx)}</li>`;
|
|
165
|
+
case 'blockquote':
|
|
166
|
+
return `<blockquote>${renderNodesToHtml(getNodeList(node.children), ctx)}</blockquote>`;
|
|
167
|
+
case 'heading':
|
|
168
|
+
return renderHeadingNode(node, ctx);
|
|
169
|
+
case 'code_block':
|
|
170
|
+
return renderCodeBlockNode(node);
|
|
171
|
+
case 'thematic_break':
|
|
172
|
+
return '<hr>';
|
|
173
|
+
case 'table':
|
|
174
|
+
return renderTableNode(node, ctx);
|
|
175
|
+
case 'table_row':
|
|
176
|
+
return renderTableRowNode(node, ctx);
|
|
177
|
+
case 'table_cell':
|
|
178
|
+
return renderTableCellNode(node, ctx);
|
|
179
|
+
case 'definition_list':
|
|
180
|
+
return renderDefinitionListNode(node, ctx);
|
|
181
|
+
case 'definition_item':
|
|
182
|
+
return renderDefinitionItemNode(node, ctx);
|
|
183
|
+
case 'footnote':
|
|
184
|
+
return renderFootnoteNode(node, ctx);
|
|
185
|
+
case 'footnote_reference':
|
|
186
|
+
return renderFootnoteReferenceNode(node);
|
|
187
|
+
case 'footnote_anchor':
|
|
188
|
+
return renderFootnoteAnchorNode(node);
|
|
189
|
+
case 'admonition':
|
|
190
|
+
return renderAdmonitionNode(node, ctx);
|
|
191
|
+
case 'checkbox':
|
|
192
|
+
case 'checkbox_input':
|
|
193
|
+
return `<input type="checkbox" disabled${node.checked ? ' checked' : ''}>`;
|
|
194
|
+
case 'emoji':
|
|
195
|
+
return escapeHtml(getString(node.raw || node.markup || node.content || node.name));
|
|
196
|
+
case 'math_inline':
|
|
197
|
+
return renderMathInlineNode(node);
|
|
198
|
+
case 'math_block':
|
|
199
|
+
return renderMathBlockNode(node);
|
|
200
|
+
case 'reference':
|
|
201
|
+
return `<span class="markstream-nested-reference">${escapeHtml(getString(node.id))}</span>`;
|
|
202
|
+
case 'html_inline':
|
|
203
|
+
case 'html_block':
|
|
204
|
+
return renderHtmlNode(node, ctx);
|
|
205
|
+
default:
|
|
206
|
+
return renderCustomOrFallbackNode(node, ctx);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function renderTextNode(node) {
|
|
210
|
+
const content = getString(node.content);
|
|
211
|
+
const escaped = escapeHtml(content);
|
|
212
|
+
const centered = !!node.center;
|
|
213
|
+
if (!centered && !content.includes('\n'))
|
|
214
|
+
return escaped;
|
|
215
|
+
const className = centered
|
|
216
|
+
? 'markstream-svelte-text-node markstream-svelte-text--centered'
|
|
217
|
+
: 'markstream-svelte-text-node';
|
|
218
|
+
return `<span class="${className}">${escaped}</span>`;
|
|
219
|
+
}
|
|
220
|
+
function renderLinkNode(node, ctx) {
|
|
221
|
+
const href = getString(node.href);
|
|
222
|
+
const title = getString(node.title);
|
|
223
|
+
const content = getNodeList(node.children).length > 0
|
|
224
|
+
? renderNodesToHtml(getNodeList(node.children), ctx)
|
|
225
|
+
: escapeHtml(getString(node.text || href));
|
|
226
|
+
const titleAttr = title ? ` title="${escapeAttr(title)}"` : '';
|
|
227
|
+
const hrefAttr = href && !isUnsafeHtmlUrl(href) ? ` href="${escapeAttr(href)}"` : '';
|
|
228
|
+
const externalAttrs = href.startsWith('#') ? '' : ' target="_blank" rel="noreferrer noopener"';
|
|
229
|
+
return `<a${hrefAttr}${titleAttr}${externalAttrs}>${content}</a>`;
|
|
230
|
+
}
|
|
231
|
+
function renderMathInlineNode(node) {
|
|
232
|
+
const source = escapeHtml(getString(node.content || node.markup || node.raw));
|
|
233
|
+
return `<span class="markstream-nested-math" data-display="inline"><span class="markstream-nested-math__source">${source}</span><span class="markstream-nested-math__render" aria-hidden="true"></span></span>`;
|
|
234
|
+
}
|
|
235
|
+
function renderMathBlockNode(node) {
|
|
236
|
+
const source = escapeHtml(getString(node.content || node.markup || node.raw));
|
|
237
|
+
return `<div class="markstream-nested-math-block"><pre class="markstream-nested-math-block__source"><code>${source}</code></pre><div class="markstream-nested-math-block__render" aria-hidden="true"></div></div>`;
|
|
238
|
+
}
|
|
239
|
+
function renderImageNode(node) {
|
|
240
|
+
const src = getString(node.src);
|
|
241
|
+
const alt = getString(node.alt);
|
|
242
|
+
const title = getString(node.title);
|
|
243
|
+
const titleAttr = title ? ` title="${escapeAttr(title)}"` : '';
|
|
244
|
+
return `<img src="${escapeAttr(src)}" alt="${escapeAttr(alt)}"${titleAttr}>`;
|
|
245
|
+
}
|
|
246
|
+
function renderListNode(node, ctx) {
|
|
247
|
+
const tag = node.ordered ? 'ol' : 'ul';
|
|
248
|
+
const start = node.ordered && Number.isFinite(node.start) ? ` start="${Number(node.start)}"` : '';
|
|
249
|
+
return `<${tag}${start}>${renderNodesToHtml(getNodeList(node.items), ctx)}</${tag}>`;
|
|
250
|
+
}
|
|
251
|
+
function renderHeadingNode(node, ctx) {
|
|
252
|
+
const level = clampHeadingLevel(node.level);
|
|
253
|
+
return `<h${level}>${renderNodesToHtml(getNodeList(node.children), ctx)}</h${level}>`;
|
|
254
|
+
}
|
|
255
|
+
function renderCodeBlockNode(node) {
|
|
256
|
+
const rawLanguage = getString(node.language).trim();
|
|
257
|
+
const language = sanitizeClassToken(rawLanguage);
|
|
258
|
+
const languageClass = language ? ` class="language-${language}"` : '';
|
|
259
|
+
const diff = !!node.diff;
|
|
260
|
+
const code = getString(node.code);
|
|
261
|
+
const loading = node.loading === true;
|
|
262
|
+
if (loading && !code.trim())
|
|
263
|
+
return '';
|
|
264
|
+
const blockAttrs = [
|
|
265
|
+
'data-markstream-code-block="1"',
|
|
266
|
+
rawLanguage ? `data-markstream-language="${escapeAttr(rawLanguage)}"` : '',
|
|
267
|
+
loading ? 'data-markstream-loading="1"' : '',
|
|
268
|
+
diff ? 'data-markstream-diff="1"' : '',
|
|
269
|
+
diff ? `data-markstream-original="${escapeAttr(encodeDataPayload(getString(node.originalCode)))}"` : '',
|
|
270
|
+
diff ? `data-markstream-updated="${escapeAttr(encodeDataPayload(getString(node.updatedCode)))}"` : '',
|
|
271
|
+
loading ? 'aria-busy="true"' : '',
|
|
272
|
+
].filter(Boolean).join(' ');
|
|
273
|
+
return `<pre ${blockAttrs}><code${languageClass}>${escapeHtml(code)}</code></pre>`;
|
|
274
|
+
}
|
|
275
|
+
function renderTableNode(node, ctx) {
|
|
276
|
+
const header = node.header ? renderTableRowNode(node.header, ctx, true) : '';
|
|
277
|
+
const rows = renderNodesToHtml(getNodeList(node.rows), ctx);
|
|
278
|
+
const thead = header ? `<thead>${header}</thead>` : '';
|
|
279
|
+
const tbody = rows ? `<tbody>${rows}</tbody>` : '';
|
|
280
|
+
return `<table>${thead}${tbody}</table>`;
|
|
281
|
+
}
|
|
282
|
+
function renderTableRowNode(node, ctx, forceHeader = false) {
|
|
283
|
+
const cells = getNodeList(node.cells).map(cell => renderTableCellNode(cell, ctx, forceHeader)).join('');
|
|
284
|
+
return `<tr>${cells}</tr>`;
|
|
285
|
+
}
|
|
286
|
+
function renderTableCellNode(node, ctx, forceHeader = false) {
|
|
287
|
+
const tag = forceHeader || !!node.header ? 'th' : 'td';
|
|
288
|
+
const align = getString(node.align);
|
|
289
|
+
const alignAttr = align ? ` style="text-align:${escapeAttr(align)}"` : '';
|
|
290
|
+
return `<${tag}${alignAttr}>${renderNodesToHtml(getNodeList(node.children), ctx)}</${tag}>`;
|
|
291
|
+
}
|
|
292
|
+
function renderDefinitionListNode(node, ctx) {
|
|
293
|
+
return `<dl>${getNodeList(node.items).map(item => renderDefinitionItemNode(item, ctx)).join('')}</dl>`;
|
|
294
|
+
}
|
|
295
|
+
function renderDefinitionItemNode(node, ctx) {
|
|
296
|
+
const term = renderNodesToHtml(getNodeList(node.term), ctx);
|
|
297
|
+
const definition = renderNodesToHtml(getNodeList(node.definition), ctx);
|
|
298
|
+
return `<dt>${term}</dt><dd>${definition}</dd>`;
|
|
299
|
+
}
|
|
300
|
+
function renderFootnoteNode(node, ctx) {
|
|
301
|
+
const id = escapeAttr(getString(node.id));
|
|
302
|
+
const idAttr = id ? ` id="fnref--${id}"` : '';
|
|
303
|
+
return `<div${idAttr} class="footnote-node markstream-nested-footnote"><div class="footnote-node__content">${renderNodesToHtml(getNodeList(node.children), ctx)}</div></div>`;
|
|
304
|
+
}
|
|
305
|
+
function renderFootnoteReferenceNode(node) {
|
|
306
|
+
const rawId = getString(node.id);
|
|
307
|
+
const id = escapeHtml(rawId);
|
|
308
|
+
const attrId = escapeAttr(rawId);
|
|
309
|
+
const idAttr = attrId ? ` id="fnref-${attrId}"` : '';
|
|
310
|
+
const hrefAttr = attrId ? ` href="#fnref--${attrId}"` : '';
|
|
311
|
+
const titleAttr = attrId ? ` title="查看脚注 ${attrId}"` : '';
|
|
312
|
+
return `<sup${idAttr} class="footnote-reference markstream-nested-footnote-ref"><span${hrefAttr}${titleAttr} class="footnote-link cursor-pointer">[${id}]</span></sup>`;
|
|
313
|
+
}
|
|
314
|
+
function renderFootnoteAnchorNode(node) {
|
|
315
|
+
const id = escapeAttr(getString(node.id));
|
|
316
|
+
const hrefAttr = id ? ` href="#fnref-${id}"` : '';
|
|
317
|
+
const titleAttr = id ? ` title="返回引用 ${id}"` : '';
|
|
318
|
+
return `<a class="footnote-anchor"${hrefAttr}${titleAttr} aria-label="${id ? `返回引用 ${id}` : '返回引用'}">↩︎</a>`;
|
|
319
|
+
}
|
|
320
|
+
function renderAdmonitionNode(node, ctx) {
|
|
321
|
+
const kind = sanitizeClassToken(getString(node.kind || 'note')) || 'note';
|
|
322
|
+
const title = getString(node.title) || capitalize(kind);
|
|
323
|
+
return `<div class="markstream-nested-admonition markstream-nested-admonition--${kind}"><div class="markstream-nested-admonition__title">${escapeHtml(title)}</div><div class="markstream-nested-admonition__content">${renderNodesToHtml(getNodeList(node.children), ctx)}</div></div>`;
|
|
324
|
+
}
|
|
325
|
+
function renderHtmlNode(node, ctx) {
|
|
326
|
+
const content = getString(node.content);
|
|
327
|
+
const rawContent = content || getString(node.raw);
|
|
328
|
+
const tag = getString(node.tag).trim().toLowerCase();
|
|
329
|
+
const children = getNodeList(node.children);
|
|
330
|
+
if (!ctx.options.allowHtml)
|
|
331
|
+
return escapeHtml(rawContent);
|
|
332
|
+
if (ctx.options.htmlPolicy === 'escape')
|
|
333
|
+
return escapeHtml(rawContent);
|
|
334
|
+
if (node.loading && !node.autoClosed)
|
|
335
|
+
return escapeHtml(rawContent);
|
|
336
|
+
if (tag && children.length > 0 && !NON_STRUCTURING_HTML_TAGS.has(tag) && !isHtmlTagBlocked(tag, ctx.options.htmlPolicy)) {
|
|
337
|
+
const attrs = serializeAttrs(node.attrs, '', ctx.options.htmlPolicy, tag);
|
|
338
|
+
return `<${tag}${attrs}>${renderNodesToHtml(children, ctx)}</${tag}>`;
|
|
339
|
+
}
|
|
340
|
+
return sanitizeHtmlContent(rawContent, ctx.options.htmlPolicy);
|
|
341
|
+
}
|
|
342
|
+
function renderCustomOrFallbackNode(node, ctx) {
|
|
343
|
+
const tagName = getString(node.tag || node.type);
|
|
344
|
+
if (!tagName || KNOWN_NODE_TYPES.has(tagName))
|
|
345
|
+
return escapeHtml(getString(node.content || node.raw));
|
|
346
|
+
const classes = [
|
|
347
|
+
'markstream-nested-custom',
|
|
348
|
+
`markstream-nested-custom--${sanitizeClassToken(tagName) || 'node'}`,
|
|
349
|
+
resolveCustomNodeClass(node, ctx.options.customNodeClass),
|
|
350
|
+
].filter(Boolean).join(' ');
|
|
351
|
+
const attrs = serializeAttrs(node.attrs, classes, ctx.options.htmlPolicy);
|
|
352
|
+
const body = resolveCustomNodeBody(node, ctx);
|
|
353
|
+
const wrapperTag = ctx.options.customNodeTag;
|
|
354
|
+
return `<${wrapperTag}${attrs} data-markstream-custom-tag="${escapeAttr(tagName)}">${body}</${wrapperTag}>`;
|
|
355
|
+
}
|
|
356
|
+
function resolveCustomNodeBody(node, ctx) {
|
|
357
|
+
const children = getNodeList(node.children);
|
|
358
|
+
if (children.length > 0)
|
|
359
|
+
return renderNodesToHtml(children, ctx);
|
|
360
|
+
const content = getString(node.content);
|
|
361
|
+
if (content)
|
|
362
|
+
return renderMarkdownFragment(content, ctx);
|
|
363
|
+
return escapeHtml(getString(node.raw));
|
|
364
|
+
}
|
|
365
|
+
function renderMarkdownFragment(content, ctx) {
|
|
366
|
+
return content ? ctx.markdown.render(content) : '';
|
|
367
|
+
}
|
|
368
|
+
function resolveCustomNodeClass(node, customNodeClass) {
|
|
369
|
+
if (!customNodeClass)
|
|
370
|
+
return '';
|
|
371
|
+
const resolved = typeof customNodeClass === 'function'
|
|
372
|
+
? customNodeClass(node)
|
|
373
|
+
: customNodeClass;
|
|
374
|
+
return serializeClassValue(resolved);
|
|
375
|
+
}
|
|
376
|
+
function serializeClassValue(value) {
|
|
377
|
+
if (Array.isArray(value))
|
|
378
|
+
return value.map(item => serializeClassValue(item)).filter(Boolean).join(' ');
|
|
379
|
+
return typeof value === 'string'
|
|
380
|
+
? value.trim()
|
|
381
|
+
: '';
|
|
382
|
+
}
|
|
383
|
+
function serializeAttrs(attrs, extraClass = '', policy = 'safe', tagName) {
|
|
384
|
+
const pairs = normalizeAttrs(attrs);
|
|
385
|
+
const record = {};
|
|
386
|
+
const mergedClasses = [extraClass];
|
|
387
|
+
for (const [name, value] of pairs) {
|
|
388
|
+
const safeName = String(name).trim();
|
|
389
|
+
if (!safeName)
|
|
390
|
+
continue;
|
|
391
|
+
if (safeName.toLowerCase() === 'class') {
|
|
392
|
+
mergedClasses.push(String(value));
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
record[safeName] = value === true ? '' : String(value);
|
|
396
|
+
}
|
|
397
|
+
const rendered = Object.entries(sanitizeHtmlAttrs(record, policy, tagName))
|
|
398
|
+
.map(([name, value]) => value === '' ? ` ${name}` : ` ${name}="${escapeAttr(String(value))}"`);
|
|
399
|
+
const className = mergedClasses.map(value => value.trim()).filter(Boolean).join(' ');
|
|
400
|
+
if (className)
|
|
401
|
+
rendered.unshift(` class="${escapeAttr(className)}"`);
|
|
402
|
+
return rendered.join('');
|
|
403
|
+
}
|
|
404
|
+
function normalizeAttrs(attrs) {
|
|
405
|
+
if (!attrs)
|
|
406
|
+
return [];
|
|
407
|
+
if (Array.isArray(attrs)) {
|
|
408
|
+
if (attrs.length === 0)
|
|
409
|
+
return [];
|
|
410
|
+
const first = attrs[0];
|
|
411
|
+
if (Array.isArray(first))
|
|
412
|
+
return attrs.map(([name, value]) => [String(name), value]);
|
|
413
|
+
return attrs.map(item => [String(item.name), item.value]);
|
|
414
|
+
}
|
|
415
|
+
return Object.entries(attrs).map(([name, value]) => [name, value]);
|
|
416
|
+
}
|
|
417
|
+
function getNodeList(value) {
|
|
418
|
+
return Array.isArray(value)
|
|
419
|
+
? value.filter((item) => !!item && typeof item === 'object')
|
|
420
|
+
: [];
|
|
421
|
+
}
|
|
422
|
+
function getString(value) {
|
|
423
|
+
return typeof value === 'string'
|
|
424
|
+
? value
|
|
425
|
+
: value == null
|
|
426
|
+
? ''
|
|
427
|
+
: String(value);
|
|
428
|
+
}
|
|
429
|
+
function sanitizeClassToken(value) {
|
|
430
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
431
|
+
}
|
|
432
|
+
function clampHeadingLevel(value) {
|
|
433
|
+
const level = Math.trunc(Number(value) || 1);
|
|
434
|
+
return Math.min(6, Math.max(1, level));
|
|
435
|
+
}
|
|
436
|
+
function escapeHtml(value) {
|
|
437
|
+
return getString(value)
|
|
438
|
+
.replace(/&/g, '&')
|
|
439
|
+
.replace(/</g, '<')
|
|
440
|
+
.replace(/>/g, '>')
|
|
441
|
+
.replace(/"/g, '"')
|
|
442
|
+
.replace(/'/g, ''');
|
|
443
|
+
}
|
|
444
|
+
function escapeAttr(value) {
|
|
445
|
+
return escapeHtml(value).replace(/`/g, '`');
|
|
446
|
+
}
|
|
447
|
+
function capitalize(value) {
|
|
448
|
+
return value ? `${value[0].toUpperCase()}${value.slice(1)}` : '';
|
|
449
|
+
}
|
|
450
|
+
function encodeDataPayload(value) {
|
|
451
|
+
var _a, _b;
|
|
452
|
+
if (!value)
|
|
453
|
+
return '';
|
|
454
|
+
const globalBuffer = (_b = (_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.require) === null || _a === void 0 ? void 0 : _a.call(globalThis, 'buffer')) === null || _b === void 0 ? void 0 : _b.Buffer;
|
|
455
|
+
if (globalBuffer === null || globalBuffer === void 0 ? void 0 : globalBuffer.from)
|
|
456
|
+
return globalBuffer.from(value, 'utf8').toString('base64');
|
|
457
|
+
if (typeof TextEncoder !== 'undefined' && typeof globalThis.btoa === 'function') {
|
|
458
|
+
const bytes = new TextEncoder().encode(value);
|
|
459
|
+
let binary = '';
|
|
460
|
+
for (const byte of bytes)
|
|
461
|
+
binary += String.fromCharCode(byte);
|
|
462
|
+
return globalThis.btoa(binary);
|
|
463
|
+
}
|
|
464
|
+
return '';
|
|
465
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizeHtmlContent.d.ts","sourceRoot":"","sources":["../src/sanitizeHtmlContent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAsJxD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,UAAmB,GAAG,MAAM,CAyExF"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { BLOCKED_HTML_TAGS as BLOCKED_TAGS, isHtmlTagBlocked, isHtmlTagHardBlocked, sanitizeHtmlAttrs, VOID_HTML_TAGS as VOID_ELEMENTS, } from 'stream-markdown-parser';
|
|
2
|
+
import { escapeAttr, escapeHtml } from './components/shared/node-helpers';
|
|
3
|
+
function sanitizeAttrs(attrs, policy, tagName) {
|
|
4
|
+
return sanitizeHtmlAttrs(attrs, policy, tagName);
|
|
5
|
+
}
|
|
6
|
+
function tokenizeHtml(html) {
|
|
7
|
+
var _a, _b, _c;
|
|
8
|
+
const tokens = [];
|
|
9
|
+
let pos = 0;
|
|
10
|
+
while (pos < html.length) {
|
|
11
|
+
if (html.startsWith('<!--', pos)) {
|
|
12
|
+
const commentEnd = html.indexOf('-->', pos);
|
|
13
|
+
if (commentEnd !== -1) {
|
|
14
|
+
pos = commentEnd + 3;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
const tagStart = html.indexOf('<', pos);
|
|
20
|
+
if (tagStart === -1) {
|
|
21
|
+
if (pos < html.length)
|
|
22
|
+
tokens.push({ type: 'text', content: html.slice(pos) });
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
if (tagStart > pos)
|
|
26
|
+
tokens.push({ type: 'text', content: html.slice(pos, tagStart) });
|
|
27
|
+
if (html.startsWith('![CDATA[', tagStart + 1)) {
|
|
28
|
+
const cdataEnd = html.indexOf(']]>', tagStart);
|
|
29
|
+
if (cdataEnd !== -1) {
|
|
30
|
+
tokens.push({ type: 'text', content: html.slice(tagStart, cdataEnd + 3) });
|
|
31
|
+
pos = cdataEnd + 3;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
if (html.startsWith('!', tagStart + 1)) {
|
|
37
|
+
const specialEnd = html.indexOf('>', tagStart);
|
|
38
|
+
if (specialEnd !== -1) {
|
|
39
|
+
pos = specialEnd + 1;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
const tagEnd = html.indexOf('>', tagStart);
|
|
45
|
+
if (tagEnd === -1)
|
|
46
|
+
break;
|
|
47
|
+
const tagContent = html.slice(tagStart + 1, tagEnd).trim();
|
|
48
|
+
if (!tagContent) {
|
|
49
|
+
pos = tagEnd + 1;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const isClosingTag = tagContent.startsWith('/');
|
|
53
|
+
const isSelfClosing = tagContent.endsWith('/');
|
|
54
|
+
if (isClosingTag) {
|
|
55
|
+
const tagName = tagContent.slice(1).trim();
|
|
56
|
+
tokens.push({ type: 'tag_close', tagName });
|
|
57
|
+
pos = tagEnd + 1;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const spaceIndex = tagContent.indexOf(' ');
|
|
61
|
+
let tagName = '';
|
|
62
|
+
let attrsStr = '';
|
|
63
|
+
if (spaceIndex === -1) {
|
|
64
|
+
tagName = isSelfClosing ? tagContent.slice(0, -1).trim() : tagContent.trim();
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
tagName = tagContent.slice(0, spaceIndex).trim();
|
|
68
|
+
attrsStr = tagContent.slice(spaceIndex + 1);
|
|
69
|
+
}
|
|
70
|
+
const attrs = {};
|
|
71
|
+
if (attrsStr) {
|
|
72
|
+
const attrRegex = /([^\s=]+)(?:=(?:"([^"]*)"|'([^']*)'|(\S*)))?/g;
|
|
73
|
+
let attrMatch;
|
|
74
|
+
while ((attrMatch = attrRegex.exec(attrsStr)) !== null) {
|
|
75
|
+
const name = attrMatch[1];
|
|
76
|
+
const value = (_c = (_b = (_a = attrMatch[2]) !== null && _a !== void 0 ? _a : attrMatch[3]) !== null && _b !== void 0 ? _b : attrMatch[4]) !== null && _c !== void 0 ? _c : '';
|
|
77
|
+
if (name && !name.endsWith('/'))
|
|
78
|
+
attrs[name] = value;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
tokens.push({
|
|
82
|
+
type: isSelfClosing || VOID_ELEMENTS.has(tagName.toLowerCase()) ? 'self_closing' : 'tag_open',
|
|
83
|
+
tagName,
|
|
84
|
+
attrs,
|
|
85
|
+
});
|
|
86
|
+
pos = tagEnd + 1;
|
|
87
|
+
}
|
|
88
|
+
return tokens;
|
|
89
|
+
}
|
|
90
|
+
function normalizeTagName(tagName) {
|
|
91
|
+
return String(tagName !== null && tagName !== void 0 ? tagName : '').trim().toLowerCase();
|
|
92
|
+
}
|
|
93
|
+
function serializeAttrs(attrs) {
|
|
94
|
+
const pairs = Object.entries(attrs);
|
|
95
|
+
if (pairs.length === 0)
|
|
96
|
+
return '';
|
|
97
|
+
return pairs
|
|
98
|
+
.map(([name, value]) => value === '' ? ` ${name}` : ` ${name}="${escapeAttr(value)}"`)
|
|
99
|
+
.join('');
|
|
100
|
+
}
|
|
101
|
+
function serializeLiteralHtmlTag(token) {
|
|
102
|
+
var _a, _b;
|
|
103
|
+
const tagName = String((_a = token.tagName) !== null && _a !== void 0 ? _a : '').trim();
|
|
104
|
+
if (!tagName)
|
|
105
|
+
return '';
|
|
106
|
+
if (token.type === 'tag_close')
|
|
107
|
+
return `</${escapeHtml(tagName)}>`;
|
|
108
|
+
const attrs = Object.entries((_b = token.attrs) !== null && _b !== void 0 ? _b : {})
|
|
109
|
+
.map(([name, value]) => value === '' ? ` ${escapeHtml(name)}` : ` ${escapeHtml(name)}="${escapeAttr(value)}"`)
|
|
110
|
+
.join('');
|
|
111
|
+
return token.type === 'self_closing'
|
|
112
|
+
? `<${escapeHtml(tagName)}${attrs} />`
|
|
113
|
+
: `<${escapeHtml(tagName)}${attrs}>`;
|
|
114
|
+
}
|
|
115
|
+
export function sanitizeHtmlContent(content, policy = 'safe') {
|
|
116
|
+
var _a, _b, _c;
|
|
117
|
+
if (!content)
|
|
118
|
+
return '';
|
|
119
|
+
if (policy === 'escape')
|
|
120
|
+
return escapeHtml(content);
|
|
121
|
+
const tokens = tokenizeHtml(content);
|
|
122
|
+
const stack = [];
|
|
123
|
+
const output = [];
|
|
124
|
+
let blockedDepth = 0;
|
|
125
|
+
for (const token of tokens) {
|
|
126
|
+
if (token.type === 'text') {
|
|
127
|
+
if (blockedDepth === 0)
|
|
128
|
+
output.push(escapeHtml((_a = token.content) !== null && _a !== void 0 ? _a : ''));
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const tagName = normalizeTagName(token.tagName);
|
|
132
|
+
if (!tagName)
|
|
133
|
+
continue;
|
|
134
|
+
if (BLOCKED_TAGS.has(tagName) || isHtmlTagHardBlocked(tagName, policy)) {
|
|
135
|
+
if (token.type === 'tag_open')
|
|
136
|
+
blockedDepth += 1;
|
|
137
|
+
else if (token.type === 'tag_close' && blockedDepth > 0)
|
|
138
|
+
blockedDepth -= 1;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (blockedDepth > 0)
|
|
142
|
+
continue;
|
|
143
|
+
if (policy === 'safe' && isHtmlTagBlocked(tagName, policy)) {
|
|
144
|
+
output.push(serializeLiteralHtmlTag(token));
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (token.type === 'self_closing') {
|
|
148
|
+
output.push(`<${tagName}${serializeAttrs(sanitizeAttrs((_b = token.attrs) !== null && _b !== void 0 ? _b : {}, policy, tagName))}>`);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (token.type === 'tag_open') {
|
|
152
|
+
output.push(`<${tagName}${serializeAttrs(sanitizeAttrs((_c = token.attrs) !== null && _c !== void 0 ? _c : {}, policy, tagName))}>`);
|
|
153
|
+
if (!VOID_ELEMENTS.has(tagName))
|
|
154
|
+
stack.push(tagName);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const matchedIndex = stack.lastIndexOf(tagName);
|
|
158
|
+
if (matchedIndex === -1)
|
|
159
|
+
continue;
|
|
160
|
+
while (stack.length > matchedIndex + 1) {
|
|
161
|
+
const danglingTag = stack.pop();
|
|
162
|
+
if (danglingTag)
|
|
163
|
+
output.push(`</${danglingTag}>`);
|
|
164
|
+
}
|
|
165
|
+
const closingTag = stack.pop();
|
|
166
|
+
if (closingTag)
|
|
167
|
+
output.push(`</${closingTag}>`);
|
|
168
|
+
}
|
|
169
|
+
while (stack.length > 0) {
|
|
170
|
+
const danglingTag = stack.pop();
|
|
171
|
+
if (danglingTag)
|
|
172
|
+
output.push(`</${danglingTag}>`);
|
|
173
|
+
}
|
|
174
|
+
return output.join('');
|
|
175
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizeSvg.d.ts","sourceRoot":"","sources":["../src/sanitizeSvg.ts"],"names":[],"mappings":"AA0DA,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,UAe7D;AAED,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,GAAG,OAUnD"}
|