@jacobbubu/md-to-lark 1.4.5 → 1.4.6
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.
|
@@ -3,26 +3,9 @@ import remarkParse from 'remark-parse';
|
|
|
3
3
|
import remarkGfm from 'remark-gfm';
|
|
4
4
|
import remarkMath from 'remark-math';
|
|
5
5
|
import remarkRehype from 'remark-rehype';
|
|
6
|
-
|
|
7
|
-
// setext-heading parsing. We re-encode it as a fenced code block.
|
|
8
|
-
function rewriteLeadingFrontmatterAsCodeFence(markdown) {
|
|
9
|
-
const hasBom = markdown.startsWith('\uFEFF');
|
|
10
|
-
const source = hasBom ? markdown.slice(1) : markdown;
|
|
11
|
-
const fmMatch = source.match(/^((?:[ \t]*\r?\n)*)(-{3}|\+{3})[ \t]*\r?\n([\s\S]*?)\r?\n(?:-{3}|\+{3}|\.{3})[ \t]*(?:\r?\n|$)/);
|
|
12
|
-
if (!fmMatch || !fmMatch[0])
|
|
13
|
-
return markdown;
|
|
14
|
-
const leadingBlankLines = fmMatch[1] ?? '';
|
|
15
|
-
const opener = fmMatch[2];
|
|
16
|
-
const body = fmMatch[3] ?? '';
|
|
17
|
-
const language = opener === '+++' ? 'toml' : 'yaml';
|
|
18
|
-
const rest = source.slice(fmMatch[0].length);
|
|
19
|
-
const normalizedBody = body.replace(/\r\n/g, '\n');
|
|
20
|
-
const trailing = normalizedBody.endsWith('\n') ? '' : '\n';
|
|
21
|
-
const rewritten = `${leadingBlankLines}\`\`\`${language}\n${normalizedBody}${trailing}\`\`\`\n${rest}`;
|
|
22
|
-
return hasBom ? `\uFEFF${rewritten}` : rewritten;
|
|
23
|
-
}
|
|
6
|
+
import { normalizeMarkdownBeforeParse } from './normalize-markdown.js';
|
|
24
7
|
export async function markdownToHast(markdown) {
|
|
25
|
-
const content =
|
|
8
|
+
const content = normalizeMarkdownBeforeParse(markdown);
|
|
26
9
|
const processor = unified()
|
|
27
10
|
.use(remarkParse)
|
|
28
11
|
.use(remarkGfm)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const CJK_BOLD_TRAILING_PUNCTUATION = ',。;:!?、】【)」』》、';
|
|
2
|
+
const BOLD_PUNCTUATION_RE = new RegExp(`\\*\\*([^*\\n]+?)([${CJK_BOLD_TRAILING_PUNCTUATION}])\\*\\*(?=[\\p{Script=Han}\\p{Letter}\\p{Number}\\[])`, 'gu');
|
|
3
|
+
function transformOutsideInlineCode(segment) {
|
|
4
|
+
return segment.replace(BOLD_PUNCTUATION_RE, (_match, content, punctuation) => {
|
|
5
|
+
return `**${content}**${punctuation}`;
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
function normalizeLineOutsideInlineCode(line) {
|
|
9
|
+
let output = '';
|
|
10
|
+
let cursor = 0;
|
|
11
|
+
while (cursor < line.length) {
|
|
12
|
+
const tickIndex = line.indexOf('`', cursor);
|
|
13
|
+
if (tickIndex === -1) {
|
|
14
|
+
output += transformOutsideInlineCode(line.slice(cursor));
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
output += transformOutsideInlineCode(line.slice(cursor, tickIndex));
|
|
18
|
+
let tickEnd = tickIndex;
|
|
19
|
+
while (tickEnd < line.length && line[tickEnd] === '`') {
|
|
20
|
+
tickEnd += 1;
|
|
21
|
+
}
|
|
22
|
+
const delimiter = line.slice(tickIndex, tickEnd);
|
|
23
|
+
const closeIndex = line.indexOf(delimiter, tickEnd);
|
|
24
|
+
if (closeIndex === -1) {
|
|
25
|
+
output += line.slice(tickIndex);
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
output += line.slice(tickIndex, closeIndex + delimiter.length);
|
|
29
|
+
cursor = closeIndex + delimiter.length;
|
|
30
|
+
}
|
|
31
|
+
return output;
|
|
32
|
+
}
|
|
33
|
+
export function rewriteLeadingFrontmatterAsCodeFence(markdown) {
|
|
34
|
+
const hasBom = markdown.startsWith('\uFEFF');
|
|
35
|
+
const source = hasBom ? markdown.slice(1) : markdown;
|
|
36
|
+
const fmMatch = source.match(/^((?:[ \t]*\r?\n)*)(-{3}|\+{3})[ \t]*\r?\n([\s\S]*?)\r?\n(?:-{3}|\+{3}|\.{3})[ \t]*(?:\r?\n|$)/);
|
|
37
|
+
if (!fmMatch || !fmMatch[0])
|
|
38
|
+
return markdown;
|
|
39
|
+
const leadingBlankLines = fmMatch[1] ?? '';
|
|
40
|
+
const opener = fmMatch[2];
|
|
41
|
+
const body = fmMatch[3] ?? '';
|
|
42
|
+
const language = opener === '+++' ? 'toml' : 'yaml';
|
|
43
|
+
const rest = source.slice(fmMatch[0].length);
|
|
44
|
+
const normalizedBody = body.replace(/\r\n/g, '\n');
|
|
45
|
+
const trailing = normalizedBody.endsWith('\n') ? '' : '\n';
|
|
46
|
+
const rewritten = `${leadingBlankLines}\`\`\`${language}\n${normalizedBody}${trailing}\`\`\`\n${rest}`;
|
|
47
|
+
return hasBom ? `\uFEFF${rewritten}` : rewritten;
|
|
48
|
+
}
|
|
49
|
+
export function normalizeChineseBoldClosingPunctuation(markdown) {
|
|
50
|
+
const lines = markdown.match(/[^\r\n]*(?:\r?\n|$)/g) ?? [];
|
|
51
|
+
let inFence = false;
|
|
52
|
+
let activeFenceChar = '';
|
|
53
|
+
let activeFenceLength = 0;
|
|
54
|
+
let output = '';
|
|
55
|
+
for (const chunk of lines) {
|
|
56
|
+
if (!chunk)
|
|
57
|
+
continue;
|
|
58
|
+
const lineBreakMatch = chunk.match(/(\r?\n)$/);
|
|
59
|
+
const lineBreak = lineBreakMatch?.[1] ?? '';
|
|
60
|
+
const line = lineBreak ? chunk.slice(0, -lineBreak.length) : chunk;
|
|
61
|
+
const fenceMatch = line.match(/^[ \t]{0,3}([`~]{3,})/);
|
|
62
|
+
if (inFence) {
|
|
63
|
+
output += chunk;
|
|
64
|
+
if (fenceMatch) {
|
|
65
|
+
const fence = fenceMatch[1] ?? '';
|
|
66
|
+
if (fence && fence[0] === activeFenceChar && fence.length >= activeFenceLength) {
|
|
67
|
+
inFence = false;
|
|
68
|
+
activeFenceChar = '';
|
|
69
|
+
activeFenceLength = 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (fenceMatch) {
|
|
75
|
+
const fence = fenceMatch[1] ?? '';
|
|
76
|
+
inFence = true;
|
|
77
|
+
activeFenceChar = fence[0] ?? '';
|
|
78
|
+
activeFenceLength = fence.length;
|
|
79
|
+
output += chunk;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
output += normalizeLineOutsideInlineCode(line) + lineBreak;
|
|
83
|
+
}
|
|
84
|
+
return output;
|
|
85
|
+
}
|
|
86
|
+
export function normalizeMarkdownBeforeParse(markdown) {
|
|
87
|
+
return normalizeChineseBoldClosingPunctuation(rewriteLeadingFrontmatterAsCodeFence(markdown));
|
|
88
|
+
}
|