@incremark/core 0.0.1 → 0.0.3

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.en.md ADDED
@@ -0,0 +1,110 @@
1
+ # @incremark/core
2
+
3
+ Incremental Markdown parser core library.
4
+
5
+ **[🇨🇳 中文](./README.md)** | 🇺🇸 English
6
+
7
+ ## Features
8
+
9
+ - 🚀 **Incremental Parsing** - Only parse new content, completed blocks are never re-processed
10
+ - 🔄 **Streaming Friendly** - Designed for AI streaming output scenarios
11
+ - 🎯 **Smart Boundary Detection** - Accurate Markdown block boundary recognition
12
+ - 📦 **Framework Agnostic** - Works with any frontend framework
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pnpm add @incremark/core
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```ts
23
+ import { createIncremarkParser } from '@incremark/core'
24
+
25
+ const parser = createIncremarkParser({ gfm: true })
26
+
27
+ // Simulate streaming input
28
+ parser.append('# Hello\n')
29
+ parser.append('\nWorld')
30
+ parser.finalize()
31
+
32
+ // Get results
33
+ console.log(parser.getCompletedBlocks())
34
+ console.log(parser.getAst())
35
+ ```
36
+
37
+ ## API
38
+
39
+ ### createIncremarkParser(options)
40
+
41
+ Create a parser instance.
42
+
43
+ ```ts
44
+ interface ParserOptions {
45
+ gfm?: boolean // Enable GFM
46
+ containers?: boolean // Enable ::: containers
47
+ extensions?: Extension[] // micromark extensions
48
+ mdastExtensions?: Extension[] // mdast extensions
49
+ }
50
+ ```
51
+
52
+ ### parser.append(chunk)
53
+
54
+ Append content, returns incremental update.
55
+
56
+ ### parser.finalize()
57
+
58
+ Complete parsing.
59
+
60
+ ### parser.reset()
61
+
62
+ Reset state.
63
+
64
+ ### parser.render(content)
65
+
66
+ Render complete Markdown at once (reset + append + finalize).
67
+
68
+ ```ts
69
+ const update = parser.render('# Hello World')
70
+ console.log(update.completed) // completed blocks
71
+ ```
72
+
73
+ ### parser.getBuffer()
74
+
75
+ Get current buffer content.
76
+
77
+ ### parser.getCompletedBlocks()
78
+
79
+ Get completed blocks.
80
+
81
+ ### parser.getPendingBlocks()
82
+
83
+ Get pending blocks.
84
+
85
+ ### parser.getAst()
86
+
87
+ Get complete AST.
88
+
89
+ ## Type Definitions
90
+
91
+ ```ts
92
+ interface ParsedBlock {
93
+ id: string
94
+ status: 'pending' | 'stable' | 'completed'
95
+ node: RootContent
96
+ startOffset: number
97
+ endOffset: number
98
+ rawText: string
99
+ }
100
+ ```
101
+
102
+ ## Framework Integration
103
+
104
+ - Vue: [@incremark/vue](../vue)
105
+ - React: [@incremark/react](../react)
106
+
107
+ ## License
108
+
109
+ MIT
110
+
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  增量式 Markdown 解析器核心库。
4
4
 
5
+ 🇨🇳 中文 | **[🇺🇸 English](./README.en.md)**
6
+
5
7
  ## 特性
6
8
 
7
9
  - 🚀 **增量解析** - 只解析新增内容,已完成的块不再重复处理
@@ -59,6 +61,15 @@ interface ParserOptions {
59
61
 
60
62
  重置状态。
61
63
 
64
+ ### parser.render(content)
65
+
66
+ 一次性渲染完整 Markdown(reset + append + finalize)。
67
+
68
+ ```ts
69
+ const update = parser.render('# Hello World')
70
+ console.log(update.completed) // 已完成的块
71
+ ```
72
+
62
73
  ### parser.getBuffer()
63
74
 
64
75
  获取当前缓冲区内容。
package/dist/index.d.ts CHANGED
@@ -80,6 +80,12 @@ declare class IncremarkParser {
80
80
  * 重置解析器状态
81
81
  */
82
82
  reset(): void;
83
+ /**
84
+ * 一次性渲染完整 Markdown(reset + append + finalize)
85
+ * @param content 完整的 Markdown 内容
86
+ * @returns 解析结果
87
+ */
88
+ render(content: string): IncrementalUpdate;
83
89
  }
84
90
  /**
85
91
  * 创建 Incremark 解析器实例
package/dist/index.js CHANGED
@@ -483,6 +483,16 @@ var IncremarkParser = class {
483
483
  this.lastPendingBlocks = [];
484
484
  this.emitChange([]);
485
485
  }
486
+ /**
487
+ * 一次性渲染完整 Markdown(reset + append + finalize)
488
+ * @param content 完整的 Markdown 内容
489
+ * @returns 解析结果
490
+ */
491
+ render(content) {
492
+ this.reset();
493
+ this.append(content);
494
+ return this.finalize();
495
+ }
486
496
  };
487
497
  function createIncremarkParser(options) {
488
498
  return new IncremarkParser(options);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/detector/index.ts","../src/parser/IncremarkParser.ts","../src/utils/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","/**\n * 增量 Markdown 解析器\n *\n * 设计思路:\n * 1. 维护一个文本缓冲区,接收流式输入\n * 2. 识别\"稳定边界\"(如空行、标题等),将已完成的块标记为 completed\n * 3. 对于正在接收的块,每次重新解析,但只解析该块的内容\n * 4. 复杂嵌套节点(如列表、引用)作为整体处理,直到确认完成\n */\n\nimport { fromMarkdown } from 'mdast-util-from-markdown'\nimport { gfmFromMarkdown } from 'mdast-util-gfm'\nimport { gfm } from 'micromark-extension-gfm'\nimport type { Extension as MicromarkExtension } from 'micromark-util-types'\nimport type { Extension as MdastExtension } from 'mdast-util-from-markdown'\n\nimport type {\n Root,\n RootContent,\n ParsedBlock,\n IncrementalUpdate,\n ParserOptions,\n BlockStatus,\n BlockContext,\n ContainerConfig\n} from '../types'\n\nimport {\n createInitialContext,\n updateContext,\n isEmptyLine,\n detectFenceStart,\n isHeading,\n isThematicBreak,\n isBlockquoteStart,\n isListItemStart,\n detectContainer\n} from '../detector'\n\n// ============ 解析器类 ============\n\nexport class IncremarkParser {\n private buffer = ''\n private lines: string[] = []\n /** 行偏移量前缀和:lineOffsets[i] = 第i行起始位置的偏移量 */\n private lineOffsets: number[] = [0]\n private completedBlocks: ParsedBlock[] = []\n private pendingStartLine = 0\n private blockIdCounter = 0\n private context: BlockContext\n private options: ParserOptions\n /** 缓存的容器配置,避免重复计算 */\n private cachedContainerConfig: ContainerConfig | undefined | null = null\n /** 上次 append 返回的 pending blocks,用于 getAst 复用 */\n private lastPendingBlocks: ParsedBlock[] = []\n\n constructor(options: ParserOptions = {}) {\n this.options = {\n gfm: true,\n ...options\n }\n this.context = createInitialContext()\n // 初始化容器配置缓存\n this.cachedContainerConfig = this.computeContainerConfig()\n }\n\n private generateBlockId(): string {\n return `block-${++this.blockIdCounter}`\n }\n\n private computeContainerConfig(): ContainerConfig | undefined {\n const containers = this.options.containers\n if (!containers) return undefined\n return containers === true ? {} : containers\n }\n\n private getContainerConfig(): ContainerConfig | undefined {\n return this.cachedContainerConfig ?? undefined\n }\n\n private parse(text: string): Root {\n const extensions: MicromarkExtension[] = []\n const mdastExtensions: MdastExtension[] = []\n\n if (this.options.gfm) {\n extensions.push(gfm())\n mdastExtensions.push(...gfmFromMarkdown())\n }\n\n // 如果用户传入了自定义扩展,添加它们\n if (this.options.extensions) {\n extensions.push(...this.options.extensions)\n }\n if (this.options.mdastExtensions) {\n mdastExtensions.push(...this.options.mdastExtensions)\n }\n\n return fromMarkdown(text, { extensions, mdastExtensions })\n }\n\n /**\n * 增量更新 lines 和 lineOffsets\n * 只处理新增的内容,避免全量 split\n */\n private updateLines(): void {\n const prevLineCount = this.lines.length\n\n if (prevLineCount === 0) {\n // 首次输入,直接 split\n this.lines = this.buffer.split('\\n')\n this.lineOffsets = [0]\n for (let i = 0; i < this.lines.length; i++) {\n this.lineOffsets.push(this.lineOffsets[i] + this.lines[i].length + 1)\n }\n return\n }\n\n // 找到最后一个不完整的行(可能被新 chunk 续上)\n const lastLineStart = this.lineOffsets[prevLineCount - 1]\n const textFromLastLine = this.buffer.slice(lastLineStart)\n\n // 重新 split 最后一行及之后的内容\n const newLines = textFromLastLine.split('\\n')\n\n // 替换最后一行并追加新行\n this.lines.length = prevLineCount - 1\n this.lineOffsets.length = prevLineCount\n\n for (let i = 0; i < newLines.length; i++) {\n this.lines.push(newLines[i])\n const prevOffset = this.lineOffsets[this.lineOffsets.length - 1]\n this.lineOffsets.push(prevOffset + newLines[i].length + 1)\n }\n }\n\n /**\n * O(1) 获取行偏移量\n */\n private getLineOffset(lineIndex: number): number {\n return this.lineOffsets[lineIndex] ?? 0\n }\n\n /**\n * 查找稳定边界\n * 返回稳定边界行号和该行对应的上下文(用于后续更新,避免重复计算)\n */\n private findStableBoundary(): { line: number; contextAtLine: BlockContext } {\n let stableLine = -1\n let stableContext: BlockContext = this.context\n let tempContext = { ...this.context }\n const containerConfig = this.getContainerConfig()\n\n for (let i = this.pendingStartLine; i < this.lines.length; i++) {\n const line = this.lines[i]\n const wasInFencedCode = tempContext.inFencedCode\n const wasInContainer = tempContext.inContainer\n const wasContainerDepth = tempContext.containerDepth\n\n tempContext = updateContext(line, tempContext, containerConfig)\n\n if (wasInFencedCode && !tempContext.inFencedCode) {\n if (i < this.lines.length - 1) {\n stableLine = i\n stableContext = { ...tempContext }\n }\n continue\n }\n\n if (tempContext.inFencedCode) {\n continue\n }\n\n if (wasInContainer && wasContainerDepth === 1 && !tempContext.inContainer) {\n if (i < this.lines.length - 1) {\n stableLine = i\n stableContext = { ...tempContext }\n }\n continue\n }\n\n if (tempContext.inContainer) {\n continue\n }\n\n const stablePoint = this.checkStability(i, containerConfig)\n if (stablePoint >= 0) {\n stableLine = stablePoint\n stableContext = { ...tempContext }\n }\n }\n\n return { line: stableLine, contextAtLine: stableContext }\n }\n\n private checkStability(\n lineIndex: number,\n containerConfig: ContainerConfig | undefined\n ): number {\n // 第一行永远不稳定\n if (lineIndex === 0) {\n return -1\n }\n\n const line = this.lines[lineIndex]\n const prevLine = this.lines[lineIndex - 1]\n\n // 前一行是独立块(标题、分割线),该块已完成\n if (isHeading(prevLine) || isThematicBreak(prevLine)) {\n return lineIndex - 1\n }\n\n // 最后一行不稳定(可能还有更多内容)\n if (lineIndex >= this.lines.length - 1) {\n return -1\n }\n\n // 前一行非空时,如果当前行是新块开始,则前一块已完成\n if (!isEmptyLine(prevLine)) {\n // 新标题开始\n if (isHeading(line)) {\n return lineIndex - 1\n }\n\n // 新代码块开始\n if (detectFenceStart(line)) {\n return lineIndex - 1\n }\n\n // 新引用块开始(排除连续引用)\n if (isBlockquoteStart(line) && !isBlockquoteStart(prevLine)) {\n return lineIndex - 1\n }\n\n // 新列表开始(排除连续列表项)\n if (isListItemStart(line) && !isListItemStart(prevLine)) {\n return lineIndex - 1\n }\n\n // 新容器开始\n if (containerConfig !== undefined) {\n const container = detectContainer(line, containerConfig)\n if (container && !container.isEnd) {\n const prevContainer = detectContainer(prevLine, containerConfig)\n if (!prevContainer || prevContainer.isEnd) {\n return lineIndex - 1\n }\n }\n }\n }\n\n // 空行标志段落结束\n if (isEmptyLine(line) && !isEmptyLine(prevLine)) {\n return lineIndex\n }\n\n return -1\n }\n\n private nodesToBlocks(\n nodes: RootContent[],\n startOffset: number,\n rawText: string,\n status: BlockStatus\n ): ParsedBlock[] {\n const blocks: ParsedBlock[] = []\n let currentOffset = startOffset\n\n for (const node of nodes) {\n const nodeStart = node.position?.start?.offset ?? currentOffset\n const nodeEnd = node.position?.end?.offset ?? currentOffset + 1\n const nodeText = rawText.substring(nodeStart - startOffset, nodeEnd - startOffset)\n\n blocks.push({\n id: this.generateBlockId(),\n status,\n node,\n startOffset: nodeStart,\n endOffset: nodeEnd,\n rawText: nodeText\n })\n\n currentOffset = nodeEnd\n }\n\n return blocks\n }\n\n /**\n * 追加新的 chunk 并返回增量更新\n */\n append(chunk: string): IncrementalUpdate {\n this.buffer += chunk\n this.updateLines()\n\n const { line: stableBoundary, contextAtLine } = this.findStableBoundary()\n\n const update: IncrementalUpdate = {\n completed: [],\n updated: [],\n pending: [],\n ast: { type: 'root', children: [] }\n }\n\n if (stableBoundary >= this.pendingStartLine && stableBoundary >= 0) {\n const stableText = this.lines.slice(this.pendingStartLine, stableBoundary + 1).join('\\n')\n const stableOffset = this.getLineOffset(this.pendingStartLine)\n\n const ast = this.parse(stableText)\n const newBlocks = this.nodesToBlocks(ast.children, stableOffset, stableText, 'completed')\n\n this.completedBlocks.push(...newBlocks)\n update.completed = newBlocks\n\n // 直接使用 findStableBoundary 计算好的上下文,避免重复遍历\n this.context = contextAtLine\n this.pendingStartLine = stableBoundary + 1\n }\n\n if (this.pendingStartLine < this.lines.length) {\n const pendingText = this.lines.slice(this.pendingStartLine).join('\\n')\n\n if (pendingText.trim()) {\n const pendingOffset = this.getLineOffset(this.pendingStartLine)\n const ast = this.parse(pendingText)\n\n update.pending = this.nodesToBlocks(ast.children, pendingOffset, pendingText, 'pending')\n }\n }\n\n // 缓存 pending blocks 供 getAst 使用\n this.lastPendingBlocks = update.pending\n\n update.ast = {\n type: 'root',\n children: [...this.completedBlocks.map((b) => b.node), ...update.pending.map((b) => b.node)]\n }\n\n // 触发状态变化回调\n this.emitChange(update.pending)\n\n return update\n }\n\n /**\n * 触发状态变化回调\n */\n private emitChange(pendingBlocks: ParsedBlock[] = []): void {\n if (this.options.onChange) {\n this.options.onChange({\n completedBlocks: this.completedBlocks,\n pendingBlocks,\n markdown: this.buffer,\n ast: {\n type: 'root',\n children: [\n ...this.completedBlocks.map((b) => b.node),\n ...pendingBlocks.map((b) => b.node)\n ]\n }\n })\n }\n }\n\n /**\n * 标记解析完成,处理剩余内容\n * 也可用于强制中断时(如用户点击停止),将 pending 内容标记为 completed\n */\n finalize(): IncrementalUpdate {\n const update: IncrementalUpdate = {\n completed: [],\n updated: [],\n pending: [],\n ast: { type: 'root', children: [] }\n }\n\n if (this.pendingStartLine < this.lines.length) {\n const remainingText = this.lines.slice(this.pendingStartLine).join('\\n')\n\n if (remainingText.trim()) {\n const remainingOffset = this.getLineOffset(this.pendingStartLine)\n const ast = this.parse(remainingText)\n\n const finalBlocks = this.nodesToBlocks(\n ast.children,\n remainingOffset,\n remainingText,\n 'completed'\n )\n\n this.completedBlocks.push(...finalBlocks)\n update.completed = finalBlocks\n }\n }\n\n // 清空 pending 缓存\n this.lastPendingBlocks = []\n this.pendingStartLine = this.lines.length\n\n update.ast = {\n type: 'root',\n children: this.completedBlocks.map((b) => b.node)\n }\n\n // 触发状态变化回调\n this.emitChange([])\n\n return update\n }\n\n /**\n * 强制中断解析,将所有待处理内容标记为完成\n * 语义上等同于 finalize(),但名称更清晰\n */\n abort(): IncrementalUpdate {\n return this.finalize()\n }\n\n /**\n * 获取当前完整的 AST\n * 复用上次 append 的 pending 结果,避免重复解析\n */\n getAst(): Root {\n return {\n type: 'root',\n children: [\n ...this.completedBlocks.map((b) => b.node),\n ...this.lastPendingBlocks.map((b) => b.node)\n ]\n }\n }\n\n /**\n * 获取所有已完成的块\n */\n getCompletedBlocks(): ParsedBlock[] {\n return [...this.completedBlocks]\n }\n\n /**\n * 获取当前缓冲区内容\n */\n getBuffer(): string {\n return this.buffer\n }\n\n /**\n * 设置状态变化回调(用于 DevTools 等)\n */\n setOnChange(callback: ((state: import('../types').ParserState) => void) | undefined): void {\n this.options.onChange = callback\n }\n\n /**\n * 重置解析器状态\n */\n reset(): void {\n this.buffer = ''\n this.lines = []\n this.lineOffsets = [0]\n this.completedBlocks = []\n this.pendingStartLine = 0\n this.blockIdCounter = 0\n this.context = createInitialContext()\n this.lastPendingBlocks = []\n\n // 触发状态变化回调\n this.emitChange([])\n }\n}\n\n/**\n * 创建 Incremark 解析器实例\n */\nexport function createIncremarkParser(options?: ParserOptions): IncremarkParser {\n return new IncremarkParser(options)\n}\n","/**\n * 工具函数\n */\n\n/**\n * 生成唯一 ID\n */\nlet idCounter = 0\nexport function generateId(prefix = 'block'): string {\n return `${prefix}-${++idCounter}`\n}\n\n/**\n * 重置 ID 计数器(用于测试)\n */\nexport function resetIdCounter(): void {\n idCounter = 0\n}\n\n/**\n * 计算行的偏移量\n */\nexport function calculateLineOffset(lines: string[], lineIndex: number): number {\n let offset = 0\n for (let i = 0; i < lineIndex && i < lines.length; i++) {\n offset += lines[i].length + 1 // +1 for newline\n }\n return offset\n}\n\n/**\n * 将文本按行分割\n */\nexport function splitLines(text: string): string[] {\n return text.split('\\n')\n}\n\n/**\n * 合并行为文本\n */\nexport function joinLines(lines: string[], start: number, end: number): string {\n return lines.slice(start, end + 1).join('\\n')\n}\n\n"]}
1
+ {"version":3,"sources":["../src/detector/index.ts","../src/parser/IncremarkParser.ts","../src/utils/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","/**\n * 增量 Markdown 解析器\n *\n * 设计思路:\n * 1. 维护一个文本缓冲区,接收流式输入\n * 2. 识别\"稳定边界\"(如空行、标题等),将已完成的块标记为 completed\n * 3. 对于正在接收的块,每次重新解析,但只解析该块的内容\n * 4. 复杂嵌套节点(如列表、引用)作为整体处理,直到确认完成\n */\n\nimport { fromMarkdown } from 'mdast-util-from-markdown'\nimport { gfmFromMarkdown } from 'mdast-util-gfm'\nimport { gfm } from 'micromark-extension-gfm'\nimport type { Extension as MicromarkExtension } from 'micromark-util-types'\nimport type { Extension as MdastExtension } from 'mdast-util-from-markdown'\n\nimport type {\n Root,\n RootContent,\n ParsedBlock,\n IncrementalUpdate,\n ParserOptions,\n BlockStatus,\n BlockContext,\n ContainerConfig\n} from '../types'\n\nimport {\n createInitialContext,\n updateContext,\n isEmptyLine,\n detectFenceStart,\n isHeading,\n isThematicBreak,\n isBlockquoteStart,\n isListItemStart,\n detectContainer\n} from '../detector'\n\n// ============ 解析器类 ============\n\nexport class IncremarkParser {\n private buffer = ''\n private lines: string[] = []\n /** 行偏移量前缀和:lineOffsets[i] = 第i行起始位置的偏移量 */\n private lineOffsets: number[] = [0]\n private completedBlocks: ParsedBlock[] = []\n private pendingStartLine = 0\n private blockIdCounter = 0\n private context: BlockContext\n private options: ParserOptions\n /** 缓存的容器配置,避免重复计算 */\n private cachedContainerConfig: ContainerConfig | undefined | null = null\n /** 上次 append 返回的 pending blocks,用于 getAst 复用 */\n private lastPendingBlocks: ParsedBlock[] = []\n\n constructor(options: ParserOptions = {}) {\n this.options = {\n gfm: true,\n ...options\n }\n this.context = createInitialContext()\n // 初始化容器配置缓存\n this.cachedContainerConfig = this.computeContainerConfig()\n }\n\n private generateBlockId(): string {\n return `block-${++this.blockIdCounter}`\n }\n\n private computeContainerConfig(): ContainerConfig | undefined {\n const containers = this.options.containers\n if (!containers) return undefined\n return containers === true ? {} : containers\n }\n\n private getContainerConfig(): ContainerConfig | undefined {\n return this.cachedContainerConfig ?? undefined\n }\n\n private parse(text: string): Root {\n const extensions: MicromarkExtension[] = []\n const mdastExtensions: MdastExtension[] = []\n\n if (this.options.gfm) {\n extensions.push(gfm())\n mdastExtensions.push(...gfmFromMarkdown())\n }\n\n // 如果用户传入了自定义扩展,添加它们\n if (this.options.extensions) {\n extensions.push(...this.options.extensions)\n }\n if (this.options.mdastExtensions) {\n mdastExtensions.push(...this.options.mdastExtensions)\n }\n\n return fromMarkdown(text, { extensions, mdastExtensions })\n }\n\n /**\n * 增量更新 lines 和 lineOffsets\n * 只处理新增的内容,避免全量 split\n */\n private updateLines(): void {\n const prevLineCount = this.lines.length\n\n if (prevLineCount === 0) {\n // 首次输入,直接 split\n this.lines = this.buffer.split('\\n')\n this.lineOffsets = [0]\n for (let i = 0; i < this.lines.length; i++) {\n this.lineOffsets.push(this.lineOffsets[i] + this.lines[i].length + 1)\n }\n return\n }\n\n // 找到最后一个不完整的行(可能被新 chunk 续上)\n const lastLineStart = this.lineOffsets[prevLineCount - 1]\n const textFromLastLine = this.buffer.slice(lastLineStart)\n\n // 重新 split 最后一行及之后的内容\n const newLines = textFromLastLine.split('\\n')\n\n // 替换最后一行并追加新行\n this.lines.length = prevLineCount - 1\n this.lineOffsets.length = prevLineCount\n\n for (let i = 0; i < newLines.length; i++) {\n this.lines.push(newLines[i])\n const prevOffset = this.lineOffsets[this.lineOffsets.length - 1]\n this.lineOffsets.push(prevOffset + newLines[i].length + 1)\n }\n }\n\n /**\n * O(1) 获取行偏移量\n */\n private getLineOffset(lineIndex: number): number {\n return this.lineOffsets[lineIndex] ?? 0\n }\n\n /**\n * 查找稳定边界\n * 返回稳定边界行号和该行对应的上下文(用于后续更新,避免重复计算)\n */\n private findStableBoundary(): { line: number; contextAtLine: BlockContext } {\n let stableLine = -1\n let stableContext: BlockContext = this.context\n let tempContext = { ...this.context }\n const containerConfig = this.getContainerConfig()\n\n for (let i = this.pendingStartLine; i < this.lines.length; i++) {\n const line = this.lines[i]\n const wasInFencedCode = tempContext.inFencedCode\n const wasInContainer = tempContext.inContainer\n const wasContainerDepth = tempContext.containerDepth\n\n tempContext = updateContext(line, tempContext, containerConfig)\n\n if (wasInFencedCode && !tempContext.inFencedCode) {\n if (i < this.lines.length - 1) {\n stableLine = i\n stableContext = { ...tempContext }\n }\n continue\n }\n\n if (tempContext.inFencedCode) {\n continue\n }\n\n if (wasInContainer && wasContainerDepth === 1 && !tempContext.inContainer) {\n if (i < this.lines.length - 1) {\n stableLine = i\n stableContext = { ...tempContext }\n }\n continue\n }\n\n if (tempContext.inContainer) {\n continue\n }\n\n const stablePoint = this.checkStability(i, containerConfig)\n if (stablePoint >= 0) {\n stableLine = stablePoint\n stableContext = { ...tempContext }\n }\n }\n\n return { line: stableLine, contextAtLine: stableContext }\n }\n\n private checkStability(\n lineIndex: number,\n containerConfig: ContainerConfig | undefined\n ): number {\n // 第一行永远不稳定\n if (lineIndex === 0) {\n return -1\n }\n\n const line = this.lines[lineIndex]\n const prevLine = this.lines[lineIndex - 1]\n\n // 前一行是独立块(标题、分割线),该块已完成\n if (isHeading(prevLine) || isThematicBreak(prevLine)) {\n return lineIndex - 1\n }\n\n // 最后一行不稳定(可能还有更多内容)\n if (lineIndex >= this.lines.length - 1) {\n return -1\n }\n\n // 前一行非空时,如果当前行是新块开始,则前一块已完成\n if (!isEmptyLine(prevLine)) {\n // 新标题开始\n if (isHeading(line)) {\n return lineIndex - 1\n }\n\n // 新代码块开始\n if (detectFenceStart(line)) {\n return lineIndex - 1\n }\n\n // 新引用块开始(排除连续引用)\n if (isBlockquoteStart(line) && !isBlockquoteStart(prevLine)) {\n return lineIndex - 1\n }\n\n // 新列表开始(排除连续列表项)\n if (isListItemStart(line) && !isListItemStart(prevLine)) {\n return lineIndex - 1\n }\n\n // 新容器开始\n if (containerConfig !== undefined) {\n const container = detectContainer(line, containerConfig)\n if (container && !container.isEnd) {\n const prevContainer = detectContainer(prevLine, containerConfig)\n if (!prevContainer || prevContainer.isEnd) {\n return lineIndex - 1\n }\n }\n }\n }\n\n // 空行标志段落结束\n if (isEmptyLine(line) && !isEmptyLine(prevLine)) {\n return lineIndex\n }\n\n return -1\n }\n\n private nodesToBlocks(\n nodes: RootContent[],\n startOffset: number,\n rawText: string,\n status: BlockStatus\n ): ParsedBlock[] {\n const blocks: ParsedBlock[] = []\n let currentOffset = startOffset\n\n for (const node of nodes) {\n const nodeStart = node.position?.start?.offset ?? currentOffset\n const nodeEnd = node.position?.end?.offset ?? currentOffset + 1\n const nodeText = rawText.substring(nodeStart - startOffset, nodeEnd - startOffset)\n\n blocks.push({\n id: this.generateBlockId(),\n status,\n node,\n startOffset: nodeStart,\n endOffset: nodeEnd,\n rawText: nodeText\n })\n\n currentOffset = nodeEnd\n }\n\n return blocks\n }\n\n /**\n * 追加新的 chunk 并返回增量更新\n */\n append(chunk: string): IncrementalUpdate {\n this.buffer += chunk\n this.updateLines()\n\n const { line: stableBoundary, contextAtLine } = this.findStableBoundary()\n\n const update: IncrementalUpdate = {\n completed: [],\n updated: [],\n pending: [],\n ast: { type: 'root', children: [] }\n }\n\n if (stableBoundary >= this.pendingStartLine && stableBoundary >= 0) {\n const stableText = this.lines.slice(this.pendingStartLine, stableBoundary + 1).join('\\n')\n const stableOffset = this.getLineOffset(this.pendingStartLine)\n\n const ast = this.parse(stableText)\n const newBlocks = this.nodesToBlocks(ast.children, stableOffset, stableText, 'completed')\n\n this.completedBlocks.push(...newBlocks)\n update.completed = newBlocks\n\n // 直接使用 findStableBoundary 计算好的上下文,避免重复遍历\n this.context = contextAtLine\n this.pendingStartLine = stableBoundary + 1\n }\n\n if (this.pendingStartLine < this.lines.length) {\n const pendingText = this.lines.slice(this.pendingStartLine).join('\\n')\n\n if (pendingText.trim()) {\n const pendingOffset = this.getLineOffset(this.pendingStartLine)\n const ast = this.parse(pendingText)\n\n update.pending = this.nodesToBlocks(ast.children, pendingOffset, pendingText, 'pending')\n }\n }\n\n // 缓存 pending blocks 供 getAst 使用\n this.lastPendingBlocks = update.pending\n\n update.ast = {\n type: 'root',\n children: [...this.completedBlocks.map((b) => b.node), ...update.pending.map((b) => b.node)]\n }\n\n // 触发状态变化回调\n this.emitChange(update.pending)\n\n return update\n }\n\n /**\n * 触发状态变化回调\n */\n private emitChange(pendingBlocks: ParsedBlock[] = []): void {\n if (this.options.onChange) {\n this.options.onChange({\n completedBlocks: this.completedBlocks,\n pendingBlocks,\n markdown: this.buffer,\n ast: {\n type: 'root',\n children: [\n ...this.completedBlocks.map((b) => b.node),\n ...pendingBlocks.map((b) => b.node)\n ]\n }\n })\n }\n }\n\n /**\n * 标记解析完成,处理剩余内容\n * 也可用于强制中断时(如用户点击停止),将 pending 内容标记为 completed\n */\n finalize(): IncrementalUpdate {\n const update: IncrementalUpdate = {\n completed: [],\n updated: [],\n pending: [],\n ast: { type: 'root', children: [] }\n }\n\n if (this.pendingStartLine < this.lines.length) {\n const remainingText = this.lines.slice(this.pendingStartLine).join('\\n')\n\n if (remainingText.trim()) {\n const remainingOffset = this.getLineOffset(this.pendingStartLine)\n const ast = this.parse(remainingText)\n\n const finalBlocks = this.nodesToBlocks(\n ast.children,\n remainingOffset,\n remainingText,\n 'completed'\n )\n\n this.completedBlocks.push(...finalBlocks)\n update.completed = finalBlocks\n }\n }\n\n // 清空 pending 缓存\n this.lastPendingBlocks = []\n this.pendingStartLine = this.lines.length\n\n update.ast = {\n type: 'root',\n children: this.completedBlocks.map((b) => b.node)\n }\n\n // 触发状态变化回调\n this.emitChange([])\n\n return update\n }\n\n /**\n * 强制中断解析,将所有待处理内容标记为完成\n * 语义上等同于 finalize(),但名称更清晰\n */\n abort(): IncrementalUpdate {\n return this.finalize()\n }\n\n /**\n * 获取当前完整的 AST\n * 复用上次 append 的 pending 结果,避免重复解析\n */\n getAst(): Root {\n return {\n type: 'root',\n children: [\n ...this.completedBlocks.map((b) => b.node),\n ...this.lastPendingBlocks.map((b) => b.node)\n ]\n }\n }\n\n /**\n * 获取所有已完成的块\n */\n getCompletedBlocks(): ParsedBlock[] {\n return [...this.completedBlocks]\n }\n\n /**\n * 获取当前缓冲区内容\n */\n getBuffer(): string {\n return this.buffer\n }\n\n /**\n * 设置状态变化回调(用于 DevTools 等)\n */\n setOnChange(callback: ((state: import('../types').ParserState) => void) | undefined): void {\n this.options.onChange = callback\n }\n\n /**\n * 重置解析器状态\n */\n reset(): void {\n this.buffer = ''\n this.lines = []\n this.lineOffsets = [0]\n this.completedBlocks = []\n this.pendingStartLine = 0\n this.blockIdCounter = 0\n this.context = createInitialContext()\n this.lastPendingBlocks = []\n\n // 触发状态变化回调\n this.emitChange([])\n }\n\n /**\n * 一次性渲染完整 Markdown(reset + append + finalize)\n * @param content 完整的 Markdown 内容\n * @returns 解析结果\n */\n render(content: string): IncrementalUpdate {\n this.reset()\n this.append(content)\n return this.finalize()\n }\n}\n\n/**\n * 创建 Incremark 解析器实例\n */\nexport function createIncremarkParser(options?: ParserOptions): IncremarkParser {\n return new IncremarkParser(options)\n}\n","/**\n * 工具函数\n */\n\n/**\n * 生成唯一 ID\n */\nlet idCounter = 0\nexport function generateId(prefix = 'block'): string {\n return `${prefix}-${++idCounter}`\n}\n\n/**\n * 重置 ID 计数器(用于测试)\n */\nexport function resetIdCounter(): void {\n idCounter = 0\n}\n\n/**\n * 计算行的偏移量\n */\nexport function calculateLineOffset(lines: string[], lineIndex: number): number {\n let offset = 0\n for (let i = 0; i < lineIndex && i < lines.length; i++) {\n offset += lines[i].length + 1 // +1 for newline\n }\n return offset\n}\n\n/**\n * 将文本按行分割\n */\nexport function splitLines(text: string): string[] {\n return text.split('\\n')\n}\n\n/**\n * 合并行为文本\n */\nexport function joinLines(lines: string[], start: number, end: number): string {\n return lines.slice(start, end + 1).join('\\n')\n}\n\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@incremark/core",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "增量式 Markdown 解析器核心库",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -47,13 +47,15 @@
47
47
  "license": "MIT",
48
48
  "repository": {
49
49
  "type": "git",
50
- "url": "https://github.com/user/incremark.git",
50
+ "url": "https://github.com/kingshuaishuai/incremark.git",
51
51
  "directory": "packages/core"
52
52
  },
53
+ "homepage": "https://incremark-docs.vercel.app/",
53
54
  "scripts": {
54
55
  "build": "tsup",
55
56
  "dev": "tsup --watch",
56
57
  "test": "vitest",
57
- "test:run": "vitest run"
58
+ "test:run": "vitest run",
59
+ "benchmark": "npx tsx src/benchmark/run.ts"
58
60
  }
59
61
  }
@@ -0,0 +1,443 @@
1
+ /**
2
+ * Incremark vs Traditional Parser Benchmark
3
+ *
4
+ * 对比增量解析和传统解析(每次重新解析全部内容)的性能差异
5
+ */
6
+
7
+ import { IncremarkParser } from '../parser/IncremarkParser'
8
+ import { fromMarkdown } from 'mdast-util-from-markdown'
9
+ import { gfm } from 'micromark-extension-gfm'
10
+ import { gfmFromMarkdown } from 'mdast-util-gfm'
11
+
12
+ // 短文本测试(~800 字符)
13
+ const shortMarkdown = `
14
+ # Hello World
15
+
16
+ This is a paragraph with **bold** and *italic* text.
17
+
18
+ ## Code Example
19
+
20
+ \`\`\`javascript
21
+ function hello() {
22
+ console.log('Hello, World!');
23
+ return {
24
+ name: 'test',
25
+ value: 42
26
+ };
27
+ }
28
+ \`\`\`
29
+
30
+ ## List Example
31
+
32
+ - Item 1
33
+ - Item 2
34
+ - Nested item 2.1
35
+ - Nested item 2.2
36
+ - Item 3
37
+
38
+ ## Table Example
39
+
40
+ | Name | Age | City |
41
+ |------|-----|------|
42
+ | Alice | 25 | NYC |
43
+ | Bob | 30 | LA |
44
+
45
+ ## Blockquote
46
+
47
+ > This is a quote
48
+ > with multiple lines
49
+ > and **formatted** text
50
+
51
+ ## More Content
52
+
53
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
54
+
55
+ ### Subsection
56
+
57
+ More text here with [links](https://example.com) and \`inline code\`.
58
+
59
+ 1. Ordered item 1
60
+ 2. Ordered item 2
61
+ 3. Ordered item 3
62
+
63
+ ---
64
+
65
+ The end.
66
+ `
67
+
68
+ // 生成长文本(模拟真实 AI 输出)
69
+ function generateLongMarkdown(targetLength: number): string {
70
+ const sections = [
71
+ `
72
+ # Introduction to Machine Learning
73
+
74
+ Machine learning is a subset of artificial intelligence (AI) that provides systems the ability to automatically learn and improve from experience without being explicitly programmed.
75
+
76
+ ## Key Concepts
77
+
78
+ ### Supervised Learning
79
+
80
+ In supervised learning, the algorithm learns from labeled training data, and makes predictions based on that data. Common algorithms include:
81
+
82
+ - **Linear Regression** - For predicting continuous values
83
+ - **Logistic Regression** - For classification problems
84
+ - **Decision Trees** - For both classification and regression
85
+ - **Random Forest** - Ensemble method using multiple decision trees
86
+ - **Support Vector Machines** - For classification with clear margins
87
+
88
+ \`\`\`python
89
+ from sklearn.model_selection import train_test_split
90
+ from sklearn.ensemble import RandomForestClassifier
91
+
92
+ # Split the data
93
+ X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
94
+
95
+ # Train the model
96
+ model = RandomForestClassifier(n_estimators=100)
97
+ model.fit(X_train, y_train)
98
+
99
+ # Make predictions
100
+ predictions = model.predict(X_test)
101
+ \`\`\`
102
+
103
+ ### Unsupervised Learning
104
+
105
+ Unsupervised learning deals with unlabeled data. The algorithm tries to find patterns and relationships in the data.
106
+
107
+ | Algorithm | Use Case | Complexity |
108
+ |-----------|----------|------------|
109
+ | K-Means | Clustering | O(n*k*i) |
110
+ | DBSCAN | Density clustering | O(n log n) |
111
+ | PCA | Dimensionality reduction | O(n*d²) |
112
+ | t-SNE | Visualization | O(n²) |
113
+
114
+ > "The goal of unsupervised learning is to discover hidden patterns or data groupings without the need for human intervention." - Andrew Ng
115
+
116
+ `,
117
+ `
118
+ ## Deep Learning
119
+
120
+ Deep learning is a subset of machine learning that uses neural networks with many layers.
121
+
122
+ ### Neural Network Architecture
123
+
124
+ \`\`\`
125
+ Input Layer → Hidden Layer 1 → Hidden Layer 2 → ... → Output Layer
126
+ ↓ ↓ ↓ ↓
127
+ Features Activations Activations Predictions
128
+ \`\`\`
129
+
130
+ ### Common Activation Functions
131
+
132
+ 1. **ReLU (Rectified Linear Unit)**
133
+ - Formula: \`f(x) = max(0, x)\`
134
+ - Most commonly used in hidden layers
135
+
136
+ 2. **Sigmoid**
137
+ - Formula: \`f(x) = 1 / (1 + e^(-x))\`
138
+ - Used for binary classification
139
+
140
+ 3. **Softmax**
141
+ - Used for multi-class classification
142
+ - Outputs probability distribution
143
+
144
+ \`\`\`python
145
+ import torch
146
+ import torch.nn as nn
147
+
148
+ class NeuralNetwork(nn.Module):
149
+ def __init__(self, input_size, hidden_size, num_classes):
150
+ super(NeuralNetwork, self).__init__()
151
+ self.layer1 = nn.Linear(input_size, hidden_size)
152
+ self.relu = nn.ReLU()
153
+ self.layer2 = nn.Linear(hidden_size, num_classes)
154
+
155
+ def forward(self, x):
156
+ out = self.layer1(x)
157
+ out = self.relu(out)
158
+ out = self.layer2(out)
159
+ return out
160
+ \`\`\`
161
+
162
+ `,
163
+ `
164
+ ## Natural Language Processing
165
+
166
+ NLP is a field of AI that focuses on the interaction between computers and humans through natural language.
167
+
168
+ ### Key Tasks
169
+
170
+ - **Text Classification** - Categorizing text into predefined categories
171
+ - **Named Entity Recognition** - Identifying entities like names, locations, organizations
172
+ - **Sentiment Analysis** - Determining the emotional tone of text
173
+ - **Machine Translation** - Translating text from one language to another
174
+ - **Question Answering** - Answering questions based on context
175
+
176
+ ### Transformer Architecture
177
+
178
+ The transformer architecture revolutionized NLP with the introduction of self-attention mechanisms.
179
+
180
+ \`\`\`
181
+ ┌─────────────────────────────────────┐
182
+ │ Transformer │
183
+ ├─────────────────────────────────────┤
184
+ │ ┌─────────────┐ ┌─────────────┐ │
185
+ │ │ Encoder │ │ Decoder │ │
186
+ │ │ │ │ │ │
187
+ │ │ Self-Attn │ │ Self-Attn │ │
188
+ │ │ Feed-Forward│ │ Cross-Attn │ │
189
+ │ │ │ │ Feed-Forward│ │
190
+ │ └─────────────┘ └─────────────┘ │
191
+ └─────────────────────────────────────┘
192
+ \`\`\`
193
+
194
+ > Transformers have become the foundation for large language models like GPT, BERT, and Claude.
195
+
196
+ `,
197
+ `
198
+ ## Best Practices
199
+
200
+ ### Data Preprocessing
201
+
202
+ 1. Handle missing values appropriately
203
+ 2. Normalize or standardize numerical features
204
+ 3. Encode categorical variables
205
+ 4. Split data into train/validation/test sets
206
+ 5. Apply data augmentation when appropriate
207
+
208
+ ### Model Evaluation
209
+
210
+ | Metric | Formula | Use Case |
211
+ |--------|---------|----------|
212
+ | Accuracy | (TP+TN)/(TP+TN+FP+FN) | Balanced classes |
213
+ | Precision | TP/(TP+FP) | When FP is costly |
214
+ | Recall | TP/(TP+FN) | When FN is costly |
215
+ | F1 Score | 2*(P*R)/(P+R) | Imbalanced classes |
216
+ | AUC-ROC | Area under ROC curve | Binary classification |
217
+
218
+ ### Hyperparameter Tuning
219
+
220
+ \`\`\`python
221
+ from sklearn.model_selection import GridSearchCV
222
+
223
+ param_grid = {
224
+ 'n_estimators': [100, 200, 300],
225
+ 'max_depth': [10, 20, 30, None],
226
+ 'min_samples_split': [2, 5, 10],
227
+ 'min_samples_leaf': [1, 2, 4]
228
+ }
229
+
230
+ grid_search = GridSearchCV(
231
+ estimator=RandomForestClassifier(),
232
+ param_grid=param_grid,
233
+ cv=5,
234
+ n_jobs=-1,
235
+ verbose=2
236
+ )
237
+
238
+ grid_search.fit(X_train, y_train)
239
+ print(f"Best parameters: {grid_search.best_params_}")
240
+ \`\`\`
241
+
242
+ ---
243
+
244
+ This concludes our overview of machine learning fundamentals.
245
+
246
+ `
247
+ ]
248
+
249
+ let result = ''
250
+ let sectionIndex = 0
251
+
252
+ while (result.length < targetLength) {
253
+ result += sections[sectionIndex % sections.length]
254
+ sectionIndex++
255
+ }
256
+
257
+ return result.slice(0, targetLength)
258
+ }
259
+
260
+ // 默认测试用的 Markdown 内容
261
+ const testMarkdown = shortMarkdown
262
+
263
+ interface BenchmarkResult {
264
+ name: string
265
+ totalTime: number
266
+ parseCount: number
267
+ avgTimePerParse: number
268
+ totalCharsParsed: number
269
+ }
270
+
271
+ /**
272
+ * 模拟流式输入,将文本按 chunk 大小分割
273
+ */
274
+ function simulateStream(text: string, chunkSize: number): string[] {
275
+ const chunks: string[] = []
276
+ for (let i = 0; i < text.length; i += chunkSize) {
277
+ chunks.push(text.slice(i, i + chunkSize))
278
+ }
279
+ return chunks
280
+ }
281
+
282
+ /**
283
+ * 传统方式:每次收到新内容都重新解析全部文本
284
+ */
285
+ function benchmarkTraditional(chunks: string[], iterations: number): BenchmarkResult {
286
+ let totalTime = 0
287
+ let totalCharsParsed = 0
288
+ let parseCount = 0
289
+
290
+ for (let iter = 0; iter < iterations; iter++) {
291
+ let buffer = ''
292
+
293
+ for (const chunk of chunks) {
294
+ buffer += chunk
295
+
296
+ const start = performance.now()
297
+ fromMarkdown(buffer, {
298
+ extensions: [gfm()],
299
+ mdastExtensions: [gfmFromMarkdown()]
300
+ })
301
+ const end = performance.now()
302
+
303
+ totalTime += (end - start)
304
+ totalCharsParsed += buffer.length
305
+ parseCount++
306
+ }
307
+ }
308
+
309
+ return {
310
+ name: 'Traditional (re-parse all)',
311
+ totalTime,
312
+ parseCount,
313
+ avgTimePerParse: totalTime / parseCount,
314
+ totalCharsParsed
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Incremark 方式:增量解析
320
+ */
321
+ function benchmarkIncremental(chunks: string[], iterations: number): BenchmarkResult {
322
+ let totalTime = 0
323
+ let totalCharsParsed = 0
324
+ let parseCount = 0
325
+
326
+ for (let iter = 0; iter < iterations; iter++) {
327
+ const parser = new IncremarkParser({ gfm: true })
328
+
329
+ for (const chunk of chunks) {
330
+ const start = performance.now()
331
+ parser.append(chunk)
332
+ const end = performance.now()
333
+
334
+ totalTime += (end - start)
335
+ totalCharsParsed += chunk.length
336
+ parseCount++
337
+ }
338
+
339
+ const start = performance.now()
340
+ parser.finalize()
341
+ const end = performance.now()
342
+ totalTime += (end - start)
343
+ }
344
+
345
+ return {
346
+ name: 'Incremark (incremental)',
347
+ totalTime,
348
+ parseCount,
349
+ avgTimePerParse: totalTime / parseCount,
350
+ totalCharsParsed
351
+ }
352
+ }
353
+
354
+ /**
355
+ * 运行 benchmark
356
+ */
357
+ export function runBenchmark(options: {
358
+ chunkSize?: number
359
+ iterations?: number
360
+ markdown?: string
361
+ markdownLength?: number
362
+ } = {}) {
363
+ const {
364
+ chunkSize = 10,
365
+ iterations = 100,
366
+ markdownLength
367
+ } = options
368
+
369
+ // 如果指定了长度,生成对应长度的 Markdown
370
+ const markdown = markdownLength
371
+ ? generateLongMarkdown(markdownLength)
372
+ : (options.markdown || testMarkdown)
373
+
374
+ const chunks = simulateStream(markdown, chunkSize)
375
+
376
+ console.log('='.repeat(60))
377
+ console.log('Incremark Benchmark')
378
+ console.log('='.repeat(60))
379
+ console.log(`Markdown length: ${markdown.length} chars`)
380
+ console.log(`Chunk size: ${chunkSize} chars`)
381
+ console.log(`Total chunks: ${chunks.length}`)
382
+ console.log(`Iterations: ${iterations}`)
383
+ console.log('='.repeat(60))
384
+ console.log('')
385
+
386
+ // 预热
387
+ console.log('Warming up...')
388
+ benchmarkTraditional(chunks, 5)
389
+ benchmarkIncremental(chunks, 5)
390
+ console.log('')
391
+
392
+ // 正式测试
393
+ console.log('Running benchmark...')
394
+ console.log('')
395
+
396
+ const traditional = benchmarkTraditional(chunks, iterations)
397
+ const incremental = benchmarkIncremental(chunks, iterations)
398
+
399
+ // 计算节省百分比
400
+ const timeSaved = ((traditional.totalTime - incremental.totalTime) / traditional.totalTime * 100).toFixed(1)
401
+ const charsSaved = ((traditional.totalCharsParsed - incremental.totalCharsParsed) / traditional.totalCharsParsed * 100).toFixed(1)
402
+
403
+ console.log('Results:')
404
+ console.log('-'.repeat(60))
405
+ console.log('')
406
+
407
+ console.log(`📊 ${traditional.name}`)
408
+ console.log(` Total time: ${traditional.totalTime.toFixed(2)} ms`)
409
+ console.log(` Parse count: ${traditional.parseCount}`)
410
+ console.log(` Avg time per parse: ${traditional.avgTimePerParse.toFixed(4)} ms`)
411
+ console.log(` Total chars parsed: ${traditional.totalCharsParsed.toLocaleString()}`)
412
+ console.log('')
413
+
414
+ console.log(`⚡ ${incremental.name}`)
415
+ console.log(` Total time: ${incremental.totalTime.toFixed(2)} ms`)
416
+ console.log(` Parse count: ${incremental.parseCount}`)
417
+ console.log(` Avg time per parse: ${incremental.avgTimePerParse.toFixed(4)} ms`)
418
+ console.log(` Total chars parsed: ${incremental.totalCharsParsed.toLocaleString()}`)
419
+ console.log('')
420
+
421
+ console.log('-'.repeat(60))
422
+ console.log('')
423
+ console.log(`🎯 Performance Improvement:`)
424
+ console.log(` Time saved: ${timeSaved}%`)
425
+ console.log(` Chars parsing saved: ${charsSaved}%`)
426
+ console.log(` Speedup: ${(traditional.totalTime / incremental.totalTime).toFixed(2)}x faster`)
427
+ console.log('')
428
+ console.log('='.repeat(60))
429
+
430
+ return {
431
+ traditional,
432
+ incremental,
433
+ timeSaved: parseFloat(timeSaved),
434
+ charsSaved: parseFloat(charsSaved),
435
+ speedup: traditional.totalTime / incremental.totalTime
436
+ }
437
+ }
438
+
439
+ // 如果直接运行此文件
440
+ if (typeof process !== 'undefined' && process.argv[1]?.includes('benchmark')) {
441
+ runBenchmark()
442
+ }
443
+
@@ -0,0 +1,93 @@
1
+ import { runBenchmark } from './index'
2
+
3
+ console.log('')
4
+ console.log('🔬 Running Incremark Benchmark Suite')
5
+ console.log('')
6
+
7
+ // 不同文档长度的测试
8
+ const documentSizes = [
9
+ { name: 'Short (~1KB)', length: 1000 },
10
+ { name: 'Medium (~5KB)', length: 5000 },
11
+ { name: 'Long (~10KB)', length: 10000 },
12
+ { name: 'Very Long (~20KB)', length: 20000 },
13
+ ]
14
+
15
+ const allResults: Array<{
16
+ docSize: string
17
+ docLength: number
18
+ chunkSize: number
19
+ timeSaved: number
20
+ charsSaved: number
21
+ speedup: number
22
+ }> = []
23
+
24
+ for (const doc of documentSizes) {
25
+ console.log(`\n${'='.repeat(60)}`)
26
+ console.log(`📄 Document Size: ${doc.name} (${doc.length} chars)`)
27
+ console.log('='.repeat(60))
28
+
29
+ // 测试不同 chunk 大小
30
+ for (const chunkSize of [10, 50]) {
31
+ console.log(`\n📦 Chunk size: ${chunkSize} chars\n`)
32
+ const result = runBenchmark({
33
+ chunkSize,
34
+ iterations: 20,
35
+ markdownLength: doc.length
36
+ })
37
+ allResults.push({
38
+ docSize: doc.name,
39
+ docLength: doc.length,
40
+ chunkSize,
41
+ timeSaved: result.timeSaved,
42
+ charsSaved: result.charsSaved,
43
+ speedup: result.speedup
44
+ })
45
+ }
46
+ }
47
+
48
+ // 汇总报告
49
+ console.log('\n')
50
+ console.log('='.repeat(80))
51
+ console.log('📈 Complete Benchmark Summary')
52
+ console.log('='.repeat(80))
53
+ console.log('')
54
+ console.log('| Document Size | Chunk | Time Saved | Chars Saved | Speedup |')
55
+ console.log('|------------------|-------|------------|-------------|---------|')
56
+ for (const r of allResults) {
57
+ console.log(`| ${r.docSize.padEnd(16)} | ${r.chunkSize.toString().padEnd(5)} | ${r.timeSaved.toFixed(1).padStart(9)}% | ${r.charsSaved.toFixed(1).padStart(10)}% | ${r.speedup.toFixed(2).padStart(6)}x |`)
58
+ }
59
+ console.log('')
60
+
61
+ // 计算平均值
62
+ const avgTimeSaved = allResults.reduce((sum, r) => sum + r.timeSaved, 0) / allResults.length
63
+ const avgCharsSaved = allResults.reduce((sum, r) => sum + r.charsSaved, 0) / allResults.length
64
+ const avgSpeedup = allResults.reduce((sum, r) => sum + r.speedup, 0) / allResults.length
65
+
66
+ // 按文档大小分组计算
67
+ const byDocSize = new Map<string, typeof allResults>()
68
+ for (const r of allResults) {
69
+ if (!byDocSize.has(r.docSize)) {
70
+ byDocSize.set(r.docSize, [])
71
+ }
72
+ byDocSize.get(r.docSize)!.push(r)
73
+ }
74
+
75
+ console.log('-'.repeat(80))
76
+ console.log('')
77
+ console.log('📊 Average by Document Size:')
78
+ console.log('')
79
+ for (const [size, results] of byDocSize) {
80
+ const avgSpeed = results.reduce((sum, r) => sum + r.speedup, 0) / results.length
81
+ const avgTime = results.reduce((sum, r) => sum + r.timeSaved, 0) / results.length
82
+ console.log(` ${size}: ${avgSpeed.toFixed(2)}x faster, ${avgTime.toFixed(1)}% time saved`)
83
+ }
84
+ console.log('')
85
+
86
+ console.log('-'.repeat(80))
87
+ console.log('')
88
+ console.log(`🎯 Overall Average:`)
89
+ console.log(` Time Saved: ${avgTimeSaved.toFixed(1)}%`)
90
+ console.log(` Chars Saved: ${avgCharsSaved.toFixed(1)}%`)
91
+ console.log(` Speedup: ${avgSpeedup.toFixed(2)}x`)
92
+ console.log('')
93
+ console.log('='.repeat(80))
@@ -466,6 +466,17 @@ export class IncremarkParser {
466
466
  // 触发状态变化回调
467
467
  this.emitChange([])
468
468
  }
469
+
470
+ /**
471
+ * 一次性渲染完整 Markdown(reset + append + finalize)
472
+ * @param content 完整的 Markdown 内容
473
+ * @returns 解析结果
474
+ */
475
+ render(content: string): IncrementalUpdate {
476
+ this.reset()
477
+ this.append(content)
478
+ return this.finalize()
479
+ }
469
480
  }
470
481
 
471
482
  /**