simple-customize-markdown-converter 1.0.7 → 1.2.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.md +74 -17
- package/dist/core/lexer/handler.d.ts +23 -0
- package/dist/core/lexer/handler.js +272 -0
- package/dist/core/lexer/index.d.ts +42 -0
- package/dist/core/lexer/index.js +177 -0
- package/dist/core/lexer.d.ts +46 -0
- package/dist/core/lexer.js +433 -0
- package/dist/core/parser/handler.d.ts +19 -0
- package/dist/core/parser/handler.js +254 -0
- package/dist/core/parser/index.d.ts +33 -0
- package/dist/core/parser/index.js +149 -0
- package/dist/core/parser.d.ts +37 -0
- package/dist/core/parser.js +346 -0
- package/dist/core/renderer.d.ts +3 -0
- package/dist/core/renderer.js +99 -0
- package/dist/core/resolver/footnote-resolver.d.ts +15 -0
- package/dist/core/resolver/footnote-resolver.js +36 -0
- package/dist/core/resolver.d.ts +15 -0
- package/dist/core/resolver.js +36 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +10 -7
- package/dist/react.d.ts +36 -0
- package/dist/react.js +56 -0
- package/dist/renderers/default/handler.d.ts +21 -0
- package/dist/renderers/default/handler.js +114 -0
- package/dist/renderers/default/index.d.ts +14 -0
- package/dist/renderers/default/index.js +117 -0
- package/dist/renderers/default.d.ts +26 -0
- package/dist/renderers/default.js +105 -0
- package/dist/renderers/index.d.ts +10 -0
- package/dist/renderers/index.js +2 -0
- package/dist/renderers/react/handler.d.ts +22 -0
- package/dist/renderers/react/handler.js +123 -0
- package/dist/renderers/react/index.d.ts +15 -0
- package/dist/renderers/react/index.js +123 -0
- package/dist/renderers/react.d.ts +26 -0
- package/dist/renderers/react.js +123 -0
- package/dist/types/options/converterOptions.d.ts +11 -0
- package/dist/types/options/converterOptions.js +2 -0
- package/dist/types/options/index.d.ts +10 -0
- package/dist/types/options/index.js +2 -0
- package/dist/types/options/reactRenderOptions.d.ts +50 -0
- package/dist/types/options/reactRenderOptions.js +2 -0
- package/dist/types/options/renderOptions.d.ts +86 -0
- package/dist/types/options/renderOptions.js +2 -0
- package/dist/types/parser.d.ts +132 -0
- package/dist/types/parser.js +2 -0
- package/dist/types/renderer.d.ts +12 -0
- package/dist/types/renderer.js +2 -0
- package/dist/types/token.d.ts +94 -74
- package/dist/utilities/parser-utils.d.ts +5 -0
- package/dist/utilities/parser-utils.js +65 -0
- package/dist/utilities/tokenizer-utils.d.ts +11 -0
- package/dist/utilities/tokenizer-utils.js +159 -0
- package/package.json +24 -3
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class Lexer {
|
|
4
|
+
constructor(input) {
|
|
5
|
+
this.pos = 0;
|
|
6
|
+
this.listToken = [];
|
|
7
|
+
// Flag for handle special syntax
|
|
8
|
+
this.listLevelFlag = 0;
|
|
9
|
+
this.input = input;
|
|
10
|
+
}
|
|
11
|
+
//Reset input and other attribute
|
|
12
|
+
setInput(input) {
|
|
13
|
+
this.input = input;
|
|
14
|
+
this.pos = 0;
|
|
15
|
+
this.listLevelFlag = 0;
|
|
16
|
+
this.listToken = [];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Tokenize the markdown into a list of tokens.
|
|
20
|
+
* @param isEof - `True` when input is whole markdown, `False` if input is just a part of markdown.
|
|
21
|
+
* @returns List of tokens
|
|
22
|
+
*/
|
|
23
|
+
tokenize(isEof = true) {
|
|
24
|
+
const TOKEN_HANDLER = [
|
|
25
|
+
//Handle escape character first
|
|
26
|
+
{
|
|
27
|
+
match: (lex) => lex.peek() === "\\" && lex.peek(1) !== undefined,
|
|
28
|
+
emit: (lex) => {
|
|
29
|
+
lex.next(1);
|
|
30
|
+
lex.handleTextBlock();
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
//For HTML
|
|
34
|
+
//Comment
|
|
35
|
+
{ match: (lex) => lex.startsWith("<!--"), emit: (lex) => lex.readUntilMatchString("-->", true), },
|
|
36
|
+
//Normal HTML
|
|
37
|
+
{
|
|
38
|
+
match: (lex) => lex.peek() === "<",
|
|
39
|
+
emit: (lex) => {
|
|
40
|
+
//Handle comment
|
|
41
|
+
const line = lex.peekUntil(">");
|
|
42
|
+
const blockRegex = /^<(h[1-6]|div|table|pre|blockquote|ul|ol|li|p|section|article|header|footer|nav|aside|hr|form|iframe)\b/i;
|
|
43
|
+
if (blockRegex.test(line)) {
|
|
44
|
+
lex.handleHtmlBlock();
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
lex.handleHtmlInline();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
//Regex: if line started with at least 3 characters: -, *, _
|
|
53
|
+
match: (lex) => /^([-*_])\1{2,}$/.test(lex.peekUntil("\n").trim()) && this.getLastToken()?.type === "NewLine",
|
|
54
|
+
emit: (lex) => lex.handleHorizontalLine()
|
|
55
|
+
},
|
|
56
|
+
{ match: (lex) => lex.startsWith("```"), emit: (lex) => lex.handleCodeBlock() },
|
|
57
|
+
{ match: (lex) => lex.startsWith("**"), emit: (lex) => lex.handleBold() },
|
|
58
|
+
{ match: (lex) => lex.startsWith("~~"), emit: (lex) => lex.handleStrikethrough() },
|
|
59
|
+
// Footnote Definition
|
|
60
|
+
{ match: (lex) => lex.isStartOfLine() && /^\[\^[^\]]+\]:/.test(lex.peekUntil("\n")), emit: (lex) => lex.handleFootnoteDef() },
|
|
61
|
+
// Footnote Reference
|
|
62
|
+
{ match: (lex) => lex.startsWith("[^"), emit: (lex) => lex.handleFootnoteRef() },
|
|
63
|
+
//For List
|
|
64
|
+
{
|
|
65
|
+
match: (lex) => lex.isStartOfLine() && /^(\s*)([-*+]) \[( |x|X)\] /.test(lex.peekUntil("\n")),
|
|
66
|
+
emit: (lex) => lex.handleList(false, true)
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
//Regex: if line started with zero or more spaces, then have - or + or * + 1 space
|
|
70
|
+
match: (lex) => lex.isStartOfLine() && /^(\s*)([-*+]) /.test(lex.peekUntil("\n")),
|
|
71
|
+
emit: (lex) => lex.handleList(false, false)
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
//Regex: if line started with zero or more spaces, then have number. character + 1 space
|
|
75
|
+
match: (lex) => lex.isStartOfLine() && /^(\s*)(\d+)\. /.test(lex.peekUntil("\n")),
|
|
76
|
+
emit: (lex) => lex.handleList(true, false)
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
match: (lex) => lex.listLevelFlag > 0 && lex.isStartOfLine() && !/^(\s*)([-+*]|\d+\.) /.test(lex.peekUntil("\n")),
|
|
80
|
+
emit: (lex) => {
|
|
81
|
+
while (lex.listLevelFlag > 0) {
|
|
82
|
+
lex.handleEndList();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
//For table
|
|
87
|
+
{ match: (lex) => lex.isStartOfLine() && /^\s*\|.*\|\s*$/.test(lex.peekUntil("\n")), emit: (lex) => lex.handleTable() },
|
|
88
|
+
//For common syntax
|
|
89
|
+
{ match: (lex) => lex.peek() === "`", emit: (lex) => lex.handleInlineBlock() },
|
|
90
|
+
{ match: (lex) => lex.peek() === "#", emit: (lex) => lex.handleHeader() },
|
|
91
|
+
{ match: (lex) => lex.peek() === "*" || lex.peek() === "_", emit: (lex) => lex.handleItalic() },
|
|
92
|
+
{ match: (lex) => lex.peek() === ">", emit: (lex) => lex.handleQuoteBlock() },
|
|
93
|
+
{ match: (lex) => lex.peek() === "[", emit: (lex) => lex.handleLink() },
|
|
94
|
+
{ match: (lex) => lex.peek() === "!" && lex.peek(1) === "[", emit: (lex) => lex.handleImage() },
|
|
95
|
+
{ match: (lex) => lex.peek() === "\n", emit: (lex) => lex.listToken.push({ type: "NewLine" }) },
|
|
96
|
+
];
|
|
97
|
+
while (!this.isEndOfFile()) {
|
|
98
|
+
let matched = false;
|
|
99
|
+
for (const handler of TOKEN_HANDLER) {
|
|
100
|
+
if (handler.match(this)) {
|
|
101
|
+
handler.emit(this);
|
|
102
|
+
matched = true;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!matched) {
|
|
107
|
+
this.handleTextBlock();
|
|
108
|
+
}
|
|
109
|
+
this.next();
|
|
110
|
+
}
|
|
111
|
+
while (this.listLevelFlag > 0) {
|
|
112
|
+
this.handleEndList();
|
|
113
|
+
}
|
|
114
|
+
if (isEof)
|
|
115
|
+
this.listToken.push({ type: "EOF" });
|
|
116
|
+
return this.listToken;
|
|
117
|
+
}
|
|
118
|
+
//Get current character with offset
|
|
119
|
+
peek(offset = 0) {
|
|
120
|
+
const i = this.pos + offset;
|
|
121
|
+
return i < this.input.length ? this.input[i] : null;
|
|
122
|
+
}
|
|
123
|
+
//Move cursor by amount
|
|
124
|
+
next(amount = 1) {
|
|
125
|
+
this.pos += amount;
|
|
126
|
+
}
|
|
127
|
+
//If current cursor startsWith given str
|
|
128
|
+
startsWith(str) {
|
|
129
|
+
return this.input.slice(this.pos, this.pos + str.length) === str;
|
|
130
|
+
}
|
|
131
|
+
isEndOfFile() {
|
|
132
|
+
return this.pos >= this.input.length;
|
|
133
|
+
}
|
|
134
|
+
getLastToken() {
|
|
135
|
+
return this.listToken[this.listToken.length - 1];
|
|
136
|
+
}
|
|
137
|
+
handleTable() {
|
|
138
|
+
const tokenizeResult = [];
|
|
139
|
+
const handler = new Lexer("");
|
|
140
|
+
const header = this.readUntil("\n", true);
|
|
141
|
+
const headerDetails = header.trim().replace(/^ *\|/, "").replace(/\| *$/, "").split("|");
|
|
142
|
+
const align = this.readUntil("\n", true);
|
|
143
|
+
const alignDetails = align.trim().replace(/^ *\|/, "").replace(/\| *$/, "").split("|");
|
|
144
|
+
if (alignDetails.length !== headerDetails.length || !alignDetails.every(c => /^:?-{3,}:?$/.test(c))) {
|
|
145
|
+
this.listToken.push({ type: "Text", value: `${header}\n${align}\n` });
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
//Handle alignment
|
|
150
|
+
const normalizeAlign = alignDetails.map(value => {
|
|
151
|
+
if (value.startsWith(":") && value.endsWith(":"))
|
|
152
|
+
return "center";
|
|
153
|
+
else if (value.endsWith(":"))
|
|
154
|
+
return "right";
|
|
155
|
+
else
|
|
156
|
+
return "left";
|
|
157
|
+
});
|
|
158
|
+
tokenizeResult.push({ type: "TableStart" });
|
|
159
|
+
//Handle header
|
|
160
|
+
tokenizeResult.push({ type: "RowStart", isHeader: true });
|
|
161
|
+
headerDetails.forEach((cell, index) => {
|
|
162
|
+
tokenizeResult.push({ type: "CellStart", align: normalizeAlign[index] ?? "left" });
|
|
163
|
+
handler.setInput(cell.trim());
|
|
164
|
+
tokenizeResult.push(...handler.tokenize(false));
|
|
165
|
+
tokenizeResult.push({ type: "CellEnd" });
|
|
166
|
+
});
|
|
167
|
+
tokenizeResult.push({ type: "RowEnd" });
|
|
168
|
+
//Handle body
|
|
169
|
+
while (!this.isEndOfFile()) {
|
|
170
|
+
const body = this.readUntil("\n", true);
|
|
171
|
+
if (!body)
|
|
172
|
+
break;
|
|
173
|
+
const line = body.trim();
|
|
174
|
+
if (!line.startsWith("|") || !line.endsWith("|"))
|
|
175
|
+
break; //End of table
|
|
176
|
+
const bodyDetail = body.trim().replace(/^ *\|/, "").replace(/\| *$/, "").split("|");
|
|
177
|
+
tokenizeResult.push({ type: "RowStart", isHeader: false });
|
|
178
|
+
bodyDetail.forEach((cell, index) => {
|
|
179
|
+
tokenizeResult.push({ type: "CellStart", align: normalizeAlign[index] ?? "left" });
|
|
180
|
+
handler.setInput(cell.trim());
|
|
181
|
+
tokenizeResult.push(...handler.tokenize(false));
|
|
182
|
+
tokenizeResult.push({ type: "CellEnd" });
|
|
183
|
+
});
|
|
184
|
+
tokenizeResult.push({ type: "RowEnd" });
|
|
185
|
+
}
|
|
186
|
+
tokenizeResult.push({ type: "TableEnd" });
|
|
187
|
+
this.listToken.push(...tokenizeResult);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
handleHeader() {
|
|
191
|
+
let level = 0;
|
|
192
|
+
while (this.peek() === "#") {
|
|
193
|
+
level++;
|
|
194
|
+
this.next();
|
|
195
|
+
}
|
|
196
|
+
if (this.peek() === " ") {
|
|
197
|
+
this.next();
|
|
198
|
+
this.pos--;
|
|
199
|
+
}
|
|
200
|
+
this.listToken.push({ type: "Header", level });
|
|
201
|
+
}
|
|
202
|
+
handleCodeBlock() {
|
|
203
|
+
let lang = "";
|
|
204
|
+
let content = "";
|
|
205
|
+
this.next(3); //Skip open block
|
|
206
|
+
while (!this.isEndOfFile() && this.peek() !== "\n") {
|
|
207
|
+
lang += this.peek();
|
|
208
|
+
this.next();
|
|
209
|
+
}
|
|
210
|
+
this.next(); //Skip \n
|
|
211
|
+
while (!this.isEndOfFile() && !this.startsWith("```")) {
|
|
212
|
+
content += this.peek();
|
|
213
|
+
this.next();
|
|
214
|
+
}
|
|
215
|
+
this.next(2); //Skip close block (due to next() after each tokenize iteration)
|
|
216
|
+
this.listToken.push({ "type": "CodeBlock", lang: lang.trim(), content: content.trimEnd() });
|
|
217
|
+
}
|
|
218
|
+
handleTextBlock() {
|
|
219
|
+
const currentChar = this.peek();
|
|
220
|
+
if (currentChar === null)
|
|
221
|
+
return;
|
|
222
|
+
const lastToken = this.getLastToken();
|
|
223
|
+
if (lastToken?.type === "Text")
|
|
224
|
+
lastToken.value += currentChar;
|
|
225
|
+
else
|
|
226
|
+
this.listToken.push({ type: "Text", value: currentChar });
|
|
227
|
+
}
|
|
228
|
+
handleItalic() {
|
|
229
|
+
this.listToken.push({ type: "Italic" });
|
|
230
|
+
}
|
|
231
|
+
handleBold() {
|
|
232
|
+
this.listToken.push({ type: "Bold" });
|
|
233
|
+
this.next(); //Skip remain *
|
|
234
|
+
}
|
|
235
|
+
handleStrikethrough() {
|
|
236
|
+
this.listToken.push({ type: "Strikethrough" });
|
|
237
|
+
this.next(); //Skip remain ~
|
|
238
|
+
}
|
|
239
|
+
handleInlineBlock() {
|
|
240
|
+
let content = "";
|
|
241
|
+
this.next(); //Skip open block
|
|
242
|
+
while (!this.isEndOfFile() && !this.startsWith("`")) {
|
|
243
|
+
content += this.peek();
|
|
244
|
+
this.next();
|
|
245
|
+
}
|
|
246
|
+
this.listToken.push({ "type": "InlineCode", content: content });
|
|
247
|
+
}
|
|
248
|
+
handleQuoteBlock() {
|
|
249
|
+
this.listToken.push({ type: "Quote" });
|
|
250
|
+
}
|
|
251
|
+
handleList(isOrdered, isTask) {
|
|
252
|
+
const line = this.peekUntil("\n");
|
|
253
|
+
if (isTask) {
|
|
254
|
+
const m = line.match(/^(\s*)([-*+]) \[( |x|X)\] (.*)$/);
|
|
255
|
+
const indent = Math.floor(m[1].length / 2) + 1;
|
|
256
|
+
while (this.listLevelFlag < indent)
|
|
257
|
+
this.handleStartList(false);
|
|
258
|
+
while (this.listLevelFlag > indent)
|
|
259
|
+
this.handleEndList();
|
|
260
|
+
this.next(m[1].length + 4);
|
|
261
|
+
this.handleTaskItem(m[3].toLowerCase() === "x");
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
//Regex: line started with: Group 1: zero or more spaces, group 2: (- or + or * + 1 space) or (number with . character), group 3: everything else in line
|
|
265
|
+
const m = isOrdered ? line.match(/^(\s*)(\d+)\. (.*)$/) : line.match(/^(\s*)([-*+]) (.*)$/);
|
|
266
|
+
const indent = Math.floor(m[1].length / 2) + 1; //m[1] to get the spaces in group 1
|
|
267
|
+
while (this.listLevelFlag < indent)
|
|
268
|
+
this.handleStartList(isOrdered);
|
|
269
|
+
while (this.listLevelFlag > indent)
|
|
270
|
+
this.handleEndList();
|
|
271
|
+
this.next(m[1].length + (isOrdered ? 1 : 0)); //+1 due to marker have 2 characters (e.g: 1.) instead 1 like unordered list
|
|
272
|
+
this.handleListItem();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
handleStartList(isOrder) {
|
|
276
|
+
this.listLevelFlag++;
|
|
277
|
+
this.listToken.push({ type: "ListStart", level: this.listLevelFlag, ordered: isOrder });
|
|
278
|
+
}
|
|
279
|
+
handleListItem() {
|
|
280
|
+
this.next(); // Skip space between - and text
|
|
281
|
+
this.listToken.push({ type: "ListItem" });
|
|
282
|
+
}
|
|
283
|
+
handleTaskItem(isChecked) {
|
|
284
|
+
this.next(); // Skip space between last ] and text
|
|
285
|
+
this.listToken.push({ type: "TaskItem", checked: isChecked });
|
|
286
|
+
}
|
|
287
|
+
handleEndList() {
|
|
288
|
+
this.listLevelFlag === 0 ? 0 : this.listLevelFlag--;
|
|
289
|
+
this.listToken.push({ type: "ListEnd" });
|
|
290
|
+
}
|
|
291
|
+
handleLink() {
|
|
292
|
+
this.next(); //Skip [
|
|
293
|
+
const text = this.readUntil("]");
|
|
294
|
+
this.next(); //Skip ]
|
|
295
|
+
if (this.peek() === "(") {
|
|
296
|
+
this.next(); //Skip (
|
|
297
|
+
const url = this.readUntil(")");
|
|
298
|
+
//Don't skip ) due to auto skip on while loop
|
|
299
|
+
this.listToken.push({ type: "Link", text: text, href: url });
|
|
300
|
+
}
|
|
301
|
+
else
|
|
302
|
+
this.listToken.push({ type: "Text", value: `[${text}]` });
|
|
303
|
+
}
|
|
304
|
+
handleImage() {
|
|
305
|
+
this.next(); //Skip !
|
|
306
|
+
if (this.peek() !== "[")
|
|
307
|
+
return;
|
|
308
|
+
this.next(); //Skip [
|
|
309
|
+
const alt = this.readUntil("]");
|
|
310
|
+
this.next(); //Skip ]
|
|
311
|
+
if (this.peek() === "(") {
|
|
312
|
+
this.next(); //Skip (
|
|
313
|
+
const src = this.readUntil(")");
|
|
314
|
+
this.next(); //Skip )
|
|
315
|
+
this.listToken.push({ type: "Image", alt: alt, src: src });
|
|
316
|
+
}
|
|
317
|
+
else
|
|
318
|
+
this.listToken.push({ type: "Text", value: `![${alt}]` });
|
|
319
|
+
}
|
|
320
|
+
handleHorizontalLine() {
|
|
321
|
+
this.next(2); //Skip two first characters, remain will be skiped after loop
|
|
322
|
+
this.listToken.push({ type: "HorizontalLine" });
|
|
323
|
+
}
|
|
324
|
+
handleHtmlBlock() {
|
|
325
|
+
const openTag = this.readUntil(">", true) + ">";
|
|
326
|
+
const matchTagName = /^<\s*([a-zA-Z0-9]+)/.exec(openTag);
|
|
327
|
+
const tagName = matchTagName ? matchTagName[1] : null;
|
|
328
|
+
//Tagname is not valid
|
|
329
|
+
if (!tagName) {
|
|
330
|
+
this.listToken.push({ type: "Text", value: "<" });
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
//If it's self-closing tag
|
|
334
|
+
if (openTag.endsWith("/>") || ["hr", "img", "br", "input", "meta", "link"].includes(tagName)) {
|
|
335
|
+
this.listToken.push({ type: "HTMLBlock", value: openTag });
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
let content = "";
|
|
339
|
+
while (!this.isEndOfFile()) {
|
|
340
|
+
if (this.peekUntilByOffset(`</${tagName}>`.length).toLowerCase() === `</${tagName}>`) {
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
content += this.peek();
|
|
344
|
+
this.next();
|
|
345
|
+
}
|
|
346
|
+
const closeTag = `</${tagName}>`;
|
|
347
|
+
this.next(closeTag.length - 1); //Skip closing tag
|
|
348
|
+
this.listToken.push({ type: "HTMLBlock", value: openTag + content + closeTag });
|
|
349
|
+
}
|
|
350
|
+
handleHtmlInline() {
|
|
351
|
+
const openTag = this.readUntil(">", true) + ">";
|
|
352
|
+
const matchTagName = /^<\s*([a-zA-Z0-9]+)/.exec(openTag);
|
|
353
|
+
const tagName = matchTagName ? matchTagName[1] : null;
|
|
354
|
+
if (!tagName) {
|
|
355
|
+
this.listToken.push({ type: "Text", value: "<" });
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const content = this.readUntilMatchString(`</${tagName}>`);
|
|
359
|
+
const closeTag = `</${tagName}>`;
|
|
360
|
+
this.next(closeTag.length - 1); //Skip closing tag
|
|
361
|
+
this.listToken.push({ type: "HTMLInline", value: openTag + content + closeTag });
|
|
362
|
+
}
|
|
363
|
+
handleFootnoteDef() {
|
|
364
|
+
const line = this.readUntil("\n");
|
|
365
|
+
const match = line.match(/^\[\^([^\]]+)\]:\s*(.*)$/);
|
|
366
|
+
if (match) {
|
|
367
|
+
const id = match[1];
|
|
368
|
+
const content = match[2];
|
|
369
|
+
this.listToken.push({ type: "FootnoteDef", id, content });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
handleFootnoteRef() {
|
|
373
|
+
this.next(2); //Skip [^
|
|
374
|
+
const id = this.readUntil("]");
|
|
375
|
+
this.listToken.push({ type: "FootnoteRef", id });
|
|
376
|
+
}
|
|
377
|
+
//Utilities function
|
|
378
|
+
readUntil(char, isConsumeChar = false) {
|
|
379
|
+
let result = "";
|
|
380
|
+
while (this.peek() !== char) {
|
|
381
|
+
result += this.peek();
|
|
382
|
+
this.next();
|
|
383
|
+
if (this.isEndOfFile())
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
if (isConsumeChar)
|
|
387
|
+
this.next(char.length); //Make cursor skip the char
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
peekUntil(char) {
|
|
391
|
+
let result = "";
|
|
392
|
+
let i = 0;
|
|
393
|
+
while (true) {
|
|
394
|
+
const current = this.peek(i++);
|
|
395
|
+
if (current == null)
|
|
396
|
+
break;
|
|
397
|
+
if (current == char)
|
|
398
|
+
break;
|
|
399
|
+
result += current;
|
|
400
|
+
}
|
|
401
|
+
return result;
|
|
402
|
+
}
|
|
403
|
+
peekUntilByOffset(offset) {
|
|
404
|
+
let result = "";
|
|
405
|
+
let i = 0;
|
|
406
|
+
while (i !== offset) {
|
|
407
|
+
const current = this.peek(i++);
|
|
408
|
+
if (current == null)
|
|
409
|
+
break;
|
|
410
|
+
if (this.isEndOfFile())
|
|
411
|
+
break;
|
|
412
|
+
result += current;
|
|
413
|
+
}
|
|
414
|
+
return result;
|
|
415
|
+
}
|
|
416
|
+
isStartOfLine() {
|
|
417
|
+
return this.pos === 0 || this.peek(-1) === "\n";
|
|
418
|
+
}
|
|
419
|
+
readUntilMatchString(str, isConsume = false) {
|
|
420
|
+
let result = "";
|
|
421
|
+
while (!this.isEndOfFile()) {
|
|
422
|
+
if (this.peekUntilByOffset(str.length) === str) {
|
|
423
|
+
if (isConsume)
|
|
424
|
+
this.next(str.length);
|
|
425
|
+
break;
|
|
426
|
+
}
|
|
427
|
+
result += this.peek();
|
|
428
|
+
this.next();
|
|
429
|
+
}
|
|
430
|
+
return result;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
exports.default = Lexer;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ParsingStrategy } from "../../types/parser";
|
|
2
|
+
declare const CodeBlockHandler: ParsingStrategy;
|
|
3
|
+
declare const HeaderHandler: ParsingStrategy;
|
|
4
|
+
declare const BoldHandler: ParsingStrategy;
|
|
5
|
+
declare const ItalicHandler: ParsingStrategy;
|
|
6
|
+
declare const StrikethroughHandler: ParsingStrategy;
|
|
7
|
+
declare const InlineHandler: ParsingStrategy;
|
|
8
|
+
declare const QuoteHandler: ParsingStrategy;
|
|
9
|
+
declare const ListHandler: ParsingStrategy;
|
|
10
|
+
declare const LinkHandler: ParsingStrategy;
|
|
11
|
+
declare const ImageHandler: ParsingStrategy;
|
|
12
|
+
declare const TableHandler: ParsingStrategy;
|
|
13
|
+
declare const HtmlBlockHandler: ParsingStrategy;
|
|
14
|
+
declare const HtmlInlineHandler: ParsingStrategy;
|
|
15
|
+
declare const HorizontalLineHandler: ParsingStrategy;
|
|
16
|
+
declare const FootnoteDefHandler: ParsingStrategy;
|
|
17
|
+
declare const FootnoteRefHandler: ParsingStrategy;
|
|
18
|
+
declare const NewLineHandler: ParsingStrategy;
|
|
19
|
+
export { BoldHandler, CodeBlockHandler, FootnoteDefHandler, FootnoteRefHandler, HeaderHandler, HorizontalLineHandler, HtmlBlockHandler, HtmlInlineHandler, ImageHandler, InlineHandler, ItalicHandler, LinkHandler, ListHandler, NewLineHandler, QuoteHandler, StrikethroughHandler, TableHandler, };
|