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
package/dist/parse.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import MarkdownExit from 'markdown-exit';
|
|
2
|
+
import pluginMdc from '@comark/markdown-it';
|
|
3
|
+
import taskList from "./plugins/task-list.js";
|
|
4
|
+
import alert from "./plugins/alert.js";
|
|
5
|
+
import { applyAutoUnwrap } from "./internal/parse/auto-unwrap.js";
|
|
6
|
+
import { marmdownItTokensToComarkTree } from "./internal/parse/token-processor.js";
|
|
7
|
+
import { autoCloseMarkdown } from "./internal/parse/auto-close/index.js";
|
|
8
|
+
import { parseFrontmatter } from "./internal/frontmatter.js";
|
|
9
|
+
import { extractReusableNodes } from "./internal/parse/incremental.js";
|
|
10
|
+
import html_block from "./internal/parse/html/html_block_rule.js";
|
|
11
|
+
import html_inline from "./internal/parse/html/html_inline_rule.js";
|
|
12
|
+
// Re-export frontmatter utilities
|
|
13
|
+
export { parseFrontmatter } from "./internal/frontmatter.js";
|
|
14
|
+
/**
|
|
15
|
+
* Creates a parser function for Comark content.
|
|
16
|
+
*
|
|
17
|
+
* Returns an async function that takes a markdown string and returns a Promise resolving to a ComarkTree AST.
|
|
18
|
+
* The returned parser applies frontmatter extraction, Comark syntax parsing, token-to-AST conversion,
|
|
19
|
+
* auto-closing of incomplete markdown, optional AST transformations and plugin hooks.
|
|
20
|
+
*
|
|
21
|
+
* @param options - Parser options controlling parsing behavior.
|
|
22
|
+
* @returns An async parser function: (markdown) => Promise<ComarkTree>
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* import { createParse } from 'comark'
|
|
27
|
+
*
|
|
28
|
+
* const parse = createParse({ autoUnwrap: false })
|
|
29
|
+
* const tree = await parse('# Hello **World**\n::alert\nhi\n::')
|
|
30
|
+
* console.log(tree.nodes)
|
|
31
|
+
* // → [ ['h1', { id: 'hello-world' }, 'Hello ', ['strong', {}, 'World'] ], ['alert', {}, 'hi'] ]
|
|
32
|
+
*
|
|
33
|
+
* // Enable HTML parsing (on by default) — HTML tags are included in the AST
|
|
34
|
+
* const parseWithHtml = createParse({ html: true })
|
|
35
|
+
* const tree2 = await parseWithHtml('<strong class="bold">Hello</strong> _world_')
|
|
36
|
+
* console.log(tree2.nodes)
|
|
37
|
+
* // → [ ['strong', { class: 'bold' }, 'Hello'], ' ', ['em', {}, 'world'] ]
|
|
38
|
+
*
|
|
39
|
+
* // Disable HTML parsing — HTML tags are treated as plain text
|
|
40
|
+
* const parseNoHtml = createParse({ html: false })
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export function createParse(options = {}) {
|
|
44
|
+
const { autoUnwrap = true, autoClose = true, plugins = [] } = options;
|
|
45
|
+
plugins.unshift(taskList());
|
|
46
|
+
plugins.unshift(alert());
|
|
47
|
+
const parser = new MarkdownExit({
|
|
48
|
+
html: false,
|
|
49
|
+
linkify: true,
|
|
50
|
+
})
|
|
51
|
+
.enable(['table', 'strikethrough'])
|
|
52
|
+
.use(pluginMdc);
|
|
53
|
+
if (options.html !== false) {
|
|
54
|
+
parser.inline.ruler.before('text', 'comark_html_inline', html_inline);
|
|
55
|
+
parser.block.ruler.before('html_block', 'comark_html_block', html_block, {
|
|
56
|
+
alt: ['paragraph', 'reference', 'blockquote'],
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
for (const plugin of plugins) {
|
|
60
|
+
for (const markdownItPlugin of (plugin.markdownItPlugins || [])) {
|
|
61
|
+
parser.use(markdownItPlugin);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
let lastOutput = null;
|
|
65
|
+
let lastInput = null;
|
|
66
|
+
return async (markdown, opts = {}) => {
|
|
67
|
+
const state = {
|
|
68
|
+
options,
|
|
69
|
+
tokens: [],
|
|
70
|
+
markdown,
|
|
71
|
+
tree: null,
|
|
72
|
+
parsedLines: 0,
|
|
73
|
+
reusableNodes: [],
|
|
74
|
+
};
|
|
75
|
+
const prevOutput = lastOutput;
|
|
76
|
+
if (opts.streaming && prevOutput && markdown.startsWith(lastInput ?? '')) {
|
|
77
|
+
const { remainingMarkdownStartLine, reusedNodes, remainingMarkdown } = extractReusableNodes(markdown, prevOutput);
|
|
78
|
+
// If there is no remaining markdown, return the previous output
|
|
79
|
+
if (!remainingMarkdown)
|
|
80
|
+
return prevOutput;
|
|
81
|
+
state.parsedLines = remainingMarkdownStartLine;
|
|
82
|
+
state.markdown = remainingMarkdown;
|
|
83
|
+
state.reusableNodes = reusedNodes;
|
|
84
|
+
}
|
|
85
|
+
if (autoClose) {
|
|
86
|
+
state.markdown = autoCloseMarkdown(state.markdown);
|
|
87
|
+
}
|
|
88
|
+
for (const plugin of options.plugins || []) {
|
|
89
|
+
await plugin.pre?.(state);
|
|
90
|
+
}
|
|
91
|
+
const { content, data } = await parseFrontmatter(state.markdown);
|
|
92
|
+
state.tokens = parser.parse(content, {});
|
|
93
|
+
// Convert tokens to Comark structure
|
|
94
|
+
let nodes = marmdownItTokensToComarkTree(state.tokens, {
|
|
95
|
+
startLine: state.parsedLines,
|
|
96
|
+
preservePositions: opts.streaming ?? false,
|
|
97
|
+
});
|
|
98
|
+
if (autoUnwrap) {
|
|
99
|
+
nodes = nodes.map((node) => applyAutoUnwrap(node));
|
|
100
|
+
}
|
|
101
|
+
if (opts.streaming) {
|
|
102
|
+
state.tree = {
|
|
103
|
+
frontmatter: state.parsedLines > 0 ? (prevOutput?.frontmatter ?? data) : data,
|
|
104
|
+
meta: {},
|
|
105
|
+
nodes: [...state.reusableNodes, ...nodes],
|
|
106
|
+
};
|
|
107
|
+
// Set last output and input for streaming mode
|
|
108
|
+
lastOutput = state.tree;
|
|
109
|
+
lastInput = markdown;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
state.tree = {
|
|
113
|
+
frontmatter: data,
|
|
114
|
+
meta: {},
|
|
115
|
+
nodes,
|
|
116
|
+
};
|
|
117
|
+
// Reset last output and input for non-streaming mode
|
|
118
|
+
lastOutput = null;
|
|
119
|
+
lastInput = null;
|
|
120
|
+
}
|
|
121
|
+
for (const plugin of plugins || []) {
|
|
122
|
+
await plugin.post?.(state);
|
|
123
|
+
}
|
|
124
|
+
return state.tree;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Parse Comark content from a string
|
|
129
|
+
*
|
|
130
|
+
* @param markdown - The markdown/Comark content as a string
|
|
131
|
+
* @param options - Parser options
|
|
132
|
+
* @returns ComarkTree - The parsed AST tree
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* import { parse } from 'comark'
|
|
137
|
+
*
|
|
138
|
+
* const content = `---
|
|
139
|
+
* title: Hello World
|
|
140
|
+
* ---
|
|
141
|
+
*
|
|
142
|
+
* # Hello World
|
|
143
|
+
*
|
|
144
|
+
* This is a **markdown** document with *Comark* components.
|
|
145
|
+
*
|
|
146
|
+
* ::alert{type="info"}
|
|
147
|
+
* This is an alert component
|
|
148
|
+
* ::
|
|
149
|
+
* `
|
|
150
|
+
*
|
|
151
|
+
* const tree = await parse(content)
|
|
152
|
+
* console.log(tree.nodes) // Array of AST nodes
|
|
153
|
+
* console.log(tree.frontmatter) // { title: 'Hello World' }
|
|
154
|
+
* console.log(tree.meta) // Additional metadata
|
|
155
|
+
*
|
|
156
|
+
* // Disable auto-unwrap
|
|
157
|
+
* const tree2 = await parse(content, { autoUnwrap: false })
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export async function parse(markdown, options = {}) {
|
|
161
|
+
const parse = createParse(options);
|
|
162
|
+
return await parse(markdown);
|
|
163
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { visit } from 'comark/utils';
|
|
2
|
+
const markers = {
|
|
3
|
+
'!TIP': {
|
|
4
|
+
type: 'tip',
|
|
5
|
+
title: 'Tip',
|
|
6
|
+
color: '#238636',
|
|
7
|
+
},
|
|
8
|
+
'!NOTE': {
|
|
9
|
+
type: 'note',
|
|
10
|
+
title: 'Note',
|
|
11
|
+
color: '#1f6feb',
|
|
12
|
+
},
|
|
13
|
+
'!IMPORTANT': {
|
|
14
|
+
type: 'important',
|
|
15
|
+
title: 'Important',
|
|
16
|
+
color: '#8957e5',
|
|
17
|
+
},
|
|
18
|
+
'!WARNING': {
|
|
19
|
+
type: 'warning',
|
|
20
|
+
title: 'Warning',
|
|
21
|
+
color: '#9e6a03',
|
|
22
|
+
},
|
|
23
|
+
'!CAUTION': {
|
|
24
|
+
type: 'caution',
|
|
25
|
+
title: 'Caution',
|
|
26
|
+
color: '#da3633',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
export default function alert() {
|
|
30
|
+
return {
|
|
31
|
+
name: 'alert',
|
|
32
|
+
post(state) {
|
|
33
|
+
visit(state.tree, node => Array.isArray(node) && node[0] === 'blockquote', (node) => {
|
|
34
|
+
const element = node;
|
|
35
|
+
if (node[2]?.[0] === 'span') {
|
|
36
|
+
const content = String(node[2][2]).toUpperCase();
|
|
37
|
+
const marker = markers[content];
|
|
38
|
+
if (marker) {
|
|
39
|
+
if (typeof node[3] === 'string') {
|
|
40
|
+
element[3] = String(element[3]).trimStart();
|
|
41
|
+
}
|
|
42
|
+
// remove span node
|
|
43
|
+
element.splice(2, 1);
|
|
44
|
+
element[1].as = marker.type;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else if (node[2]?.[0] === 'p') {
|
|
48
|
+
const paragraph = node[2];
|
|
49
|
+
if (paragraph[2]?.[0] === 'span') {
|
|
50
|
+
const content = String(paragraph[2][2]).toUpperCase();
|
|
51
|
+
const marker = markers[content];
|
|
52
|
+
if (marker) {
|
|
53
|
+
if (typeof paragraph[3] === 'string') {
|
|
54
|
+
paragraph[3] = String(paragraph[3]).trimStart();
|
|
55
|
+
}
|
|
56
|
+
// remove span node
|
|
57
|
+
paragraph.splice(2, 1);
|
|
58
|
+
// transform node
|
|
59
|
+
element[1].as = marker.type;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
// Common emoji definitions (200+ emojis)
|
|
2
|
+
// Organized by category for easier maintenance
|
|
3
|
+
const EMOJI_MAP = new Map([
|
|
4
|
+
['grinning', '😀'],
|
|
5
|
+
['smiley', '😃'],
|
|
6
|
+
['smile', '😄'],
|
|
7
|
+
['grin', '😁'],
|
|
8
|
+
['laughing', '😆'],
|
|
9
|
+
['satisfied', '😆'],
|
|
10
|
+
['sweat_smile', '😅'],
|
|
11
|
+
['joy', '😂'],
|
|
12
|
+
['wink', '😉'],
|
|
13
|
+
['blush', '😊'],
|
|
14
|
+
['innocent', '😇'],
|
|
15
|
+
['heart_eyes', '😍'],
|
|
16
|
+
['kissing_heart', '😘'],
|
|
17
|
+
['kissing', '😗'],
|
|
18
|
+
['kissing_closed_eyes', '😚'],
|
|
19
|
+
['kissing_smiling_eyes', '😙'],
|
|
20
|
+
['yum', '😋'],
|
|
21
|
+
['stuck_out_tongue', '😛'],
|
|
22
|
+
['stuck_out_tongue_winking_eye', '😜'],
|
|
23
|
+
['stuck_out_tongue_closed_eyes', '😝'],
|
|
24
|
+
['neutral_face', '😐'],
|
|
25
|
+
['expressionless', '😑'],
|
|
26
|
+
['no_mouth', '😶'],
|
|
27
|
+
['smirk', '😏'],
|
|
28
|
+
['unamused', '😒'],
|
|
29
|
+
['relieved', '😌'],
|
|
30
|
+
['pensive', '😔'],
|
|
31
|
+
['sleepy', '😪'],
|
|
32
|
+
['sleeping', '😴'],
|
|
33
|
+
['mask', '😷'],
|
|
34
|
+
['dizzy_face', '😵'],
|
|
35
|
+
['sunglasses', '😎'],
|
|
36
|
+
['confused', '😕'],
|
|
37
|
+
['worried', '😟'],
|
|
38
|
+
['open_mouth', '😮'],
|
|
39
|
+
['hushed', '😯'],
|
|
40
|
+
['astonished', '😲'],
|
|
41
|
+
['flushed', '😳'],
|
|
42
|
+
['frowning', '😦'],
|
|
43
|
+
['anguished', '😧'],
|
|
44
|
+
['fearful', '😨'],
|
|
45
|
+
['cold_sweat', '😰'],
|
|
46
|
+
['disappointed_relieved', '😥'],
|
|
47
|
+
['cry', '😢'],
|
|
48
|
+
['sob', '😭'],
|
|
49
|
+
['scream', '😱'],
|
|
50
|
+
['confounded', '😖'],
|
|
51
|
+
['persevere', '😣'],
|
|
52
|
+
['disappointed', '😞'],
|
|
53
|
+
['sweat', '😓'],
|
|
54
|
+
['weary', '😩'],
|
|
55
|
+
['tired_face', '😫'],
|
|
56
|
+
['triumph', '😤'],
|
|
57
|
+
['rage', '😡'],
|
|
58
|
+
['angry', '😠'],
|
|
59
|
+
['smiling_imp', '😈'],
|
|
60
|
+
['imp', '👿'],
|
|
61
|
+
['skull', '💀'],
|
|
62
|
+
['eyes', '👀'],
|
|
63
|
+
['eye', '👁️'],
|
|
64
|
+
['nose', '👃'],
|
|
65
|
+
['lips', '👄'],
|
|
66
|
+
['tongue', '👅'],
|
|
67
|
+
['heart', '❤️'],
|
|
68
|
+
['yellow_heart', '💛'],
|
|
69
|
+
['green_heart', '💚'],
|
|
70
|
+
['blue_heart', '💙'],
|
|
71
|
+
['purple_heart', '💜'],
|
|
72
|
+
['broken_heart', '💔'],
|
|
73
|
+
['thumbsup', '👍'],
|
|
74
|
+
['+1', '👍'],
|
|
75
|
+
['thumbsdown', '👎'],
|
|
76
|
+
['-1', '👎'],
|
|
77
|
+
['ok_hand', '👌'],
|
|
78
|
+
['facepunch', '👊'],
|
|
79
|
+
['punch', '👊'],
|
|
80
|
+
['fist', '✊'],
|
|
81
|
+
['v', '✌️'],
|
|
82
|
+
['wave', '👋'],
|
|
83
|
+
['hand', '✋'],
|
|
84
|
+
['raised_hand', '✋'],
|
|
85
|
+
['clap', '👏'],
|
|
86
|
+
['pray', '🙏'],
|
|
87
|
+
['point_up', '☝️'],
|
|
88
|
+
['point_down', '👇'],
|
|
89
|
+
['point_left', '👈'],
|
|
90
|
+
['point_right', '👉'],
|
|
91
|
+
['raised_hands', '🙌'],
|
|
92
|
+
['muscle', '💪'],
|
|
93
|
+
['writing_hand', '✍️'],
|
|
94
|
+
['nail_care', '💅'],
|
|
95
|
+
['selfie', '🤳'],
|
|
96
|
+
['fire', '🔥'],
|
|
97
|
+
['sparkles', '✨'],
|
|
98
|
+
['star', '⭐'],
|
|
99
|
+
['star2', '🌟'],
|
|
100
|
+
['zap', '⚡'],
|
|
101
|
+
['boom', '💥'],
|
|
102
|
+
['collision', '💥'],
|
|
103
|
+
['100', '💯'],
|
|
104
|
+
['tada', '🎉'],
|
|
105
|
+
['confetti_ball', '🎊'],
|
|
106
|
+
['balloon', '🎈'],
|
|
107
|
+
['gift', '🎁'],
|
|
108
|
+
['birthday', '🎂'],
|
|
109
|
+
['cake', '🍰'],
|
|
110
|
+
['rocket', '🚀'],
|
|
111
|
+
['helicopter', '🚁'],
|
|
112
|
+
['airplane', '✈️'],
|
|
113
|
+
['boat', '⛵'],
|
|
114
|
+
['ship', '🚢'],
|
|
115
|
+
['train', '🚂'],
|
|
116
|
+
['bus', '🚌'],
|
|
117
|
+
['taxi', '🚕'],
|
|
118
|
+
['car', '🚗'],
|
|
119
|
+
['bike', '🚲'],
|
|
120
|
+
['checkered_flag', '🏁'],
|
|
121
|
+
['medal', '🏅'],
|
|
122
|
+
['trophy', '🏆'],
|
|
123
|
+
['medal_sports', '🏅'],
|
|
124
|
+
['medal_military', '🎖️'],
|
|
125
|
+
['soccer', '⚽'],
|
|
126
|
+
['basketball', '🏀'],
|
|
127
|
+
['football', '🏈'],
|
|
128
|
+
['baseball', '⚾'],
|
|
129
|
+
['tennis', '🎾'],
|
|
130
|
+
['bowling', '🎳'],
|
|
131
|
+
['golf', '⛳'],
|
|
132
|
+
['dart', '🎯'],
|
|
133
|
+
['beer', '🍺'],
|
|
134
|
+
['beers', '🍻'],
|
|
135
|
+
['wine_glass', '🍷'],
|
|
136
|
+
['cocktail', '🍸'],
|
|
137
|
+
['coffee', '☕'],
|
|
138
|
+
['pizza', '🍕'],
|
|
139
|
+
['hamburger', '🍔'],
|
|
140
|
+
['fries', '🍟'],
|
|
141
|
+
['apple', '🍎'],
|
|
142
|
+
['banana', '🍌'],
|
|
143
|
+
['watermelon', '🍉'],
|
|
144
|
+
['grapes', '🍇'],
|
|
145
|
+
['strawberry', '🍓'],
|
|
146
|
+
['cherries', '🍒'],
|
|
147
|
+
['lemon', '🍋'],
|
|
148
|
+
['peach', '🍑'],
|
|
149
|
+
['pear', '🍐'],
|
|
150
|
+
['pineapple', '🍍'],
|
|
151
|
+
['tomato', '🍅'],
|
|
152
|
+
['eggplant', '🍆'],
|
|
153
|
+
['hot_pepper', '🌶️'],
|
|
154
|
+
['corn', '🌽'],
|
|
155
|
+
['bread', '🍞'],
|
|
156
|
+
['croissant', '🥐'],
|
|
157
|
+
['baguette_bread', '🥖'],
|
|
158
|
+
['cheese', '🧀'],
|
|
159
|
+
['egg', '🥚'],
|
|
160
|
+
['poultry_leg', '🍗'],
|
|
161
|
+
['meat_on_bone', '🍖'],
|
|
162
|
+
['doughnut', '🍩'],
|
|
163
|
+
['cookie', '🍪'],
|
|
164
|
+
['chocolate_bar', '🍫'],
|
|
165
|
+
['candy', '🍬'],
|
|
166
|
+
['lollipop', '🍭'],
|
|
167
|
+
['ice_cream', '🍦'],
|
|
168
|
+
['icecream', '🍨'],
|
|
169
|
+
['shaved_ice', '🍧'],
|
|
170
|
+
['tea', '🍵'],
|
|
171
|
+
['sake', '🍶'],
|
|
172
|
+
['champagne', '🍾'],
|
|
173
|
+
['tropical_drink', '🍹'],
|
|
174
|
+
['sunny', '☀️'],
|
|
175
|
+
['cloud', '☁️'],
|
|
176
|
+
['umbrella', '☂️'],
|
|
177
|
+
['snowflake', '❄️'],
|
|
178
|
+
['snowman', '⛄'],
|
|
179
|
+
['rainbow', '🌈'],
|
|
180
|
+
['ocean', '🌊'],
|
|
181
|
+
['droplet', '💧'],
|
|
182
|
+
['zap', '⚡'],
|
|
183
|
+
['fire', '🔥'],
|
|
184
|
+
['star', '⭐'],
|
|
185
|
+
['moon', '🌙'],
|
|
186
|
+
['partly_sunny', '⛅'],
|
|
187
|
+
['thunder_cloud_and_rain', '⛈️'],
|
|
188
|
+
['wind_face', '🌬️'],
|
|
189
|
+
['fog', '🌫️'],
|
|
190
|
+
['dog', '🐶'],
|
|
191
|
+
['cat', '🐱'],
|
|
192
|
+
['mouse', '🐭'],
|
|
193
|
+
['rabbit', '🐰'],
|
|
194
|
+
['fox_face', '🦊'],
|
|
195
|
+
['bear', '🐻'],
|
|
196
|
+
['panda_face', '🐼'],
|
|
197
|
+
['koala', '🐨'],
|
|
198
|
+
['tiger', '🐯'],
|
|
199
|
+
['lion', '🦁'],
|
|
200
|
+
['cow', '🐮'],
|
|
201
|
+
['pig', '🐷'],
|
|
202
|
+
['frog', '🐸'],
|
|
203
|
+
['monkey_face', '🐵'],
|
|
204
|
+
['see_no_evil', '🙈'],
|
|
205
|
+
['hear_no_evil', '🙉'],
|
|
206
|
+
['speak_no_evil', '🙊'],
|
|
207
|
+
['chicken', '🐔'],
|
|
208
|
+
['penguin', '🐧'],
|
|
209
|
+
['bird', '🐦'],
|
|
210
|
+
['baby_chick', '🐤'],
|
|
211
|
+
['bee', '🐝'],
|
|
212
|
+
['bug', '🐛'],
|
|
213
|
+
['butterfly', '🦋'],
|
|
214
|
+
['snail', '🐌'],
|
|
215
|
+
['turtle', '🐢'],
|
|
216
|
+
['snake', '🐍'],
|
|
217
|
+
['lizard', '🦎'],
|
|
218
|
+
['dragon', '🐉'],
|
|
219
|
+
['whale', '🐳'],
|
|
220
|
+
['dolphin', '🐬'],
|
|
221
|
+
['fish', '🐟'],
|
|
222
|
+
['octopus', '🐙'],
|
|
223
|
+
['shell', '🐚'],
|
|
224
|
+
['crab', '🦀'],
|
|
225
|
+
['tree', '🌲'],
|
|
226
|
+
['evergreen_tree', '🌲'],
|
|
227
|
+
['deciduous_tree', '🌳'],
|
|
228
|
+
['palm_tree', '🌴'],
|
|
229
|
+
['cactus', '🌵'],
|
|
230
|
+
['herb', '🌿'],
|
|
231
|
+
['shamrock', '☘️'],
|
|
232
|
+
['four_leaf_clover', '🍀'],
|
|
233
|
+
['maple_leaf', '🍁'],
|
|
234
|
+
['fallen_leaf', '🍂'],
|
|
235
|
+
['leaves', '🍃'],
|
|
236
|
+
['mushroom', '🍄'],
|
|
237
|
+
['cherry_blossom', '🌸'],
|
|
238
|
+
['rose', '🌹'],
|
|
239
|
+
['hibiscus', '🌺'],
|
|
240
|
+
['sunflower', '🌻'],
|
|
241
|
+
['blossom', '🌼'],
|
|
242
|
+
['bouquet', '💐'],
|
|
243
|
+
['seedling', '🌱'],
|
|
244
|
+
['christmas_tree', '🎄'],
|
|
245
|
+
['santa', '🎅'],
|
|
246
|
+
['gift_heart', '💝'],
|
|
247
|
+
['ring', '💍'],
|
|
248
|
+
['gem', '💎'],
|
|
249
|
+
['bulb', '💡'],
|
|
250
|
+
['book', '📖'],
|
|
251
|
+
['pencil', '📝'],
|
|
252
|
+
['memo', '📝'],
|
|
253
|
+
['email', '✉️'],
|
|
254
|
+
['envelope', '✉️'],
|
|
255
|
+
['phone', '☎️'],
|
|
256
|
+
['telephone', '☎️'],
|
|
257
|
+
['iphone', '📱'],
|
|
258
|
+
['camera', '📷'],
|
|
259
|
+
['video_camera', '📹'],
|
|
260
|
+
['tv', '📺'],
|
|
261
|
+
['computer', '💻'],
|
|
262
|
+
['keyboard', '⌨️'],
|
|
263
|
+
['desktop_computer', '🖥️'],
|
|
264
|
+
['printer', '🖨️'],
|
|
265
|
+
['mouse', '🖱️'],
|
|
266
|
+
['trackball', '🖲️'],
|
|
267
|
+
['joystick', '🕹️'],
|
|
268
|
+
['watch', '⌚'],
|
|
269
|
+
['alarm_clock', '⏰'],
|
|
270
|
+
['stopwatch', '⏱️'],
|
|
271
|
+
['timer_clock', '⏲️'],
|
|
272
|
+
['hourglass', '⌛'],
|
|
273
|
+
['hourglass_flowing_sand', '⏳'],
|
|
274
|
+
['satellite_antenna', '📡'],
|
|
275
|
+
['battery', '🔋'],
|
|
276
|
+
['electric_plug', '🔌'],
|
|
277
|
+
['lock', '🔒'],
|
|
278
|
+
['unlock', '🔓'],
|
|
279
|
+
['key', '🔑'],
|
|
280
|
+
['mag', '🔍'],
|
|
281
|
+
['mag_right', '🔎'],
|
|
282
|
+
['bell', '🔔'],
|
|
283
|
+
['no_bell', '🔕'],
|
|
284
|
+
['bookmark', '🔖'],
|
|
285
|
+
['link', '🔗'],
|
|
286
|
+
['wrench', '🔧'],
|
|
287
|
+
['hammer', '🔨'],
|
|
288
|
+
['nut_and_bolt', '🔩'],
|
|
289
|
+
['thinking', '🤔'],
|
|
290
|
+
['thinking_face', '🤔'],
|
|
291
|
+
['question', '❓'],
|
|
292
|
+
['grey_question', '❔'],
|
|
293
|
+
['exclamation', '❗'],
|
|
294
|
+
['grey_exclamation', '❕'],
|
|
295
|
+
['warning', '⚠️'],
|
|
296
|
+
['x', '❌'],
|
|
297
|
+
['o', '⭕'],
|
|
298
|
+
['white_check_mark', '✅'],
|
|
299
|
+
['heavy_check_mark', '✔️'],
|
|
300
|
+
['accept', '🉑'],
|
|
301
|
+
['satellite', '📡'],
|
|
302
|
+
['arrow_up', '⬆️'],
|
|
303
|
+
['arrow_down', '⬇️'],
|
|
304
|
+
['arrow_left', '⬅️'],
|
|
305
|
+
['arrow_right', '➡️'],
|
|
306
|
+
['arrow_upper_right', '↗️'],
|
|
307
|
+
['arrow_lower_right', '↘️'],
|
|
308
|
+
['arrow_lower_left', '↙️'],
|
|
309
|
+
['arrow_upper_left', '↖️'],
|
|
310
|
+
['arrow_up_down', '↕️'],
|
|
311
|
+
['left_right_arrow', '↔️'],
|
|
312
|
+
['arrows_counterclockwise', '🔄'],
|
|
313
|
+
['back', '🔙'],
|
|
314
|
+
['end', '🔚'],
|
|
315
|
+
['on', '🔛'],
|
|
316
|
+
['soon', '🔜'],
|
|
317
|
+
['top', '🔝'],
|
|
318
|
+
['red_circle', '🔴'],
|
|
319
|
+
['blue_circle', '🔵'],
|
|
320
|
+
['white_circle', '⚪'],
|
|
321
|
+
['black_circle', '⚫'],
|
|
322
|
+
['red_square', '🟥'],
|
|
323
|
+
['blue_square', '🟦'],
|
|
324
|
+
['white_square', '⬜'],
|
|
325
|
+
['black_square', '⬛'],
|
|
326
|
+
['orange_square', '🟧'],
|
|
327
|
+
['yellow_square', '🟨'],
|
|
328
|
+
['green_square', '🟩'],
|
|
329
|
+
['purple_square', '🟪'],
|
|
330
|
+
['brown_square', '🟫'],
|
|
331
|
+
['diamond_shape_with_a_dot_inside', '💠'],
|
|
332
|
+
['radio_button', '🔘'],
|
|
333
|
+
['white_square_button', '🔳'],
|
|
334
|
+
['black_square_button', '🔲'],
|
|
335
|
+
['scissors', '✂️'],
|
|
336
|
+
['paperclip', '📎'],
|
|
337
|
+
['pushpin', '📌'],
|
|
338
|
+
['round_pushpin', '📍'],
|
|
339
|
+
['triangular_flag_on_post', '🚩'],
|
|
340
|
+
['closed_book', '📕'],
|
|
341
|
+
['open_book', '📖'],
|
|
342
|
+
['green_book', '📗'],
|
|
343
|
+
['blue_book', '📘'],
|
|
344
|
+
['orange_book', '�orange_book'],
|
|
345
|
+
['notebook', '📓'],
|
|
346
|
+
['ledger', '📒'],
|
|
347
|
+
['page_with_curl', '📃'],
|
|
348
|
+
['scroll', '📜'],
|
|
349
|
+
['page_facing_up', '📄'],
|
|
350
|
+
['newspaper', '📰'],
|
|
351
|
+
['bookmark_tabs', '📑'],
|
|
352
|
+
['bar_chart', '📊'],
|
|
353
|
+
['chart_with_upwards_trend', '📈'],
|
|
354
|
+
['chart_with_downwards_trend', '📉'],
|
|
355
|
+
['calendar', '📅'],
|
|
356
|
+
['date', '📆'],
|
|
357
|
+
['clipboard', '📋'],
|
|
358
|
+
['file_folder', '📁'],
|
|
359
|
+
['open_file_folder', '📂'],
|
|
360
|
+
['briefcase', '💼'],
|
|
361
|
+
['package', '📦'],
|
|
362
|
+
['inbox_tray', '📥'],
|
|
363
|
+
['outbox_tray', '📤'],
|
|
364
|
+
['musical_note', '🎵'],
|
|
365
|
+
['notes', '🎶'],
|
|
366
|
+
['microphone', '🎤'],
|
|
367
|
+
['headphones', '🎧'],
|
|
368
|
+
['guitar', '🎸'],
|
|
369
|
+
['trumpet', '🎺'],
|
|
370
|
+
['saxophone', '🎷'],
|
|
371
|
+
['violin', '🎻'],
|
|
372
|
+
['drum', '🥁'],
|
|
373
|
+
['clapper', '🎬'],
|
|
374
|
+
['art', '🎨'],
|
|
375
|
+
['performing_arts', '🎭'],
|
|
376
|
+
['game_die', '🎲'],
|
|
377
|
+
['slot_machine', '🎰'],
|
|
378
|
+
]);
|
|
379
|
+
/**
|
|
380
|
+
* Emoji parser for markdown-it
|
|
381
|
+
* Only supports :emoji_name: syntax (no shortcuts/emoticons)
|
|
382
|
+
* Uses Map for O(1) lookups and simple string scanning
|
|
383
|
+
*/
|
|
384
|
+
const emojiRule = (state, silent) => {
|
|
385
|
+
const max = state.posMax;
|
|
386
|
+
const start = state.pos;
|
|
387
|
+
// Quick check: must start with ':'
|
|
388
|
+
if (state.src.charCodeAt(start) !== 0x3A /* : */) {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
// Find the closing ':'
|
|
392
|
+
let pos = start + 1;
|
|
393
|
+
while (pos < max) {
|
|
394
|
+
const code = state.src.charCodeAt(pos);
|
|
395
|
+
// Found closing ':'
|
|
396
|
+
if (code === 0x3A /* : */) {
|
|
397
|
+
const emojiName = state.src.slice(start + 1, pos);
|
|
398
|
+
// Check if this is a valid emoji
|
|
399
|
+
const emojiChar = EMOJI_MAP.get(emojiName);
|
|
400
|
+
if (emojiChar) {
|
|
401
|
+
if (!silent) {
|
|
402
|
+
const token = state.push('emoji', '', 0);
|
|
403
|
+
token.markup = emojiName;
|
|
404
|
+
token.content = emojiChar;
|
|
405
|
+
}
|
|
406
|
+
state.pos = pos + 1;
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
// Not a valid emoji, stop searching
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
// Only allow word characters, digits, underscores, hyphens, and plus
|
|
413
|
+
// This matches the pattern of valid emoji names
|
|
414
|
+
if ((code >= 0x61 && code <= 0x7A) // a-z
|
|
415
|
+
|| (code >= 0x41 && code <= 0x5A) // A-Z
|
|
416
|
+
|| (code >= 0x30 && code <= 0x39) // 0-9
|
|
417
|
+
|| code === 0x5F // _
|
|
418
|
+
|| code === 0x2D // -
|
|
419
|
+
|| code === 0x2B // +
|
|
420
|
+
) {
|
|
421
|
+
pos++;
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
// Invalid character in emoji name
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
// No closing ':' found
|
|
428
|
+
return false;
|
|
429
|
+
};
|
|
430
|
+
export const markdownItEmoji = (md) => {
|
|
431
|
+
md.inline.ruler.before('emphasis', 'emoji', emojiRule);
|
|
432
|
+
};
|
|
433
|
+
export default function comarkEmoji() {
|
|
434
|
+
return {
|
|
435
|
+
name: 'emoji',
|
|
436
|
+
markdownItPlugins: [markdownItEmoji],
|
|
437
|
+
};
|
|
438
|
+
}
|