emdp 1.0.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/LICENSE +21 -0
- package/README.md +124 -0
- package/dist/cjs/cli.js +36 -0
- package/dist/cjs/gfm.js +26 -0
- package/dist/cjs/index.js +26 -0
- package/dist/cjs/parser/block-parser.js +644 -0
- package/dist/cjs/parser/blocks/blockquote.js +28 -0
- package/dist/cjs/parser/blocks/code-block-fenced.js +58 -0
- package/dist/cjs/parser/blocks/code-block-indented.js +47 -0
- package/dist/cjs/parser/blocks/heading-atx.js +29 -0
- package/dist/cjs/parser/blocks/heading-setext.js +24 -0
- package/dist/cjs/parser/blocks/html-block.js +83 -0
- package/dist/cjs/parser/blocks/link-reference.js +109 -0
- package/dist/cjs/parser/blocks/list.js +155 -0
- package/dist/cjs/parser/blocks/paragraph.js +20 -0
- package/dist/cjs/parser/blocks/table.js +163 -0
- package/dist/cjs/parser/blocks/task-list.js +66 -0
- package/dist/cjs/parser/blocks/thematic-break.js +17 -0
- package/dist/cjs/parser/entities.js +2133 -0
- package/dist/cjs/parser/gfm/block-parser.js +773 -0
- package/dist/cjs/parser/gfm/index.js +125 -0
- package/dist/cjs/parser/gfm/inline-parser.js +813 -0
- package/dist/cjs/parser/gfm/renderer.js +513 -0
- package/dist/cjs/parser/index.js +104 -0
- package/dist/cjs/parser/inline-parser.js +564 -0
- package/dist/cjs/parser/inlines/autolink-extended.js +364 -0
- package/dist/cjs/parser/inlines/autolink.js +44 -0
- package/dist/cjs/parser/inlines/code-span.js +48 -0
- package/dist/cjs/parser/inlines/emphasis.js +64 -0
- package/dist/cjs/parser/inlines/entity.js +25 -0
- package/dist/cjs/parser/inlines/escape.js +25 -0
- package/dist/cjs/parser/inlines/footnote.js +41 -0
- package/dist/cjs/parser/inlines/hard-break.js +45 -0
- package/dist/cjs/parser/inlines/image.js +9 -0
- package/dist/cjs/parser/inlines/link.js +166 -0
- package/dist/cjs/parser/inlines/soft-break.js +18 -0
- package/dist/cjs/parser/inlines/strikethrough.js +48 -0
- package/dist/cjs/parser/inlines/text.js +20 -0
- package/dist/cjs/parser/renderer.js +345 -0
- package/dist/cjs/parser/types.js +5 -0
- package/dist/cjs/parser/utils.js +277 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +36 -0
- package/dist/esm/cli.js +34 -0
- package/dist/esm/gfm.js +5 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/parser/block-parser.js +640 -0
- package/dist/esm/parser/blocks/blockquote.js +22 -0
- package/dist/esm/parser/blocks/code-block-fenced.js +52 -0
- package/dist/esm/parser/blocks/code-block-indented.js +42 -0
- package/dist/esm/parser/blocks/heading-atx.js +24 -0
- package/dist/esm/parser/blocks/heading-setext.js +19 -0
- package/dist/esm/parser/blocks/html-block.js +77 -0
- package/dist/esm/parser/blocks/link-reference.js +105 -0
- package/dist/esm/parser/blocks/list.js +145 -0
- package/dist/esm/parser/blocks/paragraph.js +15 -0
- package/dist/esm/parser/blocks/table.js +152 -0
- package/dist/esm/parser/blocks/task-list.js +61 -0
- package/dist/esm/parser/blocks/thematic-break.js +13 -0
- package/dist/esm/parser/entities.js +2130 -0
- package/dist/esm/parser/gfm/block-parser.js +769 -0
- package/dist/esm/parser/gfm/index.js +115 -0
- package/dist/esm/parser/gfm/inline-parser.js +809 -0
- package/dist/esm/parser/gfm/renderer.js +509 -0
- package/dist/esm/parser/index.js +80 -0
- package/dist/esm/parser/inline-parser.js +560 -0
- package/dist/esm/parser/inlines/autolink-extended.js +357 -0
- package/dist/esm/parser/inlines/autolink.js +40 -0
- package/dist/esm/parser/inlines/code-span.js +44 -0
- package/dist/esm/parser/inlines/emphasis.js +59 -0
- package/dist/esm/parser/inlines/entity.js +21 -0
- package/dist/esm/parser/inlines/escape.js +21 -0
- package/dist/esm/parser/inlines/footnote.js +38 -0
- package/dist/esm/parser/inlines/hard-break.js +41 -0
- package/dist/esm/parser/inlines/image.js +4 -0
- package/dist/esm/parser/inlines/link.js +156 -0
- package/dist/esm/parser/inlines/soft-break.js +14 -0
- package/dist/esm/parser/inlines/strikethrough.js +42 -0
- package/dist/esm/parser/inlines/text.js +16 -0
- package/dist/esm/parser/renderer.js +341 -0
- package/dist/esm/parser/types.js +4 -0
- package/dist/esm/parser/utils.js +254 -0
- package/dist/gfm.d.ts +6 -0
- package/dist/gfm.js +26 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +26 -0
- package/dist/parser/block-parser.d.ts +25 -0
- package/dist/parser/block-parser.js +644 -0
- package/dist/parser/blocks/blockquote.d.ts +8 -0
- package/dist/parser/blocks/blockquote.js +28 -0
- package/dist/parser/blocks/code-block-fenced.d.ts +14 -0
- package/dist/parser/blocks/code-block-fenced.js +58 -0
- package/dist/parser/blocks/code-block-indented.d.ts +7 -0
- package/dist/parser/blocks/code-block-indented.js +47 -0
- package/dist/parser/blocks/heading-atx.d.ts +10 -0
- package/dist/parser/blocks/heading-atx.js +29 -0
- package/dist/parser/blocks/heading-setext.d.ts +8 -0
- package/dist/parser/blocks/heading-setext.js +24 -0
- package/dist/parser/blocks/html-block.d.ts +9 -0
- package/dist/parser/blocks/html-block.js +83 -0
- package/dist/parser/blocks/link-reference.d.ts +11 -0
- package/dist/parser/blocks/link-reference.js +109 -0
- package/dist/parser/blocks/list.d.ts +25 -0
- package/dist/parser/blocks/list.js +155 -0
- package/dist/parser/blocks/paragraph.d.ts +7 -0
- package/dist/parser/blocks/paragraph.js +20 -0
- package/dist/parser/blocks/table.d.ts +13 -0
- package/dist/parser/blocks/table.js +163 -0
- package/dist/parser/blocks/task-list.d.ts +10 -0
- package/dist/parser/blocks/task-list.js +66 -0
- package/dist/parser/blocks/thematic-break.d.ts +6 -0
- package/dist/parser/blocks/thematic-break.js +17 -0
- package/dist/parser/entities.d.ts +4 -0
- package/dist/parser/entities.js +2133 -0
- package/dist/parser/gfm/block-parser.d.ts +32 -0
- package/dist/parser/gfm/block-parser.js +773 -0
- package/dist/parser/gfm/index.d.ts +31 -0
- package/dist/parser/gfm/index.js +125 -0
- package/dist/parser/gfm/inline-parser.d.ts +25 -0
- package/dist/parser/gfm/inline-parser.js +813 -0
- package/dist/parser/gfm/renderer.d.ts +43 -0
- package/dist/parser/gfm/renderer.js +513 -0
- package/dist/parser/index.d.ts +33 -0
- package/dist/parser/index.js +104 -0
- package/dist/parser/inline-parser.d.ts +16 -0
- package/dist/parser/inline-parser.js +564 -0
- package/dist/parser/inlines/autolink-extended.d.ts +24 -0
- package/dist/parser/inlines/autolink-extended.js +364 -0
- package/dist/parser/inlines/autolink.d.ts +9 -0
- package/dist/parser/inlines/autolink.js +44 -0
- package/dist/parser/inlines/code-span.d.ts +9 -0
- package/dist/parser/inlines/code-span.js +48 -0
- package/dist/parser/inlines/emphasis.d.ts +14 -0
- package/dist/parser/inlines/emphasis.js +64 -0
- package/dist/parser/inlines/entity.d.ts +8 -0
- package/dist/parser/inlines/entity.js +25 -0
- package/dist/parser/inlines/escape.d.ts +8 -0
- package/dist/parser/inlines/escape.js +25 -0
- package/dist/parser/inlines/footnote.d.ts +9 -0
- package/dist/parser/inlines/footnote.js +41 -0
- package/dist/parser/inlines/hard-break.d.ts +9 -0
- package/dist/parser/inlines/hard-break.js +45 -0
- package/dist/parser/inlines/image.d.ts +4 -0
- package/dist/parser/inlines/image.js +9 -0
- package/dist/parser/inlines/link.d.ts +33 -0
- package/dist/parser/inlines/link.js +166 -0
- package/dist/parser/inlines/soft-break.d.ts +9 -0
- package/dist/parser/inlines/soft-break.js +18 -0
- package/dist/parser/inlines/strikethrough.d.ts +16 -0
- package/dist/parser/inlines/strikethrough.js +48 -0
- package/dist/parser/inlines/text.d.ts +6 -0
- package/dist/parser/inlines/text.js +20 -0
- package/dist/parser/renderer.d.ts +33 -0
- package/dist/parser/renderer.js +345 -0
- package/dist/parser/types.d.ts +152 -0
- package/dist/parser/types.js +5 -0
- package/dist/parser/utils.d.ts +32 -0
- package/dist/parser/utils.js +277 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/gfm.d.ts +6 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/parser/block-parser.d.ts +25 -0
- package/dist/types/parser/blocks/blockquote.d.ts +8 -0
- package/dist/types/parser/blocks/code-block-fenced.d.ts +14 -0
- package/dist/types/parser/blocks/code-block-indented.d.ts +7 -0
- package/dist/types/parser/blocks/heading-atx.d.ts +10 -0
- package/dist/types/parser/blocks/heading-setext.d.ts +8 -0
- package/dist/types/parser/blocks/html-block.d.ts +9 -0
- package/dist/types/parser/blocks/link-reference.d.ts +11 -0
- package/dist/types/parser/blocks/list.d.ts +25 -0
- package/dist/types/parser/blocks/paragraph.d.ts +7 -0
- package/dist/types/parser/blocks/table.d.ts +13 -0
- package/dist/types/parser/blocks/task-list.d.ts +10 -0
- package/dist/types/parser/blocks/thematic-break.d.ts +6 -0
- package/dist/types/parser/entities.d.ts +4 -0
- package/dist/types/parser/gfm/block-parser.d.ts +32 -0
- package/dist/types/parser/gfm/index.d.ts +31 -0
- package/dist/types/parser/gfm/inline-parser.d.ts +25 -0
- package/dist/types/parser/gfm/renderer.d.ts +43 -0
- package/dist/types/parser/index.d.ts +33 -0
- package/dist/types/parser/inline-parser.d.ts +16 -0
- package/dist/types/parser/inlines/autolink-extended.d.ts +24 -0
- package/dist/types/parser/inlines/autolink.d.ts +9 -0
- package/dist/types/parser/inlines/code-span.d.ts +9 -0
- package/dist/types/parser/inlines/emphasis.d.ts +14 -0
- package/dist/types/parser/inlines/entity.d.ts +8 -0
- package/dist/types/parser/inlines/escape.d.ts +8 -0
- package/dist/types/parser/inlines/footnote.d.ts +9 -0
- package/dist/types/parser/inlines/hard-break.d.ts +9 -0
- package/dist/types/parser/inlines/image.d.ts +4 -0
- package/dist/types/parser/inlines/link.d.ts +33 -0
- package/dist/types/parser/inlines/soft-break.d.ts +9 -0
- package/dist/types/parser/inlines/strikethrough.d.ts +16 -0
- package/dist/types/parser/inlines/text.d.ts +6 -0
- package/dist/types/parser/renderer.d.ts +33 -0
- package/dist/types/parser/types.d.ts +152 -0
- package/dist/types/parser/utils.d.ts +32 -0
- package/package.json +54 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser for GFM extended autolinks that recognize bare URLs and emails.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseWwwAutolink = parseWwwAutolink;
|
|
7
|
+
exports.parseUrlAutolink = parseUrlAutolink;
|
|
8
|
+
exports.parseEmailAutolink = parseEmailAutolink;
|
|
9
|
+
exports.parseProtocolAutolink = parseProtocolAutolink;
|
|
10
|
+
exports.parseExtendedAutolink = parseExtendedAutolink;
|
|
11
|
+
const VALID_PRECEDING_CHARS = new Set([
|
|
12
|
+
'\n', ' ', '\t', '*', '_', '~', '(', '"', "'",
|
|
13
|
+
]);
|
|
14
|
+
const VALID_EMAIL_PRECEDING_CHARS = new Set([
|
|
15
|
+
'\n', ' ', '\t', '*', '_', '~', '(', '"', "'", ':', '/',
|
|
16
|
+
]);
|
|
17
|
+
function isValidPrecedingChar(char) {
|
|
18
|
+
if (char === undefined)
|
|
19
|
+
return true;
|
|
20
|
+
return VALID_PRECEDING_CHARS.has(char);
|
|
21
|
+
}
|
|
22
|
+
function isValidEmailPrecedingChar(char) {
|
|
23
|
+
if (char === undefined)
|
|
24
|
+
return true;
|
|
25
|
+
return VALID_EMAIL_PRECEDING_CHARS.has(char);
|
|
26
|
+
}
|
|
27
|
+
function precededByOpenAngle(text, pos) {
|
|
28
|
+
let i = pos - 1;
|
|
29
|
+
while (i >= 0 && (text[i] === ' ' || text[i] === '\t' || text[i] === '\n')) {
|
|
30
|
+
i--;
|
|
31
|
+
}
|
|
32
|
+
return i >= 0 && text[i] === '<';
|
|
33
|
+
}
|
|
34
|
+
function isDomainChar(char) {
|
|
35
|
+
if (/[a-zA-Z0-9_-]/.test(char))
|
|
36
|
+
return true;
|
|
37
|
+
const code = char.codePointAt(0);
|
|
38
|
+
return code !== undefined && code > 127;
|
|
39
|
+
}
|
|
40
|
+
function isHighSurrogate(code) {
|
|
41
|
+
return code >= 0xD800 && code <= 0xDBFF;
|
|
42
|
+
}
|
|
43
|
+
function parseValidDomain(text, startPos, requirePeriod = true) {
|
|
44
|
+
let pos = startPos;
|
|
45
|
+
const segments = [];
|
|
46
|
+
let currentSegment = '';
|
|
47
|
+
while (pos < text.length) {
|
|
48
|
+
const char = text[pos];
|
|
49
|
+
const code = char.charCodeAt(0);
|
|
50
|
+
if (isHighSurrogate(code) && pos + 1 < text.length) {
|
|
51
|
+
const fullChar = text.slice(pos, pos + 2);
|
|
52
|
+
currentSegment += fullChar;
|
|
53
|
+
pos += 2;
|
|
54
|
+
}
|
|
55
|
+
else if (isDomainChar(char)) {
|
|
56
|
+
currentSegment += char;
|
|
57
|
+
pos++;
|
|
58
|
+
}
|
|
59
|
+
else if (char === '.') {
|
|
60
|
+
if (currentSegment.length === 0)
|
|
61
|
+
break;
|
|
62
|
+
segments.push(currentSegment);
|
|
63
|
+
currentSegment = '';
|
|
64
|
+
pos++;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (currentSegment.length > 0) {
|
|
71
|
+
segments.push(currentSegment);
|
|
72
|
+
}
|
|
73
|
+
if (requirePeriod && segments.length < 2)
|
|
74
|
+
return null;
|
|
75
|
+
if (!requirePeriod && segments.length < 1)
|
|
76
|
+
return null;
|
|
77
|
+
if (segments.length >= 2) {
|
|
78
|
+
const lastTwo = segments.slice(-2);
|
|
79
|
+
for (const seg of lastTwo) {
|
|
80
|
+
if (seg.includes('_'))
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
domain: segments.join('.'),
|
|
86
|
+
endPos: pos,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function trimAutolinkPath(url) {
|
|
90
|
+
let result = url;
|
|
91
|
+
const ltIndex = result.indexOf('<');
|
|
92
|
+
if (ltIndex !== -1) {
|
|
93
|
+
result = result.slice(0, ltIndex);
|
|
94
|
+
}
|
|
95
|
+
while (result.length > 0) {
|
|
96
|
+
const lastChar = result[result.length - 1];
|
|
97
|
+
if ('?!.,:*_~"\''.includes(lastChar)) {
|
|
98
|
+
result = result.slice(0, -1);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (lastChar === ')') {
|
|
102
|
+
const openCount = (result.match(/\(/g) || []).length;
|
|
103
|
+
const closeCount = (result.match(/\)/g) || []).length;
|
|
104
|
+
if (closeCount > openCount) {
|
|
105
|
+
result = result.slice(0, -1);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (lastChar === ';') {
|
|
110
|
+
const entityMatch = result.match(/&[a-zA-Z0-9]+;$/);
|
|
111
|
+
if (entityMatch) {
|
|
112
|
+
result = result.slice(0, -entityMatch[0].length);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
function parseWwwAutolink(text, pos, charBefore) {
|
|
121
|
+
if (!isValidPrecedingChar(charBefore))
|
|
122
|
+
return null;
|
|
123
|
+
if (precededByOpenAngle(text, pos))
|
|
124
|
+
return null;
|
|
125
|
+
if (!text.slice(pos).toLowerCase().startsWith('www.'))
|
|
126
|
+
return null;
|
|
127
|
+
const domainStart = pos + 4;
|
|
128
|
+
const domainResult = parseValidDomain(text, domainStart, true);
|
|
129
|
+
if (!domainResult)
|
|
130
|
+
return null;
|
|
131
|
+
let endPos = domainResult.endPos;
|
|
132
|
+
while (endPos < text.length) {
|
|
133
|
+
const char = text[endPos];
|
|
134
|
+
if (char === ' ' || char === '\t' || char === '\n' || char === '<')
|
|
135
|
+
break;
|
|
136
|
+
endPos++;
|
|
137
|
+
}
|
|
138
|
+
const fullMatch = text.slice(pos, endPos);
|
|
139
|
+
const trimmedUrl = trimAutolinkPath(fullMatch);
|
|
140
|
+
if (trimmedUrl.length <= 4)
|
|
141
|
+
return null;
|
|
142
|
+
return {
|
|
143
|
+
node: {
|
|
144
|
+
type: 'link',
|
|
145
|
+
destination: 'http://' + trimmedUrl,
|
|
146
|
+
title: '',
|
|
147
|
+
children: [{ type: 'text', literal: trimmedUrl }],
|
|
148
|
+
},
|
|
149
|
+
length: trimmedUrl.length,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function parseUrlAutolink(text, pos, charBefore) {
|
|
153
|
+
if (!isValidPrecedingChar(charBefore))
|
|
154
|
+
return null;
|
|
155
|
+
if (precededByOpenAngle(text, pos))
|
|
156
|
+
return null;
|
|
157
|
+
const remaining = text.slice(pos);
|
|
158
|
+
let scheme = null;
|
|
159
|
+
if (remaining.toLowerCase().startsWith('https://')) {
|
|
160
|
+
scheme = 'https://';
|
|
161
|
+
}
|
|
162
|
+
else if (remaining.toLowerCase().startsWith('http://')) {
|
|
163
|
+
scheme = 'http://';
|
|
164
|
+
}
|
|
165
|
+
else if (remaining.toLowerCase().startsWith('ftp://')) {
|
|
166
|
+
scheme = 'ftp://';
|
|
167
|
+
}
|
|
168
|
+
if (!scheme)
|
|
169
|
+
return null;
|
|
170
|
+
const domainStart = pos + scheme.length;
|
|
171
|
+
const domainResult = parseValidDomain(text, domainStart, false);
|
|
172
|
+
if (!domainResult)
|
|
173
|
+
return null;
|
|
174
|
+
let endPos = domainResult.endPos;
|
|
175
|
+
while (endPos < text.length) {
|
|
176
|
+
const char = text[endPos];
|
|
177
|
+
if (char === ' ' || char === '\t' || char === '\n' || char === '<')
|
|
178
|
+
break;
|
|
179
|
+
endPos++;
|
|
180
|
+
}
|
|
181
|
+
const fullMatch = text.slice(pos, endPos);
|
|
182
|
+
const trimmedUrl = trimAutolinkPath(fullMatch);
|
|
183
|
+
if (trimmedUrl.length <= scheme.length)
|
|
184
|
+
return null;
|
|
185
|
+
return {
|
|
186
|
+
node: {
|
|
187
|
+
type: 'link',
|
|
188
|
+
destination: trimmedUrl,
|
|
189
|
+
title: '',
|
|
190
|
+
children: [{ type: 'text', literal: trimmedUrl }],
|
|
191
|
+
},
|
|
192
|
+
length: trimmedUrl.length,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function parseEmailAutolink(text, pos, charBefore) {
|
|
196
|
+
if (!isValidEmailPrecedingChar(charBefore))
|
|
197
|
+
return null;
|
|
198
|
+
if (precededByOpenAngle(text, pos))
|
|
199
|
+
return null;
|
|
200
|
+
let localEnd = pos;
|
|
201
|
+
while (localEnd < text.length) {
|
|
202
|
+
const char = text[localEnd];
|
|
203
|
+
if (/[a-zA-Z0-9.\-_+]/.test(char)) {
|
|
204
|
+
localEnd++;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if (localEnd === pos)
|
|
211
|
+
return null;
|
|
212
|
+
if (text[localEnd] !== '@')
|
|
213
|
+
return null;
|
|
214
|
+
const localPart = text.slice(pos, localEnd);
|
|
215
|
+
const domainStart = localEnd + 1;
|
|
216
|
+
let domainEnd = domainStart;
|
|
217
|
+
let lastDotPos = -1;
|
|
218
|
+
let segmentStart = domainStart;
|
|
219
|
+
while (domainEnd < text.length) {
|
|
220
|
+
const char = text[domainEnd];
|
|
221
|
+
if (/[a-zA-Z0-9\-_]/.test(char)) {
|
|
222
|
+
domainEnd++;
|
|
223
|
+
}
|
|
224
|
+
else if (char === '.') {
|
|
225
|
+
if (domainEnd === segmentStart)
|
|
226
|
+
break;
|
|
227
|
+
lastDotPos = domainEnd;
|
|
228
|
+
segmentStart = domainEnd + 1;
|
|
229
|
+
domainEnd++;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (lastDotPos === -1)
|
|
236
|
+
return null;
|
|
237
|
+
const lastDomainChar = text[domainEnd - 1];
|
|
238
|
+
if (lastDomainChar === '-' || lastDomainChar === '_')
|
|
239
|
+
return null;
|
|
240
|
+
let finalEnd = domainEnd;
|
|
241
|
+
while (finalEnd > domainStart && text[finalEnd - 1] === '.') {
|
|
242
|
+
finalEnd--;
|
|
243
|
+
}
|
|
244
|
+
const domain = text.slice(domainStart, finalEnd);
|
|
245
|
+
if (!domain.includes('.'))
|
|
246
|
+
return null;
|
|
247
|
+
const email = localPart + '@' + domain;
|
|
248
|
+
return {
|
|
249
|
+
node: {
|
|
250
|
+
type: 'link',
|
|
251
|
+
destination: 'mailto:' + email,
|
|
252
|
+
title: '',
|
|
253
|
+
children: [{ type: 'text', literal: email }],
|
|
254
|
+
},
|
|
255
|
+
length: email.length,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function parseProtocolAutolink(text, pos, charBefore) {
|
|
259
|
+
if (!isValidEmailPrecedingChar(charBefore))
|
|
260
|
+
return null;
|
|
261
|
+
if (precededByOpenAngle(text, pos))
|
|
262
|
+
return null;
|
|
263
|
+
const remaining = text.slice(pos);
|
|
264
|
+
let protocol = null;
|
|
265
|
+
if (remaining.toLowerCase().startsWith('mailto:')) {
|
|
266
|
+
protocol = 'mailto:';
|
|
267
|
+
}
|
|
268
|
+
else if (remaining.toLowerCase().startsWith('xmpp:')) {
|
|
269
|
+
protocol = 'xmpp:';
|
|
270
|
+
}
|
|
271
|
+
if (!protocol)
|
|
272
|
+
return null;
|
|
273
|
+
const emailStart = pos + protocol.length;
|
|
274
|
+
let localEnd = emailStart;
|
|
275
|
+
while (localEnd < text.length) {
|
|
276
|
+
const char = text[localEnd];
|
|
277
|
+
if (/[a-zA-Z0-9.\-_+]/.test(char)) {
|
|
278
|
+
localEnd++;
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (localEnd === emailStart)
|
|
285
|
+
return null;
|
|
286
|
+
if (text[localEnd] !== '@')
|
|
287
|
+
return null;
|
|
288
|
+
const localPart = text.slice(emailStart, localEnd);
|
|
289
|
+
const domainStart = localEnd + 1;
|
|
290
|
+
let domainEnd = domainStart;
|
|
291
|
+
let lastDotPos = -1;
|
|
292
|
+
let segmentStart = domainStart;
|
|
293
|
+
while (domainEnd < text.length) {
|
|
294
|
+
const char = text[domainEnd];
|
|
295
|
+
if (/[a-zA-Z0-9\-_]/.test(char)) {
|
|
296
|
+
domainEnd++;
|
|
297
|
+
}
|
|
298
|
+
else if (char === '.') {
|
|
299
|
+
if (domainEnd === segmentStart)
|
|
300
|
+
break;
|
|
301
|
+
lastDotPos = domainEnd;
|
|
302
|
+
segmentStart = domainEnd + 1;
|
|
303
|
+
domainEnd++;
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (lastDotPos === -1)
|
|
310
|
+
return null;
|
|
311
|
+
const lastDomainChar = text[domainEnd - 1];
|
|
312
|
+
if (lastDomainChar === '-' || lastDomainChar === '_')
|
|
313
|
+
return null;
|
|
314
|
+
let finalEnd = domainEnd;
|
|
315
|
+
while (finalEnd > domainStart && text[finalEnd - 1] === '.') {
|
|
316
|
+
finalEnd--;
|
|
317
|
+
}
|
|
318
|
+
const domain = text.slice(domainStart, finalEnd);
|
|
319
|
+
if (!domain.includes('.'))
|
|
320
|
+
return null;
|
|
321
|
+
let fullUrl = protocol + localPart + '@' + domain;
|
|
322
|
+
let consumedLength = fullUrl.length;
|
|
323
|
+
if (protocol === 'xmpp:' && finalEnd < text.length && text[finalEnd] === '/') {
|
|
324
|
+
let resourceEnd = finalEnd + 1;
|
|
325
|
+
while (resourceEnd < text.length) {
|
|
326
|
+
const char = text[resourceEnd];
|
|
327
|
+
if (/[a-zA-Z0-9@.]/.test(char)) {
|
|
328
|
+
resourceEnd++;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (resourceEnd > finalEnd + 1) {
|
|
335
|
+
let resource = text.slice(finalEnd, resourceEnd);
|
|
336
|
+
const secondSlash = resource.indexOf('/', 1);
|
|
337
|
+
if (secondSlash !== -1) {
|
|
338
|
+
resource = resource.slice(0, secondSlash);
|
|
339
|
+
}
|
|
340
|
+
while (resource.length > 1 && '?!.,:*_~'.includes(resource[resource.length - 1])) {
|
|
341
|
+
resource = resource.slice(0, -1);
|
|
342
|
+
}
|
|
343
|
+
if (resource.length > 1) {
|
|
344
|
+
fullUrl += resource;
|
|
345
|
+
consumedLength = fullUrl.length;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
node: {
|
|
351
|
+
type: 'link',
|
|
352
|
+
destination: fullUrl,
|
|
353
|
+
title: '',
|
|
354
|
+
children: [{ type: 'text', literal: fullUrl }],
|
|
355
|
+
},
|
|
356
|
+
length: consumedLength,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function parseExtendedAutolink(text, pos, charBefore) {
|
|
360
|
+
return (parseProtocolAutolink(text, pos, charBefore) ||
|
|
361
|
+
parseUrlAutolink(text, pos, charBefore) ||
|
|
362
|
+
parseWwwAutolink(text, pos, charBefore) ||
|
|
363
|
+
parseEmailAutolink(text, pos, charBefore));
|
|
364
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser for autolinks enclosed in angle brackets.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseAutolink = parseAutolink;
|
|
7
|
+
exports.isAutolinkStart = isAutolinkStart;
|
|
8
|
+
const AUTOLINK_URI_REGEX = /^<([a-zA-Z][a-zA-Z0-9+.-]{1,31}:[^\s<>]*)>/;
|
|
9
|
+
const AUTOLINK_EMAIL_REGEX = /^<([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/;
|
|
10
|
+
function parseAutolink(text, pos) {
|
|
11
|
+
if (text[pos] !== '<')
|
|
12
|
+
return null;
|
|
13
|
+
const remaining = text.slice(pos);
|
|
14
|
+
const uriMatch = remaining.match(AUTOLINK_URI_REGEX);
|
|
15
|
+
if (uriMatch) {
|
|
16
|
+
const uri = uriMatch[1];
|
|
17
|
+
return {
|
|
18
|
+
node: {
|
|
19
|
+
type: 'link',
|
|
20
|
+
destination: uri,
|
|
21
|
+
title: '',
|
|
22
|
+
children: [{ type: 'text', literal: uri }],
|
|
23
|
+
},
|
|
24
|
+
length: uriMatch[0].length,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const emailMatch = remaining.match(AUTOLINK_EMAIL_REGEX);
|
|
28
|
+
if (emailMatch) {
|
|
29
|
+
const email = emailMatch[1];
|
|
30
|
+
return {
|
|
31
|
+
node: {
|
|
32
|
+
type: 'link',
|
|
33
|
+
destination: `mailto:${email}`,
|
|
34
|
+
title: '',
|
|
35
|
+
children: [{ type: 'text', literal: email }],
|
|
36
|
+
},
|
|
37
|
+
length: emailMatch[0].length,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
function isAutolinkStart(text, pos) {
|
|
43
|
+
return text[pos] === '<';
|
|
44
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser for inline code spans delimited by backticks.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseCodeSpan = parseCodeSpan;
|
|
7
|
+
exports.isCodeSpanStart = isCodeSpanStart;
|
|
8
|
+
function parseCodeSpan(text, pos) {
|
|
9
|
+
if (text[pos] !== '`')
|
|
10
|
+
return null;
|
|
11
|
+
let openingLength = 0;
|
|
12
|
+
let i = pos;
|
|
13
|
+
while (i < text.length && text[i] === '`') {
|
|
14
|
+
openingLength++;
|
|
15
|
+
i++;
|
|
16
|
+
}
|
|
17
|
+
let searchPos = i;
|
|
18
|
+
while (searchPos < text.length) {
|
|
19
|
+
const nextBacktick = text.indexOf('`', searchPos);
|
|
20
|
+
if (nextBacktick === -1)
|
|
21
|
+
return null;
|
|
22
|
+
let closingLength = 0;
|
|
23
|
+
let j = nextBacktick;
|
|
24
|
+
while (j < text.length && text[j] === '`') {
|
|
25
|
+
closingLength++;
|
|
26
|
+
j++;
|
|
27
|
+
}
|
|
28
|
+
if (closingLength === openingLength) {
|
|
29
|
+
let content = text.slice(i, nextBacktick);
|
|
30
|
+
content = content.replace(/\n/g, ' ');
|
|
31
|
+
if (content.length >= 2 && content[0] === ' ' && content[content.length - 1] === ' ' && content.trim() !== '') {
|
|
32
|
+
content = content.slice(1, -1);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
node: {
|
|
36
|
+
type: 'code_span',
|
|
37
|
+
literal: content,
|
|
38
|
+
},
|
|
39
|
+
length: j - pos,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
searchPos = j;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function isCodeSpanStart(text, pos) {
|
|
47
|
+
return text[pos] === '`';
|
|
48
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser for emphasis and strong emphasis using * and _ delimiters.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseDelimiterRun = parseDelimiterRun;
|
|
7
|
+
exports.isEmphasisDelimiter = isEmphasisDelimiter;
|
|
8
|
+
exports.canDelimitersMatch = canDelimitersMatch;
|
|
9
|
+
const utils_js_1 = require("../utils.js");
|
|
10
|
+
function parseDelimiterRun(text, pos) {
|
|
11
|
+
const char = text[pos];
|
|
12
|
+
if (char !== '*' && char !== '_')
|
|
13
|
+
return null;
|
|
14
|
+
let length = 0;
|
|
15
|
+
let i = pos;
|
|
16
|
+
while (i < text.length && text[i] === char) {
|
|
17
|
+
length++;
|
|
18
|
+
i++;
|
|
19
|
+
}
|
|
20
|
+
const charBefore = pos > 0 ? text[pos - 1] : '\n';
|
|
21
|
+
const charAfter = i < text.length ? text[i] : '\n';
|
|
22
|
+
const beforeIsWhitespace = (0, utils_js_1.isUnicodeWhitespace)(charBefore) || charBefore === '\n';
|
|
23
|
+
const afterIsWhitespace = (0, utils_js_1.isUnicodeWhitespace)(charAfter) || charAfter === '\n';
|
|
24
|
+
const beforeIsPunctuation = (0, utils_js_1.isUnicodePunctuation)(charBefore);
|
|
25
|
+
const afterIsPunctuation = (0, utils_js_1.isUnicodePunctuation)(charAfter);
|
|
26
|
+
const leftFlanking = !afterIsWhitespace &&
|
|
27
|
+
(!afterIsPunctuation || beforeIsWhitespace || beforeIsPunctuation);
|
|
28
|
+
const rightFlanking = !beforeIsWhitespace &&
|
|
29
|
+
(!beforeIsPunctuation || afterIsWhitespace || afterIsPunctuation);
|
|
30
|
+
let canOpen;
|
|
31
|
+
let canClose;
|
|
32
|
+
if (char === '*') {
|
|
33
|
+
canOpen = leftFlanking;
|
|
34
|
+
canClose = rightFlanking;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
canOpen = leftFlanking && (!rightFlanking || beforeIsPunctuation);
|
|
38
|
+
canClose = rightFlanking && (!leftFlanking || afterIsPunctuation);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
char: char,
|
|
42
|
+
length,
|
|
43
|
+
canOpen,
|
|
44
|
+
canClose,
|
|
45
|
+
position: pos,
|
|
46
|
+
origLength: length,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function isEmphasisDelimiter(text, pos) {
|
|
50
|
+
const char = text[pos];
|
|
51
|
+
return char === '*' || char === '_';
|
|
52
|
+
}
|
|
53
|
+
function canDelimitersMatch(opener, closer) {
|
|
54
|
+
if (opener.char !== closer.char)
|
|
55
|
+
return false;
|
|
56
|
+
if ((opener.canOpen && opener.canClose) || (closer.canOpen && closer.canClose)) {
|
|
57
|
+
if ((opener.origLength + closer.origLength) % 3 === 0) {
|
|
58
|
+
if (opener.origLength % 3 !== 0 || closer.origLength % 3 !== 0) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser for HTML character entities in inline content.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseEntity = parseEntity;
|
|
7
|
+
exports.isEntityStart = isEntityStart;
|
|
8
|
+
const utils_js_1 = require("../utils.js");
|
|
9
|
+
const ENTITY_REGEX = /^&(?:#[xX][0-9a-fA-F]{1,6}|#[0-9]{1,7}|[a-zA-Z][a-zA-Z0-9]{0,31});/;
|
|
10
|
+
function parseEntity(text, pos) {
|
|
11
|
+
if (text[pos] !== '&')
|
|
12
|
+
return null;
|
|
13
|
+
const remaining = text.slice(pos);
|
|
14
|
+
const match = remaining.match(ENTITY_REGEX);
|
|
15
|
+
if (!match)
|
|
16
|
+
return null;
|
|
17
|
+
const entity = match[0];
|
|
18
|
+
const decoded = (0, utils_js_1.decodeHtmlEntities)(entity);
|
|
19
|
+
if (decoded === entity)
|
|
20
|
+
return null;
|
|
21
|
+
return { char: decoded, length: entity.length };
|
|
22
|
+
}
|
|
23
|
+
function isEntityStart(text, pos) {
|
|
24
|
+
return text[pos] === '&';
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser for backslash escape sequences and escaped hard breaks.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseEscape = parseEscape;
|
|
7
|
+
exports.isEscapedChar = isEscapedChar;
|
|
8
|
+
const utils_js_1 = require("../utils.js");
|
|
9
|
+
function parseEscape(text, pos) {
|
|
10
|
+
if (text[pos] !== '\\')
|
|
11
|
+
return null;
|
|
12
|
+
if (pos + 1 >= text.length)
|
|
13
|
+
return null;
|
|
14
|
+
const nextChar = text[pos + 1];
|
|
15
|
+
if (nextChar === '\n') {
|
|
16
|
+
return { char: '\n', length: 2 };
|
|
17
|
+
}
|
|
18
|
+
if ((0, utils_js_1.isAsciiPunctuation)(nextChar)) {
|
|
19
|
+
return { char: nextChar, length: 2 };
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
function isEscapedChar(text, pos) {
|
|
24
|
+
return parseEscape(text, pos) !== null;
|
|
25
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser for GFM footnote labels.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseFootnoteLabel = parseFootnoteLabel;
|
|
7
|
+
const utils_js_1 = require("../utils.js");
|
|
8
|
+
function parseFootnoteLabel(text, pos) {
|
|
9
|
+
if (text[pos] !== '[' || text[pos + 1] !== '^')
|
|
10
|
+
return null;
|
|
11
|
+
let i = pos + 2;
|
|
12
|
+
let label = '';
|
|
13
|
+
while (i < text.length) {
|
|
14
|
+
const char = text[i];
|
|
15
|
+
if (char === '\\' && i + 1 < text.length) {
|
|
16
|
+
const next = text[i + 1];
|
|
17
|
+
if (next === '[' || next === ']' || next === '\\') {
|
|
18
|
+
label += next;
|
|
19
|
+
i += 2;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
label += '\\';
|
|
23
|
+
i++;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (char === ']') {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
label += char;
|
|
30
|
+
i++;
|
|
31
|
+
}
|
|
32
|
+
if (i >= text.length || text[i] !== ']')
|
|
33
|
+
return null;
|
|
34
|
+
if (label.length === 0 || label.length > 999)
|
|
35
|
+
return null;
|
|
36
|
+
return {
|
|
37
|
+
label,
|
|
38
|
+
normalized: (0, utils_js_1.normalizeLabel)(label),
|
|
39
|
+
length: i - pos + 1,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Parser for hard line breaks created by backslash-newline or trailing spaces.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseHardBreak = parseHardBreak;
|
|
7
|
+
exports.isHardBreakStart = isHardBreakStart;
|
|
8
|
+
function parseHardBreak(text, pos) {
|
|
9
|
+
if (text[pos] === '\\' && text[pos + 1] === '\n') {
|
|
10
|
+
return {
|
|
11
|
+
node: { type: 'hardbreak' },
|
|
12
|
+
length: 2,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
if (text[pos] === ' ') {
|
|
16
|
+
let spaces = 0;
|
|
17
|
+
let i = pos;
|
|
18
|
+
while (i < text.length && text[i] === ' ') {
|
|
19
|
+
spaces++;
|
|
20
|
+
i++;
|
|
21
|
+
}
|
|
22
|
+
if (spaces >= 2 && text[i] === '\n') {
|
|
23
|
+
return {
|
|
24
|
+
node: { type: 'hardbreak' },
|
|
25
|
+
length: spaces + 1,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function isHardBreakStart(text, pos) {
|
|
32
|
+
if (text[pos] === '\\' && text[pos + 1] === '\n')
|
|
33
|
+
return true;
|
|
34
|
+
if (text[pos] === ' ') {
|
|
35
|
+
let i = pos;
|
|
36
|
+
let spaces = 0;
|
|
37
|
+
while (i < text.length && text[i] === ' ') {
|
|
38
|
+
spaces++;
|
|
39
|
+
i++;
|
|
40
|
+
}
|
|
41
|
+
if (spaces >= 2 && text[i] === '\n')
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Image parsing helpers that reuse link logic and create image nodes.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isImageStart = exports.createImageNode = void 0;
|
|
7
|
+
var link_js_1 = require("./link.js");
|
|
8
|
+
Object.defineProperty(exports, "createImageNode", { enumerable: true, get: function () { return link_js_1.createImageNode; } });
|
|
9
|
+
Object.defineProperty(exports, "isImageStart", { enumerable: true, get: function () { return link_js_1.isImageStart; } });
|