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.
Files changed (55) hide show
  1. package/README.md +74 -17
  2. package/dist/core/lexer/handler.d.ts +23 -0
  3. package/dist/core/lexer/handler.js +272 -0
  4. package/dist/core/lexer/index.d.ts +42 -0
  5. package/dist/core/lexer/index.js +177 -0
  6. package/dist/core/lexer.d.ts +46 -0
  7. package/dist/core/lexer.js +433 -0
  8. package/dist/core/parser/handler.d.ts +19 -0
  9. package/dist/core/parser/handler.js +254 -0
  10. package/dist/core/parser/index.d.ts +33 -0
  11. package/dist/core/parser/index.js +149 -0
  12. package/dist/core/parser.d.ts +37 -0
  13. package/dist/core/parser.js +346 -0
  14. package/dist/core/renderer.d.ts +3 -0
  15. package/dist/core/renderer.js +99 -0
  16. package/dist/core/resolver/footnote-resolver.d.ts +15 -0
  17. package/dist/core/resolver/footnote-resolver.js +36 -0
  18. package/dist/core/resolver.d.ts +15 -0
  19. package/dist/core/resolver.js +36 -0
  20. package/dist/index.d.ts +6 -3
  21. package/dist/index.js +10 -7
  22. package/dist/react.d.ts +36 -0
  23. package/dist/react.js +56 -0
  24. package/dist/renderers/default/handler.d.ts +21 -0
  25. package/dist/renderers/default/handler.js +114 -0
  26. package/dist/renderers/default/index.d.ts +14 -0
  27. package/dist/renderers/default/index.js +117 -0
  28. package/dist/renderers/default.d.ts +26 -0
  29. package/dist/renderers/default.js +105 -0
  30. package/dist/renderers/index.d.ts +10 -0
  31. package/dist/renderers/index.js +2 -0
  32. package/dist/renderers/react/handler.d.ts +22 -0
  33. package/dist/renderers/react/handler.js +123 -0
  34. package/dist/renderers/react/index.d.ts +15 -0
  35. package/dist/renderers/react/index.js +123 -0
  36. package/dist/renderers/react.d.ts +26 -0
  37. package/dist/renderers/react.js +123 -0
  38. package/dist/types/options/converterOptions.d.ts +11 -0
  39. package/dist/types/options/converterOptions.js +2 -0
  40. package/dist/types/options/index.d.ts +10 -0
  41. package/dist/types/options/index.js +2 -0
  42. package/dist/types/options/reactRenderOptions.d.ts +50 -0
  43. package/dist/types/options/reactRenderOptions.js +2 -0
  44. package/dist/types/options/renderOptions.d.ts +86 -0
  45. package/dist/types/options/renderOptions.js +2 -0
  46. package/dist/types/parser.d.ts +132 -0
  47. package/dist/types/parser.js +2 -0
  48. package/dist/types/renderer.d.ts +12 -0
  49. package/dist/types/renderer.js +2 -0
  50. package/dist/types/token.d.ts +94 -74
  51. package/dist/utilities/parser-utils.d.ts +5 -0
  52. package/dist/utilities/parser-utils.js +65 -0
  53. package/dist/utilities/tokenizer-utils.d.ts +11 -0
  54. package/dist/utilities/tokenizer-utils.js +159 -0
  55. package/package.json +24 -3
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,23 @@
1
+ import { TokenizerStrategy } from "../../types/token";
2
+ declare const EscapeCharacterHandler: TokenizerStrategy;
3
+ declare const CommentHandler: TokenizerStrategy;
4
+ declare const HtmlHandler: TokenizerStrategy;
5
+ declare const HorizontalLineHandler: TokenizerStrategy;
6
+ declare const CodeBlockHandler: TokenizerStrategy;
7
+ declare const BoldHandler: TokenizerStrategy;
8
+ declare const StrikethroughHandler: TokenizerStrategy;
9
+ declare const FootnoteDefHandler: TokenizerStrategy;
10
+ declare const FootnoteRefHandler: TokenizerStrategy;
11
+ declare const TaskListHandler: TokenizerStrategy;
12
+ declare const UnorderedListHandler: TokenizerStrategy;
13
+ declare const OrderedListHandler: TokenizerStrategy;
14
+ declare const EndListHandler: TokenizerStrategy;
15
+ declare const TableHandler: TokenizerStrategy;
16
+ declare const InlineCodeHandler: TokenizerStrategy;
17
+ declare const HeaderHandler: TokenizerStrategy;
18
+ declare const ItalicHandler: TokenizerStrategy;
19
+ declare const QuoteHandler: TokenizerStrategy;
20
+ declare const LinkHandler: TokenizerStrategy;
21
+ declare const ImageHandler: TokenizerStrategy;
22
+ declare const NewLineHandler: TokenizerStrategy;
23
+ export { BoldHandler, CodeBlockHandler, CommentHandler, EndListHandler, EscapeCharacterHandler, FootnoteDefHandler, FootnoteRefHandler, HeaderHandler, HorizontalLineHandler, HtmlHandler, ImageHandler, InlineCodeHandler, ItalicHandler, LinkHandler, NewLineHandler, OrderedListHandler, QuoteHandler, StrikethroughHandler, TableHandler, TaskListHandler, UnorderedListHandler };
@@ -0,0 +1,272 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.UnorderedListHandler = exports.TaskListHandler = exports.TableHandler = exports.StrikethroughHandler = exports.QuoteHandler = exports.OrderedListHandler = exports.NewLineHandler = exports.LinkHandler = exports.ItalicHandler = exports.InlineCodeHandler = exports.ImageHandler = exports.HtmlHandler = exports.HorizontalLineHandler = exports.HeaderHandler = exports.FootnoteRefHandler = exports.FootnoteDefHandler = exports.EscapeCharacterHandler = exports.EndListHandler = exports.CommentHandler = exports.CodeBlockHandler = exports.BoldHandler = void 0;
37
+ const utils = __importStar(require("../../utilities/tokenizer-utils"));
38
+ const EscapeCharacterHandler = {
39
+ name: "EscapeCharacter",
40
+ match: (lex) => lex.peek() === "\\" && lex.peek(1) !== undefined,
41
+ emit: (lex) => {
42
+ lex.next(1);
43
+ utils.handleTextBlock(lex);
44
+ }
45
+ };
46
+ exports.EscapeCharacterHandler = EscapeCharacterHandler;
47
+ const CommentHandler = {
48
+ name: "Comment",
49
+ match: (lex) => lex.startsWith("<!--"),
50
+ emit: (lex) => lex.readUntilMatchString("-->", true)
51
+ };
52
+ exports.CommentHandler = CommentHandler;
53
+ const HtmlHandler = {
54
+ name: "HTML",
55
+ match: (lex) => lex.peek() === "<",
56
+ emit: (lex) => {
57
+ //Handle comment
58
+ const line = lex.peekUntil(">");
59
+ const blockRegex = /^<(h[1-6]|div|table|pre|blockquote|ul|ol|li|p|section|article|header|footer|nav|aside|hr|form|iframe)\b/i;
60
+ if (blockRegex.test(line)) {
61
+ utils.handleHtmlBlock(lex);
62
+ }
63
+ else {
64
+ utils.handleHtmlInline(lex);
65
+ }
66
+ }
67
+ };
68
+ exports.HtmlHandler = HtmlHandler;
69
+ const HorizontalLineHandler = {
70
+ name: "HorizontalLine",
71
+ match: (lex) => /^([-*_])\1{2,}$/.test(lex.peekUntil("\n").trim()) && lex.getLastToken()?.type === "NewLine",
72
+ emit: (lex) => {
73
+ lex.next(2); //Skip two first characters, remain will be skiped after loop
74
+ lex.listToken.push({ type: "HorizontalLine" });
75
+ }
76
+ };
77
+ exports.HorizontalLineHandler = HorizontalLineHandler;
78
+ const CodeBlockHandler = {
79
+ name: "CodeBlock",
80
+ match: (lex) => lex.startsWith("```"),
81
+ emit: (lex) => {
82
+ let lang = "";
83
+ let content = "";
84
+ lex.next(3); //Skip open block
85
+ while (!lex.isEndOfFile() && lex.peek() !== "\n") {
86
+ lang += lex.peek();
87
+ lex.next();
88
+ }
89
+ lex.next(); //Skip \n
90
+ while (!lex.isEndOfFile() && !lex.startsWith("```")) {
91
+ content += lex.peek();
92
+ lex.next();
93
+ }
94
+ lex.next(2); //Skip close block (due to next() after each tokenize iteration)
95
+ lex.listToken.push({ "type": "CodeBlock", lang: lang.trim(), content: content.trimEnd() });
96
+ }
97
+ };
98
+ exports.CodeBlockHandler = CodeBlockHandler;
99
+ const BoldHandler = {
100
+ name: "Bold",
101
+ match: (lex) => lex.startsWith("**"),
102
+ emit: (lex) => {
103
+ lex.listToken.push({ type: "Bold" });
104
+ lex.next(); //Skip remain *
105
+ }
106
+ };
107
+ exports.BoldHandler = BoldHandler;
108
+ const StrikethroughHandler = {
109
+ name: "Strikethrough",
110
+ match: (lex) => lex.startsWith("~~"),
111
+ emit: (lex) => {
112
+ lex.listToken.push({ type: "Strikethrough" });
113
+ lex.next(); //Skip remain ~
114
+ }
115
+ };
116
+ exports.StrikethroughHandler = StrikethroughHandler;
117
+ // Footnote
118
+ const FootnoteDefHandler = {
119
+ name: "FootnoteDef",
120
+ match: (lex) => lex.isStartOfLine() && /^\[\^[^\]]+\]:/.test(lex.peekUntil("\n")),
121
+ emit: (lex) => {
122
+ const line = lex.readUntil("\n");
123
+ const match = line.match(/^\[\^([^\]]+)\]:\s*(.*)$/);
124
+ if (match) {
125
+ const id = match[1];
126
+ const content = match[2];
127
+ lex.listToken.push({ type: "FootnoteDef", id, content });
128
+ }
129
+ }
130
+ };
131
+ exports.FootnoteDefHandler = FootnoteDefHandler;
132
+ const FootnoteRefHandler = {
133
+ name: "FootnoteRef",
134
+ match: (lex) => lex.startsWith("[^"),
135
+ emit: (lex) => {
136
+ lex.next(2); //Skip [^
137
+ const id = lex.readUntil("]");
138
+ lex.listToken.push({ type: "FootnoteRef", id });
139
+ }
140
+ };
141
+ exports.FootnoteRefHandler = FootnoteRefHandler;
142
+ //List
143
+ const TaskListHandler = {
144
+ name: "TaskList",
145
+ match: (lex) => lex.isStartOfLine() && /^(\s*)([-*+]) \[( |x|X)\] /.test(lex.peekUntil("\n")),
146
+ emit: (lex) => utils.handleList(lex, false, true)
147
+ };
148
+ exports.TaskListHandler = TaskListHandler;
149
+ const UnorderedListHandler = {
150
+ name: "UnorderList",
151
+ match: (lex) => lex.isStartOfLine() && /^(\s*)([-*+]) /.test(lex.peekUntil("\n")),
152
+ emit: (lex) => utils.handleList(lex, false, false)
153
+ };
154
+ exports.UnorderedListHandler = UnorderedListHandler;
155
+ const OrderedListHandler = {
156
+ name: "OrderedList",
157
+ match: (lex) => lex.isStartOfLine() && /^(\s*)(\d+)\. /.test(lex.peekUntil("\n")),
158
+ emit: (lex) => utils.handleList(lex, true, false)
159
+ };
160
+ exports.OrderedListHandler = OrderedListHandler;
161
+ const EndListHandler = {
162
+ name: "EndList",
163
+ match: (lex) => lex.listLevelFlag > 0 && lex.isStartOfLine() && !/^(\s*)([-+*]|\d+\.) /.test(lex.peekUntil("\n")),
164
+ emit: (lex) => {
165
+ while (lex.listLevelFlag > 0) {
166
+ utils.handleEndList(lex);
167
+ }
168
+ }
169
+ };
170
+ exports.EndListHandler = EndListHandler;
171
+ //Table
172
+ const TableHandler = {
173
+ name: "Table",
174
+ match: (lex) => lex.isStartOfLine() && /^\s*\|.*\|\s*$/.test(lex.peekUntil("\n")),
175
+ emit: (lex) => utils.handleTable(lex)
176
+ };
177
+ exports.TableHandler = TableHandler;
178
+ //Other common syntax
179
+ const InlineCodeHandler = {
180
+ name: "InlineCode",
181
+ match: (lex) => lex.peek() === "`",
182
+ emit: (lex) => {
183
+ let content = "";
184
+ lex.next(); //Skip open block
185
+ while (!lex.isEndOfFile() && !lex.startsWith("`")) {
186
+ content += lex.peek();
187
+ lex.next();
188
+ }
189
+ lex.listToken.push({ "type": "InlineCode", content: content });
190
+ }
191
+ };
192
+ exports.InlineCodeHandler = InlineCodeHandler;
193
+ const HeaderHandler = {
194
+ name: "Header",
195
+ match: (lex) => lex.peek() === "#",
196
+ emit: (lex) => {
197
+ let level = 0;
198
+ while (lex.peek() === "#") {
199
+ level++;
200
+ lex.next();
201
+ }
202
+ if (lex.peek() === " ") {
203
+ lex.next();
204
+ lex.pos--;
205
+ }
206
+ lex.listToken.push({ type: "Header", level });
207
+ }
208
+ };
209
+ exports.HeaderHandler = HeaderHandler;
210
+ const ItalicHandler = {
211
+ name: "Italic",
212
+ match: (lex) => lex.peek() === "*" || lex.peek() === "_",
213
+ emit: (lex) => {
214
+ lex.listToken.push({ type: "Italic" });
215
+ }
216
+ };
217
+ exports.ItalicHandler = ItalicHandler;
218
+ const QuoteHandler = {
219
+ name: "Quote",
220
+ match: (lex) => lex.peek() === ">",
221
+ emit: (lex) => {
222
+ lex.listToken.push({ type: "Quote" });
223
+ }
224
+ };
225
+ exports.QuoteHandler = QuoteHandler;
226
+ const LinkHandler = {
227
+ name: "Link",
228
+ match: (lex) => lex.peek() === "[",
229
+ emit: (lex) => {
230
+ lex.next(); //Skip [
231
+ const text = lex.readUntil("]");
232
+ lex.next(); //Skip ]
233
+ if (lex.peek() === "(") {
234
+ lex.next(); //Skip (
235
+ const url = lex.readUntil(")");
236
+ //Don't skip ) due to auto skip on while loop
237
+ lex.listToken.push({ type: "Link", text: text, href: url });
238
+ }
239
+ else
240
+ lex.listToken.push({ type: "Text", value: `[${text}]` });
241
+ }
242
+ };
243
+ exports.LinkHandler = LinkHandler;
244
+ const ImageHandler = {
245
+ name: "Image",
246
+ match: (lex) => lex.peek() === "!" && lex.peek(1) === "[",
247
+ emit: (lex) => {
248
+ lex.next(); //Skip !
249
+ if (lex.peek() !== "[")
250
+ return;
251
+ lex.next(); //Skip [
252
+ const alt = lex.readUntil("]");
253
+ lex.next(); //Skip ]
254
+ if (lex.peek() === "(") {
255
+ lex.next(); //Skip (
256
+ const src = lex.readUntil(")");
257
+ lex.next(); //Skip )
258
+ lex.listToken.push({ type: "Image", alt: alt, src: src });
259
+ }
260
+ else
261
+ lex.listToken.push({ type: "Text", value: `![${alt}]` });
262
+ }
263
+ };
264
+ exports.ImageHandler = ImageHandler;
265
+ const NewLineHandler = {
266
+ name: "NewLine",
267
+ match: (lex) => lex.peek() === "\n",
268
+ emit: (lex) => {
269
+ lex.listToken.push({ type: "NewLine" });
270
+ }
271
+ };
272
+ exports.NewLineHandler = NewLineHandler;
@@ -0,0 +1,42 @@
1
+ import { Token } from "../../types/token";
2
+ export interface ILexer {
3
+ pos: number;
4
+ input: string;
5
+ listToken: Token[];
6
+ listLevelFlag: number;
7
+ peek(offset?: number): string | null;
8
+ next(amount?: number): void;
9
+ startsWith(str: string): boolean;
10
+ readUntil(char: string, isConsume?: boolean): string;
11
+ readUntilMatchString(str: string, isConsume: boolean): string;
12
+ peekUntil(char: string): string;
13
+ peekUntilByOffset(offset: number): string;
14
+ isEndOfFile(): boolean;
15
+ isStartOfLine(): boolean;
16
+ getLastToken(): Token;
17
+ }
18
+ export default class Lexer implements ILexer {
19
+ input: string;
20
+ pos: number;
21
+ listToken: Token[];
22
+ listLevelFlag: number;
23
+ private strategies;
24
+ constructor(input: string);
25
+ setInput(input: string): void;
26
+ peek(offset?: number): string | null;
27
+ next(amount?: number): void;
28
+ startsWith(str: string): boolean;
29
+ isEndOfFile(): boolean;
30
+ getLastToken(): Token;
31
+ readUntil(char: string, isConsumeChar?: boolean): string;
32
+ peekUntil(char: string): string;
33
+ peekUntilByOffset(offset: number): string;
34
+ isStartOfLine(): boolean;
35
+ readUntilMatchString(str: string, isConsume?: boolean): string;
36
+ /**
37
+ * Tokenize the markdown into a list of tokens.
38
+ * @param isEof - `True` when input is whole markdown, `False` if input is just a part of markdown.
39
+ * @returns List of tokens
40
+ */
41
+ tokenize(isEof?: boolean): Token[];
42
+ }
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const Handlers = __importStar(require("./handler"));
37
+ const utils = __importStar(require("../../utilities/tokenizer-utils"));
38
+ class Lexer {
39
+ constructor(input) {
40
+ this.pos = 0;
41
+ this.listToken = [];
42
+ // Flag for handle special syntax
43
+ this.listLevelFlag = 0;
44
+ this.input = input;
45
+ this.strategies = [
46
+ Handlers.EscapeCharacterHandler,
47
+ Handlers.CommentHandler,
48
+ Handlers.HtmlHandler,
49
+ Handlers.HorizontalLineHandler,
50
+ Handlers.CodeBlockHandler,
51
+ Handlers.BoldHandler,
52
+ Handlers.StrikethroughHandler,
53
+ Handlers.FootnoteDefHandler,
54
+ Handlers.FootnoteRefHandler,
55
+ Handlers.TaskListHandler,
56
+ Handlers.UnorderedListHandler,
57
+ Handlers.OrderedListHandler,
58
+ Handlers.EndListHandler,
59
+ Handlers.TableHandler,
60
+ Handlers.InlineCodeHandler,
61
+ Handlers.HeaderHandler,
62
+ Handlers.ItalicHandler,
63
+ Handlers.QuoteHandler,
64
+ Handlers.LinkHandler,
65
+ Handlers.ImageHandler,
66
+ Handlers.NewLineHandler,
67
+ ];
68
+ }
69
+ //Reset input and other attribute
70
+ setInput(input) {
71
+ this.input = input;
72
+ this.pos = 0;
73
+ this.listLevelFlag = 0;
74
+ this.listToken = [];
75
+ }
76
+ //Get current character with offset
77
+ peek(offset = 0) {
78
+ const i = this.pos + offset;
79
+ return i < this.input.length ? this.input[i] : null;
80
+ }
81
+ //Move cursor by amount
82
+ next(amount = 1) {
83
+ this.pos += amount;
84
+ }
85
+ //If current cursor startsWith given str
86
+ startsWith(str) {
87
+ return this.input.slice(this.pos, this.pos + str.length) === str;
88
+ }
89
+ isEndOfFile() {
90
+ return this.pos >= this.input.length;
91
+ }
92
+ getLastToken() {
93
+ return this.listToken[this.listToken.length - 1];
94
+ }
95
+ readUntil(char, isConsumeChar = false) {
96
+ let result = "";
97
+ while (this.peek() !== char) {
98
+ result += this.peek();
99
+ this.next();
100
+ if (this.isEndOfFile())
101
+ break;
102
+ }
103
+ if (isConsumeChar)
104
+ this.next(char.length); //Make cursor skip the char
105
+ return result;
106
+ }
107
+ peekUntil(char) {
108
+ let result = "";
109
+ let i = 0;
110
+ while (true) {
111
+ const current = this.peek(i++);
112
+ if (current == null)
113
+ break;
114
+ if (current == char)
115
+ break;
116
+ result += current;
117
+ }
118
+ return result;
119
+ }
120
+ peekUntilByOffset(offset) {
121
+ let result = "";
122
+ let i = 0;
123
+ while (i !== offset) {
124
+ const current = this.peek(i++);
125
+ if (current == null)
126
+ break;
127
+ if (this.isEndOfFile())
128
+ break;
129
+ result += current;
130
+ }
131
+ return result;
132
+ }
133
+ isStartOfLine() {
134
+ return this.pos === 0 || this.peek(-1) === "\n";
135
+ }
136
+ readUntilMatchString(str, isConsume = false) {
137
+ let result = "";
138
+ while (!this.isEndOfFile()) {
139
+ if (this.peekUntilByOffset(str.length) === str) {
140
+ if (isConsume)
141
+ this.next(str.length);
142
+ break;
143
+ }
144
+ result += this.peek();
145
+ this.next();
146
+ }
147
+ return result;
148
+ }
149
+ /**
150
+ * Tokenize the markdown into a list of tokens.
151
+ * @param isEof - `True` when input is whole markdown, `False` if input is just a part of markdown.
152
+ * @returns List of tokens
153
+ */
154
+ tokenize(isEof = true) {
155
+ while (!this.isEndOfFile()) {
156
+ let matched = false;
157
+ for (const strategy of this.strategies) {
158
+ if (strategy.match(this)) {
159
+ strategy.emit(this);
160
+ matched = true;
161
+ break;
162
+ }
163
+ }
164
+ if (!matched) {
165
+ utils.handleTextBlock(this);
166
+ }
167
+ this.next();
168
+ }
169
+ while (this.listLevelFlag > 0) {
170
+ utils.handleEndList(this);
171
+ }
172
+ if (isEof)
173
+ this.listToken.push({ type: "EOF" });
174
+ return this.listToken;
175
+ }
176
+ }
177
+ exports.default = Lexer;
@@ -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
+ }