@incremark/core 0.1.0 → 0.1.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/dist/detector/index.d.ts +1 -1
- package/dist/detector/index.js +37 -14
- package/dist/detector/index.js.map +1 -1
- package/dist/{index-i_qABRHQ.d.ts → index-ChNeZ1wr.d.ts} +11 -1
- package/dist/index.d.ts +34 -7
- package/dist/index.js +247 -61
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/detector/index.ts +46 -17
- package/src/index.ts +1 -0
- package/src/parser/IncremarkParser.ts +9 -17
- package/src/transformer/BlockTransformer.ts +111 -23
- package/src/transformer/types.ts +2 -1
- package/src/transformer/utils.ts +209 -36
- package/src/types/index.ts +11 -0
package/dist/detector/index.d.ts
CHANGED
|
@@ -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-
|
|
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';
|
|
2
2
|
import 'mdast';
|
|
3
3
|
import 'micromark-util-types';
|
|
4
4
|
import 'mdast-util-from-markdown';
|
package/dist/detector/index.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
// src/detector/index.ts
|
|
2
|
+
var RE_FENCE_START = /^(\s*)((`{3,})|(~{3,}))/;
|
|
3
|
+
var RE_EMPTY_LINE = /^\s*$/;
|
|
4
|
+
var RE_HEADING = /^#{1,6}\s/;
|
|
5
|
+
var RE_THEMATIC_BREAK = /^(\*{3,}|-{3,}|_{3,})\s*$/;
|
|
6
|
+
var RE_UNORDERED_LIST = /^(\s*)([-*+])\s/;
|
|
7
|
+
var RE_ORDERED_LIST = /^(\s*)(\d{1,9})[.)]\s/;
|
|
8
|
+
var RE_BLOCKQUOTE = /^\s{0,3}>/;
|
|
9
|
+
var RE_HTML_BLOCK_1 = /^\s{0,3}<(script|pre|style|textarea|!--|!DOCTYPE|\?|!\[CDATA\[)/i;
|
|
10
|
+
var RE_HTML_BLOCK_2 = /^\s{0,3}<\/?[a-zA-Z][a-zA-Z0-9-]*(\s|>|$)/;
|
|
11
|
+
var RE_TABLE_DELIMITER = /^\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?$/;
|
|
12
|
+
var RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
|
|
13
|
+
var fenceEndPatternCache = /* @__PURE__ */ new Map();
|
|
14
|
+
var containerPatternCache = /* @__PURE__ */ new Map();
|
|
2
15
|
function detectFenceStart(line) {
|
|
3
|
-
const match = line.match(
|
|
16
|
+
const match = line.match(RE_FENCE_START);
|
|
4
17
|
if (match) {
|
|
5
18
|
const fence = match[2];
|
|
6
19
|
const char = fence[0];
|
|
@@ -12,45 +25,55 @@ function detectFenceEnd(line, context) {
|
|
|
12
25
|
if (!context.inFencedCode || !context.fenceChar || !context.fenceLength) {
|
|
13
26
|
return false;
|
|
14
27
|
}
|
|
15
|
-
const
|
|
28
|
+
const cacheKey = `${context.fenceChar}-${context.fenceLength}`;
|
|
29
|
+
let pattern = fenceEndPatternCache.get(cacheKey);
|
|
30
|
+
if (!pattern) {
|
|
31
|
+
pattern = new RegExp(`^\\s{0,3}${context.fenceChar}{${context.fenceLength},}\\s*$`);
|
|
32
|
+
fenceEndPatternCache.set(cacheKey, pattern);
|
|
33
|
+
}
|
|
16
34
|
return pattern.test(line);
|
|
17
35
|
}
|
|
18
36
|
function isEmptyLine(line) {
|
|
19
|
-
return
|
|
37
|
+
return RE_EMPTY_LINE.test(line);
|
|
20
38
|
}
|
|
21
39
|
function isHeading(line) {
|
|
22
|
-
return
|
|
40
|
+
return RE_HEADING.test(line);
|
|
23
41
|
}
|
|
24
42
|
function isThematicBreak(line) {
|
|
25
|
-
return
|
|
43
|
+
return RE_THEMATIC_BREAK.test(line.trim());
|
|
26
44
|
}
|
|
27
45
|
function isListItemStart(line) {
|
|
28
|
-
const unordered = line.match(
|
|
46
|
+
const unordered = line.match(RE_UNORDERED_LIST);
|
|
29
47
|
if (unordered) {
|
|
30
48
|
return { ordered: false, indent: unordered[1].length };
|
|
31
49
|
}
|
|
32
|
-
const ordered = line.match(
|
|
50
|
+
const ordered = line.match(RE_ORDERED_LIST);
|
|
33
51
|
if (ordered) {
|
|
34
52
|
return { ordered: true, indent: ordered[1].length };
|
|
35
53
|
}
|
|
36
54
|
return null;
|
|
37
55
|
}
|
|
38
56
|
function isBlockquoteStart(line) {
|
|
39
|
-
return
|
|
57
|
+
return RE_BLOCKQUOTE.test(line);
|
|
40
58
|
}
|
|
41
59
|
function isHtmlBlock(line) {
|
|
42
|
-
return
|
|
60
|
+
return RE_HTML_BLOCK_1.test(line) || RE_HTML_BLOCK_2.test(line);
|
|
43
61
|
}
|
|
44
62
|
function isTableDelimiter(line) {
|
|
45
|
-
return
|
|
63
|
+
return RE_TABLE_DELIMITER.test(line.trim());
|
|
46
64
|
}
|
|
47
65
|
function detectContainer(line, config) {
|
|
48
66
|
const marker = config?.marker || ":";
|
|
49
67
|
const minLength = config?.minMarkerLength || 3;
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
68
|
+
const cacheKey = `${marker}-${minLength}`;
|
|
69
|
+
let pattern = containerPatternCache.get(cacheKey);
|
|
70
|
+
if (!pattern) {
|
|
71
|
+
const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, "\\$&");
|
|
72
|
+
pattern = new RegExp(
|
|
73
|
+
`^(\\s*)(${escapedMarker}{${minLength},})(?:\\s+(\\w[\\w-]*))?(?:\\s+(.*))?\\s*$`
|
|
74
|
+
);
|
|
75
|
+
containerPatternCache.set(cacheKey, pattern);
|
|
76
|
+
}
|
|
54
77
|
const match = line.match(pattern);
|
|
55
78
|
if (!match) {
|
|
56
79
|
return null;
|
|
@@ -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\n/**\n * 检测行是否是代码块 fence 开始\n */\nexport function detectFenceStart(line: string): { char: string; length: number } | null {\n const match = line.match(/^(\\s*)((`{3,})|(~{3,}))/)\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 const pattern = new RegExp(`^\\\\s{0,3}${context.fenceChar}{${context.fenceLength},}\\\\s*$`)\n return pattern.test(line)\n}\n\n// ============ 行类型检测 ============\n\n/**\n * 检测是否是空行或仅包含空白字符\n */\nexport function isEmptyLine(line: string): boolean {\n return /^\\s*$/.test(line)\n}\n\n/**\n * 检测是否是标题行\n */\nexport function isHeading(line: string): boolean {\n return /^#{1,6}\\s/.test(line)\n}\n\n/**\n * 检测是否是 thematic break(水平线)\n */\nexport function isThematicBreak(line: string): boolean {\n return /^(\\*{3,}|-{3,}|_{3,})\\s*$/.test(line.trim())\n}\n\n/**\n * 检测是否是列表项开始\n */\nexport function isListItemStart(line: string): { ordered: boolean; indent: number } | null {\n // 无序列表: - * +\n const unordered = line.match(/^(\\s*)([-*+])\\s/)\n if (unordered) {\n return { ordered: false, indent: unordered[1].length }\n }\n\n // 有序列表: 1. 2) 等\n const ordered = line.match(/^(\\s*)(\\d{1,9})[.)]\\s/)\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 /^\\s{0,3}>/.test(line)\n}\n\n/**\n * 检测是否是 HTML 块\n */\nexport function isHtmlBlock(line: string): boolean {\n return (\n /^\\s{0,3}<(script|pre|style|textarea|!--|!DOCTYPE|\\?|!\\[CDATA\\[)/i.test(line) ||\n /^\\s{0,3}<\\/?[a-zA-Z][a-zA-Z0-9-]*(\\s|>|$)/.test(line)\n )\n}\n\n/**\n * 检测表格分隔行\n */\nexport function isTableDelimiter(line: string): boolean {\n return /^\\|?\\s*:?-{3,}:?\\s*(\\|\\s*:?-{3,}:?\\s*)*\\|?$/.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 const escapedMarker = marker.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const pattern = new RegExp(\n `^(\\\\s*)(${escapedMarker}{${minLength},})(?:\\\\s+(\\\\w[\\\\w-]*))?(?:\\\\s+(.*))?\\\\s*$`\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\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"]}
|
|
@@ -6,6 +6,16 @@ import { Extension as Extension$1 } from 'mdast-util-from-markdown';
|
|
|
6
6
|
* 解析块的状态
|
|
7
7
|
*/
|
|
8
8
|
type BlockStatus = 'pending' | 'stable' | 'completed';
|
|
9
|
+
/**
|
|
10
|
+
* AST 节点的通用接口(用于遍历)
|
|
11
|
+
* 统一定义,避免各模块重复声明
|
|
12
|
+
*/
|
|
13
|
+
interface AstNode {
|
|
14
|
+
type: string;
|
|
15
|
+
value?: string;
|
|
16
|
+
children?: AstNode[];
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
}
|
|
9
19
|
/**
|
|
10
20
|
* 解析出的块
|
|
11
21
|
*/
|
|
@@ -204,4 +214,4 @@ declare function createInitialContext(): BlockContext;
|
|
|
204
214
|
*/
|
|
205
215
|
declare function updateContext(line: string, context: BlockContext, containerConfig?: ContainerConfig | boolean): BlockContext;
|
|
206
216
|
|
|
207
|
-
export { type BlockStatus as B, type ContainerConfig as C, type IncrementalUpdate as I, type ParserOptions as P, 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, updateContext as u };
|
|
217
|
+
export { type AstNode as A, type BlockStatus as B, type ContainerConfig as C, type IncrementalUpdate as I, type ParserOptions as P, 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, updateContext as u };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as ParserOptions, I as IncrementalUpdate, a as ParsedBlock, b as ParserState } from './index-
|
|
2
|
-
export {
|
|
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';
|
|
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';
|
|
@@ -17,13 +17,12 @@ declare class IncremarkParser {
|
|
|
17
17
|
private context;
|
|
18
18
|
private options;
|
|
19
19
|
/** 缓存的容器配置,避免重复计算 */
|
|
20
|
-
private
|
|
20
|
+
private readonly containerConfig;
|
|
21
21
|
/** 上次 append 返回的 pending blocks,用于 getAst 复用 */
|
|
22
22
|
private lastPendingBlocks;
|
|
23
23
|
constructor(options?: ParserOptions);
|
|
24
24
|
private generateBlockId;
|
|
25
25
|
private computeContainerConfig;
|
|
26
|
-
private getContainerConfig;
|
|
27
26
|
private parse;
|
|
28
27
|
/**
|
|
29
28
|
* 增量更新 lines 和 lineOffsets
|
|
@@ -101,7 +100,7 @@ interface SourceBlock<T = unknown> {
|
|
|
101
100
|
/** AST 节点 */
|
|
102
101
|
node: RootContent;
|
|
103
102
|
/** 块状态 */
|
|
104
|
-
status:
|
|
103
|
+
status: BlockStatus;
|
|
105
104
|
/** 用户自定义元数据 */
|
|
106
105
|
meta?: T;
|
|
107
106
|
}
|
|
@@ -231,6 +230,12 @@ declare class BlockTransformer<T = unknown> {
|
|
|
231
230
|
private isPaused;
|
|
232
231
|
private chunks;
|
|
233
232
|
private visibilityHandler;
|
|
233
|
+
/** 缓存的已截断 displayNode(稳定的部分,避免重复遍历) */
|
|
234
|
+
private cachedDisplayNode;
|
|
235
|
+
/** 缓存的字符数(避免重复计算) */
|
|
236
|
+
private cachedTotalChars;
|
|
237
|
+
/** 当前缓存的进度(对应 cachedDisplayNode) */
|
|
238
|
+
private cachedProgress;
|
|
234
239
|
constructor(options?: TransformerOptions);
|
|
235
240
|
/**
|
|
236
241
|
* 推入新的 blocks
|
|
@@ -259,6 +264,7 @@ declare class BlockTransformer<T = unknown> {
|
|
|
259
264
|
resume(): void;
|
|
260
265
|
/**
|
|
261
266
|
* 获取用于渲染的 display blocks
|
|
267
|
+
* 优化:使用缓存的 displayNode,避免重复遍历已稳定的节点
|
|
262
268
|
*/
|
|
263
269
|
getDisplayBlocks(): DisplayBlock<T>[];
|
|
264
270
|
/**
|
|
@@ -312,6 +318,23 @@ declare class BlockTransformer<T = unknown> {
|
|
|
312
318
|
private countChars;
|
|
313
319
|
private sliceNode;
|
|
314
320
|
private notifyComplete;
|
|
321
|
+
/**
|
|
322
|
+
* 更新缓存的 displayNode
|
|
323
|
+
* 使用真正的增量追加模式:只处理新增部分,不重复遍历已稳定的节点
|
|
324
|
+
*/
|
|
325
|
+
private updateCachedDisplayNode;
|
|
326
|
+
/**
|
|
327
|
+
* 获取总字符数(带缓存)
|
|
328
|
+
*/
|
|
329
|
+
private getTotalChars;
|
|
330
|
+
/**
|
|
331
|
+
* 清除缓存(当 block 切换或内容更新时)
|
|
332
|
+
*/
|
|
333
|
+
private clearCache;
|
|
334
|
+
/**
|
|
335
|
+
* 获取累积的 chunks(用于 fade-in 效果)
|
|
336
|
+
*/
|
|
337
|
+
private getAccumulatedChunks;
|
|
315
338
|
}
|
|
316
339
|
/**
|
|
317
340
|
* 创建 BlockTransformer 实例的工厂函数
|
|
@@ -352,15 +375,19 @@ interface AccumulatedChunks {
|
|
|
352
375
|
/**
|
|
353
376
|
* 截断 AST 节点,只保留前 maxChars 个字符
|
|
354
377
|
* 支持 chunks(用于渐入动画)
|
|
378
|
+
* 支持增量模式:跳过已处理的字符,只处理新增部分
|
|
355
379
|
*
|
|
356
380
|
* @param node 原始节点
|
|
357
381
|
* @param maxChars 最大字符数
|
|
358
382
|
* @param accumulatedChunks 累积的 chunks 信息(用于渐入动画)
|
|
383
|
+
* @param skipChars 跳过前 N 个字符(已处理的部分,用于增量追加)
|
|
359
384
|
* @returns 截断后的节点,如果 maxChars <= 0 返回 null
|
|
360
385
|
*/
|
|
361
|
-
declare function sliceAst(node: RootContent, maxChars: number, accumulatedChunks?: AccumulatedChunks): RootContent | null;
|
|
386
|
+
declare function sliceAst(node: RootContent, maxChars: number, accumulatedChunks?: AccumulatedChunks, skipChars?: number): RootContent | null;
|
|
362
387
|
/**
|
|
363
388
|
* 深拷贝 AST 节点
|
|
389
|
+
* 使用递归浅拷贝实现,比 JSON.parse/stringify 更高效
|
|
390
|
+
* 且保持对象结构完整性
|
|
364
391
|
*/
|
|
365
392
|
declare function cloneNode<T extends RootContent>(node: T): T;
|
|
366
393
|
|
|
@@ -416,4 +443,4 @@ declare const allPlugins: TransformerPlugin[];
|
|
|
416
443
|
*/
|
|
417
444
|
declare function createPlugin(name: string, matcher: (node: RootContent) => boolean, options?: Partial<Omit<TransformerPlugin, 'name' | 'match'>>): TransformerPlugin;
|
|
418
445
|
|
|
419
|
-
export { type AnimationEffect, BlockTransformer, type DisplayBlock, IncremarkParser, IncrementalUpdate, ParsedBlock, ParserOptions, ParserState, type SourceBlock, type TextChunk, type TextNodeWithChunks, type TransformerOptions, type TransformerPlugin, type TransformerState, allPlugins, cloneNode, codeBlockPlugin, countChars, createBlockTransformer, createIncremarkParser, createPlugin, defaultPlugins, imagePlugin, mathPlugin, mermaidPlugin, sliceAst, thematicBreakPlugin };
|
|
446
|
+
export { type AnimationEffect, BlockStatus, BlockTransformer, type DisplayBlock, IncremarkParser, IncrementalUpdate, ParsedBlock, ParserOptions, ParserState, type SourceBlock, type TextChunk, type TextNodeWithChunks, type TransformerOptions, type TransformerPlugin, type TransformerState, allPlugins, cloneNode, codeBlockPlugin, countChars, createBlockTransformer, createIncremarkParser, createPlugin, defaultPlugins, imagePlugin, mathPlugin, mermaidPlugin, sliceAst, thematicBreakPlugin };
|