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,123 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.ReactRenderer = void 0;
|
|
40
|
+
const react_1 = __importDefault(require("react"));
|
|
41
|
+
const Handlers = __importStar(require("./handler"));
|
|
42
|
+
class ReactRenderer {
|
|
43
|
+
constructor(footnoteResolver, options = {}) {
|
|
44
|
+
this.strategies = new Map();
|
|
45
|
+
this.footnoteResolver = footnoteResolver;
|
|
46
|
+
this.options = options;
|
|
47
|
+
this.registerDefaultStrategies();
|
|
48
|
+
}
|
|
49
|
+
registerDefaultStrategies() {
|
|
50
|
+
const listDefaultStrategy = [
|
|
51
|
+
Handlers.DocumentHandler,
|
|
52
|
+
Handlers.ParagraphHandler,
|
|
53
|
+
Handlers.HeaderHandler,
|
|
54
|
+
Handlers.CodeBlockHandler,
|
|
55
|
+
Handlers.QuoteHandler,
|
|
56
|
+
Handlers.BoldHandler,
|
|
57
|
+
Handlers.ItalicHandler,
|
|
58
|
+
Handlers.StrikethroughHandler,
|
|
59
|
+
Handlers.InlineCodeHandler,
|
|
60
|
+
Handlers.LinkHandler,
|
|
61
|
+
Handlers.ImageHandler,
|
|
62
|
+
Handlers.ListHandler,
|
|
63
|
+
Handlers.ListItemHandler,
|
|
64
|
+
Handlers.TaskItemHandler,
|
|
65
|
+
Handlers.TableHandler,
|
|
66
|
+
Handlers.TextHandler,
|
|
67
|
+
Handlers.HorizontalLineHandler,
|
|
68
|
+
Handlers.HTMLBlockHandler,
|
|
69
|
+
Handlers.HTMLInlineHandler,
|
|
70
|
+
Handlers.FootnoteRefHandler
|
|
71
|
+
];
|
|
72
|
+
listDefaultStrategy.forEach(s => this.strategies.set(s.type, s));
|
|
73
|
+
}
|
|
74
|
+
render(node) {
|
|
75
|
+
const userRenderer = this.options.renderOptions?.elements?.[node.type];
|
|
76
|
+
const children = (node.children || []).map(child => this.render(child));
|
|
77
|
+
if (userRenderer) {
|
|
78
|
+
return userRenderer(node, children);
|
|
79
|
+
}
|
|
80
|
+
const strategy = this.strategies.get(node.type);
|
|
81
|
+
if (strategy) {
|
|
82
|
+
return strategy.render(node, children, this);
|
|
83
|
+
}
|
|
84
|
+
return children.join("");
|
|
85
|
+
}
|
|
86
|
+
renderFootnotes() {
|
|
87
|
+
if (this.footnoteResolver.isResolverValid()) {
|
|
88
|
+
const used = this.footnoteResolver.getUsedRef();
|
|
89
|
+
if (used.length === 0)
|
|
90
|
+
return null;
|
|
91
|
+
const items = used.map((id, i) => {
|
|
92
|
+
const def = this.footnoteResolver.getDef(id) ?? "";
|
|
93
|
+
const idx = i + 1;
|
|
94
|
+
return react_1.default.createElement("li", { id: `fn:${idx}` }, react_1.default.createElement("p", null, def + " ", react_1.default.createElement("a", { href: `#fnref:${idx}`, className: "footnote-backref" }, "↩")));
|
|
95
|
+
});
|
|
96
|
+
return react_1.default.createElement("section", { className: "footnotes" }, react_1.default.createElement("ol", null, ...items));
|
|
97
|
+
}
|
|
98
|
+
else
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
renderTable(node, children) {
|
|
102
|
+
if (node.type === "Table" && node.rows) {
|
|
103
|
+
const header = node.rows.filter(row => row.isHeader);
|
|
104
|
+
const body = node.rows.filter(row => !row.isHeader);
|
|
105
|
+
const renderRows = (row, rowIndex) => {
|
|
106
|
+
return react_1.default.createElement("tr", { key: rowIndex }, row.cells.map((cell, cellIndex) => {
|
|
107
|
+
const tag = row.isHeader ? "th" : "td";
|
|
108
|
+
return react_1.default.createElement(tag, { key: cellIndex, style: { textAlign: cell.align } }, ...cell.children.map(c => this.render(c)));
|
|
109
|
+
}));
|
|
110
|
+
};
|
|
111
|
+
const tHead = header.length
|
|
112
|
+
? react_1.default.createElement("thead", null, header.map((row, i) => renderRows(row, i)))
|
|
113
|
+
: null;
|
|
114
|
+
const tBody = body.length
|
|
115
|
+
? react_1.default.createElement("tbody", null, body.map((row, i) => renderRows(row, i)))
|
|
116
|
+
: null;
|
|
117
|
+
return react_1.default.createElement("table", null, tHead, tBody);
|
|
118
|
+
}
|
|
119
|
+
else
|
|
120
|
+
return react_1.default.createElement("p", null, ...children);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.ReactRenderer = ReactRenderer;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { FootnoteResolver } from "../core/resolver";
|
|
3
|
+
import { Node } from "../types/node";
|
|
4
|
+
import { MarkdownReactOptions } from "../types/options";
|
|
5
|
+
export default class ReactRenderer {
|
|
6
|
+
options: MarkdownReactOptions;
|
|
7
|
+
footNoteResolver: FootnoteResolver;
|
|
8
|
+
constructor(footNoteResolver: FootnoteResolver, options: MarkdownReactOptions);
|
|
9
|
+
/**
|
|
10
|
+
* Render a Node (AST) to a ReactNode according renderer options
|
|
11
|
+
*
|
|
12
|
+
* @param node - The abstract syntax tree (AST) from the Parser
|
|
13
|
+
* @returns The ReactNode representing the rendered Markdown element.
|
|
14
|
+
*/
|
|
15
|
+
render<K extends Node["type"]>(node: Extract<Node, {
|
|
16
|
+
type: K;
|
|
17
|
+
}>): ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* Select the appropriate rendering handler for a specific node type
|
|
20
|
+
* @param type - The type of AST Note
|
|
21
|
+
* @returns A function take a node and its children to procude a ReactNode.
|
|
22
|
+
*/
|
|
23
|
+
private handleRender;
|
|
24
|
+
private renderTable;
|
|
25
|
+
private renderFootnotes;
|
|
26
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
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
|
+
const react_1 = __importDefault(require("react"));
|
|
7
|
+
class ReactRenderer {
|
|
8
|
+
constructor(footNoteResolver, options) {
|
|
9
|
+
this.footNoteResolver = footNoteResolver;
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Render a Node (AST) to a ReactNode according renderer options
|
|
14
|
+
*
|
|
15
|
+
* @param node - The abstract syntax tree (AST) from the Parser
|
|
16
|
+
* @returns The ReactNode representing the rendered Markdown element.
|
|
17
|
+
*/
|
|
18
|
+
render(node) {
|
|
19
|
+
//Get proper handler type
|
|
20
|
+
const handler = this.handleRender(node.type);
|
|
21
|
+
//If node have children, recursive to handle all node's children
|
|
22
|
+
const children = "children" in node ? node.children.map((ele) => this.render(ele)) : [];
|
|
23
|
+
return handler(node, children);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Select the appropriate rendering handler for a specific node type
|
|
27
|
+
* @param type - The type of AST Note
|
|
28
|
+
* @returns A function take a node and its children to procude a ReactNode.
|
|
29
|
+
*/
|
|
30
|
+
handleRender(type) {
|
|
31
|
+
const defaultRender = {
|
|
32
|
+
//Base structural nodes
|
|
33
|
+
Document: (_node, children) => react_1.default.createElement(react_1.default.Fragment, null, ...children, this.renderFootnotes()),
|
|
34
|
+
Paragraph: (_node, children) => react_1.default.createElement("p", null, ...children),
|
|
35
|
+
//Container nodes
|
|
36
|
+
CodeBlock: (node) => react_1.default.createElement("pre", null, react_1.default.createElement("code", { className: `lang-${node.lang}` }, node.content)),
|
|
37
|
+
Header: (node, children) => react_1.default.createElement(`h${node.level}`, { style: { borderBottom: node.level <= 2 ? "1px solid #d1d9e0b3" : undefined } }, ...children),
|
|
38
|
+
Quote: (_node, children) => react_1.default.createElement("blockquote", { style: { margin: "0", padding: "0 1em", color: "#59636e", borderLeft: ".25em solid #d1d9e0" } }, ...children),
|
|
39
|
+
//For list nodes
|
|
40
|
+
List: (node, children) => node.ordered ?
|
|
41
|
+
react_1.default.createElement("ol", null, ...children) :
|
|
42
|
+
react_1.default.createElement("ul", null, ...children),
|
|
43
|
+
ListItem: (_node, children) => react_1.default.createElement("li", null, ...children),
|
|
44
|
+
TaskItem: (node, children) => react_1.default.createElement("li", null, react_1.default.createElement("input", {
|
|
45
|
+
type: "checkbox",
|
|
46
|
+
disabled: true,
|
|
47
|
+
checked: !!node.checked,
|
|
48
|
+
readOnly: true
|
|
49
|
+
}), ...children),
|
|
50
|
+
//Styling nodes
|
|
51
|
+
Bold: (_node, children) => react_1.default.createElement("strong", null, ...children),
|
|
52
|
+
Italic: (_node, children) => react_1.default.createElement("em", null, ...children),
|
|
53
|
+
Strikethrough: (_node, children) => react_1.default.createElement("s", null, ...children),
|
|
54
|
+
InlineCode: (node) => react_1.default.createElement("code", null, node.content),
|
|
55
|
+
//Media nodes
|
|
56
|
+
Link: (node) => react_1.default.createElement("a", {
|
|
57
|
+
href: node.href,
|
|
58
|
+
//Security reason
|
|
59
|
+
target: "_blank",
|
|
60
|
+
rel: "noopener"
|
|
61
|
+
}, node.text),
|
|
62
|
+
Image: (node) => react_1.default.createElement("img", {
|
|
63
|
+
src: node.src,
|
|
64
|
+
alt: node.alt,
|
|
65
|
+
}, null),
|
|
66
|
+
//Leaf nodes
|
|
67
|
+
HorizontalLine: (_node) => react_1.default.createElement("hr"),
|
|
68
|
+
Text: (node) => node.value || "",
|
|
69
|
+
//For table nodes
|
|
70
|
+
Table: (node, children) => this.renderTable(node, children),
|
|
71
|
+
//For HTML
|
|
72
|
+
HTMLBlock: (node) => this.options.converterOptions?.allowDangerousHtml
|
|
73
|
+
? react_1.default.createElement("div", { dangerouslySetInnerHTML: { __html: node.value } })
|
|
74
|
+
: react_1.default.createElement("code", null, node.value),
|
|
75
|
+
HTMLInline: (node) => this.options.converterOptions?.allowDangerousHtml
|
|
76
|
+
? react_1.default.createElement("span", { dangerouslySetInnerHTML: { __html: node.value } })
|
|
77
|
+
: react_1.default.createElement("code", null, node.value),
|
|
78
|
+
//For footnote
|
|
79
|
+
FootnoteRef: (node) => {
|
|
80
|
+
const idx = this.footNoteResolver.getUsedRefById(node.id);
|
|
81
|
+
return react_1.default.createElement("sup", { id: `fnref:${idx}` }, react_1.default.createElement("a", { href: `#fn:${idx}`, className: "footnote-ref" }, `[${idx}]`));
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
return (this.options.renderOptions?.elements?.[type] ?? defaultRender[type]);
|
|
85
|
+
}
|
|
86
|
+
renderTable(node, children) {
|
|
87
|
+
if (node.type === "Table") {
|
|
88
|
+
const header = node.rows.filter(row => row.isHeader);
|
|
89
|
+
const body = node.rows.filter(row => !row.isHeader);
|
|
90
|
+
const renderRows = (row, rowIndex) => {
|
|
91
|
+
return react_1.default.createElement("tr", { key: rowIndex }, row.cells.map((cell, cellIndex) => {
|
|
92
|
+
const tag = row.isHeader ? "th" : "td";
|
|
93
|
+
return react_1.default.createElement(tag, { key: cellIndex, style: { textAlign: cell.align } }, ...cell.children.map(c => this.render(c)));
|
|
94
|
+
}));
|
|
95
|
+
};
|
|
96
|
+
const tHead = header.length
|
|
97
|
+
? react_1.default.createElement("thead", null, header.map((row, i) => renderRows(row, i)))
|
|
98
|
+
: null;
|
|
99
|
+
const tBody = body.length
|
|
100
|
+
? react_1.default.createElement("tbody", null, body.map((row, i) => renderRows(row, i)))
|
|
101
|
+
: null;
|
|
102
|
+
return react_1.default.createElement("table", null, tHead, tBody);
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
return react_1.default.createElement("p", null, ...children);
|
|
106
|
+
}
|
|
107
|
+
renderFootnotes() {
|
|
108
|
+
if (this.footNoteResolver.isResolverValid()) {
|
|
109
|
+
const used = this.footNoteResolver.getUsedRef();
|
|
110
|
+
if (used.length === 0)
|
|
111
|
+
return null;
|
|
112
|
+
const items = used.map((id, i) => {
|
|
113
|
+
const def = this.footNoteResolver.getDef(id) ?? "";
|
|
114
|
+
const idx = i + 1;
|
|
115
|
+
return react_1.default.createElement("li", { id: `fn:${idx}` }, react_1.default.createElement("p", null, def + " ", react_1.default.createElement("a", { href: `#fnref:${idx}`, className: "footnote-backref" }, "↩")));
|
|
116
|
+
});
|
|
117
|
+
return react_1.default.createElement("section", { className: "footnotes" }, react_1.default.createElement("ol", null, ...items));
|
|
118
|
+
}
|
|
119
|
+
else
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.default = ReactRenderer;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* General option for the render process.
|
|
3
|
+
* These rules define high-level behavior for render process.
|
|
4
|
+
*/
|
|
5
|
+
export type ConvertOption = {
|
|
6
|
+
/**
|
|
7
|
+
* Allow raw HTML rendered in the Markdown input.
|
|
8
|
+
* When false (default), HTML tags are escaped for security
|
|
9
|
+
*/
|
|
10
|
+
allowDangerousHtml: boolean;
|
|
11
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ConvertOption } from "./converterOptions";
|
|
2
|
+
import { RenderOption } from './renderOptions';
|
|
3
|
+
/**
|
|
4
|
+
* General option for rendering Markdown into HTML strings
|
|
5
|
+
* @template TOutput - Output type after rendered
|
|
6
|
+
*/
|
|
7
|
+
export interface MarkdownOptions<TOutput> {
|
|
8
|
+
renderOptions?: RenderOption<TOutput>;
|
|
9
|
+
converterOptions?: ConvertOption;
|
|
10
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Node } from "../node";
|
|
2
|
+
/**
|
|
3
|
+
* Function type for rendering an AST node to a ReactNode.
|
|
4
|
+
*
|
|
5
|
+
* @template T - A subtype of `Node` corresponding to the render node
|
|
6
|
+
* @param node - The AST node to render
|
|
7
|
+
* @param children - An array of rendered `ReactNode` from the node's children
|
|
8
|
+
* @returns A `React.ReactNode` representation of the node
|
|
9
|
+
*/
|
|
10
|
+
type ReactNodeRenderer<T extends Node = Node> = (node: T, children: React.ReactNode[]) => React.ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* A mapping of AST node types to custom render functions.
|
|
13
|
+
*
|
|
14
|
+
* - The key is a `Node["type"]` string literal (e.g. `"Header"`, `"Paragraph"`)
|
|
15
|
+
* - The value is a function `ReactNodeRenderer` function:
|
|
16
|
+
* - `node` is a `Node` with its attribute depending on its `type`.
|
|
17
|
+
* (e.g. `"Header"` nodes include `level`, `"CodeBlock"` nodes include `lang` and `content`, etc)
|
|
18
|
+
* - `children` is the array of rendered `ReactNode` of its children.
|
|
19
|
+
*/
|
|
20
|
+
export type ReactRenderElements = {
|
|
21
|
+
[K in Node["type"]]?: ReactNodeRenderer<Extract<Node, {
|
|
22
|
+
type: K;
|
|
23
|
+
}>>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Options to customize how AST nodes are renderes into ReactNode elements
|
|
27
|
+
*
|
|
28
|
+
* @property elements - Optional custom rendered for one or more node types
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* // Using JSX (Recommended for most users)
|
|
33
|
+
* const renderOptions: ReactRenderOption = {
|
|
34
|
+
* elements: {
|
|
35
|
+
* Paragraph: (_node, children) => <p className="paragraph">{children}</p>,
|
|
36
|
+
* Bold: (_node, children) => <strong className="bold-text">{children}</strong>,
|
|
37
|
+
* }
|
|
38
|
+
* }
|
|
39
|
+
* // Or using React.createElement (Common in library core or without JSX)
|
|
40
|
+
* const renderOptions: ReactRenderOption = {
|
|
41
|
+
* elements: {
|
|
42
|
+
* Bold: (_node, children) => React.createElement("b", { className: "bold" }, ...children),
|
|
43
|
+
* }
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export type ReactRenderOption = {
|
|
48
|
+
elements?: ReactRenderElements;
|
|
49
|
+
};
|
|
50
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ASTNode } from '../parser';
|
|
2
|
+
/**
|
|
3
|
+
* A strategy function type for rendering a specific AST node.
|
|
4
|
+
* @template TOutput - The resulting type after rendered.
|
|
5
|
+
* @template TNode - The specific `ASTNode` processed.
|
|
6
|
+
*
|
|
7
|
+
* @param node - The `ASTNode` object containing its properties.
|
|
8
|
+
* @param children - An array of already rendered `TOutput` of this node's childrens.
|
|
9
|
+
* @returns The rendered result for the given node.
|
|
10
|
+
*
|
|
11
|
+
* @since v.1.2.0 - Introduced `GenericNodeRenderer` for defining `RenderElement`
|
|
12
|
+
*/
|
|
13
|
+
type GenericNodeRenderer<TOutput, TNode extends ASTNode = ASTNode> = (node: TNode, children: TOutput[]) => TOutput;
|
|
14
|
+
/**
|
|
15
|
+
* A mapping of `ASTNode` types to their coressponding rendering functions.
|
|
16
|
+
* @template TOutput - The target output type of the renderer.
|
|
17
|
+
*/
|
|
18
|
+
export type GenericRenderElements<TOutput> = {
|
|
19
|
+
[K in ASTNode["type"]]?: GenericNodeRenderer<TOutput, ASTNode>;
|
|
20
|
+
} & {
|
|
21
|
+
[key: string]: GenericNodeRenderer<TOutput, any> | undefined;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Options for customizing the rendering process for a specific output type.
|
|
25
|
+
* @template TOutput - The output type.
|
|
26
|
+
*/
|
|
27
|
+
export interface RenderOption<TOutput> {
|
|
28
|
+
elements?: GenericRenderElements<TOutput>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* An utilities type alias for render `ASTNode` to `HTML string`
|
|
32
|
+
* Equivalent to `GenericRenderElements<string>`.
|
|
33
|
+
* @alias DefaultRenderElements
|
|
34
|
+
*/
|
|
35
|
+
export type DefaultRenderElements = GenericRenderElements<string>;
|
|
36
|
+
/**
|
|
37
|
+
* An utilities type alias for render `ASTNode` to `React.ReactNode`
|
|
38
|
+
* Equivalent to `GenericRenderElements<React.ReactNode>`.
|
|
39
|
+
* @alias ReactRenderElements
|
|
40
|
+
*/
|
|
41
|
+
export type ReactRenderElements = GenericRenderElements<React.ReactNode>;
|
|
42
|
+
/**
|
|
43
|
+
* Options to customize how AST nodes are renderes into HTML
|
|
44
|
+
* @deprecated Use {@link RenderOption<string>} for generic support.
|
|
45
|
+
*
|
|
46
|
+
* @property elements - Optional custom rendered for one or more node types
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const renderOptions: RenderOption = {
|
|
50
|
+
* elements: {
|
|
51
|
+
* Paragraph: (_node, children) => `<div class="paragraph">${children.join("")}</div>`,
|
|
52
|
+
* Bold: (_node, children) => `<b class="bold-text">${children.join("")}</b>`,
|
|
53
|
+
* }
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
*/
|
|
58
|
+
export type DefaultRenderOption = {
|
|
59
|
+
elements?: DefaultRenderElements;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Options to customize how AST nodes are renderes into ReactNode elements
|
|
63
|
+
* @deprecated Use {@link RenderOption<React.ReactNode>} for generic support.
|
|
64
|
+
*
|
|
65
|
+
* @property elements - Optional custom rendered for one or more node types
|
|
66
|
+
* @example
|
|
67
|
+
* ```tsx
|
|
68
|
+
* // Using JSX (Recommended for most users)
|
|
69
|
+
* const renderOptions: ReactRenderOption = {
|
|
70
|
+
* elements: {
|
|
71
|
+
* Paragraph: (_node, children) => <p className="paragraph">{children}</p>,
|
|
72
|
+
* Bold: (_node, children) => <strong className="bold-text">{children}</strong>,
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
* // Or using React.createElement (Common in library core or without JSX)
|
|
76
|
+
* const renderOptions: ReactRenderOption = {
|
|
77
|
+
* elements: {
|
|
78
|
+
* Bold: (_node, children) => React.createElement("b", { className: "bold" }, ...children),
|
|
79
|
+
* }
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export type ReactRenderOption = {
|
|
84
|
+
elements?: ReactRenderElements;
|
|
85
|
+
};
|
|
86
|
+
export {};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { IParser } from '../core/parser/index';
|
|
2
|
+
import { Token } from './token';
|
|
3
|
+
/**
|
|
4
|
+
* AST (Abstract Syntax Tree) node definition.
|
|
5
|
+
*
|
|
6
|
+
* Each node represents a Markdown construct and some special nodes (Document, Paragraph).
|
|
7
|
+
* Some nodes are containers (have `children`), while others are leaf nodes (contain text).
|
|
8
|
+
*
|
|
9
|
+
* Variants:
|
|
10
|
+
* - Document: Root node, contains all other nodes.
|
|
11
|
+
* - Paragraph: A block of text, contain inline nodes.
|
|
12
|
+
* - Header: A header with given `level` (1-6)
|
|
13
|
+
* - Bold: Bold text
|
|
14
|
+
* - Italic: Italic text
|
|
15
|
+
* - Strikethrough: Strilethrough text
|
|
16
|
+
* - InlineCode: Inline code snippet, with it's `content`
|
|
17
|
+
* - Quote: A quote block
|
|
18
|
+
* - CodeBlock: A code block, with it's `lang` and `content`
|
|
19
|
+
* - List: A list, with it's level and children
|
|
20
|
+
* - ListItem: An item of a list, with it's children
|
|
21
|
+
* - TaskItem: An item for tasklist, with it's checked state
|
|
22
|
+
* - Link: A link, with it's `text` and `href`
|
|
23
|
+
* - Image: An image, with it's `src` and `alt`
|
|
24
|
+
* - HorizontalLine: A horizontal line
|
|
25
|
+
* - Text: Raw text content.
|
|
26
|
+
* - Table: A table, with it's rows
|
|
27
|
+
* - HTMLBlock: A HTML block element, with it's `value`
|
|
28
|
+
* - HTMLInline: An inline HTML element, with it's `value`
|
|
29
|
+
* - FootnoteRef: A refernce with it's `id`
|
|
30
|
+
*
|
|
31
|
+
* {@link ASTNode} for each variant's attribute listed detail
|
|
32
|
+
*/
|
|
33
|
+
export type DefaultNodeType = "Document" | "Paragraph" | "Header" | "Bold" | "Italic" | "Strikethrough" | "InlineCode" | "CodeBlock" | "Quote" | "List" | "ListItem" | "TaskItem" | "Link" | "Image" | "HorizontalLine" | "Text" | "Table" | "HTMLBlock" | "HTMLInline" | "FootnoteRef";
|
|
34
|
+
export type NodeType = DefaultNodeType | (string & {});
|
|
35
|
+
export interface TableCell {
|
|
36
|
+
align: "left" | "center" | "right";
|
|
37
|
+
children: ASTNode[];
|
|
38
|
+
}
|
|
39
|
+
export interface TableRow {
|
|
40
|
+
isHeader: boolean;
|
|
41
|
+
cells: TableCell[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* AST (Abstract Syntax Tree) node definition. It representing all possible Markdown constructs and its possible properties.
|
|
45
|
+
*
|
|
46
|
+
* Use the `type` property to determine which other properties are available.
|
|
47
|
+
*/
|
|
48
|
+
export interface ASTNode {
|
|
49
|
+
/**
|
|
50
|
+
* The type of the node, determining its structure and purpose.
|
|
51
|
+
* @see {@link DefaultNodeType} for standard types.
|
|
52
|
+
*/
|
|
53
|
+
type: NodeType;
|
|
54
|
+
/**
|
|
55
|
+
* Nested nodes within this container.
|
|
56
|
+
* Applicable for: `Document`, `Paragraph`, `Header`, `Bold`, `Italic`, `Strikethrough`, `Quote`, `List`, `ListItem`, `TaskItem`.
|
|
57
|
+
*/
|
|
58
|
+
children?: ASTNode[];
|
|
59
|
+
/**
|
|
60
|
+
* Raw string content.
|
|
61
|
+
* Applicable for: `Text` (the plain text), `HTMLBlock` & `HTMLInline` (the raw HTML code).
|
|
62
|
+
*/
|
|
63
|
+
value?: string;
|
|
64
|
+
/**
|
|
65
|
+
* The internal content of a code element.
|
|
66
|
+
* Applicable for: `InlineCode`, `CodeBlock`.
|
|
67
|
+
*/
|
|
68
|
+
content?: string;
|
|
69
|
+
/**
|
|
70
|
+
* The level of importance/depth.
|
|
71
|
+
* Applicable for: `Header` (1-6).
|
|
72
|
+
*/
|
|
73
|
+
level?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Programming language identifier for syntax highlighting.
|
|
76
|
+
* Applicable for: `CodeBlock` (e.g., "javascript", "python").
|
|
77
|
+
*/
|
|
78
|
+
lang?: string;
|
|
79
|
+
/**
|
|
80
|
+
* URL or path for external resources.
|
|
81
|
+
* Applicable for: `Link` (destination), `Image` (source).
|
|
82
|
+
*/
|
|
83
|
+
href?: string;
|
|
84
|
+
/**
|
|
85
|
+
* The clickable text label for a link.
|
|
86
|
+
* Applicable for: `Link`.
|
|
87
|
+
*/
|
|
88
|
+
text?: string;
|
|
89
|
+
/**
|
|
90
|
+
* Source URL of an image.
|
|
91
|
+
* Applicable for: `Image`.
|
|
92
|
+
*/
|
|
93
|
+
src?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Alternative text for accessibility.
|
|
96
|
+
* Applicable for: `Image`.
|
|
97
|
+
*/
|
|
98
|
+
alt?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Indicates if a list is numbered (true) or bulleted (false).
|
|
101
|
+
* Applicable for: `List`.
|
|
102
|
+
*/
|
|
103
|
+
ordered?: boolean;
|
|
104
|
+
/**
|
|
105
|
+
* The completion status of a task.
|
|
106
|
+
* Applicable for: `TaskItem`.
|
|
107
|
+
*/
|
|
108
|
+
checked?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Array of rows defining the table structure.
|
|
111
|
+
* Applicable for: `Table`.
|
|
112
|
+
*/
|
|
113
|
+
rows?: TableRow[];
|
|
114
|
+
/**
|
|
115
|
+
* Unique identifier for referencing.
|
|
116
|
+
* Applicable for: `FootnoteRef`.
|
|
117
|
+
*/
|
|
118
|
+
id?: string;
|
|
119
|
+
/**
|
|
120
|
+
* Allows custom properties for extensions or plugin-specific data.
|
|
121
|
+
*/
|
|
122
|
+
[key: string]: any;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A Strategy pattern for handle parsing process for each Token.
|
|
126
|
+
* @property type - Strategy's type
|
|
127
|
+
* @property execute - A function handle parsing a `Token` to `ASTNode`
|
|
128
|
+
*/
|
|
129
|
+
export interface ParsingStrategy {
|
|
130
|
+
type: string;
|
|
131
|
+
execute: (parser: IParser, token: Token) => ASTNode | ASTNode[] | void;
|
|
132
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IRenderer } from "../renderers";
|
|
2
|
+
import { ASTNode } from "./parser";
|
|
3
|
+
/**
|
|
4
|
+
* A Strategy pattern for handle parsing process for each ASTNode.
|
|
5
|
+
* @template TOutput - Output result after rendered by `render` property.
|
|
6
|
+
* @property type - Strategy's type
|
|
7
|
+
* @property execute - A function handle rendering a `ASTNode` to `TOutput`
|
|
8
|
+
*/
|
|
9
|
+
export interface RenderStrategy<TOutput> {
|
|
10
|
+
type: string;
|
|
11
|
+
render: (node: ASTNode, children: TOutput[], context: IRenderer<TOutput>) => TOutput;
|
|
12
|
+
}
|