simple-customize-markdown-converter 1.0.7 → 1.1.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 CHANGED
@@ -26,7 +26,7 @@ npm install simple-customize-markdown-converter
26
26
  ```
27
27
 
28
28
  ## Usage
29
- #### 1. Convert markdown to HTML
29
+ ### 1. Standard HTML
30
30
  ```js
31
31
  const input = `
32
32
  # Hello World
@@ -40,24 +40,54 @@ Output:
40
40
  <p>This is <strong>bold</strong> and <em>italic</em></p>
41
41
  ```
42
42
 
43
- #### 2. Customize your converter
43
+ ### 2. ReactJS Integration (v1.1.0+)
44
+ Render Markdown directly as ReactJS elements.
45
+
46
+ #### Using the provided component
47
+ ```tsx
48
+ import { MarkdownComponent } from 'simple-customize-markdown-converter/react';
49
+
50
+ function App() {
51
+ return (
52
+ <MarkdownComponent
53
+ content="# Hello React"
54
+ className="md-body"
55
+ options={{ converterOptions: { allowDangerousHtml: false } }}
56
+ />
57
+ );
58
+ }
59
+ ```
60
+
61
+ #### Using the render function
62
+ ```tsx
63
+ import { convertMarkdownToReactNode } from 'simple-customize-markdown-converter/react';
64
+
65
+ const node = convertMarkdownToReactNode("## Subtitle");
66
+ return <div>{node}</div>;
67
+ ```
68
+ ## Customization
69
+
70
+ #### 1. Customize your HTML converter
44
71
  You can also customize which HTML should be rendered which every commmon Markdown syntax.
45
72
 
46
73
  For example: change `<h1>` to `<h5>`, wrap paragraphs in `<div>`, or style bold text:
47
74
  ```ts
48
- const renderOptions: RenderOption = {
49
- elements: {
50
- Header: (node, children) => {
51
- //Customize for only Heading 1
52
- if (node.level === 1) {
53
- return `<h5 class="custom-h1">${children.join("")}</h5>`
54
- }
55
- //Keep all remain Heading
56
- return `<h${node.level}>${children.join("")}</h${node.level}>`
57
- },
58
- Paragraph: (_node, children) => `<div class="paragraph">${children.join("")}</div>`,
59
- Bold: (_node, children) => `<b class="bold-text">${children.join("")}</b>`,
60
- }
75
+ const options: MarkdownDefaultOptions = {
76
+ renderOptions: {
77
+ elements: {
78
+ Header: (node, children) => {
79
+ //Customize for only Heading 1
80
+ if (node.level === 1) {
81
+ return `<h5 class="custom-h1">${children.join("")}</h5>`
82
+ }
83
+ //Keep all remain Heading
84
+ return `<h${node.level}>${children.join("")}</h${node.level}>`
85
+ },
86
+ Paragraph: (_node, children) => `<div class="paragraph">${children.join("")}</div>`,
87
+ Bold: (_node, children) => `<b class="bold-text">${children.join("")}</b>`,
88
+ }
89
+ },
90
+ converterOptions: { allowDangerousHtml: false }
61
91
  }
62
92
 
63
93
  const input = `
@@ -65,11 +95,38 @@ const input = `
65
95
  Hello **World**
66
96
  `
67
97
 
68
- console.log(convertMarkdownToHTML(input, renderOptions))
98
+ console.log(convertMarkdownToHTML(input, MarkdownDefaultOptions))
69
99
  ```
70
100
 
71
101
  Output:
72
102
  ```html
73
103
  <h5 class="custom-h1">Title</h5>
74
104
  <div class="paragraph">Hello <b class="bold-text">World</b></div>
75
- ```
105
+ ```
106
+
107
+ #### 2. Customize React Elements
108
+ You can override default elements using `MarkdownReactOptions`.
109
+ ```tsx
110
+ import { MarkdownReactOptions } from 'simple-customize-markdown-converter';
111
+
112
+ const options: MarkdownReactOptions = {
113
+ renderOptions: {
114
+ elements: {
115
+ // Custom Blue Bold text
116
+ Bold: (_node, children) => <strong style={{ color: 'blue' }}>{children}</strong>,
117
+ // Custom Link behavior (e.g., for Mentions)
118
+ Link: (node) => {
119
+ if (node.href.startsWith('@')) {
120
+ return <button className="mention">{node.text}</button>;
121
+ }
122
+ return <a href={node.href}>{node.text}</a>;
123
+ }
124
+ }
125
+ }
126
+ };
127
+ ```
128
+
129
+ ## Security
130
+ By default, HTML tags in Markdown are escaped. To allow raw HTML, explicitly set `allowDangerousHtml: true` in `converterOptions`. Be sure only **enable** this for **trusted** content.
131
+
132
+ **Note**: Upgrade to `React v19.2.0` or later when using `React 19` for security reason.
@@ -0,0 +1,46 @@
1
+ import { Token } from "../types/token";
2
+ export default class Lexer {
3
+ input: string;
4
+ pos: number;
5
+ listToken: Token[];
6
+ listLevelFlag: number;
7
+ constructor(input: string);
8
+ setInput(input: string): void;
9
+ /**
10
+ * Tokenize the markdown into a list of tokens.
11
+ * @param isEof - `True` when input is whole markdown, `False` if input is just a part of markdown.
12
+ * @returns List of tokens
13
+ */
14
+ tokenize(isEof?: boolean): Token[];
15
+ private peek;
16
+ private next;
17
+ private startsWith;
18
+ private isEndOfFile;
19
+ private getLastToken;
20
+ private handleTable;
21
+ private handleHeader;
22
+ private handleCodeBlock;
23
+ private handleTextBlock;
24
+ private handleItalic;
25
+ private handleBold;
26
+ private handleStrikethrough;
27
+ private handleInlineBlock;
28
+ private handleQuoteBlock;
29
+ private handleList;
30
+ private handleStartList;
31
+ private handleListItem;
32
+ private handleTaskItem;
33
+ private handleEndList;
34
+ private handleLink;
35
+ private handleImage;
36
+ private handleHorizontalLine;
37
+ private handleHtmlBlock;
38
+ private handleHtmlInline;
39
+ private handleFootnoteDef;
40
+ private handleFootnoteRef;
41
+ private readUntil;
42
+ private peekUntil;
43
+ private peekUntilByOffset;
44
+ private isStartOfLine;
45
+ private readUntilMatchString;
46
+ }
@@ -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,37 @@
1
+ import { FootnoteResolver } from "../core/resolver";
2
+ import { Node } from "../types/node";
3
+ import { Token } from "../types/token";
4
+ export declare class Parser {
5
+ listToken: Token[];
6
+ pos: number;
7
+ footNoteResolver: FootnoteResolver;
8
+ constructor(listToken: Token[], footNoteResolver: FootnoteResolver);
9
+ /**
10
+ * Parse a list token to a node
11
+ * @return A parsed abstract syntax tree (AST)
12
+ */
13
+ parse(): Node;
14
+ private peek;
15
+ private next;
16
+ private isEnd;
17
+ private parseBlocks;
18
+ private parseParagraph;
19
+ private parseCodeBlock;
20
+ private parseHeader;
21
+ private parseBold;
22
+ private parseItalic;
23
+ private parseStrikethrough;
24
+ private parseInlineCode;
25
+ private parseQuote;
26
+ private parseList;
27
+ private parseListItem;
28
+ private parseLink;
29
+ private parseImage;
30
+ private parseTable;
31
+ private parseHtmlBlock;
32
+ private parseHtmlInline;
33
+ private parseHorizontalLine;
34
+ private parseFootnoteDef;
35
+ private parseFootnoteRef;
36
+ private parseInlineUntil;
37
+ }