simple-customize-markdown-converter 1.1.0 → 1.2.1

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 (40) hide show
  1. package/README.md +47 -0
  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/parser/handler.d.ts +19 -0
  7. package/dist/core/parser/handler.js +254 -0
  8. package/dist/core/parser/index.d.ts +33 -0
  9. package/dist/core/parser/index.js +149 -0
  10. package/dist/core/resolver/footnote-resolver.d.ts +15 -0
  11. package/dist/core/resolver/footnote-resolver.js +36 -0
  12. package/dist/index.d.ts +6 -5
  13. package/dist/index.js +4 -4
  14. package/dist/react.d.ts +3 -6
  15. package/dist/react.js +5 -5
  16. package/dist/renderers/default/handler.d.ts +21 -0
  17. package/dist/renderers/default/handler.js +159 -0
  18. package/dist/renderers/default/index.d.ts +14 -0
  19. package/dist/renderers/default/index.js +119 -0
  20. package/dist/renderers/index.d.ts +10 -0
  21. package/dist/renderers/index.js +2 -0
  22. package/dist/renderers/react/handler.d.ts +22 -0
  23. package/dist/renderers/react/handler.js +140 -0
  24. package/dist/renderers/react/index.d.ts +15 -0
  25. package/dist/renderers/react/index.js +124 -0
  26. package/dist/types/options/converterOptions.d.ts +1 -1
  27. package/dist/types/options/index.d.ts +5 -12
  28. package/dist/types/options/renderOptions.d.ts +70 -21
  29. package/dist/types/parser.d.ts +132 -0
  30. package/dist/types/parser.js +2 -0
  31. package/dist/types/renderer.d.ts +12 -0
  32. package/dist/types/renderer.js +2 -0
  33. package/dist/types/token.d.ts +94 -74
  34. package/dist/utilities/parser-utils.d.ts +5 -0
  35. package/dist/utilities/parser-utils.js +65 -0
  36. package/dist/utilities/renderer-utils.d.ts +4 -0
  37. package/dist/utilities/renderer-utils.js +20 -0
  38. package/dist/utilities/tokenizer-utils.d.ts +11 -0
  39. package/dist/utilities/tokenizer-utils.js +159 -0
  40. package/package.json +5 -3
@@ -1,32 +1,56 @@
1
- import { Node } from "../node";
1
+ import { ASTNode, NodeType } from '../parser';
2
2
  /**
3
- * Function type for rendering an AST node to HTML.
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.
4
6
  *
5
- * @template T - A subtype of `Node` corresponding to the render node
6
- * @param node - The AST node to render
7
- * @param children - Rendered HTML strings of the node's children
8
- * @returns A HTML string representation of the node
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`
9
12
  */
10
- type NodeRenderer<T extends Node = Node> = (node: T, children: string[]) => string;
13
+ type GenericNodeRenderer<TOutput, TNode extends ASTNode = ASTNode> = (node: TNode, children: TOutput[]) => TOutput;
11
14
  /**
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 `(node, children) => string`:
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 strings of its children.
15
+ * A mapping of `ASTNode` types to their coressponding rendering functions.
16
+ * @template TOutput - The target output type of the renderer.
19
17
  */
20
- export type RenderElements = {
21
- [K in Node["type"]]?: NodeRenderer<Extract<Node, {
22
- type: K;
23
- }>>;
18
+ export type GenericRenderElements<TOutput> = {
19
+ [K in ASTNode["type"]]?: GenericNodeRenderer<TOutput, ASTNode>;
20
+ } & {
21
+ [key: string]: GenericNodeRenderer<TOutput, any> | undefined;
24
22
  };
23
+ /**
24
+ * Options for customizing the rendering process for a specific output type.
25
+ * @template TOutput - The output type.
26
+ * @property className - Customize classname for one or more ASTNode types.
27
+ * @property elements - Optional custom rendered for one or more ASTNode types
28
+ */
29
+ export interface RenderOption<TOutput> {
30
+ /**
31
+ * Use "Header" for all levels
32
+ * Use additional "Header1", "Header2"... "Header6" for specific header level
33
+ */
34
+ className?: Partial<Record<NodeType | "Header1" | "Header2" | "Header3" | "Header4" | "Header5" | "Header6", string>>;
35
+ elements?: GenericRenderElements<TOutput>;
36
+ }
37
+ /**
38
+ * An utilities type alias for render `ASTNode` to `HTML string`
39
+ * Equivalent to `GenericRenderElements<string>`.
40
+ * @alias DefaultRenderElements
41
+ */
42
+ export type DefaultRenderElements = GenericRenderElements<string>;
43
+ /**
44
+ * An utilities type alias for render `ASTNode` to `React.ReactNode`
45
+ * Equivalent to `GenericRenderElements<React.ReactNode>`.
46
+ * @alias ReactRenderElements
47
+ */
48
+ export type ReactRenderElements = GenericRenderElements<React.ReactNode>;
25
49
  /**
26
50
  * Options to customize how AST nodes are renderes into HTML
51
+ * @deprecated Use {@link RenderOption<string>} instead to use newer options.
27
52
  *
28
53
  * @property elements - Optional custom rendered for one or more node types
29
- *
30
54
  * @example
31
55
  * ```ts
32
56
  * const renderOptions: RenderOption = {
@@ -38,7 +62,32 @@ export type RenderElements = {
38
62
  * ```
39
63
  *
40
64
  */
41
- export type RenderOption = {
42
- elements?: RenderElements;
65
+ export type DefaultRenderOption = {
66
+ elements?: DefaultRenderElements;
67
+ };
68
+ /**
69
+ * Options to customize how AST nodes are renderes into ReactNode elements
70
+ * @deprecated Use {@link RenderOption<React.ReactNode>} instead to use newer options.
71
+ *
72
+ * @property elements - Optional custom rendered for one or more node types
73
+ * @example
74
+ * ```tsx
75
+ * // Using JSX (Recommended for most users)
76
+ * const renderOptions: ReactRenderOption = {
77
+ * elements: {
78
+ * Paragraph: (_node, children) => <p className="paragraph">{children}</p>,
79
+ * Bold: (_node, children) => <strong className="bold-text">{children}</strong>,
80
+ * }
81
+ * }
82
+ * // Or using React.createElement (Common in library core or without JSX)
83
+ * const renderOptions: ReactRenderOption = {
84
+ * elements: {
85
+ * Bold: (_node, children) => React.createElement("b", { className: "bold" }, ...children),
86
+ * }
87
+ * }
88
+ * ```
89
+ */
90
+ export type ReactRenderOption = {
91
+ elements?: ReactRenderElements;
43
92
  };
44
93
  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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,3 +1,4 @@
1
+ import { ILexer } from "../core/lexer";
1
2
  /**
2
3
  * Token produced by the Markdown lexer.
3
4
  *
@@ -33,77 +34,96 @@
33
34
  * - FootnodeRef: The reference of a footnote
34
35
  * - EOF: A special token, this is the end of input.
35
36
  */
36
- export type Token = {
37
- type: "Header";
38
- level: number;
39
- } | {
40
- type: "CodeBlock";
41
- lang: string;
42
- content: string;
43
- } | {
44
- type: "NewLine";
45
- } | {
46
- type: "Bold";
47
- } | {
48
- type: "Italic";
49
- } | {
50
- type: "Strikethrough";
51
- } | {
52
- type: "InlineCode";
53
- content: string;
54
- } | {
55
- type: "Quote";
56
- } | {
57
- type: "ListStart";
58
- ordered: boolean;
59
- level: number;
60
- } | {
61
- type: "ListItem";
62
- } | {
63
- type: "TaskItem";
64
- checked: boolean;
65
- } | {
66
- type: "ListEnd";
67
- } | {
68
- type: "Link";
69
- text: string;
70
- href: string;
71
- } | {
72
- type: "Image";
73
- src: string;
74
- alt: string;
75
- } | {
76
- type: "HorizontalLine";
77
- } | {
78
- type: "Text";
79
- value: string;
80
- } | {
81
- type: "TableStart";
82
- } | {
83
- type: "TableEnd";
84
- } | {
85
- type: "RowStart";
86
- isHeader: boolean;
87
- } | {
88
- type: "RowEnd";
89
- } | {
90
- type: "CellStart";
91
- align: "left" | "center" | "right";
92
- } | {
93
- type: "CellEnd";
94
- } | {
95
- type: "HTMLBlock";
96
- value: string;
97
- } | {
98
- type: "HTMLInline";
99
- value: string;
100
- } | {
101
- type: "FootnoteDef";
102
- id: string;
103
- content: string;
104
- } | {
105
- type: "FootnoteRef";
106
- id: string;
107
- } | {
108
- type: "EOF";
109
- };
37
+ export type DefaultTokenType = "Header" | "CodeBlock" | "NewLine" | "Bold" | "Italic" | "Strikethrough" | "InlineCode" | "Quote" | "ListStart" | "ListItem" | "TaskItem" | "ListEnd" | "Link" | "Image" | "HorizontalLine" | "Text" | "TableStart" | "TableEnd" | "RowStart" | "RowEnd" | "CellStart" | "CellEnd" | "HTMLBlock" | "HTMLInline" | "FootnoteDef" | "FootnoteRef" | "EOF";
38
+ export type TokenType = DefaultTokenType | (string & {});
39
+ /**
40
+ * Token produced by the Markdown lexer.
41
+ *
42
+ * Tokens are the intermediate representation between raw text and the AST.
43
+ */
44
+ export interface Token {
45
+ /**
46
+ * The category of the token.
47
+ * @see {@link DefaultTokenType} for the list of built-in types.
48
+ */
49
+ type: TokenType;
50
+ /**
51
+ * Raw text or value associated with the token.
52
+ * Commonly used in: `Text`, `HTMLBlock`, `HTMLInline`.
53
+ */
54
+ value?: string;
55
+ /**
56
+ * The nesting level or importance.
57
+ * Commonly used in: `Header` (level 1-6).
58
+ */
59
+ level?: number;
60
+ /**
61
+ * Language identifier for code fences.
62
+ * Commonly used in: `CodeBlock`.
63
+ */
64
+ lang?: string;
65
+ /**
66
+ * The main body of text within a token.
67
+ * Commonly used in: `CodeBlock`, `InlineCode`.
68
+ */
69
+ content?: string;
70
+ /**
71
+ * Destination URL for links or images.
72
+ * Commonly used in: `Link`, `Image`.
73
+ */
74
+ href?: string;
75
+ /**
76
+ * Display text for links.
77
+ * Commonly used in: `Link`.
78
+ */
79
+ text?: string;
80
+ /**
81
+ * Source path/URL for images.
82
+ * Commonly used in: `Image`.
83
+ */
84
+ src?: string;
85
+ /**
86
+ * Accessible alternative text.
87
+ * Commonly used in: `Image`.
88
+ */
89
+ alt?: string;
90
+ /**
91
+ * Alignment for table cells.
92
+ * Commonly used in: `CellStart` ("left", "center", "right").
93
+ */
94
+ align?: "left" | "center" | "right";
95
+ /**
96
+ * For task lists, indicates completion.
97
+ * Commonly used in: `TaskItem` (true if `[x]`).
98
+ */
99
+ checked?: boolean;
100
+ /**
101
+ * Unique identifier for footnotes.
102
+ * Commonly used in: `FootnoteDef`, `FootnoteRef`.
103
+ */
104
+ id?: string;
105
+ /**
106
+ * All other properties used by custom `Token`.
107
+ */
108
+ [key: string]: any;
109
+ }
110
+ /**
111
+ * A Strategy pattern for handle tokenizing input.
112
+ * @property type - Strategy's type.
113
+ * @property match - A function check current cursor position matched the syntax to be processed by `emit` function.
114
+ * @property emit - A function handle tokenizing input to `Token`.
115
+ */
116
+ export interface TokenizerStrategy {
117
+ name: string;
118
+ /**
119
+ * Checks if the current cursor position in the Lexer matches this syntax
120
+ * @param lex The current `ILexer` instance providing access to the input string and cursor.
121
+ * @returns True if this strategy should handle the current input.
122
+ */
123
+ match: (lex: ILexer) => boolean;
124
+ /**
125
+ * Consumes the input and produce Tokens added in ILexer implementation class.
126
+ * @param lex The `ILexer` instance to advance the cursor and store results.
127
+ */
128
+ emit: (lex: ILexer) => void;
129
+ }
@@ -0,0 +1,5 @@
1
+ import { IParser } from "../core/parser";
2
+ import { ASTNode } from "../types/parser";
3
+ declare function parseList(parser: IParser): ASTNode;
4
+ declare function parseListItem(parser: IParser): ASTNode;
5
+ export { parseList, parseListItem };
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseList = parseList;
4
+ exports.parseListItem = parseListItem;
5
+ function parseList(parser) {
6
+ const tok = parser.peek(0);
7
+ if (tok?.type === "ListStart") {
8
+ parser.next(1); //skip marker
9
+ const result = {
10
+ type: "List",
11
+ level: tok.level,
12
+ ordered: tok.ordered,
13
+ children: [],
14
+ };
15
+ let nextToken = parser.peek(0);
16
+ while (!parser.isEnd()) {
17
+ if (nextToken?.type === "ListItem" || nextToken?.type === "TaskItem") {
18
+ result.children?.push(parseListItem(parser));
19
+ nextToken = parser.peek(0);
20
+ }
21
+ else if (nextToken?.type === "ListEnd") {
22
+ parser.next(1);
23
+ break;
24
+ }
25
+ else
26
+ break;
27
+ }
28
+ return result;
29
+ }
30
+ //Temp return
31
+ return {
32
+ type: "Text",
33
+ value: ""
34
+ };
35
+ }
36
+ function parseListItem(parser) {
37
+ const currentToken = parser.peek(0);
38
+ parser.next(1); // skip marker
39
+ const children = [];
40
+ while (!parser.isEnd()) {
41
+ const tok = parser.peek(0);
42
+ if (!tok)
43
+ break;
44
+ if (tok.type === "NewLine") {
45
+ parser.next(1);
46
+ break;
47
+ }
48
+ if (tok.type === "ListStart") {
49
+ children.push(parseList(parser));
50
+ continue;
51
+ }
52
+ if (["ListItem", "TaskItem", "ListEnd"].includes(tok.type)) {
53
+ break;
54
+ }
55
+ children.push(...parser.parseInlineUntil("NewLine", true));
56
+ }
57
+ return currentToken?.type === "TaskItem" ? {
58
+ type: "TaskItem",
59
+ checked: currentToken.type === "TaskItem" ? currentToken.checked : false,
60
+ children: children
61
+ } : {
62
+ type: "ListItem",
63
+ children: children
64
+ };
65
+ }
@@ -0,0 +1,4 @@
1
+ import { IRenderer } from "../renderers";
2
+ import { ASTNode } from "../types/parser";
3
+ export declare function escapeHtml(str: string): string;
4
+ export declare function getClassName<TOutput>(renderer: IRenderer<TOutput>, node: ASTNode, defaultClass?: string): string | undefined;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.escapeHtml = escapeHtml;
4
+ exports.getClassName = getClassName;
5
+ function escapeHtml(str) {
6
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
7
+ }
8
+ function getClassName(renderer, node, defaultClass = "") {
9
+ const { type, level } = node;
10
+ const classNames = renderer.options.renderOptions?.className;
11
+ if (!classNames)
12
+ return defaultClass || undefined;
13
+ if (type === "Header" && level) {
14
+ const levelClass = classNames[`Header${level}`];
15
+ if (levelClass)
16
+ return `${defaultClass} ${levelClass}`.trim();
17
+ }
18
+ const typeClass = classNames[type];
19
+ return [defaultClass, typeClass].filter(Boolean).join(" ") || undefined;
20
+ }
@@ -0,0 +1,11 @@
1
+ import { ILexer } from "../core/lexer";
2
+ declare function handleTextBlock(lex: ILexer): void;
3
+ declare function handleHtmlBlock(lex: ILexer): void;
4
+ declare function handleHtmlInline(lex: ILexer): void;
5
+ declare function handleList(lex: ILexer, isOrdered: boolean, isTask: boolean): void;
6
+ declare function handleStartList(lex: ILexer, isOrder: boolean): void;
7
+ declare function handleListItem(lex: ILexer): void;
8
+ declare function handleTaskItem(lex: ILexer, isChecked: boolean): void;
9
+ declare function handleEndList(lex: ILexer): void;
10
+ declare function handleTable(lex: ILexer): void;
11
+ export { handleEndList, handleHtmlBlock, handleHtmlInline, handleList, handleListItem, handleStartList, handleTable, handleTaskItem, handleTextBlock, };