comark 0.0.1 → 0.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 +104 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -0
- package/dist/internal/frontmatter.d.ts +16 -0
- package/dist/internal/frontmatter.js +43 -0
- package/dist/internal/parse/auto-close/index.d.ts +12 -0
- package/dist/internal/parse/auto-close/index.js +457 -0
- package/dist/internal/parse/auto-close/table.d.ts +4 -0
- package/dist/internal/parse/auto-close/table.js +161 -0
- package/dist/internal/parse/auto-unwrap.d.ts +20 -0
- package/dist/internal/parse/auto-unwrap.js +42 -0
- package/dist/internal/parse/html/html_block_rule.d.ts +2 -0
- package/dist/internal/parse/html/html_block_rule.js +60 -0
- package/dist/internal/parse/html/html_blocks.d.ts +2 -0
- package/dist/internal/parse/html/html_blocks.js +66 -0
- package/dist/internal/parse/html/html_inline_rule.d.ts +2 -0
- package/dist/internal/parse/html/html_inline_rule.js +43 -0
- package/dist/internal/parse/html/html_re.d.ts +3 -0
- package/dist/internal/parse/html/html_re.js +18 -0
- package/dist/internal/parse/html/index.d.ts +18 -0
- package/dist/internal/parse/html/index.js +122 -0
- package/dist/internal/parse/incremental.d.ts +12 -0
- package/dist/internal/parse/incremental.js +39 -0
- package/dist/internal/parse/token-processor.d.ts +9 -0
- package/dist/internal/parse/token-processor.js +803 -0
- package/dist/internal/props-validation.d.ts +12 -0
- package/dist/internal/props-validation.js +112 -0
- package/dist/internal/stringify/attributes.d.ts +21 -0
- package/dist/internal/stringify/attributes.js +67 -0
- package/dist/internal/stringify/handlers/a.d.ts +3 -0
- package/dist/internal/stringify/handlers/a.js +11 -0
- package/dist/internal/stringify/handlers/blockquote.d.ts +3 -0
- package/dist/internal/stringify/handlers/blockquote.js +18 -0
- package/dist/internal/stringify/handlers/br.d.ts +3 -0
- package/dist/internal/stringify/handlers/br.js +3 -0
- package/dist/internal/stringify/handlers/code.d.ts +3 -0
- package/dist/internal/stringify/handlers/code.js +11 -0
- package/dist/internal/stringify/handlers/comment.d.ts +3 -0
- package/dist/internal/stringify/handlers/comment.js +6 -0
- package/dist/internal/stringify/handlers/del.d.ts +3 -0
- package/dist/internal/stringify/handlers/del.js +4 -0
- package/dist/internal/stringify/handlers/emphesis.d.ts +3 -0
- package/dist/internal/stringify/handlers/emphesis.js +13 -0
- package/dist/internal/stringify/handlers/heading.d.ts +3 -0
- package/dist/internal/stringify/handlers/heading.js +7 -0
- package/dist/internal/stringify/handlers/hr.d.ts +3 -0
- package/dist/internal/stringify/handlers/hr.js +3 -0
- package/dist/internal/stringify/handlers/html.d.ts +3 -0
- package/dist/internal/stringify/handlers/html.js +73 -0
- package/dist/internal/stringify/handlers/img.d.ts +3 -0
- package/dist/internal/stringify/handlers/img.js +9 -0
- package/dist/internal/stringify/handlers/index.d.ts +2 -0
- package/dist/internal/stringify/handlers/index.js +56 -0
- package/dist/internal/stringify/handlers/li.d.ts +3 -0
- package/dist/internal/stringify/handlers/li.js +43 -0
- package/dist/internal/stringify/handlers/math.d.ts +3 -0
- package/dist/internal/stringify/handlers/math.js +8 -0
- package/dist/internal/stringify/handlers/mdc.d.ts +3 -0
- package/dist/internal/stringify/handlers/mdc.js +47 -0
- package/dist/internal/stringify/handlers/mermaid.d.ts +3 -0
- package/dist/internal/stringify/handlers/mermaid.js +8 -0
- package/dist/internal/stringify/handlers/ol.d.ts +3 -0
- package/dist/internal/stringify/handlers/ol.js +18 -0
- package/dist/internal/stringify/handlers/p.d.ts +3 -0
- package/dist/internal/stringify/handlers/p.js +8 -0
- package/dist/internal/stringify/handlers/pre.d.ts +3 -0
- package/dist/internal/stringify/handlers/pre.js +60 -0
- package/dist/internal/stringify/handlers/strong.d.ts +3 -0
- package/dist/internal/stringify/handlers/strong.js +13 -0
- package/dist/internal/stringify/handlers/table.d.ts +8 -0
- package/dist/internal/stringify/handlers/table.js +180 -0
- package/dist/internal/stringify/handlers/template.d.ts +3 -0
- package/dist/internal/stringify/handlers/template.js +14 -0
- package/dist/internal/stringify/handlers/ul.d.ts +3 -0
- package/dist/internal/stringify/handlers/ul.js +18 -0
- package/dist/internal/stringify/indent.d.ts +4 -0
- package/dist/internal/stringify/indent.js +8 -0
- package/dist/internal/stringify/state.d.ts +13 -0
- package/dist/internal/stringify/state.js +121 -0
- package/dist/internal/yaml.d.ts +12 -0
- package/dist/internal/yaml.js +51 -0
- package/dist/parse.d.ts +66 -0
- package/dist/parse.js +163 -0
- package/dist/plugins/alert.d.ts +2 -0
- package/dist/plugins/alert.js +66 -0
- package/dist/plugins/emoji.d.ts +3 -0
- package/dist/plugins/emoji.js +438 -0
- package/dist/plugins/headings.d.ts +48 -0
- package/dist/plugins/headings.js +85 -0
- package/dist/plugins/highlight.d.ts +63 -0
- package/dist/plugins/highlight.js +235 -0
- package/dist/plugins/math.d.ts +59 -0
- package/dist/plugins/math.js +263 -0
- package/dist/plugins/mermaid.d.ts +38 -0
- package/dist/plugins/mermaid.js +185 -0
- package/dist/plugins/security.d.ts +11 -0
- package/dist/plugins/security.js +32 -0
- package/dist/plugins/summary.d.ts +2 -0
- package/dist/plugins/summary.js +22 -0
- package/dist/plugins/task-list.d.ts +8 -0
- package/dist/plugins/task-list.js +117 -0
- package/dist/plugins/toc.d.ts +15 -0
- package/dist/plugins/toc.js +118 -0
- package/dist/render.d.ts +18 -0
- package/dist/render.js +29 -0
- package/dist/types.d.ts +258 -0
- package/dist/types.js +1 -0
- package/dist/utils/caret.d.ts +7 -0
- package/dist/utils/caret.js +36 -0
- package/dist/utils/index.d.ts +38 -0
- package/dist/utils/index.js +149 -0
- package/package.json +73 -9
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ComarkPlugin, ComarkTree } from 'comark';
|
|
2
|
+
export interface TocLink {
|
|
3
|
+
id: string;
|
|
4
|
+
text: string;
|
|
5
|
+
depth: number;
|
|
6
|
+
children?: TocLink[];
|
|
7
|
+
}
|
|
8
|
+
export interface Toc {
|
|
9
|
+
title: string;
|
|
10
|
+
depth: number;
|
|
11
|
+
searchDepth: number;
|
|
12
|
+
links: TocLink[];
|
|
13
|
+
}
|
|
14
|
+
export declare function generateFlatToc(body: ComarkTree, options: Toc): Toc;
|
|
15
|
+
export default function toc(options?: Partial<Toc>): ComarkPlugin;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const TOC_TAGS = ['h2', 'h3', 'h4', 'h5', 'h6'];
|
|
2
|
+
const TOC_TAGS_DEPTH = TOC_TAGS.reduce((tags, tag) => {
|
|
3
|
+
tags[tag] = Number(tag.charAt(tag.length - 1));
|
|
4
|
+
return tags;
|
|
5
|
+
}, {});
|
|
6
|
+
function getTag(node) {
|
|
7
|
+
if (Array.isArray(node) && node.length >= 1) {
|
|
8
|
+
return node[0];
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
function getProps(node) {
|
|
13
|
+
if (Array.isArray(node) && node.length >= 2 && typeof node[1] === 'object' && !Array.isArray(node[1])) {
|
|
14
|
+
return node[1];
|
|
15
|
+
}
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
function getChildren(node) {
|
|
19
|
+
if (Array.isArray(node) && node.length > 2) {
|
|
20
|
+
return node.slice(2);
|
|
21
|
+
}
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
function getHeaderDepth(node) {
|
|
25
|
+
const tag = getTag(node);
|
|
26
|
+
return tag ? TOC_TAGS_DEPTH[tag] || 0 : 0;
|
|
27
|
+
}
|
|
28
|
+
function getTocTags(depth) {
|
|
29
|
+
if (depth < 1 || depth > 5) {
|
|
30
|
+
console.warn(`\`toc.depth\` is set to ${depth}. It should be a number between 1 and 5. `);
|
|
31
|
+
depth = 1;
|
|
32
|
+
}
|
|
33
|
+
return TOC_TAGS.slice(0, depth);
|
|
34
|
+
}
|
|
35
|
+
function flattenNodeText(node) {
|
|
36
|
+
if (typeof node === 'string') {
|
|
37
|
+
return node;
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(node)) {
|
|
40
|
+
return getChildren(node).reduce((text, child) => {
|
|
41
|
+
return text + flattenNodeText(child);
|
|
42
|
+
}, '');
|
|
43
|
+
}
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
function flattenNodes(nodes, maxDepth, currentDepth = 0) {
|
|
47
|
+
if (currentDepth >= maxDepth) {
|
|
48
|
+
return nodes;
|
|
49
|
+
}
|
|
50
|
+
const result = [];
|
|
51
|
+
for (const node of nodes) {
|
|
52
|
+
result.push(node);
|
|
53
|
+
if (Array.isArray(node)) {
|
|
54
|
+
const children = getChildren(node);
|
|
55
|
+
if (children.length > 0) {
|
|
56
|
+
result.push(...flattenNodes(children, maxDepth, currentDepth + 1));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
function nestHeaders(headers) {
|
|
63
|
+
if (headers.length <= 1) {
|
|
64
|
+
return headers;
|
|
65
|
+
}
|
|
66
|
+
const toc = [];
|
|
67
|
+
let parent;
|
|
68
|
+
headers.forEach((header) => {
|
|
69
|
+
if (!parent || header.depth <= parent.depth) {
|
|
70
|
+
header.children = [];
|
|
71
|
+
parent = header;
|
|
72
|
+
toc.push(header);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
parent.children.push(header);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
toc.forEach((header) => {
|
|
79
|
+
if (header.children?.length) {
|
|
80
|
+
header.children = nestHeaders(header.children);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
delete header.children;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return toc;
|
|
87
|
+
}
|
|
88
|
+
export function generateFlatToc(body, options) {
|
|
89
|
+
const { searchDepth, depth, title = '' } = options;
|
|
90
|
+
const tags = getTocTags(depth);
|
|
91
|
+
const allNodes = flattenNodes(body.nodes, searchDepth);
|
|
92
|
+
const headers = allNodes.filter((node) => {
|
|
93
|
+
const tag = getTag(node);
|
|
94
|
+
return tag !== null && tags.includes(tag);
|
|
95
|
+
});
|
|
96
|
+
const links = headers.map(node => ({
|
|
97
|
+
id: getProps(node).id || '',
|
|
98
|
+
depth: getHeaderDepth(node),
|
|
99
|
+
text: flattenNodeText(node),
|
|
100
|
+
}));
|
|
101
|
+
return {
|
|
102
|
+
title,
|
|
103
|
+
searchDepth,
|
|
104
|
+
depth,
|
|
105
|
+
links,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export default function toc(options = {}) {
|
|
109
|
+
const { title = '', depth = 2, searchDepth = 2, links = [] } = options;
|
|
110
|
+
return {
|
|
111
|
+
name: 'toc',
|
|
112
|
+
post(state) {
|
|
113
|
+
const toc = generateFlatToc(state.tree, { title, depth, searchDepth, links });
|
|
114
|
+
toc.links = nestHeaders(toc.links);
|
|
115
|
+
state.tree.meta.toc = toc;
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ComarkTree, RenderOptions, RenderMarkdownOptions } from 'comark';
|
|
2
|
+
export type { NodeHandler, State, Context, RenderOptions, RenderMarkdownOptions } from './types.ts';
|
|
3
|
+
export { renderFrontmatter } from './internal/frontmatter.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Generate a string from a Comark tree
|
|
6
|
+
* @param tree - The Comark tree to render
|
|
7
|
+
* @param context - The context of the renderer
|
|
8
|
+
* @returns The string representation of the Comark tree
|
|
9
|
+
*/
|
|
10
|
+
export declare function render(tree: ComarkTree, context?: RenderOptions): Promise<string>;
|
|
11
|
+
/**
|
|
12
|
+
* Render Comark tree to markdown
|
|
13
|
+
*
|
|
14
|
+
* @param tree - The Comark tree to render
|
|
15
|
+
* @param options - Optional rendering options
|
|
16
|
+
* @returns The markdown string with optional frontmatter
|
|
17
|
+
*/
|
|
18
|
+
export declare function renderMarkdown(tree: ComarkTree, options?: RenderMarkdownOptions): Promise<string>;
|
package/dist/render.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { renderFrontmatter } from "./internal/frontmatter.js";
|
|
2
|
+
import { createState, one } from "./internal/stringify/state.js";
|
|
3
|
+
// Re-export frontmatter renderer
|
|
4
|
+
export { renderFrontmatter } from "./internal/frontmatter.js";
|
|
5
|
+
/**
|
|
6
|
+
* Generate a string from a Comark tree
|
|
7
|
+
* @param tree - The Comark tree to render
|
|
8
|
+
* @param context - The context of the renderer
|
|
9
|
+
* @returns The string representation of the Comark tree
|
|
10
|
+
*/
|
|
11
|
+
export async function render(tree, context = {}) {
|
|
12
|
+
const state = createState({ ...context, tree, handlers: context.components });
|
|
13
|
+
let result = '';
|
|
14
|
+
for (const child of tree.nodes) {
|
|
15
|
+
result += await one(child, state);
|
|
16
|
+
}
|
|
17
|
+
return result.trim() + '\n';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Render Comark tree to markdown
|
|
21
|
+
*
|
|
22
|
+
* @param tree - The Comark tree to render
|
|
23
|
+
* @param options - Optional rendering options
|
|
24
|
+
* @returns The markdown string with optional frontmatter
|
|
25
|
+
*/
|
|
26
|
+
export async function renderMarkdown(tree, options) {
|
|
27
|
+
const content = await render(tree, { format: 'markdown/comark', ...options });
|
|
28
|
+
return renderFrontmatter(tree.frontmatter, content);
|
|
29
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import type MarkdownExit from 'markdown-exit';
|
|
2
|
+
import type MarkdownIt from 'markdown-it';
|
|
3
|
+
/**
|
|
4
|
+
* The Comark text
|
|
5
|
+
* @param string - The text content
|
|
6
|
+
*/
|
|
7
|
+
export type ComarkText = string;
|
|
8
|
+
/**
|
|
9
|
+
* The Comark comment
|
|
10
|
+
* @param null - The null node
|
|
11
|
+
* @param {} - The attributes of the comment
|
|
12
|
+
* @param string - The content of the comment
|
|
13
|
+
*/
|
|
14
|
+
export type ComarkComment = [null, {}, string];
|
|
15
|
+
/**
|
|
16
|
+
* The Comark element attributes
|
|
17
|
+
* @param [key: string]: unknown - The attributes of the element
|
|
18
|
+
*/
|
|
19
|
+
export type ComarkElementAttributes = {
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
$?: {
|
|
22
|
+
line?: number;
|
|
23
|
+
html?: 0 | 1;
|
|
24
|
+
block?: 0 | 1;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The Comark element
|
|
29
|
+
* @param string - The tag of the element
|
|
30
|
+
* @param ComarkElementAttributes - The attributes of the element
|
|
31
|
+
* @param ...ComarkNode[] - The children of the element
|
|
32
|
+
*/
|
|
33
|
+
export type ComarkElement = [string, ComarkElementAttributes, ...ComarkNode[]];
|
|
34
|
+
/**
|
|
35
|
+
* The Comark node
|
|
36
|
+
*
|
|
37
|
+
* `ComarkElement` | `ComarkText` | `ComarkComment` - The node can be an element, text or comment
|
|
38
|
+
*/
|
|
39
|
+
export type ComarkNode = ComarkElement | ComarkText | ComarkComment;
|
|
40
|
+
/**
|
|
41
|
+
* The Comark tree
|
|
42
|
+
* @param nodes - The nodes of the tree
|
|
43
|
+
* @param frontmatter - The frontmatter data which is the data at the top of the file
|
|
44
|
+
* @param meta - The meta data of tree, it can be used to store additional data for the tree
|
|
45
|
+
*/
|
|
46
|
+
export interface ComarkTree {
|
|
47
|
+
nodes: ComarkNode[];
|
|
48
|
+
frontmatter: Record<string, any>;
|
|
49
|
+
meta: Record<string, any>;
|
|
50
|
+
}
|
|
51
|
+
interface StringifyOptions {
|
|
52
|
+
/**
|
|
53
|
+
* @default '\n\n'
|
|
54
|
+
*/
|
|
55
|
+
blockSeparator: string;
|
|
56
|
+
/**
|
|
57
|
+
* @default 'markdown/comark'
|
|
58
|
+
*/
|
|
59
|
+
format: 'markdown/comark' | 'markdown/html' | 'text/html' | 'text';
|
|
60
|
+
/**
|
|
61
|
+
* user defined node handlers
|
|
62
|
+
*/
|
|
63
|
+
handlers: Record<string, NodeHandler>;
|
|
64
|
+
/**
|
|
65
|
+
* @default true
|
|
66
|
+
*/
|
|
67
|
+
removeLastStyle?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Maximum number of inline attributes before switching to YAML block syntax.
|
|
70
|
+
* Set to 0 to always use YAML block syntax.
|
|
71
|
+
* @default 3
|
|
72
|
+
*/
|
|
73
|
+
maxInlineAttributes?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Additional options
|
|
76
|
+
*/
|
|
77
|
+
[key: string]: unknown;
|
|
78
|
+
}
|
|
79
|
+
export interface Context extends StringifyOptions {
|
|
80
|
+
/**
|
|
81
|
+
* true if node is inside html scope
|
|
82
|
+
*/
|
|
83
|
+
html?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* true if node is inside a list
|
|
86
|
+
*/
|
|
87
|
+
list?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* number if node is inside an ordered list
|
|
90
|
+
*/
|
|
91
|
+
order?: number;
|
|
92
|
+
[key: string]: unknown;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The NodeHandler function
|
|
96
|
+
* @param node - The node to render
|
|
97
|
+
* @param state - The state of the renderer
|
|
98
|
+
* @param parent - The parent node
|
|
99
|
+
* @returns The rendered node
|
|
100
|
+
*/
|
|
101
|
+
export type NodeHandler = (node: ComarkElement, state: State, parent?: ComarkElement) => string | Promise<string>;
|
|
102
|
+
/**
|
|
103
|
+
* The State of the renderer
|
|
104
|
+
* @param handlers - The handlers of the renderer
|
|
105
|
+
* @param context - The context of the renderer
|
|
106
|
+
* @param flow - Render children of the node
|
|
107
|
+
* @param one - Render a single node
|
|
108
|
+
* @param applyContext - The applyContext of the renderer
|
|
109
|
+
* @returns The state of the renderer
|
|
110
|
+
*/
|
|
111
|
+
export type State = {
|
|
112
|
+
/**
|
|
113
|
+
* Additional data to pass to the renderer nodes, can be used to pass pre-fetched data to the renderer nodes
|
|
114
|
+
*/
|
|
115
|
+
data: Record<string, any>;
|
|
116
|
+
/**
|
|
117
|
+
* The context of the renderer
|
|
118
|
+
*/
|
|
119
|
+
context: Context;
|
|
120
|
+
/**
|
|
121
|
+
* The handlers of the renderer
|
|
122
|
+
*/
|
|
123
|
+
handlers: Record<string, NodeHandler>;
|
|
124
|
+
/**
|
|
125
|
+
* Render children of the node
|
|
126
|
+
*/
|
|
127
|
+
flow: (node: ComarkElement, state: State, parent?: ComarkElement) => Promise<string>;
|
|
128
|
+
/**
|
|
129
|
+
* Render a single node
|
|
130
|
+
*/
|
|
131
|
+
one: (node: ComarkNode, state: State, parent?: ComarkElement) => Promise<string>;
|
|
132
|
+
/**
|
|
133
|
+
* Render the input
|
|
134
|
+
*/
|
|
135
|
+
render: (input: ComarkNode[] | ComarkElement) => Promise<string>;
|
|
136
|
+
/**
|
|
137
|
+
* Apply the context
|
|
138
|
+
* @param edit - The edit to apply to the context
|
|
139
|
+
* @returns The revert of the edit
|
|
140
|
+
*/
|
|
141
|
+
applyContext: (edit: Record<string, unknown>) => Record<string, unknown>;
|
|
142
|
+
/**
|
|
143
|
+
* The depth of the node in the tree
|
|
144
|
+
*/
|
|
145
|
+
nodeDepthInTree?: number;
|
|
146
|
+
[key: string]: unknown;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* The context of the renderer
|
|
150
|
+
*/
|
|
151
|
+
export interface RenderOptions {
|
|
152
|
+
/**
|
|
153
|
+
* Additional node handlers to pass to the renderer
|
|
154
|
+
*/
|
|
155
|
+
components?: Record<string, NodeHandler>;
|
|
156
|
+
/**
|
|
157
|
+
* Additional data to pass to the renderer nodes, can be used to pass pre-fetched data to the renderer nodes
|
|
158
|
+
*/
|
|
159
|
+
data?: Record<string, any>;
|
|
160
|
+
[key: string]: unknown;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The options for rendering markdown
|
|
164
|
+
*/
|
|
165
|
+
export interface RenderMarkdownOptions extends RenderOptions {
|
|
166
|
+
/**
|
|
167
|
+
* Maximum number of inline attributes before switching to YAML block syntax.
|
|
168
|
+
* Set to 0 to always use YAML block syntax.
|
|
169
|
+
* @default 3
|
|
170
|
+
*/
|
|
171
|
+
maxInlineAttributes?: number;
|
|
172
|
+
}
|
|
173
|
+
export type MarkdownExitPlugin = (md: MarkdownExit) => void;
|
|
174
|
+
export type MarkdownItPlugin = (md: MarkdownIt) => void;
|
|
175
|
+
export type ComarkParsePreState = {
|
|
176
|
+
markdown: string;
|
|
177
|
+
options: ParseOptions;
|
|
178
|
+
[key: string]: any;
|
|
179
|
+
};
|
|
180
|
+
export type ComarkParsePostState = {
|
|
181
|
+
markdown: string;
|
|
182
|
+
tree: ComarkTree;
|
|
183
|
+
options: ParseOptions;
|
|
184
|
+
tokens: unknown[];
|
|
185
|
+
[key: string]: any;
|
|
186
|
+
};
|
|
187
|
+
export type ComarkPlugin = {
|
|
188
|
+
name: string;
|
|
189
|
+
markdownItPlugins?: MarkdownItPlugin[];
|
|
190
|
+
pre?: (state: ComarkParsePreState) => Promise<void> | void;
|
|
191
|
+
post?: (state: ComarkParsePostState) => Promise<void> | void;
|
|
192
|
+
};
|
|
193
|
+
export type ComponentManifest = (name: string) => Promise<unknown> | undefined | null;
|
|
194
|
+
export interface ComarkContextProvider {
|
|
195
|
+
components: Record<string, any>;
|
|
196
|
+
componentManifest: ComponentManifest;
|
|
197
|
+
}
|
|
198
|
+
export interface ParseOptions {
|
|
199
|
+
/**
|
|
200
|
+
* Whether to automatically unwrap single paragraphs in container components.
|
|
201
|
+
* When enabled, if a container component (alert, card, callout, note, warning, tip, info)
|
|
202
|
+
* has only a single paragraph child, the paragraph wrapper is removed and its children
|
|
203
|
+
* become direct children of the container. This creates cleaner HTML output.
|
|
204
|
+
*
|
|
205
|
+
* @default true
|
|
206
|
+
* @example
|
|
207
|
+
* // With autoUnwrap: true (default)
|
|
208
|
+
* // <alert><strong>Text</strong></alert>
|
|
209
|
+
*
|
|
210
|
+
* // With autoUnwrap: false
|
|
211
|
+
* // <alert><p><strong>Text</strong></p></alert>
|
|
212
|
+
*/
|
|
213
|
+
autoUnwrap?: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Whether to automatically close unclosed markdown and Comark components.
|
|
216
|
+
* @default true
|
|
217
|
+
*/
|
|
218
|
+
autoClose?: boolean;
|
|
219
|
+
/**
|
|
220
|
+
* Whether to parse HTML tags embedded in Comark/markdown content.
|
|
221
|
+
* When enabled, HTML block and inline elements are parsed into AST nodes and can be
|
|
222
|
+
* mixed freely with Comark components and markdown syntax.
|
|
223
|
+
*
|
|
224
|
+
* @default true
|
|
225
|
+
* @example
|
|
226
|
+
* // With html: true (default) — HTML is parsed into AST nodes
|
|
227
|
+
* // Input: `<strong class="bold">text</strong>`
|
|
228
|
+
* // AST: ['strong', { class: 'bold' }, 'text']
|
|
229
|
+
*
|
|
230
|
+
* // HTML can be mixed with Comark components:
|
|
231
|
+
* // Input:
|
|
232
|
+
* // <div>
|
|
233
|
+
* // ::alert
|
|
234
|
+
* // Hello <em>world</em>
|
|
235
|
+
* // ::
|
|
236
|
+
* // </div>
|
|
237
|
+
*
|
|
238
|
+
* // With html: false — HTML tags are left as raw text / ignored
|
|
239
|
+
*/
|
|
240
|
+
html?: boolean;
|
|
241
|
+
/**
|
|
242
|
+
* Additional plugins to use
|
|
243
|
+
* @default []
|
|
244
|
+
*/
|
|
245
|
+
plugins?: ComarkPlugin[];
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Type signature for the options object passed to the Comark parser function returned by createParse().
|
|
249
|
+
*/
|
|
250
|
+
export type ComarkParseFnOptions = {
|
|
251
|
+
streaming?: boolean;
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* Type signature for the async Comark parser function returned by createParse().
|
|
255
|
+
* Accepts a markdown string and optional parsing options, and returns a Promise of ComarkTree.
|
|
256
|
+
*/
|
|
257
|
+
export type ComarkParseFn = (markdown: string, opts?: ComarkParseFnOptions) => Promise<ComarkTree>;
|
|
258
|
+
export {};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ComarkElement } from '../';
|
|
2
|
+
interface CaretOptions {
|
|
3
|
+
class?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function getCaret(options: boolean | CaretOptions): ComarkElement | null;
|
|
6
|
+
export declare function findLastTextNodeAndAppendNode(parent: ComarkElement, nodeToAppend: ComarkElement): boolean;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const CARET_TEXT = ' '; // thin space is used to avoid wide spaces between text and caret
|
|
2
|
+
const CARET_STYLE = 'background-color: currentColor; display: inline-block; margin-left: 0.25rem; margin-right: 0.25rem; animation: pulse 0.75s cubic-bezier(0.4,0,0.6,1) infinite;';
|
|
3
|
+
export function getCaret(options) {
|
|
4
|
+
if (options === true) {
|
|
5
|
+
return ['span', { key: 'stream-caret', style: CARET_STYLE }, CARET_TEXT];
|
|
6
|
+
}
|
|
7
|
+
if (typeof options === 'object') {
|
|
8
|
+
return [
|
|
9
|
+
'span',
|
|
10
|
+
{
|
|
11
|
+
key: 'stream-caret',
|
|
12
|
+
...(options.class ? { class: options.class } : { style: CARET_STYLE }),
|
|
13
|
+
},
|
|
14
|
+
CARET_TEXT,
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
export function findLastTextNodeAndAppendNode(parent, nodeToAppend) {
|
|
20
|
+
// Traverse nodes backwards to find the last text node
|
|
21
|
+
for (let i = parent.length - 1; i >= 2; i--) {
|
|
22
|
+
const node = parent[i];
|
|
23
|
+
if (typeof node === 'string' && parent[1]?.key !== 'stream-caret') {
|
|
24
|
+
// Found a text node - insert stream indicator after it
|
|
25
|
+
parent.push(nodeToAppend);
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(node)) {
|
|
29
|
+
// This is an element node - recursively check its children
|
|
30
|
+
if (findLastTextNodeAndAppendNode(node, nodeToAppend)) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ComarkNode, ComarkTree } from 'comark';
|
|
2
|
+
/**
|
|
3
|
+
* Get the text content of a Comark node
|
|
4
|
+
*
|
|
5
|
+
* @param node - The Comark node
|
|
6
|
+
* @param options - The options
|
|
7
|
+
* @param options.decodeUnicodeEntities - Whether to decode Unicode entities
|
|
8
|
+
* @returns The text content
|
|
9
|
+
*/
|
|
10
|
+
export declare function textContent(node: ComarkNode, options?: {
|
|
11
|
+
decodeUnicodeEntities?: boolean;
|
|
12
|
+
}): string;
|
|
13
|
+
/**
|
|
14
|
+
* Visit a Comark tree and apply a visitor function to each node
|
|
15
|
+
*
|
|
16
|
+
* @param tree - The Comark tree
|
|
17
|
+
* @param checker - A function that checks if a node should be visited
|
|
18
|
+
* @param visitor - A function that visits a node
|
|
19
|
+
*/
|
|
20
|
+
export declare function visit(tree: ComarkTree, checker: (node: ComarkNode) => boolean, visitor: (node: ComarkNode) => ComarkNode | false | undefined | void): void;
|
|
21
|
+
/**
|
|
22
|
+
* Convert a string to pascal case
|
|
23
|
+
* @param str - The string to convert
|
|
24
|
+
* @returns The pascal case string
|
|
25
|
+
*/
|
|
26
|
+
export declare function pascalCase(str: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Convert a string to kebab case
|
|
29
|
+
* @param str - The string to convert
|
|
30
|
+
* @returns The kebab case string
|
|
31
|
+
*/
|
|
32
|
+
export declare function kebabCase(str: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Convert a string to camel case
|
|
35
|
+
* @param str - The string to convert
|
|
36
|
+
* @returns The camel case string
|
|
37
|
+
*/
|
|
38
|
+
export declare function camelCase(str: string): string;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// #region Tree Utils
|
|
2
|
+
import { decodeHTML } from 'entities';
|
|
3
|
+
/**
|
|
4
|
+
* Get the text content of a Comark node
|
|
5
|
+
*
|
|
6
|
+
* @param node - The Comark node
|
|
7
|
+
* @param options - The options
|
|
8
|
+
* @param options.decodeUnicodeEntities - Whether to decode Unicode entities
|
|
9
|
+
* @returns The text content
|
|
10
|
+
*/
|
|
11
|
+
export function textContent(node, options = {}) {
|
|
12
|
+
if (typeof node === 'string') {
|
|
13
|
+
if (options.decodeUnicodeEntities) {
|
|
14
|
+
return decodeHTML(node);
|
|
15
|
+
}
|
|
16
|
+
return node;
|
|
17
|
+
}
|
|
18
|
+
let out = '';
|
|
19
|
+
const len = node.length;
|
|
20
|
+
for (let i = 2; i < len; i++) {
|
|
21
|
+
out += textContent(node[i], options);
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Visit a Comark tree and apply a visitor function to each node
|
|
27
|
+
*
|
|
28
|
+
* @param tree - The Comark tree
|
|
29
|
+
* @param checker - A function that checks if a node should be visited
|
|
30
|
+
* @param visitor - A function that visits a node
|
|
31
|
+
*/
|
|
32
|
+
export function visit(tree, checker,
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
34
|
+
visitor) {
|
|
35
|
+
function walk(node, parent, index) {
|
|
36
|
+
let currentNode = node;
|
|
37
|
+
if (checker(node)) {
|
|
38
|
+
const res = visitor(node);
|
|
39
|
+
if (res === false) {
|
|
40
|
+
// remove the node from the parent
|
|
41
|
+
parent.splice(index, 1);
|
|
42
|
+
return true; // signal that node was removed
|
|
43
|
+
}
|
|
44
|
+
if (res !== undefined) {
|
|
45
|
+
parent[index] = res;
|
|
46
|
+
currentNode = res;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (Array.isArray(currentNode) && currentNode.length > 2) {
|
|
50
|
+
// Use a while loop to handle removals correctly - don't increment if node was removed
|
|
51
|
+
let i = 2;
|
|
52
|
+
while (i < currentNode.length) {
|
|
53
|
+
const childRemoved = walk(currentNode[i], currentNode, i);
|
|
54
|
+
if (childRemoved) {
|
|
55
|
+
// If removed, i stays the same (next node is now at this index)
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
i += 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
// Use a while loop to handle removals correctly - don't increment if node was removed
|
|
64
|
+
let i = 0;
|
|
65
|
+
while (i < tree.nodes.length) {
|
|
66
|
+
const removed = walk(tree.nodes[i], tree.nodes, i);
|
|
67
|
+
if (removed) {
|
|
68
|
+
// If removed, i stays the same (next node is now at this index)
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
i += 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// #region String Utils
|
|
75
|
+
/**
|
|
76
|
+
* Convert a string to pascal case
|
|
77
|
+
* @param str - The string to convert
|
|
78
|
+
* @returns The pascal case string
|
|
79
|
+
*/
|
|
80
|
+
export function pascalCase(str) {
|
|
81
|
+
return str ? splitByCase(str).map(p => p[0].toUpperCase() + p.slice(1)).join('') : '';
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Convert a string to kebab case
|
|
85
|
+
* @param str - The string to convert
|
|
86
|
+
* @returns The kebab case string
|
|
87
|
+
*/
|
|
88
|
+
export function kebabCase(str) {
|
|
89
|
+
return str ? splitByCase(str).map(p => p.toLowerCase()).join('-') : '';
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Convert a string to camel case
|
|
93
|
+
* @param str - The string to convert
|
|
94
|
+
* @returns The camel case string
|
|
95
|
+
*/
|
|
96
|
+
export function camelCase(str) {
|
|
97
|
+
if (!str) {
|
|
98
|
+
return '';
|
|
99
|
+
}
|
|
100
|
+
str = pascalCase(str);
|
|
101
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
102
|
+
}
|
|
103
|
+
// split a string by case
|
|
104
|
+
function splitByCase(str) {
|
|
105
|
+
const parts = [];
|
|
106
|
+
if (!str) {
|
|
107
|
+
return parts;
|
|
108
|
+
}
|
|
109
|
+
let buff = '';
|
|
110
|
+
let previousUpper;
|
|
111
|
+
let previousSplitter;
|
|
112
|
+
for (let i = 0; i < str.length; i++) {
|
|
113
|
+
const char = str[i];
|
|
114
|
+
// Fast splitter check using direct character comparisons
|
|
115
|
+
const isSplitter = char === '-' || char === '_' || char === '/' || char === '.';
|
|
116
|
+
if (isSplitter === true) {
|
|
117
|
+
parts.push(buff);
|
|
118
|
+
buff = '';
|
|
119
|
+
previousUpper = void 0;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
// Fast number check using character codes
|
|
123
|
+
const charCode = char.charCodeAt(0);
|
|
124
|
+
const isNumber = charCode >= 48 && charCode <= 57; // '0' to '9'
|
|
125
|
+
// Fast uppercase check using character codes
|
|
126
|
+
const isUpper = isNumber ? void 0 : (charCode >= 65 && charCode <= 90); // 'A' to 'Z'
|
|
127
|
+
if (previousSplitter === false) {
|
|
128
|
+
if (previousUpper === false && isUpper === true) {
|
|
129
|
+
parts.push(buff);
|
|
130
|
+
buff = char;
|
|
131
|
+
previousUpper = isUpper;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (previousUpper === true && isUpper === false && buff.length > 1) {
|
|
135
|
+
const lastChar = buff[buff.length - 1];
|
|
136
|
+
parts.push(buff.slice(0, buff.length - 1));
|
|
137
|
+
buff = lastChar + char;
|
|
138
|
+
previousUpper = isUpper;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
buff += char;
|
|
143
|
+
previousUpper = isUpper;
|
|
144
|
+
previousSplitter = isSplitter;
|
|
145
|
+
}
|
|
146
|
+
parts.push(buff);
|
|
147
|
+
return parts;
|
|
148
|
+
}
|
|
149
|
+
// #endregion
|