@incremark/core 0.2.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +132 -23
- package/dist/MarkedAstBuildter-BsjxZko_.d.ts +72 -0
- package/dist/detector/index.d.ts +1 -1
- package/dist/engines/marked/index.d.ts +29 -0
- package/dist/engines/marked/index.js +1541 -0
- package/dist/engines/marked/index.js.map +1 -0
- package/dist/engines/micromark/index.d.ts +106 -0
- package/dist/engines/micromark/index.js +1161 -0
- package/dist/engines/micromark/index.js.map +1 -0
- package/dist/{index-CfgnWMWh.d.ts → index-mZ7yCqNH.d.ts} +1 -1
- package/dist/index.d.ts +59 -16
- package/dist/index.js +1170 -639
- package/dist/index.js.map +1 -1
- package/dist/types-C_EW5vfp.d.ts +123 -0
- package/package.json +17 -3
package/dist/index.js
CHANGED
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { gfmFromMarkdown } from 'mdast-util-gfm';
|
|
3
|
-
import { gfm } from 'micromark-extension-gfm';
|
|
4
|
-
import { gfmFootnoteFromMarkdown } from 'mdast-util-gfm-footnote';
|
|
5
|
-
import { math } from 'micromark-extension-math';
|
|
6
|
-
import { mathFromMarkdown } from 'mdast-util-math';
|
|
7
|
-
import { directive } from 'micromark-extension-directive';
|
|
8
|
-
import { directiveFromMarkdown } from 'mdast-util-directive';
|
|
9
|
-
import { codes, constants, types } from 'micromark-util-symbol';
|
|
10
|
-
import { markdownLineEndingOrSpace } from 'micromark-util-character';
|
|
11
|
-
import { factoryDestination } from 'micromark-factory-destination';
|
|
12
|
-
import { factoryTitle } from 'micromark-factory-title';
|
|
13
|
-
import { factoryLabel } from 'micromark-factory-label';
|
|
14
|
-
import { factoryWhitespace } from 'micromark-factory-whitespace';
|
|
15
|
-
import { gfmFootnote } from 'micromark-extension-gfm-footnote';
|
|
16
|
-
import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
|
|
1
|
+
import { Lexer, lexer } from 'marked';
|
|
17
2
|
|
|
18
3
|
// src/detector/index.ts
|
|
19
4
|
var RE_FENCE_START = /^(\s*)((`{3,})|(~{3,}))/;
|
|
@@ -458,6 +443,8 @@ var EmptyLineBoundaryChecker = class {
|
|
|
458
443
|
var BoundaryDetector = class {
|
|
459
444
|
containerConfig;
|
|
460
445
|
checkers;
|
|
446
|
+
/** 缓存每一行结束时对应的 Context,避免重复计算 */
|
|
447
|
+
contextCache = /* @__PURE__ */ new Map();
|
|
461
448
|
constructor(config = {}) {
|
|
462
449
|
this.containerConfig = config.containers;
|
|
463
450
|
this.checkers = [
|
|
@@ -468,6 +455,17 @@ var BoundaryDetector = class {
|
|
|
468
455
|
new EmptyLineBoundaryChecker()
|
|
469
456
|
];
|
|
470
457
|
}
|
|
458
|
+
/**
|
|
459
|
+
* 清空上下文缓存
|
|
460
|
+
* 当 pendingStartLine 推进后调用,释放不再需要的缓存
|
|
461
|
+
*/
|
|
462
|
+
clearContextCache(beforeLine) {
|
|
463
|
+
for (const key of this.contextCache.keys()) {
|
|
464
|
+
if (key < beforeLine) {
|
|
465
|
+
this.contextCache.delete(key);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
471
469
|
/**
|
|
472
470
|
* 查找稳定边界
|
|
473
471
|
* 返回稳定边界行号和该行对应的上下文(用于后续更新,避免重复计算)
|
|
@@ -480,13 +478,14 @@ var BoundaryDetector = class {
|
|
|
480
478
|
findStableBoundary(lines, startLine, context) {
|
|
481
479
|
let stableLine = -1;
|
|
482
480
|
let stableContext = context;
|
|
483
|
-
let tempContext = { ...context };
|
|
481
|
+
let tempContext = startLine > 0 && this.contextCache.has(startLine - 1) ? { ...this.contextCache.get(startLine - 1) } : { ...context };
|
|
484
482
|
for (let i = startLine; i < lines.length; i++) {
|
|
485
483
|
const line = lines[i];
|
|
486
484
|
const wasInFencedCode = tempContext.inFencedCode;
|
|
487
485
|
const wasInContainer = tempContext.inContainer;
|
|
488
486
|
const wasContainerDepth = tempContext.containerDepth;
|
|
489
487
|
tempContext = updateContext(line, tempContext, this.containerConfig);
|
|
488
|
+
this.contextCache.set(i, { ...tempContext });
|
|
490
489
|
if (wasInFencedCode && !tempContext.inFencedCode) {
|
|
491
490
|
if (i < lines.length - 1) {
|
|
492
491
|
stableLine = i;
|
|
@@ -638,6 +637,9 @@ var DefinitionManager = class {
|
|
|
638
637
|
// src/parser/manager/FootnoteManager.ts
|
|
639
638
|
var FootnoteManager = class {
|
|
640
639
|
definitions = {};
|
|
640
|
+
/** 已完成部分的脚注引用顺序(缓存) */
|
|
641
|
+
completedReferenceOrder = [];
|
|
642
|
+
/** 所有脚注引用顺序(包括 pending 部分) */
|
|
641
643
|
referenceOrder = [];
|
|
642
644
|
/**
|
|
643
645
|
* 从已完成的 blocks 中提取 footnote definitions
|
|
@@ -666,9 +668,52 @@ var FootnoteManager = class {
|
|
|
666
668
|
return acc;
|
|
667
669
|
}, {});
|
|
668
670
|
}
|
|
671
|
+
/**
|
|
672
|
+
* 从已完成的 blocks 中收集脚注引用(增量更新)
|
|
673
|
+
* 只收集新完成的 blocks 中的引用,并缓存结果
|
|
674
|
+
*
|
|
675
|
+
* @param blocks 新完成的 blocks
|
|
676
|
+
*/
|
|
677
|
+
collectReferencesFromCompletedBlocks(blocks) {
|
|
678
|
+
const newReferences = /* @__PURE__ */ new Set();
|
|
679
|
+
blocks.forEach((block) => {
|
|
680
|
+
traverseAst(block.node, (n) => {
|
|
681
|
+
if (n.type === "footnoteReference") {
|
|
682
|
+
const identifier = n.identifier;
|
|
683
|
+
if (!this.completedReferenceOrder.includes(identifier)) {
|
|
684
|
+
newReferences.add(identifier);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
this.completedReferenceOrder.push(...Array.from(newReferences));
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* 收集 pending blocks 中的脚注引用
|
|
693
|
+
* 返回完整的引用顺序(已完成 + pending)
|
|
694
|
+
*
|
|
695
|
+
* @param pendingBlocks pending blocks
|
|
696
|
+
* @returns 完整的脚注引用顺序
|
|
697
|
+
*/
|
|
698
|
+
collectReferencesFromPending(pendingBlocks) {
|
|
699
|
+
const pendingReferences = /* @__PURE__ */ new Set();
|
|
700
|
+
pendingBlocks.forEach((block) => {
|
|
701
|
+
traverseAst(block.node, (n) => {
|
|
702
|
+
if (n.type === "footnoteReference") {
|
|
703
|
+
const identifier = n.identifier;
|
|
704
|
+
if (!this.completedReferenceOrder.includes(identifier)) {
|
|
705
|
+
pendingReferences.add(identifier);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
});
|
|
710
|
+
this.referenceOrder = [...this.completedReferenceOrder, ...Array.from(pendingReferences)];
|
|
711
|
+
return this.referenceOrder;
|
|
712
|
+
}
|
|
669
713
|
/**
|
|
670
714
|
* 收集 AST 中的脚注引用(按出现顺序)
|
|
671
715
|
*
|
|
716
|
+
* @deprecated 使用 collectReferencesFromCompletedBlocks 和 collectReferencesFromPending 代替
|
|
672
717
|
* @param nodes AST 节点列表
|
|
673
718
|
*/
|
|
674
719
|
collectReferences(nodes) {
|
|
@@ -704,6 +749,7 @@ var FootnoteManager = class {
|
|
|
704
749
|
*/
|
|
705
750
|
clear() {
|
|
706
751
|
this.definitions = {};
|
|
752
|
+
this.completedReferenceOrder = [];
|
|
707
753
|
this.referenceOrder = [];
|
|
708
754
|
}
|
|
709
755
|
};
|
|
@@ -985,11 +1031,104 @@ function isHtmlNode(node) {
|
|
|
985
1031
|
function hasChildren(node) {
|
|
986
1032
|
return "children" in node && Array.isArray(node.children);
|
|
987
1033
|
}
|
|
988
|
-
function
|
|
1034
|
+
function mergeFragmentedHtmlNodes(nodes) {
|
|
989
1035
|
const result = [];
|
|
990
1036
|
let i = 0;
|
|
991
1037
|
while (i < nodes.length) {
|
|
992
1038
|
const node = nodes[i];
|
|
1039
|
+
if (!isHtmlNode(node)) {
|
|
1040
|
+
result.push(node);
|
|
1041
|
+
i++;
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
const unclosedTags = findUnclosedTags(node.value);
|
|
1045
|
+
if (unclosedTags.length === 0) {
|
|
1046
|
+
result.push(node);
|
|
1047
|
+
i++;
|
|
1048
|
+
continue;
|
|
1049
|
+
}
|
|
1050
|
+
const mergedParts = [node.value];
|
|
1051
|
+
let j = i + 1;
|
|
1052
|
+
let currentUnclosed = [...unclosedTags];
|
|
1053
|
+
while (j < nodes.length && currentUnclosed.length > 0) {
|
|
1054
|
+
const nextNode = nodes[j];
|
|
1055
|
+
if (isHtmlNode(nextNode)) {
|
|
1056
|
+
const closingInfo = checkClosingTags(nextNode.value, currentUnclosed);
|
|
1057
|
+
if (closingInfo.hasRelevantClosing) {
|
|
1058
|
+
mergedParts.push(nextNode.value);
|
|
1059
|
+
currentUnclosed = closingInfo.remainingUnclosed;
|
|
1060
|
+
if (currentUnclosed.length === 0) {
|
|
1061
|
+
j++;
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
} else {
|
|
1065
|
+
mergedParts.push(nextNode.value);
|
|
1066
|
+
}
|
|
1067
|
+
} else {
|
|
1068
|
+
break;
|
|
1069
|
+
}
|
|
1070
|
+
j++;
|
|
1071
|
+
}
|
|
1072
|
+
if (mergedParts.length > 1) {
|
|
1073
|
+
const mergedValue = mergedParts.join("\n");
|
|
1074
|
+
const mergedNode = {
|
|
1075
|
+
type: "html",
|
|
1076
|
+
value: mergedValue
|
|
1077
|
+
};
|
|
1078
|
+
result.push(mergedNode);
|
|
1079
|
+
i = j;
|
|
1080
|
+
} else {
|
|
1081
|
+
result.push(node);
|
|
1082
|
+
i++;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
return result;
|
|
1086
|
+
}
|
|
1087
|
+
function findUnclosedTags(html) {
|
|
1088
|
+
const tagStack = [];
|
|
1089
|
+
const tagRegex = /<\/?([a-zA-Z][a-zA-Z0-9-]*)[^>]*\/?>/g;
|
|
1090
|
+
let match;
|
|
1091
|
+
while ((match = tagRegex.exec(html)) !== null) {
|
|
1092
|
+
const fullTag = match[0];
|
|
1093
|
+
const tagName = match[1].toLowerCase();
|
|
1094
|
+
if (VOID_ELEMENTS.includes(tagName) || fullTag.endsWith("/>")) {
|
|
1095
|
+
continue;
|
|
1096
|
+
}
|
|
1097
|
+
if (fullTag.startsWith("</")) {
|
|
1098
|
+
const lastIndex = tagStack.lastIndexOf(tagName);
|
|
1099
|
+
if (lastIndex !== -1) {
|
|
1100
|
+
tagStack.splice(lastIndex, 1);
|
|
1101
|
+
}
|
|
1102
|
+
} else {
|
|
1103
|
+
tagStack.push(tagName);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return tagStack;
|
|
1107
|
+
}
|
|
1108
|
+
function checkClosingTags(html, unclosedTags) {
|
|
1109
|
+
const remaining = [...unclosedTags];
|
|
1110
|
+
let hasRelevant = false;
|
|
1111
|
+
const closeTagRegex = /<\/([a-zA-Z][a-zA-Z0-9-]*)\s*>/g;
|
|
1112
|
+
let match;
|
|
1113
|
+
while ((match = closeTagRegex.exec(html)) !== null) {
|
|
1114
|
+
const tagName = match[1].toLowerCase();
|
|
1115
|
+
const index = remaining.lastIndexOf(tagName);
|
|
1116
|
+
if (index !== -1) {
|
|
1117
|
+
remaining.splice(index, 1);
|
|
1118
|
+
hasRelevant = true;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return {
|
|
1122
|
+
hasRelevantClosing: hasRelevant,
|
|
1123
|
+
remainingUnclosed: remaining
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
function processHtmlNodesInArray(nodes, options) {
|
|
1127
|
+
const mergedNodes = mergeFragmentedHtmlNodes(nodes);
|
|
1128
|
+
const result = [];
|
|
1129
|
+
let i = 0;
|
|
1130
|
+
while (i < mergedNodes.length) {
|
|
1131
|
+
const node = mergedNodes[i];
|
|
993
1132
|
if (isHtmlNode(node)) {
|
|
994
1133
|
const contentType = detectHtmlContentType(node.value);
|
|
995
1134
|
if (contentType === "fragment") {
|
|
@@ -1030,8 +1169,8 @@ function processHtmlNodesInArray(nodes, options) {
|
|
|
1030
1169
|
let depth = 1;
|
|
1031
1170
|
let j = i + 1;
|
|
1032
1171
|
let foundClosing = false;
|
|
1033
|
-
while (j <
|
|
1034
|
-
const nextNode =
|
|
1172
|
+
while (j < mergedNodes.length && depth > 0) {
|
|
1173
|
+
const nextNode = mergedNodes[j];
|
|
1035
1174
|
if (isHtmlNode(nextNode)) {
|
|
1036
1175
|
const nextType = detectHtmlContentType(nextNode.value);
|
|
1037
1176
|
if (nextType === "closing") {
|
|
@@ -1094,585 +1233,1050 @@ function transformHtmlNodes(ast, options = {}) {
|
|
|
1094
1233
|
children: processHtmlNodesInArray(ast.children, options)
|
|
1095
1234
|
};
|
|
1096
1235
|
}
|
|
1097
|
-
|
|
1236
|
+
|
|
1237
|
+
// src/parser/ast/types.ts
|
|
1238
|
+
function extractMarkedExtensions(plugins) {
|
|
1239
|
+
const extensions = [];
|
|
1240
|
+
for (const plugin of plugins) {
|
|
1241
|
+
if ((plugin.type === "marked" || plugin.type === "both") && plugin.marked) {
|
|
1242
|
+
extensions.push(...plugin.marked.extensions);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
return extensions;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// src/extensions/marked-extensions/explicitDefinitionExtension.ts
|
|
1249
|
+
function createExplicitDefinitionExtension() {
|
|
1098
1250
|
return {
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1251
|
+
name: "explicitDefinition",
|
|
1252
|
+
level: "block",
|
|
1253
|
+
// 🔑 关键修复:start 必须匹配完整的 definition 模式 [id]:,
|
|
1254
|
+
// 而不能只匹配 [,否则会把 ![alt][id] 中的 [alt] 误认为是 definition 开头
|
|
1255
|
+
// 同时排除脚注定义 [^id]:
|
|
1256
|
+
start(src) {
|
|
1257
|
+
const match = src.match(/^ {0,3}\[(?!\^)[^\]]+\]:/m);
|
|
1258
|
+
return match?.index;
|
|
1259
|
+
},
|
|
1260
|
+
tokenizer(src) {
|
|
1261
|
+
const rule = /^ {0,3}\[(?!\^)[^\]]+\]:.*?(?:\n+|$)/;
|
|
1262
|
+
const match = rule.exec(src);
|
|
1263
|
+
if (match) {
|
|
1264
|
+
const raw = match[0];
|
|
1265
|
+
const contentMatch = raw.match(
|
|
1266
|
+
/^ {0,3}\[([^\]]+)\]:\s*(\S+)(?:\s+["'(](.*?)["')])?/
|
|
1267
|
+
);
|
|
1268
|
+
if (contentMatch) {
|
|
1269
|
+
const identifier = contentMatch[1].toLowerCase();
|
|
1270
|
+
const url = contentMatch[2];
|
|
1271
|
+
const title = contentMatch[3];
|
|
1272
|
+
if (this.lexer?.tokens?.links) {
|
|
1273
|
+
this.lexer.tokens.links[identifier] = { href: url, title };
|
|
1274
|
+
}
|
|
1275
|
+
return {
|
|
1276
|
+
type: "explicitDefinition",
|
|
1277
|
+
raw,
|
|
1278
|
+
identifier,
|
|
1279
|
+
url,
|
|
1280
|
+
title
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
return { type: "explicitDefinition", raw, identifier: "", url: "" };
|
|
1108
1284
|
}
|
|
1285
|
+
return void 0;
|
|
1286
|
+
},
|
|
1287
|
+
renderer() {
|
|
1288
|
+
return "";
|
|
1109
1289
|
}
|
|
1110
1290
|
};
|
|
1111
1291
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1292
|
+
|
|
1293
|
+
// src/extensions/marked-extensions/optimisticReferenceExtension.ts
|
|
1294
|
+
function createOptimisticReferenceExtension() {
|
|
1295
|
+
return {
|
|
1296
|
+
name: "optimisticReference",
|
|
1297
|
+
level: "inline",
|
|
1298
|
+
start(src) {
|
|
1299
|
+
return src.match(/!?\[/)?.index;
|
|
1300
|
+
},
|
|
1301
|
+
tokenizer(src) {
|
|
1302
|
+
const rule = /^(!?)\[((?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*)\](?:\s*\[((?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*)\])?/;
|
|
1303
|
+
const match = rule.exec(src);
|
|
1304
|
+
if (match) {
|
|
1305
|
+
const fullMatch = match[0];
|
|
1306
|
+
if (src.length > fullMatch.length && src[fullMatch.length] === "(") {
|
|
1307
|
+
return void 0;
|
|
1308
|
+
}
|
|
1309
|
+
if (src.length > fullMatch.length && src[fullMatch.length] === ":") {
|
|
1310
|
+
return void 0;
|
|
1311
|
+
}
|
|
1312
|
+
const isImage = match[1] === "!";
|
|
1313
|
+
const text = match[2];
|
|
1314
|
+
const refRaw = match[3];
|
|
1315
|
+
if (text.startsWith("^")) {
|
|
1316
|
+
return void 0;
|
|
1317
|
+
}
|
|
1318
|
+
let identifier = "";
|
|
1319
|
+
let referenceType = "shortcut";
|
|
1320
|
+
if (refRaw !== void 0) {
|
|
1321
|
+
if (refRaw === "") {
|
|
1322
|
+
referenceType = "collapsed";
|
|
1323
|
+
identifier = text;
|
|
1324
|
+
} else {
|
|
1325
|
+
referenceType = "full";
|
|
1326
|
+
identifier = refRaw;
|
|
1327
|
+
}
|
|
1328
|
+
} else {
|
|
1329
|
+
referenceType = "shortcut";
|
|
1330
|
+
identifier = text;
|
|
1331
|
+
if (text.match(/^[ xX]$/)) {
|
|
1332
|
+
return void 0;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return {
|
|
1336
|
+
type: "optimisticReference",
|
|
1337
|
+
raw: fullMatch,
|
|
1338
|
+
isImage,
|
|
1339
|
+
text,
|
|
1340
|
+
identifier: identifier.toLowerCase(),
|
|
1341
|
+
label: identifier,
|
|
1342
|
+
referenceType
|
|
1343
|
+
};
|
|
1142
1344
|
}
|
|
1143
|
-
|
|
1144
|
-
|
|
1345
|
+
return void 0;
|
|
1346
|
+
},
|
|
1347
|
+
renderer() {
|
|
1348
|
+
return "";
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
// src/extensions/marked-extensions/mathExtension.ts
|
|
1354
|
+
function createBlockMathExtension() {
|
|
1355
|
+
return {
|
|
1356
|
+
name: "blockMath",
|
|
1357
|
+
level: "block",
|
|
1358
|
+
start(src) {
|
|
1359
|
+
const match = src.match(/^ {0,3}\$\$/m);
|
|
1360
|
+
return match?.index;
|
|
1361
|
+
},
|
|
1362
|
+
tokenizer(src) {
|
|
1363
|
+
const rule = /^ {0,3}\$\$([\s\S]*?)\$\$ *(?:\n+|$)/;
|
|
1364
|
+
const match = rule.exec(src);
|
|
1365
|
+
if (match) {
|
|
1366
|
+
return {
|
|
1367
|
+
type: "blockMath",
|
|
1368
|
+
raw: match[0],
|
|
1369
|
+
text: match[1].trim()
|
|
1370
|
+
};
|
|
1145
1371
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1372
|
+
return void 0;
|
|
1373
|
+
},
|
|
1374
|
+
renderer() {
|
|
1375
|
+
return "";
|
|
1376
|
+
}
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
function createInlineMathExtension() {
|
|
1380
|
+
return {
|
|
1381
|
+
name: "inlineMath",
|
|
1382
|
+
level: "inline",
|
|
1383
|
+
start(src) {
|
|
1384
|
+
const index = src.indexOf("$");
|
|
1385
|
+
if (index === -1) return void 0;
|
|
1386
|
+
if (src[index + 1] === "$") return void 0;
|
|
1387
|
+
return index;
|
|
1388
|
+
},
|
|
1389
|
+
tokenizer(src) {
|
|
1390
|
+
const rule = /^\$(?!\$)((?:\\.|[^\\\n$])+?)\$(?!\d)/;
|
|
1391
|
+
const match = rule.exec(src);
|
|
1392
|
+
if (match) {
|
|
1393
|
+
return {
|
|
1394
|
+
type: "inlineMath",
|
|
1395
|
+
raw: match[0],
|
|
1396
|
+
text: match[1].trim()
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
return void 0;
|
|
1400
|
+
},
|
|
1401
|
+
renderer() {
|
|
1402
|
+
return "";
|
|
1403
|
+
}
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
// src/extensions/marked-extensions/footnoteDefinitionExtension.ts
|
|
1408
|
+
function createFootnoteDefinitionExtension() {
|
|
1409
|
+
return {
|
|
1410
|
+
name: "footnoteDefinitionBlock",
|
|
1411
|
+
level: "block",
|
|
1412
|
+
start(src) {
|
|
1413
|
+
const match = src.match(/^ {0,3}\[\^[^\]]+\]:/m);
|
|
1414
|
+
return match?.index;
|
|
1415
|
+
},
|
|
1416
|
+
tokenizer(src) {
|
|
1417
|
+
const firstLineRule = /^ {0,3}\[\^([a-zA-Z0-9_-]+)\]:\s*(.*)/;
|
|
1418
|
+
const firstLineMatch = firstLineRule.exec(src);
|
|
1419
|
+
if (!firstLineMatch) return void 0;
|
|
1420
|
+
const identifier = firstLineMatch[1];
|
|
1421
|
+
let content = firstLineMatch[2];
|
|
1422
|
+
let raw = firstLineMatch[0];
|
|
1423
|
+
const remaining = src.slice(raw.length);
|
|
1424
|
+
const lines = remaining.split("\n");
|
|
1425
|
+
let lineIndex = 0;
|
|
1426
|
+
if (lines[0] === "" && remaining.startsWith("\n")) {
|
|
1427
|
+
lineIndex = 1;
|
|
1428
|
+
raw += "\n";
|
|
1429
|
+
content += "\n";
|
|
1430
|
+
}
|
|
1431
|
+
while (lineIndex < lines.length) {
|
|
1432
|
+
const line = lines[lineIndex];
|
|
1433
|
+
if (line.trim() === "") {
|
|
1434
|
+
let hasIndentedLineAfter = false;
|
|
1435
|
+
for (let j = lineIndex + 1; j < lines.length; j++) {
|
|
1436
|
+
const nextLine = lines[j];
|
|
1437
|
+
if (nextLine.trim() === "") continue;
|
|
1438
|
+
if (nextLine.match(/^( |\t)/)) {
|
|
1439
|
+
hasIndentedLineAfter = true;
|
|
1440
|
+
}
|
|
1441
|
+
break;
|
|
1442
|
+
}
|
|
1443
|
+
if (hasIndentedLineAfter) {
|
|
1444
|
+
raw += line + (lineIndex < lines.length - 1 ? "\n" : "");
|
|
1445
|
+
content += "\n" + line;
|
|
1446
|
+
lineIndex++;
|
|
1447
|
+
continue;
|
|
1448
|
+
} else {
|
|
1449
|
+
break;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
if (line.match(/^( |\t)/)) {
|
|
1453
|
+
raw += line + (lineIndex < lines.length - 1 ? "\n" : "");
|
|
1454
|
+
content += "\n" + line;
|
|
1455
|
+
lineIndex++;
|
|
1456
|
+
continue;
|
|
1457
|
+
}
|
|
1458
|
+
if (line.match(/^ {0,3}\[\^[^\]]+\]:/)) {
|
|
1151
1459
|
break;
|
|
1152
1460
|
}
|
|
1461
|
+
break;
|
|
1462
|
+
}
|
|
1463
|
+
const trimmedContent = content.replace(/\n+$/, "");
|
|
1464
|
+
return {
|
|
1465
|
+
type: "footnoteDefinitionBlock",
|
|
1466
|
+
raw,
|
|
1467
|
+
identifier,
|
|
1468
|
+
content: trimmedContent
|
|
1469
|
+
};
|
|
1470
|
+
},
|
|
1471
|
+
renderer() {
|
|
1472
|
+
return "";
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/extensions/marked-extensions/inlineHtmlExtension.ts
|
|
1478
|
+
var SELF_CLOSING_TAGS = /* @__PURE__ */ new Set([
|
|
1479
|
+
"area",
|
|
1480
|
+
"base",
|
|
1481
|
+
"br",
|
|
1482
|
+
"col",
|
|
1483
|
+
"embed",
|
|
1484
|
+
"hr",
|
|
1485
|
+
"img",
|
|
1486
|
+
"input",
|
|
1487
|
+
"link",
|
|
1488
|
+
"meta",
|
|
1489
|
+
"param",
|
|
1490
|
+
"source",
|
|
1491
|
+
"track",
|
|
1492
|
+
"wbr"
|
|
1493
|
+
]);
|
|
1494
|
+
function createInlineHtmlExtension() {
|
|
1495
|
+
return {
|
|
1496
|
+
name: "inlineHtml",
|
|
1497
|
+
level: "inline",
|
|
1498
|
+
start(src) {
|
|
1499
|
+
const index = src.indexOf("<");
|
|
1500
|
+
if (index === -1) return void 0;
|
|
1501
|
+
const afterLt = src.slice(index + 1);
|
|
1502
|
+
if (!/^[a-zA-Z\/]/.test(afterLt)) return void 0;
|
|
1503
|
+
return index;
|
|
1504
|
+
},
|
|
1505
|
+
tokenizer(src) {
|
|
1506
|
+
const completeTagMatch = matchCompleteHtmlElement(src);
|
|
1507
|
+
if (completeTagMatch) {
|
|
1508
|
+
return {
|
|
1509
|
+
type: "inlineHtml",
|
|
1510
|
+
raw: completeTagMatch,
|
|
1511
|
+
text: completeTagMatch
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
const selfClosingMatch = matchSelfClosingTag(src);
|
|
1515
|
+
if (selfClosingMatch) {
|
|
1516
|
+
return {
|
|
1517
|
+
type: "inlineHtml",
|
|
1518
|
+
raw: selfClosingMatch,
|
|
1519
|
+
text: selfClosingMatch
|
|
1520
|
+
};
|
|
1153
1521
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1522
|
+
return void 0;
|
|
1523
|
+
},
|
|
1524
|
+
renderer() {
|
|
1525
|
+
return "";
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
function matchCompleteHtmlElement(src) {
|
|
1530
|
+
const openTagMatch = /^<([a-zA-Z][a-zA-Z0-9]*)((?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*)\s*>/.exec(src);
|
|
1531
|
+
if (!openTagMatch) return null;
|
|
1532
|
+
const tagName = openTagMatch[1].toLowerCase();
|
|
1533
|
+
const openTag = openTagMatch[0];
|
|
1534
|
+
if (SELF_CLOSING_TAGS.has(tagName)) {
|
|
1535
|
+
return openTag;
|
|
1536
|
+
}
|
|
1537
|
+
const afterOpenTag = src.slice(openTag.length);
|
|
1538
|
+
let depth = 1;
|
|
1539
|
+
let pos = 0;
|
|
1540
|
+
const openPattern = new RegExp(`<${tagName}(?:\\s[^>]*)?>`, "gi");
|
|
1541
|
+
const closePattern = new RegExp(`</${tagName}>`, "gi");
|
|
1542
|
+
while (depth > 0 && pos < afterOpenTag.length) {
|
|
1543
|
+
openPattern.lastIndex = pos;
|
|
1544
|
+
closePattern.lastIndex = pos;
|
|
1545
|
+
const nextOpen = openPattern.exec(afterOpenTag);
|
|
1546
|
+
const nextClose = closePattern.exec(afterOpenTag);
|
|
1547
|
+
if (!nextClose) {
|
|
1548
|
+
return null;
|
|
1549
|
+
}
|
|
1550
|
+
if (nextOpen && nextOpen.index < nextClose.index) {
|
|
1551
|
+
depth++;
|
|
1552
|
+
pos = nextOpen.index + nextOpen[0].length;
|
|
1553
|
+
} else {
|
|
1554
|
+
depth--;
|
|
1555
|
+
pos = nextClose.index + nextClose[0].length;
|
|
1156
1556
|
}
|
|
1157
1557
|
}
|
|
1158
|
-
if (
|
|
1159
|
-
return
|
|
1558
|
+
if (depth === 0) {
|
|
1559
|
+
return src.slice(0, openTag.length + pos);
|
|
1160
1560
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1561
|
+
return null;
|
|
1562
|
+
}
|
|
1563
|
+
function matchSelfClosingTag(src) {
|
|
1564
|
+
const explicitSelfClosing = /^<([a-zA-Z][a-zA-Z0-9]*)((?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*)\s*\/>/.exec(src);
|
|
1565
|
+
if (explicitSelfClosing) {
|
|
1566
|
+
return explicitSelfClosing[0];
|
|
1567
|
+
}
|
|
1568
|
+
const implicitSelfClosing = /^<([a-zA-Z][a-zA-Z0-9]*)((?:\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+))?)*)\s*>/.exec(src);
|
|
1569
|
+
if (implicitSelfClosing && SELF_CLOSING_TAGS.has(implicitSelfClosing[1].toLowerCase())) {
|
|
1570
|
+
return implicitSelfClosing[0];
|
|
1571
|
+
}
|
|
1572
|
+
return null;
|
|
1573
|
+
}
|
|
1574
|
+
function transformBlockMath(token) {
|
|
1575
|
+
return {
|
|
1576
|
+
type: "math",
|
|
1577
|
+
value: token.text,
|
|
1578
|
+
meta: null
|
|
1165
1579
|
};
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1580
|
+
}
|
|
1581
|
+
function transformFootnoteDefinitionBlock(token, ctx) {
|
|
1582
|
+
const children = ctx.parseFootnoteContent(token.content);
|
|
1583
|
+
return {
|
|
1584
|
+
type: "footnoteDefinition",
|
|
1585
|
+
identifier: token.identifier,
|
|
1586
|
+
label: token.identifier,
|
|
1587
|
+
children
|
|
1170
1588
|
};
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1589
|
+
}
|
|
1590
|
+
function transformExplicitDefinition(token) {
|
|
1591
|
+
if (!token.identifier || !token.url) return null;
|
|
1592
|
+
return {
|
|
1593
|
+
type: "definition",
|
|
1594
|
+
identifier: token.identifier,
|
|
1595
|
+
label: token.identifier,
|
|
1596
|
+
url: token.url,
|
|
1597
|
+
title: token.title ?? null
|
|
1175
1598
|
};
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
events[close - 2],
|
|
1186
|
-
events[close - 1],
|
|
1187
|
-
["exit", label, context]
|
|
1188
|
-
);
|
|
1189
|
-
media.push(...events.slice(close + 1));
|
|
1190
|
-
media.push(["exit", group, context]);
|
|
1191
|
-
events.splice(open, events.length - open, ...media);
|
|
1192
|
-
return events;
|
|
1193
|
-
}
|
|
1194
|
-
function tokenizeLabelEnd(effects, ok, nok) {
|
|
1195
|
-
const self = this;
|
|
1196
|
-
let index = self.events.length;
|
|
1197
|
-
let labelStart;
|
|
1198
|
-
while (index--) {
|
|
1199
|
-
if ((self.events[index][1].type === types.labelImage || self.events[index][1].type === types.labelLink) && !self.events[index][1]._balanced) {
|
|
1200
|
-
labelStart = self.events[index][1];
|
|
1201
|
-
break;
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
return start;
|
|
1205
|
-
function start(code) {
|
|
1206
|
-
if (!labelStart) {
|
|
1207
|
-
return nok(code);
|
|
1208
|
-
}
|
|
1209
|
-
if (labelStart._inactive) {
|
|
1210
|
-
return labelEndNok(code);
|
|
1211
|
-
}
|
|
1212
|
-
if (labelStart.type === types.labelLink) {
|
|
1213
|
-
const labelText = self.sliceSerialize({ start: labelStart.end, end: self.now() });
|
|
1214
|
-
if (labelText.startsWith("^")) {
|
|
1215
|
-
return nok(code);
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
effects.enter(types.labelEnd);
|
|
1219
|
-
effects.enter(types.labelMarker);
|
|
1220
|
-
effects.consume(code);
|
|
1221
|
-
effects.exit(types.labelMarker);
|
|
1222
|
-
effects.exit(types.labelEnd);
|
|
1223
|
-
return after;
|
|
1224
|
-
}
|
|
1225
|
-
function after(code) {
|
|
1226
|
-
if (code === codes.leftParenthesis) {
|
|
1227
|
-
return effects.attempt(
|
|
1228
|
-
{
|
|
1229
|
-
tokenize: tokenizeResource,
|
|
1230
|
-
partial: false
|
|
1231
|
-
},
|
|
1232
|
-
labelEndOk,
|
|
1233
|
-
labelEndNok
|
|
1234
|
-
// 修复:resource 解析失败时返回 nok
|
|
1235
|
-
)(code);
|
|
1236
|
-
}
|
|
1237
|
-
if (code === codes.leftSquareBracket) {
|
|
1238
|
-
return effects.attempt(
|
|
1599
|
+
}
|
|
1600
|
+
function transformDef(token) {
|
|
1601
|
+
if (token.tag.startsWith("^")) {
|
|
1602
|
+
const footnoteId = token.tag.slice(1);
|
|
1603
|
+
return {
|
|
1604
|
+
type: "footnoteDefinition",
|
|
1605
|
+
identifier: footnoteId,
|
|
1606
|
+
label: footnoteId,
|
|
1607
|
+
children: [
|
|
1239
1608
|
{
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
// 修改:即使不是 full reference,也尝试 collapsed
|
|
1246
|
-
)(code);
|
|
1247
|
-
}
|
|
1248
|
-
return labelEndOk(code);
|
|
1609
|
+
type: "paragraph",
|
|
1610
|
+
children: [{ type: "text", value: token.href }]
|
|
1611
|
+
}
|
|
1612
|
+
]
|
|
1613
|
+
};
|
|
1249
1614
|
}
|
|
1250
|
-
function referenceNotFull(code) {
|
|
1251
|
-
return effects.attempt(
|
|
1252
|
-
{
|
|
1253
|
-
tokenize: tokenizeReferenceCollapsed,
|
|
1254
|
-
partial: false
|
|
1255
|
-
},
|
|
1256
|
-
labelEndOk,
|
|
1257
|
-
labelEndOk
|
|
1258
|
-
// 修改:即使失败也返回 ok
|
|
1259
|
-
)(code);
|
|
1260
|
-
}
|
|
1261
|
-
function labelEndOk(code) {
|
|
1262
|
-
return ok(code);
|
|
1263
|
-
}
|
|
1264
|
-
function labelEndNok(code) {
|
|
1265
|
-
labelStart._balanced = true;
|
|
1266
|
-
return nok(code);
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
function tokenizeResource(effects, ok, nok) {
|
|
1270
|
-
return resourceStart;
|
|
1271
|
-
function resourceStart(code) {
|
|
1272
|
-
if (code !== codes.leftParenthesis) {
|
|
1273
|
-
return nok(code);
|
|
1274
|
-
}
|
|
1275
|
-
effects.enter(types.resource);
|
|
1276
|
-
effects.enter(types.resourceMarker);
|
|
1277
|
-
effects.consume(code);
|
|
1278
|
-
effects.exit(types.resourceMarker);
|
|
1279
|
-
return resourceBefore;
|
|
1280
|
-
}
|
|
1281
|
-
function resourceBefore(code) {
|
|
1282
|
-
return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceOpen)(code) : resourceOpen(code);
|
|
1283
|
-
}
|
|
1284
|
-
function resourceOpen(code) {
|
|
1285
|
-
if (code === codes.rightParenthesis) {
|
|
1286
|
-
return resourceEnd(code);
|
|
1287
|
-
}
|
|
1288
|
-
return factoryDestination(
|
|
1289
|
-
effects,
|
|
1290
|
-
resourceDestinationAfter,
|
|
1291
|
-
resourceDestinationMissing,
|
|
1292
|
-
types.resourceDestination,
|
|
1293
|
-
types.resourceDestinationLiteral,
|
|
1294
|
-
types.resourceDestinationLiteralMarker,
|
|
1295
|
-
types.resourceDestinationRaw,
|
|
1296
|
-
types.resourceDestinationString,
|
|
1297
|
-
constants.linkResourceDestinationBalanceMax
|
|
1298
|
-
)(code);
|
|
1299
|
-
}
|
|
1300
|
-
function resourceDestinationAfter(code) {
|
|
1301
|
-
return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceBetween)(code) : resourceEnd(code);
|
|
1302
|
-
}
|
|
1303
|
-
function resourceDestinationMissing(code) {
|
|
1304
|
-
return nok(code);
|
|
1305
|
-
}
|
|
1306
|
-
function resourceBetween(code) {
|
|
1307
|
-
if (code === codes.quotationMark || code === codes.apostrophe || code === codes.leftParenthesis) {
|
|
1308
|
-
return factoryTitle(
|
|
1309
|
-
effects,
|
|
1310
|
-
resourceTitleAfter,
|
|
1311
|
-
nok,
|
|
1312
|
-
types.resourceTitle,
|
|
1313
|
-
types.resourceTitleMarker,
|
|
1314
|
-
types.resourceTitleString
|
|
1315
|
-
)(code);
|
|
1316
|
-
}
|
|
1317
|
-
return resourceEnd(code);
|
|
1318
|
-
}
|
|
1319
|
-
function resourceTitleAfter(code) {
|
|
1320
|
-
return markdownLineEndingOrSpace(code) ? factoryWhitespace(effects, resourceEnd)(code) : resourceEnd(code);
|
|
1321
|
-
}
|
|
1322
|
-
function resourceEnd(code) {
|
|
1323
|
-
if (code === codes.rightParenthesis) {
|
|
1324
|
-
effects.enter(types.resourceMarker);
|
|
1325
|
-
effects.consume(code);
|
|
1326
|
-
effects.exit(types.resourceMarker);
|
|
1327
|
-
effects.exit(types.resource);
|
|
1328
|
-
return ok;
|
|
1329
|
-
}
|
|
1330
|
-
return nok(code);
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
function tokenizeReferenceFull(effects, ok, nok) {
|
|
1334
|
-
const self = this;
|
|
1335
|
-
return referenceFull;
|
|
1336
|
-
function referenceFull(code) {
|
|
1337
|
-
if (code !== codes.leftSquareBracket) {
|
|
1338
|
-
return nok(code);
|
|
1339
|
-
}
|
|
1340
|
-
return factoryLabel.call(
|
|
1341
|
-
self,
|
|
1342
|
-
effects,
|
|
1343
|
-
referenceFullAfter,
|
|
1344
|
-
referenceFullMissing,
|
|
1345
|
-
types.reference,
|
|
1346
|
-
types.referenceMarker,
|
|
1347
|
-
types.referenceString
|
|
1348
|
-
)(code);
|
|
1349
|
-
}
|
|
1350
|
-
function referenceFullAfter(code) {
|
|
1351
|
-
return ok(code);
|
|
1352
|
-
}
|
|
1353
|
-
function referenceFullMissing(code) {
|
|
1354
|
-
return nok(code);
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
function tokenizeReferenceCollapsed(effects, ok, nok) {
|
|
1358
|
-
return referenceCollapsedStart;
|
|
1359
|
-
function referenceCollapsedStart(code) {
|
|
1360
|
-
if (code !== codes.leftSquareBracket) {
|
|
1361
|
-
return nok(code);
|
|
1362
|
-
}
|
|
1363
|
-
effects.enter(types.reference);
|
|
1364
|
-
effects.enter(types.referenceMarker);
|
|
1365
|
-
effects.consume(code);
|
|
1366
|
-
effects.exit(types.referenceMarker);
|
|
1367
|
-
return referenceCollapsedOpen;
|
|
1368
|
-
}
|
|
1369
|
-
function referenceCollapsedOpen(code) {
|
|
1370
|
-
if (code === codes.rightSquareBracket) {
|
|
1371
|
-
effects.enter(types.referenceMarker);
|
|
1372
|
-
effects.consume(code);
|
|
1373
|
-
effects.exit(types.referenceMarker);
|
|
1374
|
-
effects.exit(types.reference);
|
|
1375
|
-
return ok;
|
|
1376
|
-
}
|
|
1377
|
-
return nok(code);
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
function gfmFootnoteIncremental() {
|
|
1381
|
-
const original = gfmFootnote();
|
|
1382
1615
|
return {
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1616
|
+
type: "definition",
|
|
1617
|
+
identifier: token.tag,
|
|
1618
|
+
label: token.tag,
|
|
1619
|
+
url: token.href,
|
|
1620
|
+
title: token.title ?? null
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
function transformContainer(token, ctx) {
|
|
1624
|
+
const attributes = {};
|
|
1625
|
+
const attrRegex = /([a-zA-Z0-9_-]+)=?("([^"]*)"|'([^']*)'|([^ ]*))?/g;
|
|
1626
|
+
let match;
|
|
1627
|
+
while ((match = attrRegex.exec(token.attrs)) !== null) {
|
|
1628
|
+
attributes[match[1]] = match[3] || match[4] || match[5] || "";
|
|
1629
|
+
}
|
|
1630
|
+
const children = ctx.transformTokensWithPosition(token.tokens);
|
|
1631
|
+
return {
|
|
1632
|
+
type: "containerDirective",
|
|
1633
|
+
name: token.name,
|
|
1634
|
+
attributes,
|
|
1635
|
+
children
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1638
|
+
function transformFootnoteDefToken(token, ctx) {
|
|
1639
|
+
return {
|
|
1640
|
+
type: "footnoteDefinition",
|
|
1641
|
+
identifier: token.identifier,
|
|
1642
|
+
label: token.identifier,
|
|
1643
|
+
children: [
|
|
1644
|
+
{
|
|
1645
|
+
type: "paragraph",
|
|
1646
|
+
children: ctx.transformInline(token.tokens)
|
|
1395
1647
|
}
|
|
1396
|
-
|
|
1648
|
+
]
|
|
1397
1649
|
};
|
|
1398
1650
|
}
|
|
1399
|
-
function
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1651
|
+
function transformHeading(token, ctx) {
|
|
1652
|
+
return {
|
|
1653
|
+
type: "heading",
|
|
1654
|
+
depth: token.depth,
|
|
1655
|
+
children: ctx.transformInline(token.tokens)
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
function transformParagraph(token, ctx) {
|
|
1659
|
+
return {
|
|
1660
|
+
type: "paragraph",
|
|
1661
|
+
children: ctx.transformInline(token.tokens)
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
function transformCode(token) {
|
|
1665
|
+
return {
|
|
1666
|
+
type: "code",
|
|
1667
|
+
lang: token.lang || null,
|
|
1668
|
+
meta: null,
|
|
1669
|
+
// 对齐 micromark 输出
|
|
1670
|
+
value: token.text
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
function transformBlockquote(token, ctx) {
|
|
1674
|
+
const children = ctx.transformTokens(token.tokens);
|
|
1675
|
+
return {
|
|
1676
|
+
type: "blockquote",
|
|
1677
|
+
children
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
function transformList(token, ctx) {
|
|
1681
|
+
const children = token.items.map((item) => ({
|
|
1682
|
+
type: "listItem",
|
|
1683
|
+
spread: item.loose,
|
|
1684
|
+
checked: item.checked ?? null,
|
|
1685
|
+
// 对齐 micromark 输出(GFM 任务列表)
|
|
1686
|
+
children: ctx.transformTokens(item.tokens)
|
|
1687
|
+
}));
|
|
1688
|
+
return {
|
|
1689
|
+
type: "list",
|
|
1690
|
+
ordered: token.ordered,
|
|
1691
|
+
start: token.ordered ? token.start || 1 : null,
|
|
1692
|
+
// 对齐 micromark:有序列表有 start,无序列表为 null
|
|
1693
|
+
spread: token.loose,
|
|
1694
|
+
children
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
function transformTable(token, ctx) {
|
|
1698
|
+
const headerCells = token.header.map((cell) => ({
|
|
1699
|
+
type: "tableCell",
|
|
1700
|
+
children: ctx.transformInline(cell.tokens)
|
|
1701
|
+
}));
|
|
1702
|
+
const bodyRows = token.rows.map((row) => ({
|
|
1703
|
+
type: "tableRow",
|
|
1704
|
+
children: row.map((cell) => ({
|
|
1705
|
+
type: "tableCell",
|
|
1706
|
+
children: ctx.transformInline(cell.tokens)
|
|
1707
|
+
}))
|
|
1708
|
+
}));
|
|
1709
|
+
return {
|
|
1710
|
+
type: "table",
|
|
1711
|
+
align: token.align,
|
|
1712
|
+
children: [{ type: "tableRow", children: headerCells }, ...bodyRows]
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
function transformHr() {
|
|
1716
|
+
return { type: "thematicBreak" };
|
|
1717
|
+
}
|
|
1718
|
+
function transformHtml(token) {
|
|
1719
|
+
return {
|
|
1720
|
+
type: "html",
|
|
1721
|
+
value: token.text
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
function transformTextBlock(token, ctx) {
|
|
1725
|
+
if (token.tokens) {
|
|
1726
|
+
return {
|
|
1727
|
+
type: "paragraph",
|
|
1728
|
+
children: ctx.transformInline(token.tokens)
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
return {
|
|
1732
|
+
type: "paragraph",
|
|
1733
|
+
children: [{ type: "text", value: token.text }]
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
function transformInlineMath(token) {
|
|
1737
|
+
return {
|
|
1738
|
+
type: "inlineMath",
|
|
1739
|
+
value: token.text
|
|
1740
|
+
};
|
|
1741
|
+
}
|
|
1742
|
+
function transformOptimisticReference(token, ctx) {
|
|
1743
|
+
if (token.isImage) {
|
|
1744
|
+
return {
|
|
1745
|
+
type: "imageReference",
|
|
1746
|
+
identifier: token.identifier,
|
|
1747
|
+
label: token.label,
|
|
1748
|
+
referenceType: token.referenceType,
|
|
1749
|
+
alt: token.text
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
const labelChildren = ctx.transformInline(new Lexer().inlineTokens(token.text));
|
|
1753
|
+
return {
|
|
1754
|
+
type: "linkReference",
|
|
1755
|
+
identifier: token.identifier,
|
|
1756
|
+
label: token.label,
|
|
1757
|
+
referenceType: token.referenceType,
|
|
1758
|
+
children: labelChildren.length ? labelChildren : [{ type: "text", value: token.text }]
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
function transformLink(token, ctx) {
|
|
1762
|
+
if (token.text.startsWith("^") && token.text.length > 1) {
|
|
1763
|
+
const footnoteId = token.text.slice(1);
|
|
1764
|
+
return {
|
|
1765
|
+
type: "footnoteReference",
|
|
1766
|
+
identifier: footnoteId,
|
|
1767
|
+
label: footnoteId
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
return {
|
|
1771
|
+
type: "link",
|
|
1772
|
+
url: token.href,
|
|
1773
|
+
title: token.title || null,
|
|
1774
|
+
// 对齐 micromark 输出
|
|
1775
|
+
children: ctx.transformInline(token.tokens)
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
function transformImage(token) {
|
|
1779
|
+
return {
|
|
1780
|
+
type: "image",
|
|
1781
|
+
url: token.href,
|
|
1782
|
+
title: token.title || null,
|
|
1783
|
+
// 对齐 micromark 输出
|
|
1784
|
+
alt: token.text
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
function transformText(token) {
|
|
1788
|
+
const results = [];
|
|
1789
|
+
const text = token.text;
|
|
1790
|
+
const footnoteRegex = /\[\^([a-zA-Z0-9_-]+)\]/g;
|
|
1791
|
+
let lastIndex = 0;
|
|
1792
|
+
let match;
|
|
1793
|
+
while ((match = footnoteRegex.exec(text)) !== null) {
|
|
1794
|
+
if (match.index > lastIndex) {
|
|
1795
|
+
results.push({
|
|
1796
|
+
type: "text",
|
|
1797
|
+
value: text.substring(lastIndex, match.index)
|
|
1798
|
+
});
|
|
1489
1799
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1800
|
+
results.push({
|
|
1801
|
+
type: "footnoteReference",
|
|
1802
|
+
identifier: match[1],
|
|
1803
|
+
label: match[1]
|
|
1804
|
+
});
|
|
1805
|
+
lastIndex = match.index + match[0].length;
|
|
1494
1806
|
}
|
|
1807
|
+
if (lastIndex < text.length) {
|
|
1808
|
+
results.push({
|
|
1809
|
+
type: "text",
|
|
1810
|
+
value: text.substring(lastIndex)
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
return results;
|
|
1495
1814
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
"tableCell",
|
|
1502
|
-
"delete",
|
|
1503
|
-
"emphasis",
|
|
1504
|
-
"strong",
|
|
1505
|
-
"link",
|
|
1506
|
-
"linkReference"
|
|
1507
|
-
];
|
|
1508
|
-
function isInlineContainer(node) {
|
|
1509
|
-
return INLINE_CONTAINER_TYPES.includes(node.type);
|
|
1815
|
+
function transformStrong(token, ctx) {
|
|
1816
|
+
return {
|
|
1817
|
+
type: "strong",
|
|
1818
|
+
children: ctx.transformInline(token.tokens)
|
|
1819
|
+
};
|
|
1510
1820
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1821
|
+
function transformEmphasis(token, ctx) {
|
|
1822
|
+
return {
|
|
1823
|
+
type: "emphasis",
|
|
1824
|
+
children: ctx.transformInline(token.tokens)
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
function transformCodespan(token) {
|
|
1828
|
+
return {
|
|
1829
|
+
type: "inlineCode",
|
|
1830
|
+
value: token.text
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
function transformBreak() {
|
|
1834
|
+
return { type: "break" };
|
|
1835
|
+
}
|
|
1836
|
+
function transformDelete(token, ctx) {
|
|
1837
|
+
return {
|
|
1838
|
+
type: "delete",
|
|
1839
|
+
children: ctx.transformInline(token.tokens)
|
|
1840
|
+
};
|
|
1841
|
+
}
|
|
1842
|
+
function transformInlineHtml(token) {
|
|
1843
|
+
const parsed = parseHtmlFragment(token.text);
|
|
1844
|
+
if (parsed.length > 0) {
|
|
1845
|
+
return parsed;
|
|
1519
1846
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1847
|
+
return { type: "text", value: token.text };
|
|
1848
|
+
}
|
|
1849
|
+
function isTokenType(token, type) {
|
|
1850
|
+
return token.type === type;
|
|
1851
|
+
}
|
|
1852
|
+
var builtinBlockTransformers = {
|
|
1853
|
+
blockMath: (token) => {
|
|
1854
|
+
if (isTokenType(token, "blockMath")) return transformBlockMath(token);
|
|
1855
|
+
return null;
|
|
1856
|
+
},
|
|
1857
|
+
footnoteDefinitionBlock: (token, ctx) => {
|
|
1858
|
+
if (isTokenType(token, "footnoteDefinitionBlock"))
|
|
1859
|
+
return transformFootnoteDefinitionBlock(token, ctx);
|
|
1860
|
+
return null;
|
|
1861
|
+
},
|
|
1862
|
+
explicitDefinition: (token) => {
|
|
1863
|
+
if (isTokenType(token, "explicitDefinition"))
|
|
1864
|
+
return transformExplicitDefinition(token);
|
|
1865
|
+
return null;
|
|
1866
|
+
},
|
|
1867
|
+
def: (token) => {
|
|
1868
|
+
if (isTokenType(token, "def")) return transformDef(token);
|
|
1869
|
+
return null;
|
|
1870
|
+
},
|
|
1871
|
+
container: (token, ctx) => {
|
|
1872
|
+
if (isTokenType(token, "container")) return transformContainer(token, ctx);
|
|
1873
|
+
return null;
|
|
1874
|
+
},
|
|
1875
|
+
footnoteDefinition: (token, ctx) => {
|
|
1876
|
+
if (isTokenType(token, "footnoteDefinition"))
|
|
1877
|
+
return transformFootnoteDefToken(token, ctx);
|
|
1878
|
+
return null;
|
|
1879
|
+
},
|
|
1880
|
+
heading: (token, ctx) => {
|
|
1881
|
+
if (isTokenType(token, "heading")) return transformHeading(token, ctx);
|
|
1882
|
+
return null;
|
|
1883
|
+
},
|
|
1884
|
+
paragraph: (token, ctx) => {
|
|
1885
|
+
if (isTokenType(token, "paragraph")) return transformParagraph(token, ctx);
|
|
1886
|
+
return null;
|
|
1887
|
+
},
|
|
1888
|
+
code: (token) => {
|
|
1889
|
+
if (isTokenType(token, "code")) return transformCode(token);
|
|
1890
|
+
return null;
|
|
1891
|
+
},
|
|
1892
|
+
blockquote: (token, ctx) => {
|
|
1893
|
+
if (isTokenType(token, "blockquote")) return transformBlockquote(token, ctx);
|
|
1894
|
+
return null;
|
|
1895
|
+
},
|
|
1896
|
+
list: (token, ctx) => {
|
|
1897
|
+
if (isTokenType(token, "list")) return transformList(token, ctx);
|
|
1898
|
+
return null;
|
|
1899
|
+
},
|
|
1900
|
+
table: (token, ctx) => {
|
|
1901
|
+
if (isTokenType(token, "table")) return transformTable(token, ctx);
|
|
1902
|
+
return null;
|
|
1903
|
+
},
|
|
1904
|
+
hr: () => transformHr(),
|
|
1905
|
+
html: (token) => {
|
|
1906
|
+
if (isTokenType(token, "html")) return transformHtml(token);
|
|
1907
|
+
return null;
|
|
1908
|
+
},
|
|
1909
|
+
space: () => null,
|
|
1910
|
+
text: (token, ctx) => {
|
|
1911
|
+
if (isTokenType(token, "text")) return transformTextBlock(token, ctx);
|
|
1912
|
+
return null;
|
|
1527
1913
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1914
|
+
};
|
|
1915
|
+
var builtinInlineTransformers = {
|
|
1916
|
+
inlineMath: (token) => {
|
|
1917
|
+
if (isTokenType(token, "inlineMath")) return transformInlineMath(token);
|
|
1918
|
+
return null;
|
|
1919
|
+
},
|
|
1920
|
+
optimisticReference: (token, ctx) => {
|
|
1921
|
+
if (isTokenType(token, "optimisticReference"))
|
|
1922
|
+
return transformOptimisticReference(token, ctx);
|
|
1923
|
+
return null;
|
|
1924
|
+
},
|
|
1925
|
+
link: (token, ctx) => {
|
|
1926
|
+
if (isTokenType(token, "link")) return transformLink(token, ctx);
|
|
1927
|
+
return null;
|
|
1928
|
+
},
|
|
1929
|
+
image: (token) => {
|
|
1930
|
+
if (isTokenType(token, "image")) return transformImage(token);
|
|
1931
|
+
return null;
|
|
1932
|
+
},
|
|
1933
|
+
text: (token) => {
|
|
1934
|
+
if (isTokenType(token, "text")) return transformText(token);
|
|
1935
|
+
return null;
|
|
1936
|
+
},
|
|
1937
|
+
escape: (token) => {
|
|
1938
|
+
if (isTokenType(token, "escape")) return transformText(token);
|
|
1939
|
+
return null;
|
|
1940
|
+
},
|
|
1941
|
+
strong: (token, ctx) => {
|
|
1942
|
+
if (isTokenType(token, "strong")) return transformStrong(token, ctx);
|
|
1943
|
+
return null;
|
|
1944
|
+
},
|
|
1945
|
+
em: (token, ctx) => {
|
|
1946
|
+
if (isTokenType(token, "em")) return transformEmphasis(token, ctx);
|
|
1947
|
+
return null;
|
|
1948
|
+
},
|
|
1949
|
+
codespan: (token) => {
|
|
1950
|
+
if (isTokenType(token, "codespan")) return transformCodespan(token);
|
|
1951
|
+
return null;
|
|
1952
|
+
},
|
|
1953
|
+
br: () => transformBreak(),
|
|
1954
|
+
del: (token, ctx) => {
|
|
1955
|
+
if (isTokenType(token, "del")) return transformDelete(token, ctx);
|
|
1956
|
+
return null;
|
|
1957
|
+
},
|
|
1958
|
+
inlineHtml: (token) => {
|
|
1959
|
+
if (isTokenType(token, "inlineHtml")) return transformInlineHtml(token);
|
|
1960
|
+
return null;
|
|
1535
1961
|
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1962
|
+
};
|
|
1963
|
+
function transformBlockToken(token, ctx) {
|
|
1964
|
+
const tokenType = token.type;
|
|
1965
|
+
if (ctx.customBlockTransformers?.[tokenType]) {
|
|
1966
|
+
const result = ctx.customBlockTransformers[tokenType](token, ctx);
|
|
1967
|
+
if (result !== void 0) return result;
|
|
1968
|
+
}
|
|
1969
|
+
if (builtinBlockTransformers[tokenType]) {
|
|
1970
|
+
const result = builtinBlockTransformers[tokenType](token, ctx);
|
|
1971
|
+
if (result !== void 0) return result;
|
|
1972
|
+
}
|
|
1973
|
+
if ("text" in token && typeof token.text === "string") {
|
|
1974
|
+
const paragraph = {
|
|
1975
|
+
type: "paragraph",
|
|
1976
|
+
children: [{ type: "text", value: token.text }]
|
|
1977
|
+
};
|
|
1978
|
+
return paragraph;
|
|
1979
|
+
}
|
|
1980
|
+
return null;
|
|
1981
|
+
}
|
|
1982
|
+
function transformInlineToken(token, ctx) {
|
|
1983
|
+
const tokenType = token.type;
|
|
1984
|
+
if (ctx.customInlineTransformers?.[tokenType]) {
|
|
1985
|
+
const result = ctx.customInlineTransformers[tokenType](token, ctx);
|
|
1986
|
+
if (result !== void 0) return result;
|
|
1987
|
+
}
|
|
1988
|
+
if (builtinInlineTransformers[tokenType]) {
|
|
1989
|
+
const result = builtinInlineTransformers[tokenType](token, ctx);
|
|
1990
|
+
if (result !== void 0) return result;
|
|
1991
|
+
}
|
|
1992
|
+
if ("text" in token && typeof token.text === "string") {
|
|
1993
|
+
const text = { type: "text", value: token.text };
|
|
1994
|
+
return text;
|
|
1995
|
+
}
|
|
1996
|
+
return null;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
// src/parser/ast/MarkedAstBuildter.ts
|
|
2000
|
+
var MarkedAstBuilder = class {
|
|
2001
|
+
constructor(options = {}) {
|
|
2002
|
+
this.options = options;
|
|
2003
|
+
this.containerConfig = typeof options.containers === "object" ? options.containers : options.containers === true ? {} : void 0;
|
|
2004
|
+
this.htmlTreeOptions = typeof options.htmlTree === "object" ? options.htmlTree : options.htmlTree === true ? {} : void 0;
|
|
2005
|
+
if (options.plugins) {
|
|
2006
|
+
this.userExtensions.push(...extractMarkedExtensions(options.plugins));
|
|
2007
|
+
}
|
|
2008
|
+
if (options.markedExtensions) {
|
|
2009
|
+
this.userExtensions.push(...options.markedExtensions);
|
|
2010
|
+
}
|
|
2011
|
+
this.transformContext = {
|
|
2012
|
+
transformTokens: this.transformTokens.bind(this),
|
|
2013
|
+
transformTokensWithPosition: this.transformTokensWithPosition.bind(this),
|
|
2014
|
+
transformInline: this.transformInline.bind(this),
|
|
2015
|
+
parseFootnoteContent: this.parseFootnoteContent.bind(this)
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
containerConfig;
|
|
2019
|
+
htmlTreeOptions;
|
|
2020
|
+
globalLinks = {};
|
|
2021
|
+
/** 用户传入的 marked 扩展 */
|
|
2022
|
+
userExtensions = [];
|
|
2023
|
+
/** 转换上下文(用于递归转换) */
|
|
2024
|
+
transformContext;
|
|
1542
2025
|
parse(text) {
|
|
1543
|
-
const
|
|
1544
|
-
const
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
2026
|
+
const normalizedText = text.replace(/[\u00A0\u200b\u202f]/g, " ");
|
|
2027
|
+
const optimisticRefExt = createOptimisticReferenceExtension();
|
|
2028
|
+
const explicitDefExt = createExplicitDefinitionExtension();
|
|
2029
|
+
const footnoteDefExt = createFootnoteDefinitionExtension();
|
|
2030
|
+
const userBlockExts = [];
|
|
2031
|
+
const userBlockStartExts = [];
|
|
2032
|
+
const userInlineExts = [];
|
|
2033
|
+
const userInlineStartExts = [];
|
|
2034
|
+
for (const ext of this.userExtensions) {
|
|
2035
|
+
if (ext.level === "block") {
|
|
2036
|
+
if (ext.tokenizer) userBlockExts.push(ext.tokenizer);
|
|
2037
|
+
if (ext.start) userBlockStartExts.push(ext.start);
|
|
2038
|
+
} else if (ext.level === "inline") {
|
|
2039
|
+
if (ext.tokenizer) userInlineExts.push(ext.tokenizer);
|
|
2040
|
+
if (ext.start) userInlineStartExts.push(ext.start);
|
|
2041
|
+
}
|
|
1548
2042
|
}
|
|
2043
|
+
const blockExts = [
|
|
2044
|
+
footnoteDefExt.tokenizer,
|
|
2045
|
+
explicitDefExt.tokenizer,
|
|
2046
|
+
...userBlockExts
|
|
2047
|
+
];
|
|
2048
|
+
const blockStartExts = [
|
|
2049
|
+
footnoteDefExt.start,
|
|
2050
|
+
explicitDefExt.start,
|
|
2051
|
+
...userBlockStartExts
|
|
2052
|
+
];
|
|
2053
|
+
const inlineExts = [optimisticRefExt.tokenizer, ...userInlineExts];
|
|
2054
|
+
const inlineStartExts = [optimisticRefExt.start, ...userInlineStartExts];
|
|
1549
2055
|
if (this.options.math) {
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
}
|
|
1557
|
-
if (this.
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
2056
|
+
const blockMathExt = createBlockMathExtension();
|
|
2057
|
+
const inlineMathExt = createInlineMathExtension();
|
|
2058
|
+
blockExts.unshift(blockMathExt.tokenizer);
|
|
2059
|
+
blockStartExts.unshift(blockMathExt.start);
|
|
2060
|
+
inlineExts.unshift(inlineMathExt.tokenizer);
|
|
2061
|
+
inlineStartExts.unshift(inlineMathExt.start);
|
|
2062
|
+
}
|
|
2063
|
+
if (this.htmlTreeOptions) {
|
|
2064
|
+
const inlineHtmlExt = createInlineHtmlExtension();
|
|
2065
|
+
inlineExts.unshift(inlineHtmlExt.tokenizer);
|
|
2066
|
+
inlineStartExts.unshift(inlineHtmlExt.start);
|
|
2067
|
+
}
|
|
2068
|
+
const lexerOptions = {
|
|
2069
|
+
gfm: true,
|
|
2070
|
+
breaks: false,
|
|
2071
|
+
// 关闭软换行转 break,与 Micromark 保持一致
|
|
2072
|
+
...this.options,
|
|
2073
|
+
extensions: {
|
|
2074
|
+
inline: inlineExts,
|
|
2075
|
+
startInline: inlineStartExts,
|
|
2076
|
+
block: blockExts,
|
|
2077
|
+
startBlock: blockStartExts
|
|
2078
|
+
}
|
|
2079
|
+
};
|
|
2080
|
+
const lexerInstance = new Lexer(lexerOptions);
|
|
2081
|
+
if (lexerInstance.tokens && lexerInstance.tokens.links) {
|
|
2082
|
+
Object.assign(lexerInstance.tokens.links, this.globalLinks);
|
|
1562
2083
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
2084
|
+
let tokens = lexerInstance.lex(normalizedText);
|
|
2085
|
+
if (lexerInstance.tokens && lexerInstance.tokens.links) {
|
|
2086
|
+
Object.assign(this.globalLinks, lexerInstance.tokens.links);
|
|
1565
2087
|
}
|
|
1566
|
-
|
|
1567
|
-
let
|
|
1568
|
-
if (this.
|
|
1569
|
-
|
|
1570
|
-
} else {
|
|
1571
|
-
ast = this.convertHtmlToText(ast);
|
|
2088
|
+
tokens = this.preprocessTokens(tokens);
|
|
2089
|
+
let children = this.transformTokensWithPosition(tokens);
|
|
2090
|
+
if (this.htmlTreeOptions) {
|
|
2091
|
+
children = this.processHtmlNodes(children);
|
|
1572
2092
|
}
|
|
1573
|
-
return
|
|
2093
|
+
return {
|
|
2094
|
+
type: "root",
|
|
2095
|
+
children
|
|
2096
|
+
};
|
|
1574
2097
|
}
|
|
1575
2098
|
/**
|
|
1576
|
-
*
|
|
2099
|
+
* 预处理 tokens
|
|
1577
2100
|
*
|
|
1578
|
-
*
|
|
1579
|
-
* @returns 转换后的 AST
|
|
2101
|
+
* 处理容器指令和遗留的脚注定义(从 paragraph 中提取)
|
|
1580
2102
|
*/
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
2103
|
+
preprocessTokens(tokens) {
|
|
2104
|
+
const result = [];
|
|
2105
|
+
let i = 0;
|
|
2106
|
+
while (i < tokens.length) {
|
|
2107
|
+
const token = tokens[i];
|
|
2108
|
+
if (token.type === "paragraph") {
|
|
2109
|
+
const text = token.text;
|
|
2110
|
+
const footnoteMatch = text.match(/^\[\^([a-zA-Z0-9_-]+)\]:\s+([\s\S]*)$/);
|
|
2111
|
+
if (footnoteMatch) {
|
|
2112
|
+
const defToken = {
|
|
2113
|
+
type: "footnoteDefinition",
|
|
2114
|
+
identifier: footnoteMatch[1],
|
|
2115
|
+
text: footnoteMatch[2],
|
|
2116
|
+
tokens: new Lexer().inlineTokens(footnoteMatch[2]),
|
|
2117
|
+
raw: token.raw
|
|
2118
|
+
};
|
|
2119
|
+
result.push(defToken);
|
|
2120
|
+
i++;
|
|
2121
|
+
continue;
|
|
2122
|
+
}
|
|
2123
|
+
const containerStartMatch = text.match(/^:::(\s*)([a-zA-Z0-9_-]+)(.*?)(\n|$)/);
|
|
2124
|
+
if (containerStartMatch) {
|
|
2125
|
+
const name = containerStartMatch[2];
|
|
2126
|
+
const attrs = containerStartMatch[3].trim();
|
|
2127
|
+
let rawAccumulator = "";
|
|
2128
|
+
let j = i;
|
|
2129
|
+
let depth = 0;
|
|
2130
|
+
let foundEnd = false;
|
|
2131
|
+
let contentRaw = "";
|
|
2132
|
+
while (j < tokens.length) {
|
|
2133
|
+
const currentToken = tokens[j];
|
|
2134
|
+
rawAccumulator += currentToken.raw;
|
|
2135
|
+
const lines = rawAccumulator.split("\n");
|
|
2136
|
+
depth = 0;
|
|
2137
|
+
let startLineIndex = -1;
|
|
2138
|
+
let endLineIndex = -1;
|
|
2139
|
+
for (let k = 0; k < lines.length; k++) {
|
|
2140
|
+
const line = lines[k];
|
|
2141
|
+
if (line.match(/^:::(\s*)([a-zA-Z0-9_-]+)/)) {
|
|
2142
|
+
if (depth === 0 && startLineIndex === -1) startLineIndex = k;
|
|
2143
|
+
depth++;
|
|
2144
|
+
} else if (line.trim() === ":::") {
|
|
2145
|
+
depth--;
|
|
2146
|
+
if (depth === 0) {
|
|
2147
|
+
endLineIndex = k;
|
|
2148
|
+
foundEnd = true;
|
|
2149
|
+
break;
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
if (foundEnd) {
|
|
2154
|
+
const contentLines = lines.slice(startLineIndex + 1, endLineIndex);
|
|
2155
|
+
contentRaw = contentLines.join("\n");
|
|
2156
|
+
const remainingLines = lines.slice(endLineIndex + 1);
|
|
2157
|
+
const remainingText = remainingLines.join("\n");
|
|
2158
|
+
const containerToken = {
|
|
2159
|
+
type: "container",
|
|
2160
|
+
name,
|
|
2161
|
+
attrs,
|
|
2162
|
+
tokens: this.preprocessTokens(lexer(contentRaw)),
|
|
2163
|
+
raw: rawAccumulator
|
|
2164
|
+
};
|
|
2165
|
+
result.push(containerToken);
|
|
2166
|
+
if (remainingText.trim()) {
|
|
2167
|
+
const remainingTokens = this.preprocessTokens(lexer(remainingText));
|
|
2168
|
+
result.push(...remainingTokens);
|
|
2169
|
+
}
|
|
2170
|
+
i = j + 1;
|
|
2171
|
+
break;
|
|
2172
|
+
}
|
|
2173
|
+
j++;
|
|
2174
|
+
}
|
|
2175
|
+
if (foundEnd) continue;
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
result.push(token);
|
|
2179
|
+
i++;
|
|
2180
|
+
}
|
|
2181
|
+
return result;
|
|
1586
2182
|
}
|
|
1587
2183
|
/**
|
|
1588
|
-
*
|
|
2184
|
+
* 转换 tokens 为 MDAST 节点(带位置信息)
|
|
1589
2185
|
*/
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
2186
|
+
transformTokensWithPosition(tokens) {
|
|
2187
|
+
if (!tokens) return [];
|
|
2188
|
+
const results = [];
|
|
2189
|
+
let currentOffset = 0;
|
|
2190
|
+
for (const token of tokens) {
|
|
2191
|
+
const rawLength = token.raw?.length ?? 0;
|
|
2192
|
+
const node = transformBlockToken(token, this.transformContext);
|
|
2193
|
+
if (node) {
|
|
2194
|
+
node.position = {
|
|
2195
|
+
start: { line: 0, column: 0, offset: currentOffset },
|
|
2196
|
+
end: { line: 0, column: 0, offset: currentOffset + rawLength }
|
|
1601
2197
|
};
|
|
2198
|
+
results.push(node);
|
|
1602
2199
|
}
|
|
1603
|
-
|
|
1604
|
-
}
|
|
2200
|
+
currentOffset += rawLength;
|
|
2201
|
+
}
|
|
2202
|
+
return results;
|
|
1605
2203
|
}
|
|
1606
2204
|
/**
|
|
1607
|
-
*
|
|
2205
|
+
* 转换 tokens 为 MDAST 节点(不带位置信息)
|
|
1608
2206
|
*/
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
2207
|
+
transformTokens(tokens) {
|
|
2208
|
+
if (!tokens) return [];
|
|
2209
|
+
return tokens.map((t) => transformBlockToken(t, this.transformContext)).filter(Boolean);
|
|
2210
|
+
}
|
|
2211
|
+
/**
|
|
2212
|
+
* 转换行内 tokens
|
|
2213
|
+
*/
|
|
2214
|
+
transformInline(tokens) {
|
|
2215
|
+
if (!tokens) return [];
|
|
2216
|
+
const results = [];
|
|
2217
|
+
for (const token of tokens) {
|
|
2218
|
+
const result = transformInlineToken(token, this.transformContext);
|
|
2219
|
+
if (result) {
|
|
2220
|
+
if (Array.isArray(result)) {
|
|
2221
|
+
results.push(...result);
|
|
2222
|
+
} else {
|
|
2223
|
+
results.push(result);
|
|
2224
|
+
}
|
|
1621
2225
|
}
|
|
1622
|
-
|
|
1623
|
-
|
|
2226
|
+
}
|
|
2227
|
+
return results;
|
|
1624
2228
|
}
|
|
1625
2229
|
/**
|
|
1626
|
-
*
|
|
2230
|
+
* 解析脚注内容为 AST 节点
|
|
1627
2231
|
*/
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
};
|
|
1638
|
-
|
|
2232
|
+
parseFootnoteContent(content) {
|
|
2233
|
+
if (!content.trim()) {
|
|
2234
|
+
return [];
|
|
2235
|
+
}
|
|
2236
|
+
const normalizedContent = content.split("\n").map((line, index) => {
|
|
2237
|
+
if (index === 0) return line;
|
|
2238
|
+
if (line.startsWith(" ")) return line.slice(4);
|
|
2239
|
+
if (line.startsWith(" ")) return line.slice(1);
|
|
2240
|
+
return line;
|
|
2241
|
+
}).join("\n");
|
|
2242
|
+
const contentLexer = new Lexer({ gfm: true, breaks: true });
|
|
2243
|
+
const tokens = contentLexer.lex(normalizedContent);
|
|
2244
|
+
return this.transformTokens(tokens);
|
|
1639
2245
|
}
|
|
1640
2246
|
/**
|
|
1641
|
-
*
|
|
2247
|
+
* 处理 HTML 节点
|
|
2248
|
+
*
|
|
2249
|
+
* 使用 html-extension 的 transformHtmlNodes 来处理:
|
|
2250
|
+
* - 合并被空行分割的 HTML 节点
|
|
2251
|
+
* - 将 HTML 解析为 HtmlElementNode 树结构
|
|
1642
2252
|
*/
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
type: "
|
|
1646
|
-
|
|
1647
|
-
position: htmlNode.position
|
|
2253
|
+
processHtmlNodes(nodes) {
|
|
2254
|
+
const tempRoot = {
|
|
2255
|
+
type: "root",
|
|
2256
|
+
children: nodes
|
|
1648
2257
|
};
|
|
2258
|
+
const transformed = transformHtmlNodes(tempRoot, this.htmlTreeOptions);
|
|
2259
|
+
return transformed.children;
|
|
1649
2260
|
}
|
|
1650
2261
|
/**
|
|
1651
2262
|
* 将 AST 节点转换为 ParsedBlock
|
|
1652
|
-
*
|
|
1653
|
-
* @param nodes AST 节点列表
|
|
1654
|
-
* @param startOffset 起始偏移量
|
|
1655
|
-
* @param rawText 原始文本
|
|
1656
|
-
* @param status 块状态
|
|
1657
|
-
* @param generateBlockId 生成块 ID 的函数
|
|
1658
|
-
* @returns ParsedBlock 列表
|
|
1659
2263
|
*/
|
|
1660
2264
|
nodesToBlocks(nodes, startOffset, rawText, status, generateBlockId) {
|
|
1661
2265
|
const blocks = [];
|
|
1662
|
-
let currentOffset = startOffset;
|
|
1663
2266
|
for (const node of nodes) {
|
|
1664
|
-
const
|
|
1665
|
-
const
|
|
1666
|
-
const nodeText = rawText.substring(
|
|
2267
|
+
const relativeStart = node.position?.start?.offset ?? 0;
|
|
2268
|
+
const relativeEnd = node.position?.end?.offset ?? rawText.length;
|
|
2269
|
+
const nodeText = rawText.substring(relativeStart, relativeEnd);
|
|
2270
|
+
const absoluteStart = startOffset + relativeStart;
|
|
2271
|
+
const absoluteEnd = startOffset + relativeEnd;
|
|
1667
2272
|
blocks.push({
|
|
1668
2273
|
id: generateBlockId(),
|
|
1669
2274
|
status,
|
|
1670
2275
|
node,
|
|
1671
|
-
startOffset:
|
|
1672
|
-
endOffset:
|
|
2276
|
+
startOffset: absoluteStart,
|
|
2277
|
+
endOffset: absoluteEnd,
|
|
1673
2278
|
rawText: nodeText
|
|
1674
2279
|
});
|
|
1675
|
-
currentOffset = nodeEnd;
|
|
1676
2280
|
}
|
|
1677
2281
|
return blocks;
|
|
1678
2282
|
}
|
|
@@ -1680,7 +2284,6 @@ var AstBuilder = class {
|
|
|
1680
2284
|
|
|
1681
2285
|
// src/parser/IncremarkParser.ts
|
|
1682
2286
|
var IncremarkParser = class {
|
|
1683
|
-
buffer = "";
|
|
1684
2287
|
lines = [];
|
|
1685
2288
|
/** 行偏移量前缀和:lineOffsets[i] = 第i行起始位置的偏移量 */
|
|
1686
2289
|
lineOffsets = [0];
|
|
@@ -1705,7 +2308,8 @@ var IncremarkParser = class {
|
|
|
1705
2308
|
...options
|
|
1706
2309
|
};
|
|
1707
2310
|
this.context = createInitialContext();
|
|
1708
|
-
|
|
2311
|
+
const BuilderClass = options.astBuilder || MarkedAstBuilder;
|
|
2312
|
+
this.astBuilder = new BuilderClass(this.options);
|
|
1709
2313
|
this.boundaryDetector = new BoundaryDetector({ containers: this.astBuilder.containerConfig });
|
|
1710
2314
|
this.definitionManager = new DefinitionManager();
|
|
1711
2315
|
this.footnoteManager = new FootnoteManager();
|
|
@@ -1720,36 +2324,30 @@ var IncremarkParser = class {
|
|
|
1720
2324
|
this.definitionManager.extractFromBlocks(blocks);
|
|
1721
2325
|
this.footnoteManager.extractDefinitionsFromBlocks(blocks);
|
|
1722
2326
|
}
|
|
1723
|
-
/**
|
|
1724
|
-
* 收集 AST 中的脚注引用(按出现顺序)
|
|
1725
|
-
* 用于确定脚注的显示顺序
|
|
1726
|
-
*/
|
|
1727
|
-
collectFootnoteReferences(nodes) {
|
|
1728
|
-
this.footnoteManager.collectReferences(nodes);
|
|
1729
|
-
}
|
|
1730
2327
|
/**
|
|
1731
2328
|
* 增量更新 lines 和 lineOffsets
|
|
1732
|
-
*
|
|
2329
|
+
* 优化策略:只 split 新增的 chunk,不拼接旧字符串,避免长行性能劣化
|
|
1733
2330
|
*/
|
|
1734
|
-
updateLines() {
|
|
2331
|
+
updateLines(chunk) {
|
|
1735
2332
|
const prevLineCount = this.lines.length;
|
|
1736
2333
|
if (prevLineCount === 0) {
|
|
1737
|
-
this.lines =
|
|
2334
|
+
this.lines = chunk.split("\n");
|
|
1738
2335
|
this.lineOffsets = [0];
|
|
1739
|
-
for (let i = 0; i < this.lines.length; i++) {
|
|
2336
|
+
for (let i = 0; i < this.lines.length - 1; i++) {
|
|
1740
2337
|
this.lineOffsets.push(this.lineOffsets[i] + this.lines[i].length + 1);
|
|
1741
2338
|
}
|
|
1742
2339
|
return;
|
|
1743
2340
|
}
|
|
1744
|
-
const
|
|
1745
|
-
const
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
this.lines
|
|
1751
|
-
const
|
|
1752
|
-
this.lineOffsets.push(
|
|
2341
|
+
const chunkLines = chunk.split("\n");
|
|
2342
|
+
const lastLineIndex = prevLineCount - 1;
|
|
2343
|
+
this.lines[lastLineIndex] += chunkLines[0];
|
|
2344
|
+
for (let i = 1; i < chunkLines.length; i++) {
|
|
2345
|
+
const prevLineIndex = this.lines.length - 1;
|
|
2346
|
+
const prevLineStart = this.lineOffsets[prevLineIndex];
|
|
2347
|
+
const prevLineLength = this.lines[prevLineIndex].length;
|
|
2348
|
+
const newOneOffset = prevLineStart + prevLineLength + 1;
|
|
2349
|
+
this.lineOffsets.push(newOneOffset);
|
|
2350
|
+
this.lines.push(chunkLines[i]);
|
|
1753
2351
|
}
|
|
1754
2352
|
}
|
|
1755
2353
|
/**
|
|
@@ -1774,8 +2372,7 @@ var IncremarkParser = class {
|
|
|
1774
2372
|
* 追加新的 chunk 并返回增量更新
|
|
1775
2373
|
*/
|
|
1776
2374
|
append(chunk) {
|
|
1777
|
-
this.
|
|
1778
|
-
this.updateLines();
|
|
2375
|
+
this.updateLines(chunk);
|
|
1779
2376
|
const { line: stableBoundary, contextAtLine } = this.findStableBoundary();
|
|
1780
2377
|
const update = {
|
|
1781
2378
|
completed: [],
|
|
@@ -1794,6 +2391,8 @@ var IncremarkParser = class {
|
|
|
1794
2391
|
this.completedBlocks.push(...newBlocks);
|
|
1795
2392
|
update.completed = newBlocks;
|
|
1796
2393
|
this.updateDefinitionsFromCompletedBlocks(newBlocks);
|
|
2394
|
+
this.footnoteManager.collectReferencesFromCompletedBlocks(newBlocks);
|
|
2395
|
+
this.boundaryDetector.clearContextCache(this.pendingStartLine);
|
|
1797
2396
|
this.context = contextAtLine;
|
|
1798
2397
|
this.pendingStartLine = stableBoundary + 1;
|
|
1799
2398
|
}
|
|
@@ -1810,10 +2409,9 @@ var IncremarkParser = class {
|
|
|
1810
2409
|
type: "root",
|
|
1811
2410
|
children: [...this.completedBlocks.map((b) => b.node), ...update.pending.map((b) => b.node)]
|
|
1812
2411
|
};
|
|
1813
|
-
this.
|
|
2412
|
+
update.footnoteReferenceOrder = this.footnoteManager.collectReferencesFromPending(update.pending);
|
|
1814
2413
|
update.definitions = this.getDefinitionMap();
|
|
1815
2414
|
update.footnoteDefinitions = this.getFootnoteDefinitionMap();
|
|
1816
|
-
update.footnoteReferenceOrder = this.getFootnoteReferenceOrder();
|
|
1817
2415
|
this.emitChange(update.pending);
|
|
1818
2416
|
return update;
|
|
1819
2417
|
}
|
|
@@ -1825,7 +2423,7 @@ var IncremarkParser = class {
|
|
|
1825
2423
|
const state = {
|
|
1826
2424
|
completedBlocks: this.completedBlocks,
|
|
1827
2425
|
pendingBlocks,
|
|
1828
|
-
markdown: this.
|
|
2426
|
+
markdown: this.lines.join("\n"),
|
|
1829
2427
|
ast: {
|
|
1830
2428
|
type: "root",
|
|
1831
2429
|
children: [
|
|
@@ -1868,6 +2466,8 @@ var IncremarkParser = class {
|
|
|
1868
2466
|
this.completedBlocks.push(...finalBlocks);
|
|
1869
2467
|
update.completed = finalBlocks;
|
|
1870
2468
|
this.updateDefinitionsFromCompletedBlocks(finalBlocks);
|
|
2469
|
+
this.footnoteManager.collectReferencesFromCompletedBlocks(finalBlocks);
|
|
2470
|
+
this.boundaryDetector.clearContextCache(this.pendingStartLine);
|
|
1871
2471
|
}
|
|
1872
2472
|
}
|
|
1873
2473
|
this.lastPendingBlocks = [];
|
|
@@ -1876,7 +2476,6 @@ var IncremarkParser = class {
|
|
|
1876
2476
|
type: "root",
|
|
1877
2477
|
children: this.completedBlocks.map((b) => b.node)
|
|
1878
2478
|
};
|
|
1879
|
-
this.collectFootnoteReferences(update.ast.children);
|
|
1880
2479
|
update.definitions = this.getDefinitionMap();
|
|
1881
2480
|
update.footnoteDefinitions = this.getFootnoteDefinitionMap();
|
|
1882
2481
|
update.footnoteReferenceOrder = this.getFootnoteReferenceOrder();
|
|
@@ -1899,7 +2498,7 @@ var IncremarkParser = class {
|
|
|
1899
2498
|
...this.completedBlocks.map((b) => b.node),
|
|
1900
2499
|
...this.lastPendingBlocks.map((b) => b.node)
|
|
1901
2500
|
];
|
|
1902
|
-
this.
|
|
2501
|
+
this.footnoteManager.collectReferencesFromPending(this.lastPendingBlocks);
|
|
1903
2502
|
return {
|
|
1904
2503
|
type: "root",
|
|
1905
2504
|
children
|
|
@@ -1915,7 +2514,7 @@ var IncremarkParser = class {
|
|
|
1915
2514
|
* 获取当前缓冲区内容
|
|
1916
2515
|
*/
|
|
1917
2516
|
getBuffer() {
|
|
1918
|
-
return this.
|
|
2517
|
+
return this.lines.join("\n");
|
|
1919
2518
|
}
|
|
1920
2519
|
/**
|
|
1921
2520
|
* 获取 Definition 映射表(用于引用式图片和链接)
|
|
@@ -1949,7 +2548,6 @@ var IncremarkParser = class {
|
|
|
1949
2548
|
* 重置解析器状态
|
|
1950
2549
|
*/
|
|
1951
2550
|
reset() {
|
|
1952
|
-
this.buffer = "";
|
|
1953
2551
|
this.lines = [];
|
|
1954
2552
|
this.lineOffsets = [0];
|
|
1955
2553
|
this.completedBlocks = [];
|
|
@@ -1993,10 +2591,9 @@ function countCharsInNode(n) {
|
|
|
1993
2591
|
}
|
|
1994
2592
|
return 1;
|
|
1995
2593
|
}
|
|
1996
|
-
function sliceAst(node, maxChars, accumulatedChunks
|
|
2594
|
+
function sliceAst(node, maxChars, accumulatedChunks) {
|
|
1997
2595
|
if (maxChars <= 0) return null;
|
|
1998
|
-
|
|
1999
|
-
let remaining = maxChars - skipChars;
|
|
2596
|
+
let remaining = maxChars;
|
|
2000
2597
|
let charIndex = 0;
|
|
2001
2598
|
const chunkRanges = [];
|
|
2002
2599
|
if (accumulatedChunks && accumulatedChunks.chunks.length > 0) {
|
|
@@ -2015,16 +2612,10 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
2015
2612
|
if (n.value && typeof n.value === "string") {
|
|
2016
2613
|
const nodeStart = charIndex;
|
|
2017
2614
|
const nodeEnd = charIndex + n.value.length;
|
|
2018
|
-
|
|
2019
|
-
charIndex = nodeEnd;
|
|
2020
|
-
return null;
|
|
2021
|
-
}
|
|
2022
|
-
const skipInNode = Math.max(0, skipChars - nodeStart);
|
|
2023
|
-
const take = Math.min(n.value.length - skipInNode, remaining);
|
|
2615
|
+
const take = Math.min(n.value.length, remaining);
|
|
2024
2616
|
remaining -= take;
|
|
2025
|
-
if (take === 0) return null;
|
|
2026
|
-
const slicedValue = n.value.slice(skipInNode, skipInNode + take);
|
|
2027
2617
|
charIndex = nodeEnd;
|
|
2618
|
+
const slicedValue = n.value.slice(0, take);
|
|
2028
2619
|
const result = {
|
|
2029
2620
|
...n,
|
|
2030
2621
|
value: slicedValue
|
|
@@ -2033,11 +2624,11 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
2033
2624
|
const nodeChunks = [];
|
|
2034
2625
|
let firstChunkLocalStart = take;
|
|
2035
2626
|
for (const range of chunkRanges) {
|
|
2036
|
-
const overlapStart = Math.max(range.start, nodeStart
|
|
2037
|
-
const overlapEnd = Math.min(range.end, nodeStart +
|
|
2627
|
+
const overlapStart = Math.max(range.start, nodeStart);
|
|
2628
|
+
const overlapEnd = Math.min(range.end, nodeStart + take);
|
|
2038
2629
|
if (overlapStart < overlapEnd) {
|
|
2039
|
-
const localStart = overlapStart -
|
|
2040
|
-
const localEnd = overlapEnd -
|
|
2630
|
+
const localStart = overlapStart - nodeStart;
|
|
2631
|
+
const localEnd = overlapEnd - nodeStart;
|
|
2041
2632
|
const chunkText = slicedValue.slice(localStart, localEnd);
|
|
2042
2633
|
if (chunkText.length > 0) {
|
|
2043
2634
|
if (nodeChunks.length === 0) {
|
|
@@ -2059,24 +2650,12 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
2059
2650
|
}
|
|
2060
2651
|
if (n.children && Array.isArray(n.children)) {
|
|
2061
2652
|
const newChildren = [];
|
|
2062
|
-
let childCharIndex = charIndex;
|
|
2063
2653
|
for (const child of n.children) {
|
|
2064
2654
|
if (remaining <= 0) break;
|
|
2065
|
-
const childChars = countCharsInNode(child);
|
|
2066
|
-
const childStart = childCharIndex;
|
|
2067
|
-
const childEnd = childCharIndex + childChars;
|
|
2068
|
-
if (childEnd <= skipChars) {
|
|
2069
|
-
childCharIndex = childEnd;
|
|
2070
|
-
continue;
|
|
2071
|
-
}
|
|
2072
|
-
const savedCharIndex = charIndex;
|
|
2073
|
-
charIndex = childStart;
|
|
2074
2655
|
const processed = process(child);
|
|
2075
|
-
charIndex = savedCharIndex;
|
|
2076
2656
|
if (processed) {
|
|
2077
2657
|
newChildren.push(processed);
|
|
2078
2658
|
}
|
|
2079
|
-
childCharIndex = childEnd;
|
|
2080
2659
|
}
|
|
2081
2660
|
if (newChildren.length === 0) {
|
|
2082
2661
|
return null;
|
|
@@ -2089,10 +2668,7 @@ function sliceAst(node, maxChars, accumulatedChunks, skipChars = 0) {
|
|
|
2089
2668
|
}
|
|
2090
2669
|
return process(node);
|
|
2091
2670
|
}
|
|
2092
|
-
function appendToAst(baseNode, sourceNode,
|
|
2093
|
-
if (endChars <= startChars) {
|
|
2094
|
-
return baseNode;
|
|
2095
|
-
}
|
|
2671
|
+
function appendToAst(baseNode, sourceNode, endChars, accumulatedChunks) {
|
|
2096
2672
|
const fullSlice = sliceAst(sourceNode, endChars, accumulatedChunks);
|
|
2097
2673
|
if (!fullSlice) {
|
|
2098
2674
|
return baseNode;
|
|
@@ -2617,7 +3193,6 @@ var BlockTransformer = class {
|
|
|
2617
3193
|
this.cachedDisplayNode = appendToAst(
|
|
2618
3194
|
this.cachedDisplayNode,
|
|
2619
3195
|
block.node,
|
|
2620
|
-
this.cachedProgress,
|
|
2621
3196
|
currentProgress,
|
|
2622
3197
|
this.getAccumulatedChunks()
|
|
2623
3198
|
);
|
|
@@ -2733,51 +3308,7 @@ function createPlugin(name, matcher, options = {}) {
|
|
|
2733
3308
|
...options
|
|
2734
3309
|
};
|
|
2735
3310
|
}
|
|
2736
|
-
/**
|
|
2737
|
-
* @file Micromark 扩展:支持增量解析的 Reference 语法
|
|
2738
|
-
*
|
|
2739
|
-
* @description
|
|
2740
|
-
* 在增量解析场景中,引用式图片/链接(如 `![Alt][id]`)可能在定义(`[id]: url`)之前出现。
|
|
2741
|
-
* 标准 micromark 会检查 parser.defined,如果 id 未定义就解析为文本。
|
|
2742
|
-
*
|
|
2743
|
-
* 本扩展通过覆盖 labelEnd 构造,移除 parser.defined 检查,
|
|
2744
|
-
* 使得 reference 语法总是被解析为 reference token,
|
|
2745
|
-
* 由渲染层根据实际的 definitionMap 决定如何渲染。
|
|
2746
|
-
*
|
|
2747
|
-
* @module micromark-reference-extension
|
|
2748
|
-
*
|
|
2749
|
-
* @features
|
|
2750
|
-
* - ✅ 支持所有 resource 语法(带 title 的图片/链接)
|
|
2751
|
-
* - ✅ 支持所有 reference 语法(full, collapsed, shortcut)
|
|
2752
|
-
* - ✅ 延迟验证:解析时不检查定义是否存在
|
|
2753
|
-
* - ✅ 使用官方 factory 函数,保证与 CommonMark 标准一致
|
|
2754
|
-
*
|
|
2755
|
-
* @dependencies
|
|
2756
|
-
* - micromark-factory-destination: 解析 URL(支持尖括号、括号平衡)
|
|
2757
|
-
* - micromark-factory-title: 解析 title(支持三种引号,支持多行)
|
|
2758
|
-
* - micromark-factory-label: 解析 label(支持转义、长度限制)
|
|
2759
|
-
* - micromark-factory-whitespace: 解析空白符(正确生成 lineEnding/linePrefix token)
|
|
2760
|
-
* - micromark-util-character: 字符判断工具
|
|
2761
|
-
* - micromark-util-symbol: 常量(codes, types, constants)
|
|
2762
|
-
* - micromark-util-types: TypeScript 类型定义
|
|
2763
|
-
*
|
|
2764
|
-
* @see {@link https://github.com/micromark/micromark} - micromark 官方文档
|
|
2765
|
-
* @see {@link https://spec.commonmark.org/0.30/#images} - CommonMark 图片规范
|
|
2766
|
-
* @see {@link https://spec.commonmark.org/0.30/#links} - CommonMark 链接规范
|
|
2767
|
-
*
|
|
2768
|
-
* @example
|
|
2769
|
-
* ```typescript
|
|
2770
|
-
* import { micromarkReferenceExtension } from './micromark-reference-extension'
|
|
2771
|
-
* import { fromMarkdown } from 'mdast-util-from-markdown'
|
|
2772
|
-
*
|
|
2773
|
-
* const extensions = [micromarkReferenceExtension()]
|
|
2774
|
-
* const ast = fromMarkdown(text, { extensions })
|
|
2775
|
-
* ```
|
|
2776
|
-
*
|
|
2777
|
-
* @author Incremark Team
|
|
2778
|
-
* @license MIT
|
|
2779
|
-
*/
|
|
2780
3311
|
|
|
2781
|
-
export { BlockTransformer, IncremarkParser, allPlugins, cloneNode, codeBlockPlugin, countChars, createBlockTransformer, createIncremarkParser, createPlugin, defaultPlugins, imagePlugin, mathPlugin, mermaidPlugin, sliceAst, thematicBreakPlugin };
|
|
3312
|
+
export { BlockTransformer, IncremarkParser, MarkedAstBuilder, allPlugins, cloneNode, codeBlockPlugin, countChars, createBlockTransformer, createIncremarkParser, createPlugin, defaultPlugins, imagePlugin, mathPlugin, mermaidPlugin, sliceAst, thematicBreakPlugin };
|
|
2782
3313
|
//# sourceMappingURL=index.js.map
|
|
2783
3314
|
//# sourceMappingURL=index.js.map
|