@incremark/core 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- export { r as createInitialContext, o as detectContainer, p as detectContainerEnd, g as detectFenceEnd, f as detectFenceStart, q as isBlockBoundary, l as isBlockquoteStart, i as isEmptyLine, h as isHeading, m as isHtmlBlock, k as isListItemStart, n as isTableDelimiter, j as isThematicBreak, u as updateContext } from '../index-ChNeZ1wr.js';
1
+ export { r as createInitialContext, o as detectContainer, p as detectContainerEnd, g as detectFenceEnd, f as detectFenceStart, q as isBlockBoundary, l as isBlockquoteStart, i as isEmptyLine, T as isFootnoteContinuation, S as isFootnoteDefinitionStart, h as isHeading, m as isHtmlBlock, k as isListItemStart, n as isTableDelimiter, j as isThematicBreak, u as updateContext } from '../index-3rgnFbip.js';
2
2
  import 'mdast';
3
3
  import 'micromark-util-types';
4
4
  import 'mdast-util-from-markdown';
@@ -10,6 +10,8 @@ var RE_HTML_BLOCK_1 = /^\s{0,3}<(script|pre|style|textarea|!--|!DOCTYPE|\?|!\[CD
10
10
  var RE_HTML_BLOCK_2 = /^\s{0,3}<\/?[a-zA-Z][a-zA-Z0-9-]*(\s|>|$)/;
11
11
  var RE_TABLE_DELIMITER = /^\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?$/;
12
12
  var RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
13
+ var RE_FOOTNOTE_DEFINITION = /^\[\^[^\]]+\]:\s/;
14
+ var RE_FOOTNOTE_CONTINUATION = /^(?: |\t)/;
13
15
  var fenceEndPatternCache = /* @__PURE__ */ new Map();
14
16
  var containerPatternCache = /* @__PURE__ */ new Map();
15
17
  function detectFenceStart(line) {
@@ -62,6 +64,12 @@ function isHtmlBlock(line) {
62
64
  function isTableDelimiter(line) {
63
65
  return RE_TABLE_DELIMITER.test(line.trim());
64
66
  }
67
+ function isFootnoteDefinitionStart(line) {
68
+ return RE_FOOTNOTE_DEFINITION.test(line);
69
+ }
70
+ function isFootnoteContinuation(line) {
71
+ return RE_FOOTNOTE_CONTINUATION.test(line);
72
+ }
65
73
  function detectContainer(line, config) {
66
74
  const marker = config?.marker || ":";
67
75
  const minLength = config?.minMarkerLength || 3;
@@ -173,6 +181,6 @@ function updateContext(line, context, containerConfig) {
173
181
  return newContext;
174
182
  }
175
183
 
176
- export { createInitialContext, detectContainer, detectContainerEnd, detectFenceEnd, detectFenceStart, isBlockBoundary, isBlockquoteStart, isEmptyLine, isHeading, isHtmlBlock, isListItemStart, isTableDelimiter, isThematicBreak, updateContext };
184
+ export { createInitialContext, detectContainer, detectContainerEnd, detectFenceEnd, detectFenceStart, isBlockBoundary, isBlockquoteStart, isEmptyLine, isFootnoteContinuation, isFootnoteDefinitionStart, isHeading, isHtmlBlock, isListItemStart, isTableDelimiter, isThematicBreak, updateContext };
177
185
  //# sourceMappingURL=index.js.map
178
186
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/detector/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 块类型检测与边界判断\n *\n * Markdown 块级元素的识别规则\n */\n\nimport type { BlockContext, ContainerConfig, ContainerMatch } from '../types'\n\n// ============ 预编译正则表达式(性能优化) ============\n\nconst RE_FENCE_START = /^(\\s*)((`{3,})|(~{3,}))/\nconst RE_EMPTY_LINE = /^\\s*$/\nconst RE_HEADING = /^#{1,6}\\s/\nconst RE_THEMATIC_BREAK = /^(\\*{3,}|-{3,}|_{3,})\\s*$/\nconst RE_UNORDERED_LIST = /^(\\s*)([-*+])\\s/\nconst RE_ORDERED_LIST = /^(\\s*)(\\d{1,9})[.)]\\s/\nconst RE_BLOCKQUOTE = /^\\s{0,3}>/\nconst RE_HTML_BLOCK_1 = /^\\s{0,3}<(script|pre|style|textarea|!--|!DOCTYPE|\\?|!\\[CDATA\\[)/i\nconst RE_HTML_BLOCK_2 = /^\\s{0,3}<\\/?[a-zA-Z][a-zA-Z0-9-]*(\\s|>|$)/\nconst RE_TABLE_DELIMITER = /^\\|?\\s*:?-{3,}:?\\s*(\\|\\s*:?-{3,}:?\\s*)*\\|?$/\nconst RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\\]\\\\]/g\n\n/** fence 结束模式缓存 */\nconst fenceEndPatternCache = new Map<string, RegExp>()\n\n/** 容器模式缓存 */\nconst containerPatternCache = new Map<string, RegExp>()\n\n// ============ 代码块检测 ============\n\n/**\n * 检测行是否是代码块 fence 开始\n */\nexport function detectFenceStart(line: string): { char: string; length: number } | null {\n const match = line.match(RE_FENCE_START)\n if (match) {\n const fence = match[2]\n const char = fence[0]\n return { char, length: fence.length }\n }\n return null\n}\n\n/**\n * 检测行是否是代码块 fence 结束\n */\nexport function detectFenceEnd(line: string, context: BlockContext): boolean {\n if (!context.inFencedCode || !context.fenceChar || !context.fenceLength) {\n return false\n }\n\n // 使用缓存的正则表达式\n const cacheKey = `${context.fenceChar}-${context.fenceLength}`\n let pattern = fenceEndPatternCache.get(cacheKey)\n if (!pattern) {\n pattern = new RegExp(`^\\\\s{0,3}${context.fenceChar}{${context.fenceLength},}\\\\s*$`)\n fenceEndPatternCache.set(cacheKey, pattern)\n }\n return pattern.test(line)\n}\n\n// ============ 行类型检测 ============\n\n/**\n * 检测是否是空行或仅包含空白字符\n */\nexport function isEmptyLine(line: string): boolean {\n return RE_EMPTY_LINE.test(line)\n}\n\n/**\n * 检测是否是标题行\n */\nexport function isHeading(line: string): boolean {\n return RE_HEADING.test(line)\n}\n\n/**\n * 检测是否是 thematic break(水平线)\n */\nexport function isThematicBreak(line: string): boolean {\n return RE_THEMATIC_BREAK.test(line.trim())\n}\n\n/**\n * 检测是否是列表项开始\n */\nexport function isListItemStart(line: string): { ordered: boolean; indent: number } | null {\n // 无序列表: - * +\n const unordered = line.match(RE_UNORDERED_LIST)\n if (unordered) {\n return { ordered: false, indent: unordered[1].length }\n }\n\n // 有序列表: 1. 2) 等\n const ordered = line.match(RE_ORDERED_LIST)\n if (ordered) {\n return { ordered: true, indent: ordered[1].length }\n }\n\n return null\n}\n\n/**\n * 检测是否是引用块开始\n */\nexport function isBlockquoteStart(line: string): boolean {\n return RE_BLOCKQUOTE.test(line)\n}\n\n/**\n * 检测是否是 HTML 块\n */\nexport function isHtmlBlock(line: string): boolean {\n return RE_HTML_BLOCK_1.test(line) || RE_HTML_BLOCK_2.test(line)\n}\n\n/**\n * 检测表格分隔行\n */\nexport function isTableDelimiter(line: string): boolean {\n return RE_TABLE_DELIMITER.test(line.trim())\n}\n\n// ============ 容器检测 ============\n\n/**\n * 检测容器开始或结束\n *\n * 支持格式:\n * - ::: name 开始\n * - ::: name attr 开始(带属性)\n * - ::: 结束\n * - :::::: name 开始(更长的标记,用于嵌套)\n */\nexport function detectContainer(line: string, config?: ContainerConfig): ContainerMatch | null {\n const marker = config?.marker || ':'\n const minLength = config?.minMarkerLength || 3\n\n // 使用缓存的正则表达式\n const cacheKey = `${marker}-${minLength}`\n let pattern = containerPatternCache.get(cacheKey)\n if (!pattern) {\n const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, '\\\\$&')\n pattern = new RegExp(\n `^(\\\\s*)(${escapedMarker}{${minLength},})(?:\\\\s+(\\\\w[\\\\w-]*))?(?:\\\\s+(.*))?\\\\s*$`\n )\n containerPatternCache.set(cacheKey, pattern)\n }\n\n const match = line.match(pattern)\n if (!match) {\n return null\n }\n\n const markerLength = match[2].length\n const name = match[3] || ''\n const isEnd = !name && !match[4]\n\n if (!isEnd && config?.allowedNames && config.allowedNames.length > 0) {\n if (!config.allowedNames.includes(name)) {\n return null\n }\n }\n\n return { name, markerLength, isEnd }\n}\n\n/**\n * 检测容器结束\n */\nexport function detectContainerEnd(\n line: string,\n context: BlockContext,\n config?: ContainerConfig\n): boolean {\n if (!context.inContainer || !context.containerMarkerLength) {\n return false\n }\n\n const result = detectContainer(line, config)\n if (!result) {\n return false\n }\n\n return result.isEnd && result.markerLength >= context.containerMarkerLength\n}\n\n// ============ 边界检测 ============\n\n/**\n * 判断两行之间是否构成块边界\n */\nexport function isBlockBoundary(\n prevLine: string,\n currentLine: string,\n context: BlockContext\n): boolean {\n if (context.inFencedCode) {\n return detectFenceEnd(currentLine, context)\n }\n\n if (isEmptyLine(prevLine) && !isEmptyLine(currentLine)) {\n return true\n }\n\n if (isHeading(currentLine) && !isEmptyLine(prevLine)) {\n return true\n }\n\n if (isThematicBreak(currentLine)) {\n return true\n }\n\n if (detectFenceStart(currentLine)) {\n return true\n }\n\n return false\n}\n\n// ============ 上下文管理 ============\n\n/**\n * 创建初始上下文\n */\nexport function createInitialContext(): BlockContext {\n return {\n inFencedCode: false,\n listDepth: 0,\n blockquoteDepth: 0,\n inContainer: false,\n containerDepth: 0\n }\n}\n\n/**\n * 更新上下文(处理一行后)\n */\nexport function updateContext(\n line: string,\n context: BlockContext,\n containerConfig?: ContainerConfig | boolean\n): BlockContext {\n const newContext = { ...context }\n\n const containerCfg =\n containerConfig === true ? {} : containerConfig === false ? undefined : containerConfig\n\n // 代码块优先级最高\n if (context.inFencedCode) {\n if (detectFenceEnd(line, context)) {\n newContext.inFencedCode = false\n newContext.fenceChar = undefined\n newContext.fenceLength = undefined\n }\n return newContext\n }\n\n const fence = detectFenceStart(line)\n if (fence) {\n newContext.inFencedCode = true\n newContext.fenceChar = fence.char\n newContext.fenceLength = fence.length\n return newContext\n }\n\n // 容器处理\n if (containerCfg !== undefined) {\n if (context.inContainer) {\n if (detectContainerEnd(line, context, containerCfg)) {\n newContext.containerDepth = context.containerDepth - 1\n if (newContext.containerDepth === 0) {\n newContext.inContainer = false\n newContext.containerMarkerLength = undefined\n newContext.containerName = undefined\n }\n return newContext\n }\n\n const nested = detectContainer(line, containerCfg)\n if (nested && !nested.isEnd) {\n newContext.containerDepth = context.containerDepth + 1\n return newContext\n }\n } else {\n const container = detectContainer(line, containerCfg)\n if (container && !container.isEnd) {\n newContext.inContainer = true\n newContext.containerMarkerLength = container.markerLength\n newContext.containerName = container.name\n newContext.containerDepth = 1\n return newContext\n }\n }\n }\n\n return newContext\n}\n\n"]}
1
+ {"version":3,"sources":["../../src/detector/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 块类型检测与边界判断\n *\n * Markdown 块级元素的识别规则\n */\n\nimport type { BlockContext, ContainerConfig, ContainerMatch } from '../types'\n\n// ============ 预编译正则表达式(性能优化) ============\n\nconst RE_FENCE_START = /^(\\s*)((`{3,})|(~{3,}))/\nconst RE_EMPTY_LINE = /^\\s*$/\nconst RE_HEADING = /^#{1,6}\\s/\nconst RE_THEMATIC_BREAK = /^(\\*{3,}|-{3,}|_{3,})\\s*$/\nconst RE_UNORDERED_LIST = /^(\\s*)([-*+])\\s/\nconst RE_ORDERED_LIST = /^(\\s*)(\\d{1,9})[.)]\\s/\nconst RE_BLOCKQUOTE = /^\\s{0,3}>/\nconst RE_HTML_BLOCK_1 = /^\\s{0,3}<(script|pre|style|textarea|!--|!DOCTYPE|\\?|!\\[CDATA\\[)/i\nconst RE_HTML_BLOCK_2 = /^\\s{0,3}<\\/?[a-zA-Z][a-zA-Z0-9-]*(\\s|>|$)/\nconst RE_TABLE_DELIMITER = /^\\|?\\s*:?-{3,}:?\\s*(\\|\\s*:?-{3,}:?\\s*)*\\|?$/\nconst RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\\]\\\\]/g\nconst RE_FOOTNOTE_DEFINITION = /^\\[\\^[^\\]]+\\]:\\s/\nconst RE_FOOTNOTE_CONTINUATION = /^(?: |\\t)/\n\n/** fence 结束模式缓存 */\nconst fenceEndPatternCache = new Map<string, RegExp>()\n\n/** 容器模式缓存 */\nconst containerPatternCache = new Map<string, RegExp>()\n\n// ============ 代码块检测 ============\n\n/**\n * 检测行是否是代码块 fence 开始\n */\nexport function detectFenceStart(line: string): { char: string; length: number } | null {\n const match = line.match(RE_FENCE_START)\n if (match) {\n const fence = match[2]\n const char = fence[0]\n return { char, length: fence.length }\n }\n return null\n}\n\n/**\n * 检测行是否是代码块 fence 结束\n */\nexport function detectFenceEnd(line: string, context: BlockContext): boolean {\n if (!context.inFencedCode || !context.fenceChar || !context.fenceLength) {\n return false\n }\n\n // 使用缓存的正则表达式\n const cacheKey = `${context.fenceChar}-${context.fenceLength}`\n let pattern = fenceEndPatternCache.get(cacheKey)\n if (!pattern) {\n pattern = new RegExp(`^\\\\s{0,3}${context.fenceChar}{${context.fenceLength},}\\\\s*$`)\n fenceEndPatternCache.set(cacheKey, pattern)\n }\n return pattern.test(line)\n}\n\n// ============ 行类型检测 ============\n\n/**\n * 检测是否是空行或仅包含空白字符\n */\nexport function isEmptyLine(line: string): boolean {\n return RE_EMPTY_LINE.test(line)\n}\n\n/**\n * 检测是否是标题行\n */\nexport function isHeading(line: string): boolean {\n return RE_HEADING.test(line)\n}\n\n/**\n * 检测是否是 thematic break(水平线)\n */\nexport function isThematicBreak(line: string): boolean {\n return RE_THEMATIC_BREAK.test(line.trim())\n}\n\n/**\n * 检测是否是列表项开始\n */\nexport function isListItemStart(line: string): { ordered: boolean; indent: number } | null {\n // 无序列表: - * +\n const unordered = line.match(RE_UNORDERED_LIST)\n if (unordered) {\n return { ordered: false, indent: unordered[1].length }\n }\n\n // 有序列表: 1. 2) 等\n const ordered = line.match(RE_ORDERED_LIST)\n if (ordered) {\n return { ordered: true, indent: ordered[1].length }\n }\n\n return null\n}\n\n/**\n * 检测是否是引用块开始\n */\nexport function isBlockquoteStart(line: string): boolean {\n return RE_BLOCKQUOTE.test(line)\n}\n\n/**\n * 检测是否是 HTML 块\n */\nexport function isHtmlBlock(line: string): boolean {\n return RE_HTML_BLOCK_1.test(line) || RE_HTML_BLOCK_2.test(line)\n}\n\n/**\n * 检测表格分隔行\n */\nexport function isTableDelimiter(line: string): boolean {\n return RE_TABLE_DELIMITER.test(line.trim())\n}\n\n// ============ 脚注检测 ============\n\n/**\n * 检测是否是脚注定义的起始行\n * 格式: [^id]: content\n * \n * @example\n * isFootnoteDefinitionStart('[^1]: 脚注内容') // true\n * isFootnoteDefinitionStart('[^note]: 内容') // true\n * isFootnoteDefinitionStart(' 缩进内容') // false\n */\nexport function isFootnoteDefinitionStart(line: string): boolean {\n return RE_FOOTNOTE_DEFINITION.test(line)\n}\n\n/**\n * 检测是否是脚注定义的延续行(缩进行)\n * 至少4个空格或1个tab\n * \n * @example\n * isFootnoteContinuation(' 第二行') // true\n * isFootnoteContinuation('\\t第二行') // true\n * isFootnoteContinuation(' 两个空格') // false\n */\nexport function isFootnoteContinuation(line: string): boolean {\n return RE_FOOTNOTE_CONTINUATION.test(line)\n}\n\n// ============ 容器检测 ============\n\n/**\n * 检测容器开始或结束\n *\n * 支持格式:\n * - ::: name 开始\n * - ::: name attr 开始(带属性)\n * - ::: 结束\n * - :::::: name 开始(更长的标记,用于嵌套)\n */\nexport function detectContainer(line: string, config?: ContainerConfig): ContainerMatch | null {\n const marker = config?.marker || ':'\n const minLength = config?.minMarkerLength || 3\n\n // 使用缓存的正则表达式\n const cacheKey = `${marker}-${minLength}`\n let pattern = containerPatternCache.get(cacheKey)\n if (!pattern) {\n const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, '\\\\$&')\n pattern = new RegExp(\n `^(\\\\s*)(${escapedMarker}{${minLength},})(?:\\\\s+(\\\\w[\\\\w-]*))?(?:\\\\s+(.*))?\\\\s*$`\n )\n containerPatternCache.set(cacheKey, pattern)\n }\n\n const match = line.match(pattern)\n if (!match) {\n return null\n }\n\n const markerLength = match[2].length\n const name = match[3] || ''\n const isEnd = !name && !match[4]\n\n if (!isEnd && config?.allowedNames && config.allowedNames.length > 0) {\n if (!config.allowedNames.includes(name)) {\n return null\n }\n }\n\n return { name, markerLength, isEnd }\n}\n\n/**\n * 检测容器结束\n */\nexport function detectContainerEnd(\n line: string,\n context: BlockContext,\n config?: ContainerConfig\n): boolean {\n if (!context.inContainer || !context.containerMarkerLength) {\n return false\n }\n\n const result = detectContainer(line, config)\n if (!result) {\n return false\n }\n\n return result.isEnd && result.markerLength >= context.containerMarkerLength\n}\n\n// ============ 边界检测 ============\n\n/**\n * 判断两行之间是否构成块边界\n */\nexport function isBlockBoundary(\n prevLine: string,\n currentLine: string,\n context: BlockContext\n): boolean {\n if (context.inFencedCode) {\n return detectFenceEnd(currentLine, context)\n }\n\n if (isEmptyLine(prevLine) && !isEmptyLine(currentLine)) {\n return true\n }\n\n if (isHeading(currentLine) && !isEmptyLine(prevLine)) {\n return true\n }\n\n if (isThematicBreak(currentLine)) {\n return true\n }\n\n if (detectFenceStart(currentLine)) {\n return true\n }\n\n return false\n}\n\n// ============ 上下文管理 ============\n\n/**\n * 创建初始上下文\n */\nexport function createInitialContext(): BlockContext {\n return {\n inFencedCode: false,\n listDepth: 0,\n blockquoteDepth: 0,\n inContainer: false,\n containerDepth: 0\n }\n}\n\n/**\n * 更新上下文(处理一行后)\n */\nexport function updateContext(\n line: string,\n context: BlockContext,\n containerConfig?: ContainerConfig | boolean\n): BlockContext {\n const newContext = { ...context }\n\n const containerCfg =\n containerConfig === true ? {} : containerConfig === false ? undefined : containerConfig\n\n // 代码块优先级最高\n if (context.inFencedCode) {\n if (detectFenceEnd(line, context)) {\n newContext.inFencedCode = false\n newContext.fenceChar = undefined\n newContext.fenceLength = undefined\n }\n return newContext\n }\n\n const fence = detectFenceStart(line)\n if (fence) {\n newContext.inFencedCode = true\n newContext.fenceChar = fence.char\n newContext.fenceLength = fence.length\n return newContext\n }\n\n // 容器处理\n if (containerCfg !== undefined) {\n if (context.inContainer) {\n if (detectContainerEnd(line, context, containerCfg)) {\n newContext.containerDepth = context.containerDepth - 1\n if (newContext.containerDepth === 0) {\n newContext.inContainer = false\n newContext.containerMarkerLength = undefined\n newContext.containerName = undefined\n }\n return newContext\n }\n\n const nested = detectContainer(line, containerCfg)\n if (nested && !nested.isEnd) {\n newContext.containerDepth = context.containerDepth + 1\n return newContext\n }\n } else {\n const container = detectContainer(line, containerCfg)\n if (container && !container.isEnd) {\n newContext.inContainer = true\n newContext.containerMarkerLength = container.markerLength\n newContext.containerName = container.name\n newContext.containerDepth = 1\n return newContext\n }\n }\n }\n\n return newContext\n}\n\n"]}
@@ -0,0 +1,396 @@
1
+ import { Parent, RootContent, Root, Definition, FootnoteDefinition } from 'mdast';
2
+ import { Extension as Extension$1 } from 'micromark-util-types';
3
+ import { Extension } from 'mdast-util-from-markdown';
4
+
5
+ declare module 'mdast' {
6
+ interface RootContentMap {
7
+ htmlElement: HtmlElementNode;
8
+ }
9
+ interface PhrasingContentMap {
10
+ htmlElement: HtmlElementNode;
11
+ }
12
+ }
13
+ /**
14
+ * 自定义 HTML 元素节点类型
15
+ */
16
+ interface HtmlElementNode extends Parent {
17
+ type: 'htmlElement';
18
+ tagName: string;
19
+ attrs: Record<string, string>;
20
+ children: RootContent[];
21
+ data?: {
22
+ rawHtml?: string;
23
+ parsed?: boolean;
24
+ originalType?: string;
25
+ };
26
+ }
27
+ /**
28
+ * HTML 属性信息
29
+ */
30
+ interface HtmlAttrInfo {
31
+ name: string;
32
+ value: string;
33
+ }
34
+ /**
35
+ * 解析后的 HTML 标签信息
36
+ */
37
+ interface ParsedHtmlTag {
38
+ tagName: string;
39
+ attrs: Record<string, string>;
40
+ isClosing: boolean;
41
+ isSelfClosing: boolean;
42
+ rawHtml: string;
43
+ }
44
+ /**
45
+ * HTML 树扩展配置
46
+ */
47
+ interface HtmlTreeExtensionOptions {
48
+ /**
49
+ * 标签黑名单 - 这些标签会被过滤掉(XSS 防护)
50
+ * 默认包含危险标签:script, style, iframe, object, embed, form, input, button, textarea, select
51
+ */
52
+ tagBlacklist?: string[];
53
+ /**
54
+ * 属性黑名单 - 这些属性会被过滤掉(XSS 防护)
55
+ * 默认包含所有 on* 事件属性和 javascript: 协议
56
+ */
57
+ attrBlacklist?: string[];
58
+ /**
59
+ * 协议黑名单 - URL 属性中禁止的协议
60
+ * 默认包含 javascript:, vbscript:, data: (允许 data:image/)
61
+ */
62
+ protocolBlacklist?: string[];
63
+ /**
64
+ * 是否保留原始 HTML 在 data 中
65
+ * 默认为 true
66
+ */
67
+ preserveRawHtml?: boolean;
68
+ /**
69
+ * 自定义标签处理器
70
+ * 可以对特定标签进行自定义处理
71
+ */
72
+ tagHandlers?: Record<string, (node: HtmlElementNode) => HtmlElementNode | null>;
73
+ }
74
+ /**
75
+ * 危险标签黑名单(XSS 防护)
76
+ */
77
+ declare const DEFAULT_TAG_BLACKLIST: string[];
78
+ /**
79
+ * 危险属性黑名单(XSS 防护)
80
+ * 包含所有 on* 事件属性
81
+ */
82
+ declare const DEFAULT_ATTR_BLACKLIST: string[];
83
+ /**
84
+ * 危险协议黑名单
85
+ */
86
+ declare const DEFAULT_PROTOCOL_BLACKLIST: string[];
87
+ /**
88
+ * HTML 内容类型
89
+ */
90
+ type HtmlContentType = 'opening' | 'closing' | 'self-closing' | 'fragment' | 'unknown';
91
+ /**
92
+ * 判断 HTML 内容的类型
93
+ * - opening: 单个开标签,如 <span class="foo">
94
+ * - closing: 单个闭标签,如 </span>
95
+ * - self-closing: 自闭合标签,如 <br /> 或 <img src="...">
96
+ * - fragment: 完整的 HTML 片段,包含多个标签
97
+ * - unknown: 无法识别
98
+ */
99
+ declare function detectHtmlContentType(html: string): HtmlContentType;
100
+ /**
101
+ * 解析单个 HTML 标签(开标签、闭标签或自闭合标签)
102
+ * 只处理单个标签,不处理完整的 HTML 片段
103
+ */
104
+ declare function parseHtmlTag(html: string): ParsedHtmlTag | null;
105
+ /**
106
+ * 解析完整的 HTML 片段为 AST
107
+ */
108
+ declare function parseHtmlFragment(html: string, options?: HtmlTreeExtensionOptions): HtmlElementNode[];
109
+ /**
110
+ * 转换整个 AST,处理所有 HTML 节点
111
+ */
112
+ declare function transformHtmlNodes(ast: Root, options?: HtmlTreeExtensionOptions): Root;
113
+ /**
114
+ * 创建 HTML 树转换器
115
+ * 这是一个 unified 兼容的转换器
116
+ */
117
+ declare function createHtmlTreeTransformer(options?: HtmlTreeExtensionOptions): (tree: Root) => Root;
118
+ /**
119
+ * mdast-util-from-markdown 扩展
120
+ * 注意:此扩展主要用于类型声明,实际转换在后处理阶段完成
121
+ */
122
+ declare const htmlTreeExtension: Extension;
123
+ /**
124
+ * 判断节点是否是 HtmlElementNode
125
+ */
126
+ declare function isHtmlElementNode(node: RootContent): node is HtmlElementNode;
127
+ /**
128
+ * 遍历所有 HTML 元素节点
129
+ */
130
+ declare function walkHtmlElements(node: RootContent | Root, callback: (node: HtmlElementNode, parent: Parent | Root | null) => void, parent?: Parent | Root | null): void;
131
+ /**
132
+ * 查找特定标签的所有节点
133
+ */
134
+ declare function findHtmlElementsByTag(root: Root, tagName: string): HtmlElementNode[];
135
+ /**
136
+ * 将 HtmlElementNode 转回 HTML 字符串
137
+ */
138
+ declare function htmlElementToString(node: HtmlElementNode): string;
139
+
140
+ /**
141
+ * Definition 映射类型
142
+ */
143
+ interface DefinitionMap {
144
+ [identifier: string]: Definition;
145
+ }
146
+ interface FootnoteDefinitionMap {
147
+ [identifier: string]: FootnoteDefinition;
148
+ }
149
+ /**
150
+ * 解析块的状态
151
+ */
152
+ type BlockStatus = 'pending' | 'stable' | 'completed';
153
+ /**
154
+ * AST 节点的通用接口(用于遍历)
155
+ * 统一定义,避免各模块重复声明
156
+ */
157
+ interface AstNode {
158
+ type: string;
159
+ value?: string;
160
+ children?: AstNode[];
161
+ [key: string]: unknown;
162
+ }
163
+ /**
164
+ * 解析出的块
165
+ */
166
+ interface ParsedBlock {
167
+ /** 块的唯一 ID */
168
+ id: string;
169
+ /** 块状态 */
170
+ status: BlockStatus;
171
+ /** AST 节点 */
172
+ node: RootContent;
173
+ /** 原始文本起始位置(相对于完整文档) */
174
+ startOffset: number;
175
+ /** 原始文本结束位置 */
176
+ endOffset: number;
177
+ /** 原始文本内容 */
178
+ rawText: string;
179
+ }
180
+ /**
181
+ * 增量更新事件
182
+ */
183
+ interface IncrementalUpdate {
184
+ /** 新完成的块 */
185
+ completed: ParsedBlock[];
186
+ /** 更新的块(内容变化) */
187
+ updated: ParsedBlock[];
188
+ /** 当前正在解析中的块(可能不完整) */
189
+ pending: ParsedBlock[];
190
+ /** 完整的 AST(包含所有已解析的内容) */
191
+ ast: Root;
192
+ /** Definition 映射表(用于引用式图片和链接) */
193
+ definitions: DefinitionMap;
194
+ /** Footnote Definition 映射表 */
195
+ footnoteDefinitions: FootnoteDefinitionMap;
196
+ /** 脚注引用的出现顺序(用于渲染时排序) */
197
+ footnoteReferenceOrder: string[];
198
+ }
199
+ /**
200
+ * 容器语法配置
201
+ */
202
+ interface ContainerConfig {
203
+ /** 容器标记字符,默认 ':' */
204
+ marker?: string;
205
+ /** 最小标记长度,默认 3 */
206
+ minMarkerLength?: number;
207
+ /** 允许的容器名称(如 ['warning', 'info', 'youtube']),undefined 表示允许所有 */
208
+ allowedNames?: string[];
209
+ }
210
+ /**
211
+ * 解析器状态变化事件
212
+ */
213
+ interface ParserState {
214
+ /** 已完成的块 */
215
+ completedBlocks: ParsedBlock[];
216
+ /** 待处理的块 */
217
+ pendingBlocks: ParsedBlock[];
218
+ /** 完整的 Markdown 内容 */
219
+ markdown: string;
220
+ /** 完整的 AST */
221
+ ast: Root;
222
+ definitions: DefinitionMap;
223
+ footnoteDefinitions: FootnoteDefinitionMap;
224
+ }
225
+ /**
226
+ * 解析器配置
227
+ */
228
+ interface ParserOptions {
229
+ /** 启用 GFM 扩展(表格、任务列表等) */
230
+ gfm?: boolean;
231
+ /**
232
+ * 启用 ::: 容器语法支持(用于边界检测)
233
+ * - false: 禁用(默认)
234
+ * - true: 使用默认配置启用
235
+ * - ContainerConfig: 使用自定义配置启用
236
+ */
237
+ containers?: boolean | ContainerConfig;
238
+ /**
239
+ * 启用 HTML 树转换
240
+ * - false/undefined: 禁用(默认),HTML 节点保持原始 type: 'html' 格式
241
+ * - true: 使用默认配置启用,将 HTML 节点转换为结构化的 htmlElement 节点
242
+ * - HtmlTreeExtensionOptions: 使用自定义配置启用(可配置黑名单等)
243
+ */
244
+ htmlTree?: boolean | HtmlTreeExtensionOptions;
245
+ /** 自定义块边界检测函数 */
246
+ blockBoundaryDetector?: (content: string, position: number) => boolean;
247
+ /** 自定义 micromark 扩展(如 directive) */
248
+ extensions?: Extension$1[];
249
+ /** 自定义 mdast 扩展(如 directiveFromMarkdown) */
250
+ mdastExtensions?: Extension[];
251
+ /** 状态变化回调 */
252
+ onChange?: (state: ParserState) => void;
253
+ }
254
+ /**
255
+ * 块上下文
256
+ */
257
+ interface BlockContext {
258
+ /** 当前是否在代码块中 */
259
+ inFencedCode: boolean;
260
+ /** 代码块的 fence 字符(` 或 ~) */
261
+ fenceChar?: string;
262
+ /** 代码块的 fence 长度 */
263
+ fenceLength?: number;
264
+ /** 当前列表嵌套深度 */
265
+ listDepth: number;
266
+ /** 当前引用嵌套深度 */
267
+ blockquoteDepth: number;
268
+ /** 当前是否在容器块中 */
269
+ inContainer: boolean;
270
+ /** 容器的标记长度 */
271
+ containerMarkerLength?: number;
272
+ /** 容器名称 */
273
+ containerName?: string;
274
+ /** 容器嵌套深度(支持嵌套容器) */
275
+ containerDepth: number;
276
+ }
277
+ /**
278
+ * 容器检测结果
279
+ */
280
+ interface ContainerMatch {
281
+ /** 容器名称 */
282
+ name: string;
283
+ /** 标记长度(冒号数量) */
284
+ markerLength: number;
285
+ /** 是否是结束标记 */
286
+ isEnd: boolean;
287
+ }
288
+ /**
289
+ * 块类型检测结果
290
+ */
291
+ interface BlockTypeInfo {
292
+ type: string;
293
+ /** 是否是容器节点(可以包含其他块) */
294
+ isContainer: boolean;
295
+ /** 是否需要显式关闭(如代码块) */
296
+ requiresClosing: boolean;
297
+ /** 关闭模式 */
298
+ closingPattern?: RegExp;
299
+ }
300
+
301
+ /**
302
+ * 块类型检测与边界判断
303
+ *
304
+ * Markdown 块级元素的识别规则
305
+ */
306
+
307
+ /**
308
+ * 检测行是否是代码块 fence 开始
309
+ */
310
+ declare function detectFenceStart(line: string): {
311
+ char: string;
312
+ length: number;
313
+ } | null;
314
+ /**
315
+ * 检测行是否是代码块 fence 结束
316
+ */
317
+ declare function detectFenceEnd(line: string, context: BlockContext): boolean;
318
+ /**
319
+ * 检测是否是空行或仅包含空白字符
320
+ */
321
+ declare function isEmptyLine(line: string): boolean;
322
+ /**
323
+ * 检测是否是标题行
324
+ */
325
+ declare function isHeading(line: string): boolean;
326
+ /**
327
+ * 检测是否是 thematic break(水平线)
328
+ */
329
+ declare function isThematicBreak(line: string): boolean;
330
+ /**
331
+ * 检测是否是列表项开始
332
+ */
333
+ declare function isListItemStart(line: string): {
334
+ ordered: boolean;
335
+ indent: number;
336
+ } | null;
337
+ /**
338
+ * 检测是否是引用块开始
339
+ */
340
+ declare function isBlockquoteStart(line: string): boolean;
341
+ /**
342
+ * 检测是否是 HTML 块
343
+ */
344
+ declare function isHtmlBlock(line: string): boolean;
345
+ /**
346
+ * 检测表格分隔行
347
+ */
348
+ declare function isTableDelimiter(line: string): boolean;
349
+ /**
350
+ * 检测是否是脚注定义的起始行
351
+ * 格式: [^id]: content
352
+ *
353
+ * @example
354
+ * isFootnoteDefinitionStart('[^1]: 脚注内容') // true
355
+ * isFootnoteDefinitionStart('[^note]: 内容') // true
356
+ * isFootnoteDefinitionStart(' 缩进内容') // false
357
+ */
358
+ declare function isFootnoteDefinitionStart(line: string): boolean;
359
+ /**
360
+ * 检测是否是脚注定义的延续行(缩进行)
361
+ * 至少4个空格或1个tab
362
+ *
363
+ * @example
364
+ * isFootnoteContinuation(' 第二行') // true
365
+ * isFootnoteContinuation('\t第二行') // true
366
+ * isFootnoteContinuation(' 两个空格') // false
367
+ */
368
+ declare function isFootnoteContinuation(line: string): boolean;
369
+ /**
370
+ * 检测容器开始或结束
371
+ *
372
+ * 支持格式:
373
+ * - ::: name 开始
374
+ * - ::: name attr 开始(带属性)
375
+ * - ::: 结束
376
+ * - :::::: name 开始(更长的标记,用于嵌套)
377
+ */
378
+ declare function detectContainer(line: string, config?: ContainerConfig): ContainerMatch | null;
379
+ /**
380
+ * 检测容器结束
381
+ */
382
+ declare function detectContainerEnd(line: string, context: BlockContext, config?: ContainerConfig): boolean;
383
+ /**
384
+ * 判断两行之间是否构成块边界
385
+ */
386
+ declare function isBlockBoundary(prevLine: string, currentLine: string, context: BlockContext): boolean;
387
+ /**
388
+ * 创建初始上下文
389
+ */
390
+ declare function createInitialContext(): BlockContext;
391
+ /**
392
+ * 更新上下文(处理一行后)
393
+ */
394
+ declare function updateContext(line: string, context: BlockContext, containerConfig?: ContainerConfig | boolean): BlockContext;
395
+
396
+ export { type AstNode as A, type BlockStatus as B, type ContainerConfig as C, type DefinitionMap as D, findHtmlElementsByTag as E, type FootnoteDefinitionMap as F, htmlElementToString as G, DEFAULT_TAG_BLACKLIST as H, type IncrementalUpdate as I, DEFAULT_ATTR_BLACKLIST as J, DEFAULT_PROTOCOL_BLACKLIST as K, htmlTreeExtension as L, type HtmlElementNode as M, type HtmlAttrInfo as N, type ParsedHtmlTag as O, type ParserOptions as P, type HtmlTreeExtensionOptions as Q, type HtmlContentType as R, isFootnoteDefinitionStart as S, isFootnoteContinuation as T, type ParsedBlock as a, type ParserState as b, type BlockContext as c, type ContainerMatch as d, type BlockTypeInfo as e, detectFenceStart as f, detectFenceEnd as g, isHeading as h, isEmptyLine as i, isThematicBreak as j, isListItemStart as k, isBlockquoteStart as l, isHtmlBlock as m, isTableDelimiter as n, detectContainer as o, detectContainerEnd as p, isBlockBoundary as q, createInitialContext as r, createHtmlTreeTransformer as s, transformHtmlNodes as t, updateContext as u, parseHtmlTag as v, parseHtmlFragment as w, detectHtmlContentType as x, isHtmlElementNode as y, walkHtmlElements as z };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ParserOptions, I as IncrementalUpdate, a as ParsedBlock, b as ParserState, B as BlockStatus } from './index-ChNeZ1wr.js';
2
- export { A as AstNode, c as BlockContext, e as BlockTypeInfo, C as ContainerConfig, d as ContainerMatch, r as createInitialContext, o as detectContainer, p as detectContainerEnd, g as detectFenceEnd, f as detectFenceStart, q as isBlockBoundary, l as isBlockquoteStart, i as isEmptyLine, h as isHeading, m as isHtmlBlock, k as isListItemStart, n as isTableDelimiter, j as isThematicBreak, u as updateContext } from './index-ChNeZ1wr.js';
1
+ import { P as ParserOptions, I as IncrementalUpdate, a as ParsedBlock, D as DefinitionMap, F as FootnoteDefinitionMap, b as ParserState, B as BlockStatus } from './index-3rgnFbip.js';
2
+ export { A as AstNode, c as BlockContext, e as BlockTypeInfo, C as ContainerConfig, d as ContainerMatch, J as HTML_ATTR_BLACKLIST, K as HTML_PROTOCOL_BLACKLIST, H as HTML_TAG_BLACKLIST, N as HtmlAttrInfo, R as HtmlContentType, M as HtmlElementNode, Q as HtmlTreeExtensionOptions, O as ParsedHtmlTag, s as createHtmlTreeTransformer, r as createInitialContext, o as detectContainer, p as detectContainerEnd, g as detectFenceEnd, f as detectFenceStart, x as detectHtmlContentType, E as findHtmlElementsByTag, G as htmlElementToString, L as htmlTreeExtension, q as isBlockBoundary, l as isBlockquoteStart, i as isEmptyLine, h as isHeading, m as isHtmlBlock, y as isHtmlElementNode, k as isListItemStart, n as isTableDelimiter, j as isThematicBreak, w as parseHtmlFragment, v as parseHtmlTag, t as transformHtmlNodes, u as updateContext, z as walkHtmlElements } from './index-3rgnFbip.js';
3
3
  import { Root, RootContent, Text } from 'mdast';
4
4
  export { Root, RootContent } from 'mdast';
5
5
  export { calculateLineOffset, generateId, joinLines, resetIdCounter, splitLines } from './utils/index.js';
@@ -18,12 +18,36 @@ declare class IncremarkParser {
18
18
  private options;
19
19
  /** 缓存的容器配置,避免重复计算 */
20
20
  private readonly containerConfig;
21
+ /** 缓存的 HTML 树配置,避免重复计算 */
22
+ private readonly htmlTreeConfig;
21
23
  /** 上次 append 返回的 pending blocks,用于 getAst 复用 */
22
24
  private lastPendingBlocks;
25
+ /** Definition 映射表(用于引用式图片和链接) */
26
+ private definitionMap;
27
+ /** Footnote Definition 映射表 */
28
+ private footnoteDefinitionMap;
29
+ /** Footnote Reference 出现顺序(按引用在文档中的顺序) */
30
+ private footnoteReferenceOrder;
23
31
  constructor(options?: ParserOptions);
24
32
  private generateBlockId;
25
33
  private computeContainerConfig;
34
+ private computeHtmlTreeConfig;
35
+ /**
36
+ * 将 HTML 节点转换为纯文本
37
+ * 递归处理 AST 中所有 html 类型的节点
38
+ * - 块级 HTML 节点 → 转换为 paragraph 包含 text
39
+ * - 内联 HTML 节点(在段落内部)→ 转换为 text 节点
40
+ */
41
+ private convertHtmlToText;
26
42
  private parse;
43
+ private updateDefinationsFromComplatedBlocks;
44
+ private findDefinition;
45
+ private findFootnoteDefinition;
46
+ /**
47
+ * 收集 AST 中的脚注引用(按出现顺序)
48
+ * 用于确定脚注的显示顺序
49
+ */
50
+ private collectFootnoteReferences;
27
51
  /**
28
52
  * 增量更新 lines 和 lineOffsets
29
53
  * 只处理新增的内容,避免全量 split
@@ -39,6 +63,21 @@ declare class IncremarkParser {
39
63
  */
40
64
  private findStableBoundary;
41
65
  private checkStability;
66
+ /**
67
+ * 从指定行向上查找脚注定义的起始行
68
+ *
69
+ * @param fromLine 开始查找的行索引
70
+ * @returns 脚注起始行索引,如果不属于脚注返回 -1
71
+ *
72
+ * @example
73
+ * // 假设 lines 为:
74
+ * // 0: "[^1]: 第一行"
75
+ * // 1: " 第二行"
76
+ * // 2: " 第三行"
77
+ * findFootnoteStart(2) // 返回 0
78
+ * findFootnoteStart(1) // 返回 0
79
+ */
80
+ private findFootnoteStart;
42
81
  private nodesToBlocks;
43
82
  /**
44
83
  * 追加新的 chunk 并返回增量更新
@@ -71,6 +110,18 @@ declare class IncremarkParser {
71
110
  * 获取当前缓冲区内容
72
111
  */
73
112
  getBuffer(): string;
113
+ /**
114
+ * 获取 Definition 映射表(用于引用式图片和链接)
115
+ */
116
+ getDefinitionMap(): DefinitionMap;
117
+ /**
118
+ * 获取 Footnote Definition 映射表
119
+ */
120
+ getFootnoteDefinitionMap(): FootnoteDefinitionMap;
121
+ /**
122
+ * 获取脚注引用的出现顺序
123
+ */
124
+ getFootnoteReferenceOrder(): string[];
74
125
  /**
75
126
  * 设置状态变化回调(用于 DevTools 等)
76
127
  */