# k-markdown-parser
**Repository Path**: kuankuan2007/k-markdown-parser
## Basic Information
- **Project Name**: k-markdown-parser
- **Description**: a Markdown parser
- **Primary Language**: TypeScript
- **License**: MulanPSL-2.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-07-09
- **Last Updated**: 2026-05-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# k-markdown-parser
[](https://www.npmjs.com/package/@kuankuan/k-markdown-parser)
[](./LICENSE)
[](https://github.com/kuankuan2007/k-markdown-parser)
一个由 [宽宽](https://www.kuankuan.site/ "宽宽的小天地") 独立开发与维护的轻量、快速的 Markdown 解析器。它将 Markdown 文本转换为结构化的语法树(AST),无需任何运行时环境或外部依赖,可在任意 JavaScript 平台上运行,底层逻辑大量使用正则表达式驱动。
## 特性
- **零依赖** — 纯 JavaScript/TypeScript,可在任意环境中运行
- **极致轻量** — IIFE 打包版本仅约 28 KB
- **性能可观** — 速度与 Showdown 相近
- **高度可扩展** — 语法规则、节点类型、解析流水线均可自由定制
- **正则驱动** — 核心解析逻辑基于正则表达式,灵活且高效
- **多模块格式** — 同时提供 ESM、CJS 与 IIFE 格式
## 安装
```bash
npm install @kuankuan/k-markdown-parser
```
或通过 CDN 直接引入(IIFE 格式,全局变量为 `KMarkdownParser`):
```html
```
## 快速上手
```js
import KMarkdownParser from '@kuankuan/k-markdown-parser';
const parser = new KMarkdownParser(/* 配置项 */);
const root = parser.parse('# Hello World\n\nThis is **bold** text.');
console.log(root); // KMarkdownRootNode
console.log(root.content); // 子节点或字符串组成的数组
```
`root` 是一个 `KMarkdownRootNode`,即语法树的根节点。树中的每个节点都是 `KMarkdownNode` 的实例。
## 语法树
### `KMarkdownNode`
所有解析节点均继承自 `KMarkdownNode`,包含以下属性:
| 属性 | 类型 | 说明 |
| --- | --- | --- |
| `id` | `string` | 节点类型标识符(如 `"bold"`、`"title"`) |
| `content` | `(string \| KMarkdownNode)[]` | 节点的子内容 |
| `args` | `T` | 节点特有参数(如标题节点的 `{ level: 2 }`) |
| `createBy` | `string \| null` | 产生该节点的语法组名称 |
### 内置节点类型
| 节点 ID | 类名 | `args` |
| --- | --- | --- |
| `root` | `KMarkdownRootNode` | `{ createOption: FullOption }` |
| `title` | `KMarkdownTitleNode` | `{ level: number, id?: string }` |
| `paragraph` | `KMarkdownParagraphNode` | — |
| `quote-block` | `KMarkdownQuoteBlockNode` | — |
| `code-block` | `KMarkdownCodeBlockNode` | `{ language?: string }` |
| `code-inline` | `KMarkdownCodeInlineNode` | — |
| `latex-block` | `KMarkdownLatexBlockNode` | — |
| `latex-inline` | `KMarkdownLatexInlineNode` | — |
| `line-between` | `KMarkdownLineBetweenNode` | — |
| `bold` | `KMarkdownBoldNode` | — |
| `italic` | `KMarkdownItalicNode` | — |
| `delete-line` | `KMarkdownDeleteLineNode` | — |
| `subscript` | `KMarkdownSubscriptNode` | — |
| `superscript` | `KMarkdownSuperscriptNode` | — |
| `image` | `KMarkdownImageNode` | `{ src: string, alt: string, title?: string }` |
| `link` | `KMarkdownLinkNode` | `{ href: string, alt: string }` |
| `email` | `KMarkdownEmailNode` | `{ email: string }` |
| `emoji` | `KMarkdownEmojiNode` | `{ name: string }` |
| `unordered-list` | `KMarkdownUnorderedListNode` | — |
| `unordered-list-item` | `KMarkdownUnorderedListItemNode` | — |
| `ordered-list` | `KMarkdownOrderedListNode` | — |
| `ordered-list-item` | `KMarkdownOrderedListItemNode` | `{ indexInWriting: number }` |
| `task-list` | `KMarkdownTaskListNode` | — |
| `task-list-item` | `KMarkdownTaskListItemNode` | `{ finished: boolean }` |
| `table` | `KMarkdownTableNode` | — |
| `table-row` | `KMarkdownTableRowNode` | `{ isHeader: boolean }` |
| `table-cell` | `KMarkdownTableCellNode` | `{ align?: 'left' \| 'center' \| 'right' }` |
| `xml` | `KMarkdownXMLNode` | `{ name: string, attributes: Record }` |
## `KMarkdownParser` API
### `new KMarkdownParser(options?)`
创建一个新的解析器实例。配置项说明见 [配置项](#配置项) 章节。
### `parser.parse(text: string): KMarkdownRootNode`
解析 Markdown 字符串,返回语法树的根节点。
### `parser.markdown2Inner(text: string): string`
将原始 Markdown 文本转换为解析器内部的转义表示。通常用于底层定制开发。
### `parser.inner2Markdown(text: string): string`
将解析器内部表示还原为 Markdown 文本(带转义字符)。当需要从节点中取回原始 Markdown 内容时使用。
### `parser.inner2Plant(text: string): string`
将解析器内部表示转换为纯文本(去除转义字符)。当需要从节点内容中提取可读纯文本时使用。
## 配置项
```ts
type Option = Readonly<{
syntaxes?: SyntaxesGroup[];
replacerTagStart?: string;
replacerTagMap?: {
[key: symbol]: string;
'\\': string;
[key: string]: string;
};
nodeMap?: {
[key: string]: typeof KMarkdownNode;
};
autoParseLink?: boolean;
}>;
```
所有配置项均为可选。默认值可从 `@kuankuan/k-markdown-parser/options` 导入:
```js
import {
defaultOptions,
defaultSyntaxes,
defaultNodeMap,
defaultReplacerTagMap,
} from '@kuankuan/k-markdown-parser/options';
```
---
### `syntaxes`
以**命名语法组**数组(`SyntaxesGroup[]`)的形式定义解析流水线。
```ts
type SyntaxesGroup = {
name: string;
syntaxes: KMarkdownSyntax[];
next?: string | null | (string | null)[];
};
```
- `name` — 该语法组的唯一标识符
- `syntaxes` — 该轮解析使用的语法规则列表
- `next` — 该语法组产生的节点在递归解析子内容时所使用的语法组:
- `undefined`(默认)— 从当前组起的所有后续组
- `string` — 从指定名称的组起往后
- `null` — 不再递归解析子内容
- `(string | null)[]` — 显式指定列表;`null` 表示"上一个指定组之后的所有剩余组"
默认解析流水线如下:
| 语法组 | 包含语法 | 子内容解析起始组 |
| --- | --- | --- |
| `block` | 代码块、LaTeX 块 | (不解析) |
| `post-block` | `#` 标题、`---` 标题、分割线 | `inline` |
| `quote` | 引用块 | (默认) |
| `pre-paragraph` | 分段处理 | (默认) |
| `paragraph` | 表格、任务列表、无序列表、有序列表、段落 | (默认) |
| `post-paragraph` | 段落 | (默认) |
| `inline` | 行内代码、行内 LaTeX、粗体/斜体、删除线、上下标、图片、链接、邮箱、Emoji、XML | (默认) |
---
### `replacerTagStart` 与 `replacerTagMap`
在正式解析前,解析器会将特殊字符与转义序列替换为内部占位符,以防不同语法规则之间相互干扰。
- `replacerTagStart` — 所有占位符的前缀字符(默认:`'¨'`)
- `replacerTagMap` — 将每个特殊字符映射到一个简短的唯一标签字符串
例如,使用默认配置时,Markdown 转义 `\\` 会变为内部标记 `¨SL`,字符 `(` 会变为 `¨LP`。
---
### `nodeMap`
将节点 ID 字符串映射到对应的 `KMarkdownNode` 子类。通过覆盖其中的条目,可以将内置节点替换为自定义实现:
```js
import { defaultNodeMap } from '@kuankuan/k-markdown-parser/options';
import { KMarkdownNode } from '@kuankuan/k-markdown-parser';
class MyBoldNode extends KMarkdownNode {
id = 'bold';
toHTML() {
return `...`;
}
}
const parser = new KMarkdownParser({
nodeMap: { ...defaultNodeMap, bold: MyBoldNode },
});
```
---
### `autoParseLink`
为 `true`(默认)时,文本中裸露的 URL 会自动解析为 `link` 节点。
---
## 自定义语法
实现 `KMarkdownSyntax` 接口即可定义自己的语法规则:
```ts
interface KMarkdownSyntax {
name: string;
matcher: (
text: string,
option: FullOption,
parentNode: KMarkdownNode
) => KMarkdownSyntaxMatchResult[];
}
type KMarkdownSyntaxMatchResult = {
startIndex: number; // 匹配起始位置
length: number; // 匹配的总长度
node?: { // 省略则保留匹配文本原样
name: string;
content: (string | KMarkdownNodeCreateOptions)[];
option?: Record;
canParseSubContent?: boolean;
};
};
```
示例——添加自定义的 `==highlight==` 语法:
```js
import KMarkdownParser from '@kuankuan/k-markdown-parser';
import { defaultSyntaxes, defaultNodeMap } from '@kuankuan/k-markdown-parser/options';
import { KMarkdownNode } from '@kuankuan/k-markdown-parser';
class KMarkdownHighlightNode extends KMarkdownNode {
id = 'highlight';
}
const highlightSyntax = {
name: 'highlight',
matcher(text) {
return [...text.matchAll(/==(.+?)==/gs)].map((m) => ({
startIndex: m.index,
length: m[0].length,
node: { name: 'highlight', content: [m[1]] },
}));
},
};
const parser = new KMarkdownParser({
syntaxes: defaultSyntaxes.map((group) =>
group.name === 'inline'
? { ...group, syntaxes: [...group.syntaxes, highlightSyntax] }
: group
),
nodeMap: { ...defaultNodeMap, highlight: KMarkdownHighlightNode },
});
```
## 许可证
本项目采用 [木兰宽松许可证 第2版(MulanPSL-2.0)](./LICENSE) 授权。