stream-markdown-parser 0.0.40 → 0.0.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -13
- package/README.zh-CN.md +17 -13
- package/dist/index.js +61 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
[](README.zh-CN.md)
|
|
5
5
|
[](https://www.npmjs.com/package/stream-markdown-parser)
|
|
6
6
|
[](https://bundlephobia.com/package/stream-markdown-parser)
|
|
7
|
-
[](
|
|
7
|
+
[](https://github.com/Simon-He95/markstream-vue/blob/main/license)
|
|
8
8
|
|
|
9
9
|
Pure markdown parser and renderer utilities with streaming support - framework agnostic.
|
|
10
10
|
|
|
11
|
-
This package contains the core markdown parsing logic extracted from `
|
|
11
|
+
This package contains the core markdown parsing logic extracted from `markstream-vue`, making it usable in any JavaScript/TypeScript project without Vue dependencies.
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
@@ -42,7 +42,7 @@ yarn add stream-markdown-parser
|
|
|
42
42
|
|
|
43
43
|
## Quick API (TL;DR)
|
|
44
44
|
|
|
45
|
-
- `getMarkdown(options)` — create a `markdown-it-ts` instance with built-in plugins (task lists, sub/sup, math helpers, etc.). Also accepts `plugin`, `apply`, and `i18n` options.
|
|
45
|
+
- `getMarkdown(msgId?, options?)` — create a `markdown-it-ts` instance with built-in plugins (task lists, sub/sup, math helpers, etc.). Also accepts `plugin`, `apply`, and `i18n` options.
|
|
46
46
|
- `registerMarkdownPlugin(plugin)` / `clearRegisteredMarkdownPlugins()` — add or remove global plugins that run for every `getMarkdown()` call (useful for feature flags or tests).
|
|
47
47
|
- `parseMarkdownToStructure(markdown, md, parseOptions)` — convert Markdown into the streaming-friendly AST consumed by `markstream-vue` and other renderers.
|
|
48
48
|
- `processTokens(tokens)` / `parseInlineTokens(children, content)` — low-level helpers if you want to bypass the built-in AST pipeline.
|
|
@@ -59,7 +59,7 @@ parseMarkdownToStructure() → AST (ParsedNode[])
|
|
|
59
59
|
↓ feed into your renderer (markstream-vue, custom UI, workers)
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
Reuse the same `md` instance when parsing multiple documents—plugin setup is the heaviest step. When integrating with [`markstream-vue`](
|
|
62
|
+
Reuse the same `md` instance when parsing multiple documents—plugin setup is the heaviest step. When integrating with [`markstream-vue`](https://www.npmjs.com/package/markstream-vue), pass the AST to `<MarkdownRender :nodes="nodes" />` or supply raw `content` and hand it the same parser options.
|
|
63
63
|
|
|
64
64
|
### Incremental / streaming example
|
|
65
65
|
|
|
@@ -78,7 +78,7 @@ async function handleChunk(chunk: string) {
|
|
|
78
78
|
}
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
In the UI layer, render `nodes` with `<MarkdownRender :nodes="nodes" />` to avoid re-parsing. See the [docs usage guide](
|
|
81
|
+
In the UI layer, render `nodes` with `<MarkdownRender :nodes="nodes" />` to avoid re-parsing. See the [docs usage guide](https://markstream-vue-docs.simonhe.me/guide/usage) for end-to-end wiring.
|
|
82
82
|
|
|
83
83
|
### Basic example
|
|
84
84
|
|
|
@@ -301,7 +301,7 @@ Find the matching closing delimiter in a string, handling nested pairs.
|
|
|
301
301
|
- **Reuse parser instances**: cache `getMarkdown()` results per worker/request to avoid re-registering plugins.
|
|
302
302
|
- **Server-side parsing**: run `parseMarkdownToStructure` on the server, ship the AST to the client, and hydrate with `markstream-vue` for deterministic output.
|
|
303
303
|
- **Custom HTML widgets**: pre-extract `<MyWidget>` blocks before parsing (replace with placeholders) and reinject them during rendering instead of mutating `html_block` nodes post-parse.
|
|
304
|
-
- **Styling**: when piping nodes into `markstream-vue`, follow the docs [CSS checklist](
|
|
304
|
+
- **Styling**: when piping nodes into `markstream-vue`, follow the docs [CSS checklist](https://markstream-vue-docs.simonhe.me/guide/troubleshooting#css-looks-wrong-start-here) so Tailwind/UnoCSS don’t override library styles.
|
|
305
305
|
- **Error handling**: the `apply` hook swallows exceptions to maintain backwards compatibility. If you want strict mode, wrap your mutators before passing them in and rethrow/log as needed.
|
|
306
306
|
|
|
307
307
|
#### `parseFenceToken(token)`
|
|
@@ -424,7 +424,7 @@ This package comes with the following markdown-it plugins pre-configured:
|
|
|
424
424
|
While this package is framework-agnostic, it's designed to work seamlessly with:
|
|
425
425
|
|
|
426
426
|
- ✅ **Node.js** - Server-side rendering
|
|
427
|
-
- ✅ **Vue 3** - Use with `
|
|
427
|
+
- ✅ **Vue 3** - Use with `markstream-vue` (or your own renderer)
|
|
428
428
|
- ✅ **React** - Use parsed nodes for custom rendering
|
|
429
429
|
- ✅ **Vanilla JS** - Direct HTML rendering
|
|
430
430
|
- ✅ **Any framework** - Parse to AST and render as needed
|
|
@@ -464,26 +464,30 @@ worker.addEventListener('message', (event) => {
|
|
|
464
464
|
|
|
465
465
|
This pattern keeps Markdown-it work off the main thread and lets you reuse the same AST in any framework.
|
|
466
466
|
|
|
467
|
-
## Migration from `
|
|
467
|
+
## Migration from `markstream-vue` (parser exports)
|
|
468
468
|
|
|
469
|
-
If you're
|
|
469
|
+
If you're currently importing parser helpers from `markstream-vue`, you can switch to the dedicated package:
|
|
470
470
|
|
|
471
471
|
```typescript
|
|
472
|
+
// before
|
|
473
|
+
import { getMarkdown } from 'markstream-vue'
|
|
474
|
+
|
|
475
|
+
// after
|
|
472
476
|
import { getMarkdown } from 'stream-markdown-parser'
|
|
473
477
|
```
|
|
474
478
|
|
|
475
|
-
All APIs remain the same. See [migration guide](
|
|
479
|
+
All APIs remain the same. See the [migration guide](https://markstream-vue-docs.simonhe.me/monorepo-migration) for details.
|
|
476
480
|
|
|
477
481
|
## Performance
|
|
478
482
|
|
|
479
483
|
- **Lightweight**: ~65KB minified (13KB gzipped)
|
|
480
484
|
- **Fast**: Optimized for real-time parsing
|
|
481
485
|
- **Tree-shakeable**: Only import what you need
|
|
482
|
-
- **
|
|
486
|
+
- **Few dependencies**: `markdown-it-ts` + a small set of markdown-it plugins
|
|
483
487
|
|
|
484
488
|
## Contributing
|
|
485
489
|
|
|
486
|
-
Issues and PRs welcome! Please read the [contribution guidelines](
|
|
490
|
+
Issues and PRs welcome! Please read the [contribution guidelines](https://github.com/Simon-He95/markstream-vue/blob/main/CONTRIBUTING.md).
|
|
487
491
|
|
|
488
492
|
## License
|
|
489
493
|
|
|
@@ -491,4 +495,4 @@ MIT © Simon He
|
|
|
491
495
|
|
|
492
496
|
## Related
|
|
493
497
|
|
|
494
|
-
- [
|
|
498
|
+
- [markstream-vue](https://www.npmjs.com/package/markstream-vue) - Full-featured Vue 3 Markdown renderer
|
package/README.zh-CN.md
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
[](README.md)
|
|
5
5
|
[](https://www.npmjs.com/package/stream-markdown-parser)
|
|
6
6
|
[](https://bundlephobia.com/package/stream-markdown-parser)
|
|
7
|
-
[](
|
|
7
|
+
[](https://github.com/Simon-He95/markstream-vue/blob/main/license)
|
|
8
8
|
|
|
9
9
|
纯 JavaScript Markdown 解析器和渲染工具,支持流式处理 - 框架无关。
|
|
10
10
|
|
|
11
|
-
该包包含从 `
|
|
11
|
+
该包包含从 `markstream-vue` 中提取的核心 Markdown 解析逻辑,使其可以在任何 JavaScript/TypeScript 项目中使用,无需 Vue 依赖。
|
|
12
12
|
|
|
13
13
|
## 特性
|
|
14
14
|
|
|
@@ -42,7 +42,7 @@ yarn add stream-markdown-parser
|
|
|
42
42
|
|
|
43
43
|
## 快速 API 速览
|
|
44
44
|
|
|
45
|
-
- `getMarkdown(options)` — 返回一个预配置的 `markdown-it-ts` 实例;支持 `plugin`、`apply`、`i18n` 等选项(内置任务列表、上下标、数学等插件)。
|
|
45
|
+
- `getMarkdown(msgId?, options?)` — 返回一个预配置的 `markdown-it-ts` 实例;支持 `plugin`、`apply`、`i18n` 等选项(内置任务列表、上下标、数学等插件)。
|
|
46
46
|
- `registerMarkdownPlugin(plugin)` / `clearRegisteredMarkdownPlugins()` — 全局注册/清除插件,在所有 `getMarkdown()` 调用中生效(适合特性开关或测试环境)。
|
|
47
47
|
- `parseMarkdownToStructure(markdown, md, parseOptions)` — 将 Markdown 转换为可供 `markstream-vue` 等渲染器使用的 AST。
|
|
48
48
|
- `processTokens(tokens)` / `parseInlineTokens(children, content)` — 更底层的 token → 节点工具,方便自定义管线。
|
|
@@ -59,7 +59,7 @@ parseMarkdownToStructure() → AST (ParsedNode[])
|
|
|
59
59
|
↓ 交给你的渲染器(markstream-vue、自定义 UI、Worker 等)
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
多次解析时复用同一个 `md` 实例可以避免重复注册插件。与 [`markstream-vue`](
|
|
62
|
+
多次解析时复用同一个 `md` 实例可以避免重复注册插件。与 [`markstream-vue`](https://www.npmjs.com/package/markstream-vue) 一起使用时,你可以把 AST 传给 `<MarkdownRender :nodes="nodes" />`,或仅传入原始 `content` 并共享同一套解析配置。
|
|
63
63
|
|
|
64
64
|
### 增量 / 流式示例
|
|
65
65
|
|
|
@@ -78,7 +78,7 @@ async function handleChunk(chunk: string) {
|
|
|
78
78
|
}
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
在前端通过 `<MarkdownRender :nodes="nodes" />` 渲染即可避免重复解析。具体串联示例见[文档用法指南](
|
|
81
|
+
在前端通过 `<MarkdownRender :nodes="nodes" />` 渲染即可避免重复解析。具体串联示例见[文档用法指南](https://markstream-vue-docs.simonhe.me/zh/guide/usage)。
|
|
82
82
|
|
|
83
83
|
### 基础示例
|
|
84
84
|
|
|
@@ -300,7 +300,7 @@ const parseOptions = {
|
|
|
300
300
|
- **复用解析实例**:缓存 `getMarkdown()` 的结果,避免重复注册插件。
|
|
301
301
|
- **服务端解析**:在服务端运行 `parseMarkdownToStructure` 后把 AST 下发给客户端,配合 `markstream-vue` 实现确定性输出。
|
|
302
302
|
- **自定义 HTML 组件**:在解析前先把 `<MyWidget>` 这类片段替换为占位符,渲染时再注入,避免在 `html_block` 上进行脆弱的字符串操作。
|
|
303
|
-
- **样式提示**:如果将节点交给 `markstream-vue`,务必按照文档的 [CSS 排查清单](
|
|
303
|
+
- **样式提示**:如果将节点交给 `markstream-vue`,务必按照文档的 [CSS 排查清单](https://markstream-vue-docs.simonhe.me/zh/guide/troubleshooting#css-looks-wrong-start-here) 调整 reset / layer,防止 Tailwind/UnoCSS 覆盖样式。
|
|
304
304
|
- **错误处理**:`apply` 钩子内部默认捕获异常后打印日志,如需在 CI/生产中抛出错误,可在传入前自行封装并 rethrow。
|
|
305
305
|
|
|
306
306
|
#### `parseFenceToken(token)`
|
|
@@ -423,31 +423,35 @@ import type {
|
|
|
423
423
|
虽然该包与框架无关,但它被设计为可以无缝配合以下框架使用:
|
|
424
424
|
|
|
425
425
|
- ✅ **Node.js** - 服务器端渲染
|
|
426
|
-
- ✅ **Vue 3** - 配合 `
|
|
426
|
+
- ✅ **Vue 3** - 配合 `markstream-vue`(或你的自定义渲染层)使用
|
|
427
427
|
- ✅ **React** - 使用解析的节点进行自定义渲染
|
|
428
428
|
- ✅ **Vanilla JS** - 直接 HTML 渲染
|
|
429
429
|
- ✅ **任何框架** - 解析为 AST 并按需渲染
|
|
430
430
|
|
|
431
|
-
## 从 `
|
|
431
|
+
## 从 `markstream-vue` 迁移(解析器导出)
|
|
432
432
|
|
|
433
|
-
|
|
433
|
+
如果你当前是从 `markstream-vue` 引入解析器相关 helper,可以切换为使用独立包:
|
|
434
434
|
|
|
435
435
|
```typescript
|
|
436
|
+
// 之前
|
|
437
|
+
import { getMarkdown } from 'markstream-vue'
|
|
438
|
+
|
|
439
|
+
// 现在
|
|
436
440
|
import { getMarkdown } from 'stream-markdown-parser'
|
|
437
441
|
```
|
|
438
442
|
|
|
439
|
-
所有 API 保持不变。详见[迁移指南](
|
|
443
|
+
所有 API 保持不变。详见[迁移指南](https://markstream-vue-docs.simonhe.me/zh/monorepo-migration)。
|
|
440
444
|
|
|
441
445
|
## 性能
|
|
442
446
|
|
|
443
447
|
- **轻量级**: ~65KB 压缩后(13KB gzipped)
|
|
444
448
|
- **快速**: 针对实时解析优化
|
|
445
449
|
- **Tree-shakeable**: 只导入你需要的部分
|
|
446
|
-
-
|
|
450
|
+
- **依赖很少**: `markdown-it-ts` + 少量 markdown-it 插件
|
|
447
451
|
|
|
448
452
|
## 贡献
|
|
449
453
|
|
|
450
|
-
欢迎提交 Issues 和 PRs!请阅读[贡献指南](
|
|
454
|
+
欢迎提交 Issues 和 PRs!请阅读[贡献指南](https://github.com/Simon-He95/markstream-vue/blob/main/CONTRIBUTING.md)。
|
|
451
455
|
|
|
452
456
|
## 许可证
|
|
453
457
|
|
|
@@ -455,4 +459,4 @@ MIT © Simon He
|
|
|
455
459
|
|
|
456
460
|
## 相关项目
|
|
457
461
|
|
|
458
|
-
- [
|
|
462
|
+
- [markstream-vue](https://www.npmjs.com/package/markstream-vue) - 功能完整的 Vue 3 Markdown 渲染器
|
package/dist/index.js
CHANGED
|
@@ -6888,7 +6888,24 @@ const BASE_COMMON_HTML_TAGS = new Set([
|
|
|
6888
6888
|
]);
|
|
6889
6889
|
const OPEN_TAG_RE = /<([A-Z][\w-]*)(?=[\s/>]|$)/gi;
|
|
6890
6890
|
const CLOSE_TAG_RE = /<\/\s*([A-Z][\w-]*)(?=[\s/>]|$)/gi;
|
|
6891
|
-
const
|
|
6891
|
+
const TAG_NAME_AT_START_RE = /^<\s*(?:\/\s*)?([A-Z][\w-]*)/i;
|
|
6892
|
+
function findTagCloseIndexOutsideQuotes(html) {
|
|
6893
|
+
let inSingle = false;
|
|
6894
|
+
let inDouble = false;
|
|
6895
|
+
for (let i = 0; i < html.length; i++) {
|
|
6896
|
+
const ch = html[i];
|
|
6897
|
+
if (ch === "\"" && !inSingle) {
|
|
6898
|
+
inDouble = !inDouble;
|
|
6899
|
+
continue;
|
|
6900
|
+
}
|
|
6901
|
+
if (ch === "'" && !inDouble) {
|
|
6902
|
+
inSingle = !inSingle;
|
|
6903
|
+
continue;
|
|
6904
|
+
}
|
|
6905
|
+
if (ch === ">" && !inSingle && !inDouble) return i;
|
|
6906
|
+
}
|
|
6907
|
+
return -1;
|
|
6908
|
+
}
|
|
6892
6909
|
function tokenToRaw$1(token) {
|
|
6893
6910
|
const shape = token;
|
|
6894
6911
|
return String(shape.raw ?? shape.content ?? shape.markup ?? "");
|
|
@@ -6916,7 +6933,7 @@ function findFirstIncompleteTag(content, tagSet) {
|
|
|
6916
6933
|
if (idx < 0) continue;
|
|
6917
6934
|
const tag = (m[1] ?? "").toLowerCase();
|
|
6918
6935
|
if (!tagSet.has(tag)) continue;
|
|
6919
|
-
if (content.slice(idx)
|
|
6936
|
+
if (findTagCloseIndexOutsideQuotes(content.slice(idx)) !== -1) continue;
|
|
6920
6937
|
if (!first || idx < first.index) first = {
|
|
6921
6938
|
index: idx,
|
|
6922
6939
|
tag,
|
|
@@ -6928,7 +6945,7 @@ function findFirstIncompleteTag(content, tagSet) {
|
|
|
6928
6945
|
if (idx < 0) continue;
|
|
6929
6946
|
const tag = (m[1] ?? "").toLowerCase();
|
|
6930
6947
|
if (!isCommonHtmlTagOrPrefix(tag, tagSet)) continue;
|
|
6931
|
-
if (content.slice(idx)
|
|
6948
|
+
if (findTagCloseIndexOutsideQuotes(content.slice(idx)) !== -1) continue;
|
|
6932
6949
|
if (!first || idx < first.index) first = {
|
|
6933
6950
|
index: idx,
|
|
6934
6951
|
tag,
|
|
@@ -6987,14 +7004,21 @@ function fixStreamingHtmlInlineChildren(children, tagSet) {
|
|
|
6987
7004
|
break;
|
|
6988
7005
|
}
|
|
6989
7006
|
pushTextPart(chunk.slice(cursor, lt), baseToken);
|
|
6990
|
-
const
|
|
6991
|
-
|
|
7007
|
+
const sub = chunk.slice(lt);
|
|
7008
|
+
const tagMatch = sub.match(TAG_NAME_AT_START_RE);
|
|
7009
|
+
if (!tagMatch) {
|
|
7010
|
+
pushTextPart("<", baseToken);
|
|
7011
|
+
cursor = lt + 1;
|
|
7012
|
+
continue;
|
|
7013
|
+
}
|
|
7014
|
+
const closeIdx = findTagCloseIndexOutsideQuotes(sub);
|
|
7015
|
+
if (closeIdx === -1) {
|
|
6992
7016
|
pushTextPart("<", baseToken);
|
|
6993
7017
|
cursor = lt + 1;
|
|
6994
7018
|
continue;
|
|
6995
7019
|
}
|
|
6996
|
-
const tagText =
|
|
6997
|
-
const tagName = (
|
|
7020
|
+
const tagText = sub.slice(0, closeIdx + 1);
|
|
7021
|
+
const tagName = (tagMatch[1] ?? "").toLowerCase();
|
|
6998
7022
|
if (tagSet.has(tagName)) out.push({
|
|
6999
7023
|
type: "html_inline",
|
|
7000
7024
|
tag: "",
|
|
@@ -7025,7 +7049,7 @@ function fixStreamingHtmlInlineChildren(children, tagSet) {
|
|
|
7025
7049
|
if (pending) {
|
|
7026
7050
|
pending.buffer += tokenToRaw$1(child);
|
|
7027
7051
|
pendingAtEnd = pending.buffer;
|
|
7028
|
-
const closeIdx = pending.buffer
|
|
7052
|
+
const closeIdx = findTagCloseIndexOutsideQuotes(pending.buffer);
|
|
7029
7053
|
if (closeIdx === -1) continue;
|
|
7030
7054
|
const tagChunk = pending.buffer.slice(0, closeIdx + 1);
|
|
7031
7055
|
const afterChunk = pending.buffer.slice(closeIdx + 1);
|
|
@@ -7040,6 +7064,19 @@ function fixStreamingHtmlInlineChildren(children, tagSet) {
|
|
|
7040
7064
|
if (afterChunk) processTextChunk(afterChunk);
|
|
7041
7065
|
continue;
|
|
7042
7066
|
}
|
|
7067
|
+
if (child.type === "html_inline") {
|
|
7068
|
+
const content = tokenToRaw$1(child);
|
|
7069
|
+
const tagName = (content.match(TAG_NAME_AT_START_RE)?.[1] ?? "").toLowerCase();
|
|
7070
|
+
if (tagName && tagSet.has(tagName) && findTagCloseIndexOutsideQuotes(content) === -1) {
|
|
7071
|
+
pending = {
|
|
7072
|
+
tag: tagName,
|
|
7073
|
+
buffer: content,
|
|
7074
|
+
closing: /^<\s*\//.test(content)
|
|
7075
|
+
};
|
|
7076
|
+
pendingAtEnd = pending.buffer;
|
|
7077
|
+
continue;
|
|
7078
|
+
}
|
|
7079
|
+
}
|
|
7043
7080
|
if (child.type === "text") {
|
|
7044
7081
|
const content = String(child.content ?? "");
|
|
7045
7082
|
if (!content.includes("<")) {
|
|
@@ -7165,16 +7202,26 @@ function applyFixHtmlInlineTokens(md, options = {}) {
|
|
|
7165
7202
|
attrs.push([attrName, attrValue]);
|
|
7166
7203
|
}
|
|
7167
7204
|
if (customTagSet.has(tag)) {
|
|
7168
|
-
const contentMatch = t.content?.match(new RegExp(`<\\s*${tag}[^>]*>([\\s\\S]
|
|
7205
|
+
const contentMatch = t.content?.match(new RegExp(`<\\s*${tag}[^>]*>([\\s\\S]*)`, "i"));
|
|
7169
7206
|
const raw$1 = t.content;
|
|
7207
|
+
const endTagRegex = new RegExp(`</\\s*${tag}\\s*>`, "i");
|
|
7208
|
+
const endTagIndex = t.content?.toLowerCase().indexOf(`</${tag}>`) ?? -1;
|
|
7170
7209
|
t.children = [{
|
|
7171
7210
|
type: tag,
|
|
7172
|
-
content: contentMatch ? contentMatch[1] : "",
|
|
7211
|
+
content: endTagIndex !== -1 ? contentMatch[1].split(endTagRegex)[0] ? contentMatch ? contentMatch[1] : "" : "" : contentMatch ? contentMatch[1].replace(/<.*$/, "") : "",
|
|
7173
7212
|
raw: raw$1,
|
|
7174
7213
|
attrs,
|
|
7175
7214
|
tag,
|
|
7176
7215
|
loading
|
|
7177
7216
|
}];
|
|
7217
|
+
if (endTagIndex !== -1) {
|
|
7218
|
+
const afterContent = t.content?.slice(endTagIndex + tag.length + 3) || "";
|
|
7219
|
+
if (afterContent.trim()) toks.splice(i + 1, 0, {
|
|
7220
|
+
type: "text",
|
|
7221
|
+
content: afterContent,
|
|
7222
|
+
raw: afterContent
|
|
7223
|
+
});
|
|
7224
|
+
}
|
|
7178
7225
|
} else t.children = [{
|
|
7179
7226
|
type: "html_block",
|
|
7180
7227
|
content: t.content,
|
|
@@ -9591,6 +9638,10 @@ function parseInlineTokens(tokens, raw, pPreToken, options) {
|
|
|
9591
9638
|
case "reference":
|
|
9592
9639
|
handleReference(token);
|
|
9593
9640
|
break;
|
|
9641
|
+
case "text_special":
|
|
9642
|
+
pushText(String(token.content ?? ""), String(token.content ?? ""));
|
|
9643
|
+
i++;
|
|
9644
|
+
break;
|
|
9594
9645
|
default:
|
|
9595
9646
|
pushToken(token);
|
|
9596
9647
|
i++;
|