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 +74 -17
- package/dist/core/lexer.d.ts +46 -0
- package/dist/core/lexer.js +433 -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.d.ts +15 -0
- package/dist/core/resolver.js +36 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +9 -6
- package/dist/react.d.ts +39 -0
- package/dist/react.js +56 -0
- package/dist/renderers/default.d.ts +26 -0
- package/dist/renderers/default.js +105 -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 +17 -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 +44 -0
- package/dist/types/options/renderOptions.js +2 -0
- package/package.json +20 -1
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Parser = void 0;
|
|
4
|
+
class Parser {
|
|
5
|
+
constructor(listToken, footNoteResolver) {
|
|
6
|
+
this.pos = 0;
|
|
7
|
+
this.listToken = listToken;
|
|
8
|
+
this.footNoteResolver = footNoteResolver;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse a list token to a node
|
|
12
|
+
* @return A parsed abstract syntax tree (AST)
|
|
13
|
+
*/
|
|
14
|
+
parse() {
|
|
15
|
+
return {
|
|
16
|
+
type: "Document",
|
|
17
|
+
children: this.parseBlocks()
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
peek(offset = 0) {
|
|
21
|
+
const i = this.pos + offset;
|
|
22
|
+
return i < this.listToken.length ? this.listToken[i] : null;
|
|
23
|
+
}
|
|
24
|
+
next(amount = 1) {
|
|
25
|
+
this.pos += amount;
|
|
26
|
+
}
|
|
27
|
+
isEnd() {
|
|
28
|
+
return this.peek()?.type === "EOF";
|
|
29
|
+
}
|
|
30
|
+
parseBlocks() {
|
|
31
|
+
const listNode = [];
|
|
32
|
+
while (!this.isEnd()) {
|
|
33
|
+
const currentNode = this.peek();
|
|
34
|
+
if (!currentNode)
|
|
35
|
+
break;
|
|
36
|
+
switch (currentNode.type) {
|
|
37
|
+
case "Header": {
|
|
38
|
+
listNode.push(this.parseHeader());
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case "CodeBlock": {
|
|
42
|
+
listNode.push(this.parseCodeBlock());
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
case "Quote": {
|
|
46
|
+
listNode.push(this.parseQuote());
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case "Image": {
|
|
50
|
+
listNode.push(this.parseImage());
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case "HorizontalLine": {
|
|
54
|
+
listNode.push(this.parseHorizontalLine());
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case "ListStart": {
|
|
58
|
+
listNode.push(this.parseList());
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case "TableStart": {
|
|
62
|
+
listNode.push(this.parseTable());
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case "HTMLBlock": {
|
|
66
|
+
listNode.push(this.parseHtmlBlock());
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case "FootnoteDef": {
|
|
70
|
+
this.parseFootnoteDef();
|
|
71
|
+
this.next();
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case "NewLine": {
|
|
75
|
+
this.next(); // skip
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
default: listNode.push(this.parseParagraph());
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return listNode;
|
|
82
|
+
}
|
|
83
|
+
parseParagraph() {
|
|
84
|
+
return {
|
|
85
|
+
type: "Paragraph",
|
|
86
|
+
children: this.parseInlineUntil("NewLine")
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
parseCodeBlock() {
|
|
90
|
+
const tok = this.peek();
|
|
91
|
+
this.next();
|
|
92
|
+
return {
|
|
93
|
+
type: "CodeBlock",
|
|
94
|
+
lang: tok?.type === "CodeBlock" ? tok.lang : "",
|
|
95
|
+
content: tok?.type === "CodeBlock" ? tok.content : ""
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
parseHeader() {
|
|
99
|
+
const currentNode = this.peek();
|
|
100
|
+
this.next();
|
|
101
|
+
return {
|
|
102
|
+
type: "Header",
|
|
103
|
+
level: currentNode?.type === "Header" ? currentNode.level : 1,
|
|
104
|
+
children: this.parseInlineUntil("NewLine") //Temp
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
parseBold() {
|
|
108
|
+
this.next(); // skip marker
|
|
109
|
+
return { type: "Bold", children: this.parseInlineUntil("Bold") };
|
|
110
|
+
}
|
|
111
|
+
parseItalic() {
|
|
112
|
+
this.next(); // skip marker
|
|
113
|
+
return { type: "Italic", children: this.parseInlineUntil("Italic") };
|
|
114
|
+
}
|
|
115
|
+
parseStrikethrough() {
|
|
116
|
+
this.next(); // skip marker
|
|
117
|
+
return { type: "Strikethrough", children: this.parseInlineUntil("Strikethrough") };
|
|
118
|
+
}
|
|
119
|
+
parseInlineCode() {
|
|
120
|
+
const tok = this.peek();
|
|
121
|
+
this.next();
|
|
122
|
+
return {
|
|
123
|
+
type: "InlineCode",
|
|
124
|
+
content: tok?.type === "InlineCode" ? tok.content : ""
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
parseQuote() {
|
|
128
|
+
this.next(); //skip marker
|
|
129
|
+
return { type: "Quote", children: [{ type: "Paragraph", children: this.parseInlineUntil("NewLine") }] };
|
|
130
|
+
}
|
|
131
|
+
parseList() {
|
|
132
|
+
const tok = this.peek();
|
|
133
|
+
if (tok?.type === "ListStart") {
|
|
134
|
+
this.next(); //skip marker
|
|
135
|
+
const result = {
|
|
136
|
+
type: "List",
|
|
137
|
+
level: tok.level,
|
|
138
|
+
ordered: tok.ordered,
|
|
139
|
+
children: [],
|
|
140
|
+
};
|
|
141
|
+
let nextToken = this.peek();
|
|
142
|
+
while (!this.isEnd()) {
|
|
143
|
+
if (nextToken?.type === "ListItem" || nextToken?.type === "TaskItem") {
|
|
144
|
+
result.children.push(this.parseListItem());
|
|
145
|
+
nextToken = this.peek();
|
|
146
|
+
}
|
|
147
|
+
else if (nextToken?.type === "ListEnd") {
|
|
148
|
+
this.next();
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
else
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
//Temp return
|
|
157
|
+
return {
|
|
158
|
+
type: "Text",
|
|
159
|
+
value: ""
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
parseListItem() {
|
|
163
|
+
const currentToken = this.peek();
|
|
164
|
+
this.next(); // skip marker
|
|
165
|
+
const children = [];
|
|
166
|
+
while (!this.isEnd()) {
|
|
167
|
+
const tok = this.peek();
|
|
168
|
+
if (!tok)
|
|
169
|
+
break;
|
|
170
|
+
if (tok.type === "NewLine") {
|
|
171
|
+
this.next();
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (tok.type === "ListStart") {
|
|
175
|
+
children.push(this.parseList());
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (["ListItem", "TaskItem", "ListEnd"].includes(tok.type)) {
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
children.push(...this.parseInlineUntil("NewLine"));
|
|
182
|
+
}
|
|
183
|
+
return currentToken?.type === "TaskItem" ? {
|
|
184
|
+
type: "TaskItem",
|
|
185
|
+
checked: currentToken.type === "TaskItem" ? currentToken.checked : false,
|
|
186
|
+
children: children
|
|
187
|
+
} : {
|
|
188
|
+
type: "ListItem",
|
|
189
|
+
children: children
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
parseLink() {
|
|
193
|
+
const tok = this.peek();
|
|
194
|
+
this.next();
|
|
195
|
+
if (tok?.type === "Link") {
|
|
196
|
+
return {
|
|
197
|
+
type: "Link",
|
|
198
|
+
href: tok.href,
|
|
199
|
+
text: tok.text
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return { type: "Link", href: "", text: "" };
|
|
203
|
+
}
|
|
204
|
+
parseImage() {
|
|
205
|
+
const tok = this.peek();
|
|
206
|
+
this.next();
|
|
207
|
+
if (tok?.type === "Image") {
|
|
208
|
+
return {
|
|
209
|
+
type: "Image",
|
|
210
|
+
src: tok.src,
|
|
211
|
+
alt: tok.alt
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
else
|
|
215
|
+
return { type: "Image", src: "", alt: "" };
|
|
216
|
+
}
|
|
217
|
+
parseTable() {
|
|
218
|
+
this.next(); // skip TableStart token
|
|
219
|
+
const parseRow = () => {
|
|
220
|
+
const rowStartToken = this.peek();
|
|
221
|
+
if (rowStartToken?.type !== "RowStart")
|
|
222
|
+
return { isHeader: false, cells: [] };
|
|
223
|
+
this.next(); // skip RowStart token
|
|
224
|
+
const cells = [];
|
|
225
|
+
while (this.peek() && this.peek().type !== "RowEnd") {
|
|
226
|
+
cells.push(parseCell());
|
|
227
|
+
}
|
|
228
|
+
this.next(); // skip RowEnd token
|
|
229
|
+
return {
|
|
230
|
+
isHeader: rowStartToken.isHeader,
|
|
231
|
+
cells: cells
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
const parseCell = () => {
|
|
235
|
+
const cellStartToken = this.peek();
|
|
236
|
+
if (cellStartToken?.type !== "CellStart")
|
|
237
|
+
return { align: "left", children: [] };
|
|
238
|
+
this.next(); // skip CellStart token
|
|
239
|
+
const childrens = this.parseInlineUntil("CellEnd");
|
|
240
|
+
return {
|
|
241
|
+
align: cellStartToken.align,
|
|
242
|
+
children: childrens
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
const rows = [];
|
|
246
|
+
while (this.peek()?.type !== "TableEnd") {
|
|
247
|
+
rows.push(parseRow());
|
|
248
|
+
if (this.isEnd())
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
this.next();
|
|
252
|
+
return {
|
|
253
|
+
type: "Table",
|
|
254
|
+
rows: rows
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
parseHtmlBlock() {
|
|
258
|
+
const tok = this.peek();
|
|
259
|
+
this.next(); // skip marker
|
|
260
|
+
if (tok?.type === "HTMLBlock") {
|
|
261
|
+
return { type: "HTMLBlock", value: tok.value };
|
|
262
|
+
}
|
|
263
|
+
else
|
|
264
|
+
return { type: "Text", value: "" };
|
|
265
|
+
}
|
|
266
|
+
parseHtmlInline() {
|
|
267
|
+
const tok = this.peek();
|
|
268
|
+
this.next(); // skip marker
|
|
269
|
+
if (tok?.type === "HTMLInline") {
|
|
270
|
+
return { type: "HTMLInline", value: tok.value };
|
|
271
|
+
}
|
|
272
|
+
else
|
|
273
|
+
return { type: "Text", value: "" };
|
|
274
|
+
}
|
|
275
|
+
parseHorizontalLine() {
|
|
276
|
+
this.next(); // skip marker
|
|
277
|
+
return { type: "HorizontalLine" };
|
|
278
|
+
}
|
|
279
|
+
parseFootnoteDef() {
|
|
280
|
+
const tok = this.peek();
|
|
281
|
+
if (tok?.type !== "FootnoteDef")
|
|
282
|
+
return;
|
|
283
|
+
this.footNoteResolver.addDef(tok.id, tok.content);
|
|
284
|
+
}
|
|
285
|
+
parseFootnoteRef() {
|
|
286
|
+
const tok = this.peek();
|
|
287
|
+
this.next();
|
|
288
|
+
if (tok?.type !== "FootnoteRef")
|
|
289
|
+
return { type: "Text", value: "" };
|
|
290
|
+
this.footNoteResolver.addUsedRef(tok.id);
|
|
291
|
+
return { type: "FootnoteRef", id: tok.id };
|
|
292
|
+
}
|
|
293
|
+
parseInlineUntil(stopType, isConsumeStopToken = true) {
|
|
294
|
+
const stop = Array.isArray(stopType) ? stopType : [stopType];
|
|
295
|
+
const listNode = [];
|
|
296
|
+
while (!this.isEnd()) {
|
|
297
|
+
const currentNode = this.peek();
|
|
298
|
+
if (!currentNode)
|
|
299
|
+
break;
|
|
300
|
+
if (stop.includes(currentNode.type))
|
|
301
|
+
break;
|
|
302
|
+
switch (currentNode.type) {
|
|
303
|
+
case "Bold": {
|
|
304
|
+
listNode.push(this.parseBold());
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
case "Italic": {
|
|
308
|
+
listNode.push(this.parseItalic());
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
case "Strikethrough": {
|
|
312
|
+
listNode.push(this.parseStrikethrough());
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
case "InlineCode": {
|
|
316
|
+
listNode.push(this.parseInlineCode());
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
case "Text": {
|
|
320
|
+
listNode.push({ type: "Text", value: currentNode.value });
|
|
321
|
+
this.next();
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
case "Link": {
|
|
325
|
+
listNode.push(this.parseLink());
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
case "HTMLInline": {
|
|
329
|
+
listNode.push(this.parseHtmlInline());
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
case "FootnoteRef": {
|
|
333
|
+
listNode.push(this.parseFootnoteRef());
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
//Special case
|
|
337
|
+
case "HTMLBlock": return listNode;
|
|
338
|
+
default: this.next();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (isConsumeStopToken)
|
|
342
|
+
this.next(); //Skip stop token
|
|
343
|
+
return listNode;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
exports.Parser = Parser;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated - Delete this file later
|
|
4
|
+
*/
|
|
5
|
+
// import { FootnoteResolver } from "./resolver"
|
|
6
|
+
// import { Node, TableRow } from "../types/node"
|
|
7
|
+
// import { RenderElements, RenderOption } from "../types/options/renderOptions"
|
|
8
|
+
// export default class Renderer {
|
|
9
|
+
// option: RenderOption
|
|
10
|
+
// footNoteResolver: FootnoteResolver
|
|
11
|
+
// constructor(option: RenderOption, footNoteResolver: FootnoteResolver) {
|
|
12
|
+
// this.option = option
|
|
13
|
+
// this.footNoteResolver = footNoteResolver
|
|
14
|
+
// }
|
|
15
|
+
// /**
|
|
16
|
+
// * Render a Node (AST) to a HTML string according renderer options
|
|
17
|
+
// *
|
|
18
|
+
// * @param node - The abstract syntax tree (AST) from the Parser
|
|
19
|
+
// * @returns The rendered HTML string.
|
|
20
|
+
// */
|
|
21
|
+
// render<K extends Node["type"]>(node: Extract<Node, { type: K }>): string {
|
|
22
|
+
// //Get proper handler type
|
|
23
|
+
// const handler = this.handleRender(node.type)
|
|
24
|
+
// //If node have children, recursive to handle all node's children
|
|
25
|
+
// const children = "children" in node ? node.children.map((ele) => this.render(ele)) : []
|
|
26
|
+
// return handler(node, children)
|
|
27
|
+
// }
|
|
28
|
+
// private handleRender<K extends Node["type"]>(type: K): NonNullable<RenderElements[K]> {
|
|
29
|
+
// const defaultRender: RenderElements = {
|
|
30
|
+
// //Base structural nodes
|
|
31
|
+
// Document: (_node, children) => children.join("") + this.renderFootnotes(),
|
|
32
|
+
// Paragraph: (_node, children) => `<p>${children.join("")}</p>`,
|
|
33
|
+
// //Container nodes
|
|
34
|
+
// CodeBlock: (node) => `<pre><code class="lang-${node.lang}">${this.escapeHtml(node.content)}</code></pre>`,
|
|
35
|
+
// Header: (node, children) => `<h${node.level}${node.level <= 2 ? ' style="border-bottom: 1px solid #d1d9e0b3"' : ''}>${children.join("")}</h${node.level}>`,
|
|
36
|
+
// Quote: (_node, children) => `<blockquote style="margin:0; padding:0 1em; color:#59636e; border-left:.25em solid #d1d9e0;">${children.join("")}</blockquote>`,
|
|
37
|
+
// //For list nodes
|
|
38
|
+
// List: (node, children) => node.ordered ? `<ol>${children.join("")}</ol>` : `<ul>${children.join("")}</ul>`,
|
|
39
|
+
// ListItem: (_node, children) => `<li>${children.join("")}</li>`,
|
|
40
|
+
// TaskItem: (node, children) => `<li><input type="checkbox" disabled ${node.checked ? "checked" : ""}>${children.join("")}</li>`,
|
|
41
|
+
// //Styling nodes
|
|
42
|
+
// Bold: (_node, children) => `<strong>${children.join("")}</strong>`,
|
|
43
|
+
// Italic: (_node, children) => `<em>${children.join("")}</em>`,
|
|
44
|
+
// Strikethrough: (_node, children) => `<s>${children.join("")}</s>`,
|
|
45
|
+
// InlineCode: (node) => `<code>${this.escapeHtml(node.content)}</code>`,
|
|
46
|
+
// //Media nodes
|
|
47
|
+
// Link: (node) => `<a href="${node.href}">${node.text}</a>`,
|
|
48
|
+
// Image: (node) => `<img src="${node.src}" alt="${node.alt}"/>`,
|
|
49
|
+
// //Leaf nodes
|
|
50
|
+
// HorizontalLine: (_node) => `<hr>`,
|
|
51
|
+
// Text: (node) => node.value,
|
|
52
|
+
// //For table nodes
|
|
53
|
+
// Table: (node, children) => this.renderTable(node, children),
|
|
54
|
+
// //For HTML
|
|
55
|
+
// HTMLBlock: (node) => node.value,
|
|
56
|
+
// HTMLInline: (node) => node.value,
|
|
57
|
+
// //For footnote
|
|
58
|
+
// FootnoteRef: (node) => {
|
|
59
|
+
// const idx = this.footNoteResolver.getUsedRefById(node.id)
|
|
60
|
+
// return `<sup id="fnref:${idx}"><a href="#fn:${idx}" class="footnote-ref">[${idx}]</a></sup>`
|
|
61
|
+
// }
|
|
62
|
+
// }
|
|
63
|
+
// return (this.option.elements?.[type] ?? defaultRender[type])!
|
|
64
|
+
// }
|
|
65
|
+
// private renderTable(node: Node, children: string[]) {
|
|
66
|
+
// if (node.type === "Table") {
|
|
67
|
+
// const header = node.rows.filter(row => row.isHeader)
|
|
68
|
+
// const body = node.rows.filter(row => !row.isHeader)
|
|
69
|
+
// const renderRows = (row: TableRow) => {
|
|
70
|
+
// const tag = row.isHeader ? "th" : "td"
|
|
71
|
+
// const cells = row.cells.map(cell => {
|
|
72
|
+
// const align = `style="text-align:${cell.align}"`
|
|
73
|
+
// return `<${tag} ${align}>${cell.children.map(c => this.render(c)).join("")}</${tag}>`
|
|
74
|
+
// }).join("")
|
|
75
|
+
// return `<tr>${cells}</tr>`
|
|
76
|
+
// }
|
|
77
|
+
// const tHead = header.length ? `<thead>${header.map(renderRows).join("")}</thead>` : ""
|
|
78
|
+
// const tBody = body.length ? `<tbody>${body.map(renderRows).join("")}</tbody>` : ""
|
|
79
|
+
// return `<table>${tHead}${tBody}</table>`
|
|
80
|
+
// }
|
|
81
|
+
// else return `<p>${children.join("\n")}</p>`
|
|
82
|
+
// }
|
|
83
|
+
// private escapeHtml(str: string) {
|
|
84
|
+
// return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
|
|
85
|
+
// }
|
|
86
|
+
// private renderFootnotes(): string {
|
|
87
|
+
// if (this.footNoteResolver.isResolverValid()) {
|
|
88
|
+
// const used = this.footNoteResolver.getUsedRef()
|
|
89
|
+
// if (used.length === 0) return ""
|
|
90
|
+
// const items = used.map((id, i) => {
|
|
91
|
+
// const def = this.footNoteResolver.getDef(id) ?? ""
|
|
92
|
+
// const idx = i + 1
|
|
93
|
+
// return `<li id="fn:${idx}"><p>${def} <a href="#fnref:${idx}" class="footnote-backref">↩</a></p></li>`
|
|
94
|
+
// })
|
|
95
|
+
// return `<section class="footnotes"><ol>${items.join("")}</ol></section>`
|
|
96
|
+
// }
|
|
97
|
+
// else return ""
|
|
98
|
+
// }
|
|
99
|
+
// }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare abstract class Resolver {
|
|
2
|
+
abstract isResolverValid(): boolean;
|
|
3
|
+
}
|
|
4
|
+
export declare class FootnoteResolver extends Resolver {
|
|
5
|
+
private defs;
|
|
6
|
+
private usedRef;
|
|
7
|
+
addDef(id: string, content: string): void;
|
|
8
|
+
addUsedRef(id: string): void;
|
|
9
|
+
resolve(id: string): string | undefined;
|
|
10
|
+
getUsedRef(): string[];
|
|
11
|
+
getUsedRefById(id: string): number;
|
|
12
|
+
getDef(id: string): string | undefined;
|
|
13
|
+
isResolverValid(): boolean;
|
|
14
|
+
}
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FootnoteResolver = void 0;
|
|
4
|
+
class Resolver {
|
|
5
|
+
}
|
|
6
|
+
class FootnoteResolver extends Resolver {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.defs = new Map();
|
|
10
|
+
this.usedRef = [];
|
|
11
|
+
}
|
|
12
|
+
addDef(id, content) {
|
|
13
|
+
this.defs.set(id, content);
|
|
14
|
+
}
|
|
15
|
+
addUsedRef(id) {
|
|
16
|
+
if (!this.usedRef.includes(id)) {
|
|
17
|
+
this.usedRef.push(id);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
resolve(id) {
|
|
21
|
+
return this.defs.get(id);
|
|
22
|
+
}
|
|
23
|
+
getUsedRef() {
|
|
24
|
+
return this.usedRef;
|
|
25
|
+
}
|
|
26
|
+
getUsedRefById(id) {
|
|
27
|
+
return this.usedRef.indexOf(id) + 1;
|
|
28
|
+
}
|
|
29
|
+
getDef(id) {
|
|
30
|
+
return this.defs.get(id);
|
|
31
|
+
}
|
|
32
|
+
isResolverValid() {
|
|
33
|
+
return this.defs.size !== 0 && this.usedRef.length !== 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.FootnoteResolver = FootnoteResolver;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { MarkdownDefaultOptions } from "./types/options";
|
|
2
|
+
import { RenderOption } from "./types/options/renderOptions";
|
|
3
|
+
import { Node } from "./types/node";
|
|
4
|
+
export { RenderOption, MarkdownDefaultOptions, Node };
|
|
3
5
|
/**
|
|
4
6
|
* Convert a Markdown string into HTML.
|
|
5
7
|
* @param input - The Markdown source string
|
|
@@ -12,4 +14,4 @@ export { RenderOption };
|
|
|
12
14
|
* // => <p>Hello <strong>world</strong></p>
|
|
13
15
|
* ```
|
|
14
16
|
*/
|
|
15
|
-
export declare function convertMarkdownToHTML(input: string, options?:
|
|
17
|
+
export declare function convertMarkdownToHTML(input: string, options?: MarkdownDefaultOptions): string;
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.convertMarkdownToHTML = convertMarkdownToHTML;
|
|
7
|
-
const lexer_1 = __importDefault(require("./lexer"));
|
|
8
|
-
const parser_1 = require("./parser");
|
|
9
|
-
const
|
|
10
|
-
const
|
|
7
|
+
const lexer_1 = __importDefault(require("./core/lexer"));
|
|
8
|
+
const parser_1 = require("./core/parser");
|
|
9
|
+
const resolver_1 = require("./core/resolver");
|
|
10
|
+
const default_1 = __importDefault(require("./renderers/default"));
|
|
11
11
|
/**
|
|
12
12
|
* Convert a Markdown string into HTML.
|
|
13
13
|
* @param input - The Markdown source string
|
|
@@ -20,9 +20,12 @@ const resolver_1 = require("./resolver");
|
|
|
20
20
|
* // => <p>Hello <strong>world</strong></p>
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
function convertMarkdownToHTML(input, options = {
|
|
23
|
+
function convertMarkdownToHTML(input, options = {
|
|
24
|
+
renderOptions: {},
|
|
25
|
+
converterOptions: { allowDangerousHtml: false }
|
|
26
|
+
}) {
|
|
24
27
|
const tokens = new lexer_1.default(input).tokenize();
|
|
25
28
|
const footNoteResolver = new resolver_1.FootnoteResolver();
|
|
26
29
|
const nodes = new parser_1.Parser(tokens, footNoteResolver).parse();
|
|
27
|
-
return new
|
|
30
|
+
return new default_1.default(options, footNoteResolver).render(nodes);
|
|
28
31
|
}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ReactRenderOption } from "./types/options/reactRenderOptions";
|
|
3
|
+
import { MarkdownReactOptions } from "./types/options";
|
|
4
|
+
import { Node } from "./types/node";
|
|
5
|
+
export { MarkdownReactOptions, ReactRenderOption, Node };
|
|
6
|
+
/**
|
|
7
|
+
* Convert a Markdown string into a ReactNode.
|
|
8
|
+
* @param input - The Markdown source string
|
|
9
|
+
* @param renderOptions - Optional rendering options
|
|
10
|
+
* @param options - Optional handle options
|
|
11
|
+
* @returns The rendered `React.ReactNode` ready to be rendered into a React component.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* const node = convertMarkdownToReactNode("## Hello React");
|
|
16
|
+
* return <div>{node}</div>;
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function convertMarkdownToReactNode(input: string, options?: MarkdownReactOptions): React.ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* A React commponent that renders Markdown content.
|
|
22
|
+
* Using `React.useMemo` to ensure performance and prevent unnecessary re-render.
|
|
23
|
+
* @param props.content - The Markdown source to render.
|
|
24
|
+
* @param props.options - Optional configuration for the renderer.
|
|
25
|
+
* @param props.className - Optional CSS classes for the wrapping `div` element.
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* <MarkdownComponent
|
|
29
|
+
* content="**Bold text here**"
|
|
30
|
+
* className="markdown markdown-rendered"
|
|
31
|
+
* options={{ converterOptions: { allowDangerousHtml: true } }}
|
|
32
|
+
* />
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare const MarkdownComponent: React.FC<{
|
|
36
|
+
content: string;
|
|
37
|
+
options?: MarkdownReactOptions;
|
|
38
|
+
className?: string;
|
|
39
|
+
}>;
|
package/dist/react.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MarkdownComponent = void 0;
|
|
7
|
+
exports.convertMarkdownToReactNode = convertMarkdownToReactNode;
|
|
8
|
+
const react_1 = __importDefault(require("react"));
|
|
9
|
+
const lexer_1 = __importDefault(require("./core/lexer"));
|
|
10
|
+
const resolver_1 = require("./core/resolver");
|
|
11
|
+
const parser_1 = require("./core/parser");
|
|
12
|
+
const react_2 = __importDefault(require("./renderers/react"));
|
|
13
|
+
/**
|
|
14
|
+
* Convert a Markdown string into a ReactNode.
|
|
15
|
+
* @param input - The Markdown source string
|
|
16
|
+
* @param renderOptions - Optional rendering options
|
|
17
|
+
* @param options - Optional handle options
|
|
18
|
+
* @returns The rendered `React.ReactNode` ready to be rendered into a React component.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const node = convertMarkdownToReactNode("## Hello React");
|
|
23
|
+
* return <div>{node}</div>;
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
function convertMarkdownToReactNode(input, options = {
|
|
27
|
+
renderOptions: {},
|
|
28
|
+
converterOptions: { allowDangerousHtml: false }
|
|
29
|
+
}) {
|
|
30
|
+
const tokens = new lexer_1.default(input).tokenize();
|
|
31
|
+
const footNoteResolver = new resolver_1.FootnoteResolver();
|
|
32
|
+
const nodes = new parser_1.Parser(tokens, footNoteResolver).parse();
|
|
33
|
+
return new react_2.default(footNoteResolver, options).render(nodes);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A React commponent that renders Markdown content.
|
|
37
|
+
* Using `React.useMemo` to ensure performance and prevent unnecessary re-render.
|
|
38
|
+
* @param props.content - The Markdown source to render.
|
|
39
|
+
* @param props.options - Optional configuration for the renderer.
|
|
40
|
+
* @param props.className - Optional CSS classes for the wrapping `div` element.
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* <MarkdownComponent
|
|
44
|
+
* content="**Bold text here**"
|
|
45
|
+
* className="markdown markdown-rendered"
|
|
46
|
+
* options={{ converterOptions: { allowDangerousHtml: true } }}
|
|
47
|
+
* />
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
const MarkdownComponent = ({ content, className, options }) => {
|
|
51
|
+
const rendered = react_1.default.useMemo(() => {
|
|
52
|
+
return convertMarkdownToReactNode(content, options);
|
|
53
|
+
}, [content, options]);
|
|
54
|
+
return react_1.default.createElement("div", { className }, rendered);
|
|
55
|
+
};
|
|
56
|
+
exports.MarkdownComponent = MarkdownComponent;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FootnoteResolver } from "../core/resolver";
|
|
2
|
+
import { Node } from "../types/node";
|
|
3
|
+
import { MarkdownDefaultOptions } from "../types/options";
|
|
4
|
+
export default class DefaultRenderer {
|
|
5
|
+
options: MarkdownDefaultOptions;
|
|
6
|
+
footNoteResolver: FootnoteResolver;
|
|
7
|
+
constructor(options: MarkdownDefaultOptions, footNoteResolver: FootnoteResolver);
|
|
8
|
+
/**
|
|
9
|
+
* Render a Node (AST) to a HTML string according renderer options
|
|
10
|
+
*
|
|
11
|
+
* @param node - The abstract syntax tree (AST) from the Parser
|
|
12
|
+
* @returns The rendered HTML string.
|
|
13
|
+
*/
|
|
14
|
+
render<K extends Node["type"]>(node: Extract<Node, {
|
|
15
|
+
type: K;
|
|
16
|
+
}>): string;
|
|
17
|
+
/**
|
|
18
|
+
* Select the appropriate rendering handler for a specific node type
|
|
19
|
+
* @param type - The type of AST Note
|
|
20
|
+
* @returns A function take a node and its children to procude a string.
|
|
21
|
+
*/
|
|
22
|
+
private handleRender;
|
|
23
|
+
private renderTable;
|
|
24
|
+
private escapeHtml;
|
|
25
|
+
private renderFootnotes;
|
|
26
|
+
}
|