comark 0.3.1 → 0.3.2
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/dist/devtools/index.d.ts +1 -0
- package/dist/devtools/index.js +1 -0
- package/dist/devtools/register.d.ts +1 -0
- package/dist/devtools/register.js +1 -0
- package/dist/devtools/registry.d.ts +1 -0
- package/dist/devtools/registry.js +1 -0
- package/dist/devtools/vite.d.ts +1 -0
- package/dist/devtools/vite.js +1 -0
- package/dist/internal/frontmatter.d.ts +1 -0
- package/dist/internal/frontmatter.js +4 -2
- package/dist/internal/parse/auto-close/index.js +25 -13
- package/dist/internal/parse/auto-close/table.js +12 -9
- package/dist/internal/parse/auto-unwrap.js +2 -10
- package/dist/internal/parse/html/html_block_rule.js +1 -1
- package/dist/internal/parse/html/html_inline_rule.js +3 -7
- package/dist/internal/parse/html/html_re.js +1 -1
- package/dist/internal/parse/html/index.js +14 -2
- package/dist/internal/parse/syntax/block-params.d.ts +9 -0
- package/dist/internal/parse/syntax/block-params.js +48 -0
- package/dist/internal/parse/syntax/brackets.d.ts +8 -0
- package/dist/internal/parse/syntax/brackets.js +20 -0
- package/dist/internal/parse/syntax/props.d.ts +5 -0
- package/dist/internal/parse/syntax/props.js +119 -0
- package/dist/internal/parse/token-processor.js +25 -24
- package/dist/internal/props-validation.js +4 -9
- package/dist/internal/stringify/attributes.js +4 -1
- package/dist/internal/stringify/handlers/a.js +1 -3
- package/dist/internal/stringify/handlers/blockquote.js +2 -4
- package/dist/internal/stringify/handlers/code.js +1 -3
- package/dist/internal/stringify/handlers/emphesis.js +1 -3
- package/dist/internal/stringify/handlers/html.js +26 -16
- package/dist/internal/stringify/handlers/img.js +1 -3
- package/dist/internal/stringify/handlers/li.js +14 -8
- package/dist/internal/stringify/handlers/mdc.js +2 -3
- package/dist/internal/stringify/handlers/ol.js +1 -1
- package/dist/internal/stringify/handlers/p.d.ts +1 -1
- package/dist/internal/stringify/handlers/p.js +4 -1
- package/dist/internal/stringify/handlers/pre.js +10 -13
- package/dist/internal/stringify/handlers/strong.js +1 -3
- package/dist/internal/stringify/handlers/table.js +7 -5
- package/dist/internal/stringify/handlers/template.js +1 -1
- package/dist/internal/stringify/handlers/ul.js +1 -1
- package/dist/internal/stringify/indent.d.ts +1 -5
- package/dist/internal/stringify/indent.js +1 -9
- package/dist/internal/stringify/state.js +1 -1
- package/dist/internal/yaml.js +1 -1
- package/dist/parse.js +14 -8
- package/dist/plugins/alert.js +1 -1
- package/dist/plugins/binding.js +1 -3
- package/dist/plugins/breaks.js +1 -1
- package/dist/plugins/emoji.js +8 -8
- package/dist/plugins/footnotes.js +19 -13
- package/dist/plugins/headings.js +2 -4
- package/dist/plugins/highlight.d.ts +1 -11
- package/dist/plugins/highlight.js +198 -103
- package/dist/plugins/json-render.js +5 -9
- package/dist/plugins/math.js +4 -6
- package/dist/plugins/mermaid.js +6 -20
- package/dist/plugins/punctuation.js +5 -6
- package/dist/plugins/security.js +2 -2
- package/dist/plugins/syntax.d.ts +49 -0
- package/dist/plugins/syntax.js +522 -0
- package/dist/plugins/task-list.d.ts +1 -1
- package/dist/plugins/task-list.js +11 -8
- package/dist/plugins/toc.js +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/utils/comark.tmLanguage.d.ts +335 -0
- package/dist/utils/comark.tmLanguage.js +597 -0
- package/dist/utils/helpers.js +1 -3
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +25 -3
- package/package.json +39 -40
- package/skills/skills/comark/AGENTS.md +0 -261
- package/skills/skills/comark/SKILL.md +0 -489
- package/skills/skills/comark/references/markdown-syntax.md +0 -599
- package/skills/skills/comark/references/parsing-ast.md +0 -378
- package/skills/skills/comark/references/rendering-react.md +0 -445
- package/skills/skills/comark/references/rendering-svelte.md +0 -453
- package/skills/skills/comark/references/rendering-vue.md +0 -462
- /package/skills/{skills/migrate-mdc-to-comark → migrate-mdc-to-comark}/SKILL.md +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { defineComarkPlugin } from "../utils/helpers.js";
|
|
2
2
|
import { createShikiPrimitive } from 'shiki';
|
|
3
3
|
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';
|
|
4
|
-
import { codeToHast } from 'shiki/core';
|
|
4
|
+
import { codeToHast, codeToTokens, getTokenStyleObject, stringifyTokenStyle } from 'shiki/core';
|
|
5
|
+
import comakLanguage from "../utils/comark.tmLanguage.js";
|
|
5
6
|
let highlighter = null;
|
|
6
7
|
let highlighterPromise = null;
|
|
7
8
|
const loadedThemes = new Set();
|
|
@@ -11,11 +12,14 @@ const loadedLanguages = new Set();
|
|
|
11
12
|
* Uses a singleton pattern to avoid creating multiple highlighters
|
|
12
13
|
*/
|
|
13
14
|
export async function getHighlighter(options = {}) {
|
|
14
|
-
// If highlighter exists, load any new themes that aren't loaded yet
|
|
15
15
|
if (highlighter) {
|
|
16
|
+
// Fast path: skip registerDefaults() when no custom themes/languages are requested
|
|
17
|
+
if (!options.themes && !options.languages) {
|
|
18
|
+
return highlighter;
|
|
19
|
+
}
|
|
16
20
|
const { themes, languages } = await registerDefaults(options);
|
|
17
|
-
await Promise.all(themes.map(theme => loadTheme(highlighter, theme)));
|
|
18
|
-
await Promise.all(languages.map(language => loadLanguage(highlighter, language)));
|
|
21
|
+
await Promise.all(themes.map((theme) => loadTheme(highlighter, theme)));
|
|
22
|
+
await Promise.all(languages.map((language) => loadLanguage(highlighter, language)));
|
|
19
23
|
return highlighter;
|
|
20
24
|
}
|
|
21
25
|
if (highlighterPromise) {
|
|
@@ -28,16 +32,16 @@ export async function getHighlighter(options = {}) {
|
|
|
28
32
|
themes: themes,
|
|
29
33
|
langs: languages,
|
|
30
34
|
langAlias: {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
md: 'mdc',
|
|
36
|
+
markdown: 'mdc',
|
|
37
|
+
comark: 'mdc',
|
|
34
38
|
'json-render': 'json',
|
|
35
39
|
'yaml-render': 'yaml',
|
|
36
40
|
},
|
|
37
41
|
engine: createJavaScriptRegexEngine({ forgiving: true }),
|
|
38
42
|
});
|
|
39
|
-
await Promise.all(themes.map(theme => loadTheme(hl, theme)));
|
|
40
|
-
await Promise.all(languages.map(language => loadLanguage(hl, language)));
|
|
43
|
+
await Promise.all(themes.map((theme) => loadTheme(hl, theme)));
|
|
44
|
+
await Promise.all(languages.map((language) => loadLanguage(hl, language)));
|
|
41
45
|
return hl;
|
|
42
46
|
})();
|
|
43
47
|
highlighter = await highlighterPromise;
|
|
@@ -54,10 +58,18 @@ async function registerDefaults(options) {
|
|
|
54
58
|
const languages = options.languages || [];
|
|
55
59
|
const promises = [];
|
|
56
60
|
if (options.registerDefaultThemes !== false) {
|
|
57
|
-
promises.push(import('shiki/dist/themes/material-theme-lighter.mjs').then(
|
|
61
|
+
promises.push(import('shiki/dist/themes/material-theme-lighter.mjs').then((m) => ({
|
|
62
|
+
type: 'theme',
|
|
63
|
+
value: m.default,
|
|
64
|
+
})), import('shiki/dist/themes/material-theme-palenight.mjs').then((m) => ({
|
|
65
|
+
type: 'theme',
|
|
66
|
+
value: m.default,
|
|
67
|
+
})));
|
|
58
68
|
}
|
|
59
69
|
if (options.registerDefaultLanguages !== false) {
|
|
60
|
-
promises.push(import('shiki/dist/langs/vue.mjs').then(m => ({ type: 'lang', value: m.default })), import('shiki/dist/langs/tsx.mjs').then(
|
|
70
|
+
promises.push(import('shiki/dist/langs/vue.mjs').then((m) => ({ type: 'lang', value: m.default })), import('shiki/dist/langs/tsx.mjs').then((m) => ({ type: 'lang', value: m.default })), import('shiki/dist/langs/svelte.mjs').then((m) => ({ type: 'lang', value: m.default })), import('shiki/dist/langs/typescript.mjs').then((m) => ({ type: 'lang', value: m.default })), import('shiki/dist/langs/javascript.mjs').then((m) => ({ type: 'lang', value: m.default })),
|
|
71
|
+
// import('shiki/dist/langs/mdc.mjs').then(m => ({ type: 'lang' as const, value: m.default })),
|
|
72
|
+
import('shiki/dist/langs/bash.mjs').then((m) => ({ type: 'lang', value: m.default })), import('shiki/dist/langs/json.mjs').then((m) => ({ type: 'lang', value: m.default })), import('shiki/dist/langs/yaml.mjs').then((m) => ({ type: 'lang', value: m.default })), import('shiki/dist/langs/astro.mjs').then((m) => ({ type: 'lang', value: m.default })));
|
|
61
73
|
}
|
|
62
74
|
const results = await Promise.all(promises);
|
|
63
75
|
for (const result of results) {
|
|
@@ -66,6 +78,8 @@ async function registerDefaults(options) {
|
|
|
66
78
|
else
|
|
67
79
|
languages.push(result.value);
|
|
68
80
|
}
|
|
81
|
+
// Remove custom language after updating language in shiki core
|
|
82
|
+
languages.push(comakLanguage);
|
|
69
83
|
return { themes, languages };
|
|
70
84
|
}
|
|
71
85
|
async function loadTheme(hl, theme) {
|
|
@@ -76,64 +90,37 @@ async function loadTheme(hl, theme) {
|
|
|
76
90
|
loadedThemes.add(theme.name || '');
|
|
77
91
|
}
|
|
78
92
|
async function loadLanguage(hl, language) {
|
|
79
|
-
if (loadedLanguages.has(Array.isArray(language) ? language.map(l => l.name || '').join(',') : language.name || '')) {
|
|
93
|
+
if (loadedLanguages.has(Array.isArray(language) ? language.map((l) => l.name || '').join(',') : language.name || '')) {
|
|
80
94
|
return;
|
|
81
95
|
}
|
|
82
96
|
await hl.loadLanguage(language);
|
|
83
|
-
loadedLanguages.add(Array.isArray(language) ? language.map(l => l.name || '').join(',') : language.name || '');
|
|
97
|
+
loadedLanguages.add(Array.isArray(language) ? language.map((l) => l.name || '').join(',') : language.name || '');
|
|
84
98
|
}
|
|
85
99
|
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
100
|
+
* Convert a hast (HTML AST) node into a ComarkNode.
|
|
101
|
+
* Uses pre-allocated arrays to avoid spread overhead.
|
|
88
102
|
*/
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// Use codeToTokens to get raw tokens
|
|
98
|
-
const result = await codeToHast(hl, code, {
|
|
99
|
-
lang: language,
|
|
100
|
-
transformers: options.transformers,
|
|
101
|
-
themes: {
|
|
102
|
-
light: lightTheme,
|
|
103
|
-
dark: lightTheme !== darkTheme ? darkTheme : undefined,
|
|
104
|
-
},
|
|
105
|
-
meta: {
|
|
106
|
-
__raw: attrs.meta,
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
const allTokens = result.children.map(hastToMinimarkNode);
|
|
110
|
-
return {
|
|
111
|
-
nodes: allTokens,
|
|
112
|
-
language,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
// If highlighting fails, return the original code
|
|
117
|
-
console.error('Shiki highlighting error:', error);
|
|
118
|
-
return {
|
|
119
|
-
nodes: [code],
|
|
120
|
-
language,
|
|
121
|
-
};
|
|
103
|
+
function hastToComarkNode(input) {
|
|
104
|
+
if (input.type === 'text')
|
|
105
|
+
return input.value;
|
|
106
|
+
if (input.type === 'comment')
|
|
107
|
+
return [null, {}, input.value];
|
|
108
|
+
const props = input.properties || {};
|
|
109
|
+
if (input.tag === 'code' && props?.className && props.className.length === 0) {
|
|
110
|
+
delete props.className;
|
|
122
111
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
props,
|
|
134
|
-
...(input.children || []).map(hastToMinimarkNode),
|
|
135
|
-
];
|
|
112
|
+
const children = input.children;
|
|
113
|
+
if (!children || children.length === 0)
|
|
114
|
+
return [input.tagName, props];
|
|
115
|
+
const len = children.length;
|
|
116
|
+
// eslint-disable-next-line unicorn/no-new-array -- pre-allocated for perf
|
|
117
|
+
const result = new Array(len + 2);
|
|
118
|
+
result[0] = input.tagName;
|
|
119
|
+
result[1] = props;
|
|
120
|
+
for (let i = 0; i < len; i++) {
|
|
121
|
+
result[i + 2] = hastToComarkNode(children[i]);
|
|
136
122
|
}
|
|
123
|
+
return result;
|
|
137
124
|
}
|
|
138
125
|
/**
|
|
139
126
|
* Apply syntax highlighting to all code blocks in a Comark tree
|
|
@@ -141,63 +128,159 @@ export async function highlightCode(code, attrs, options = {}) {
|
|
|
141
128
|
*/
|
|
142
129
|
export async function highlightCodeBlocks(tree, options = {}) {
|
|
143
130
|
const codeBlocks = [];
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
131
|
+
const pathBuf = [];
|
|
132
|
+
// Recursively find <pre><code> blocks, tracking their path via push/pop on a shared buffer
|
|
133
|
+
const walkChildren = (element) => {
|
|
134
|
+
for (let i = 2; i < element.length; i++) {
|
|
135
|
+
const child = element[i];
|
|
136
|
+
if (typeof child === 'string')
|
|
148
137
|
continue;
|
|
149
|
-
if (!Array.isArray(
|
|
138
|
+
if (!Array.isArray(child) || child.length < 3)
|
|
150
139
|
continue;
|
|
151
|
-
|
|
152
|
-
|
|
140
|
+
pathBuf.push(i - 2);
|
|
141
|
+
if (child[0] === 'pre' && Array.isArray(child[2]) && child[2][0] === 'code') {
|
|
142
|
+
const codeContent = child[2][2];
|
|
153
143
|
if (typeof codeContent === 'string') {
|
|
154
|
-
codeBlocks.push({ node, path:
|
|
144
|
+
codeBlocks.push({ node: child, path: pathBuf.slice() });
|
|
155
145
|
}
|
|
156
146
|
}
|
|
157
|
-
|
|
147
|
+
walkChildren(child);
|
|
148
|
+
pathBuf.pop();
|
|
158
149
|
}
|
|
159
150
|
};
|
|
160
|
-
|
|
151
|
+
for (let i = 0; i < tree.nodes.length; i++) {
|
|
152
|
+
const node = tree.nodes[i];
|
|
153
|
+
if (typeof node === 'string')
|
|
154
|
+
continue;
|
|
155
|
+
if (!Array.isArray(node) || node.length < 3)
|
|
156
|
+
continue;
|
|
157
|
+
if (node[0] === 'pre' && Array.isArray(node[2]) && node[2][0] === 'code') {
|
|
158
|
+
const codeContent = node[2][2];
|
|
159
|
+
if (typeof codeContent === 'string') {
|
|
160
|
+
codeBlocks.push({ node, path: [i] });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
pathBuf.length = 1;
|
|
164
|
+
pathBuf[0] = i;
|
|
165
|
+
walkChildren(node);
|
|
166
|
+
}
|
|
161
167
|
if (codeBlocks.length === 0)
|
|
162
168
|
return tree;
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
169
|
+
const hl = await getHighlighter(options);
|
|
170
|
+
const { themes = { light: 'material-theme-lighter', dark: 'material-theme-palenight' } } = options;
|
|
171
|
+
const lightTheme = themes.light || themes.dark || 'material-theme-lighter';
|
|
172
|
+
const darkTheme = themes.dark || themes.light || 'material-theme-palenight';
|
|
173
|
+
const themeOptions = {
|
|
174
|
+
light: lightTheme,
|
|
175
|
+
dark: lightTheme !== darkTheme ? darkTheme : undefined,
|
|
176
|
+
};
|
|
177
|
+
const hasTransformers = options.transformers && options.transformers.length > 0;
|
|
178
|
+
const darkClassSuffix = options.themes?.dark?.name ? ` dark:${options.themes.dark.name}` : '';
|
|
179
|
+
// Build new nodes array, spine-copying only paths to modified <pre> nodes
|
|
180
|
+
const newNodes = [...tree.nodes];
|
|
167
181
|
for (let i = 0; i < codeBlocks.length; i++) {
|
|
168
182
|
const { node, path } = codeBlocks[i];
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
183
|
+
const code = node[2][2];
|
|
184
|
+
const attrs = node[1];
|
|
185
|
+
const preAttrs = attrs;
|
|
186
|
+
const language = attrs?.language;
|
|
187
|
+
let classStr;
|
|
188
|
+
let codeChildren;
|
|
189
|
+
try {
|
|
190
|
+
if (hasTransformers) {
|
|
191
|
+
// Transformers operate on hast, so we must go through codeToHast
|
|
192
|
+
const result = codeToHast(hl, code, {
|
|
193
|
+
lang: language,
|
|
194
|
+
transformers: options.transformers,
|
|
195
|
+
themes: themeOptions,
|
|
196
|
+
meta: { __raw: attrs.meta },
|
|
197
|
+
});
|
|
198
|
+
const preNode = result.children.map(hastToComarkNode)[0];
|
|
199
|
+
const cls = preNode[1].class;
|
|
200
|
+
classStr = Array.isArray(cls) ? cls.join(' ') : String(cls);
|
|
201
|
+
codeChildren = preNode[2].slice(2);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
// Fast path: build ComarkNodes directly from tokens, skipping hast
|
|
205
|
+
const result = codeToTokens(hl, code, {
|
|
206
|
+
lang: language,
|
|
207
|
+
themes: themeOptions,
|
|
208
|
+
});
|
|
209
|
+
classStr = `shiki ${result.themeName || ''}`;
|
|
210
|
+
// Replicate shiki's mergeWhitespaceTokens: merge pure-whitespace tokens
|
|
211
|
+
// into the following token (unless underline/strikethrough styled)
|
|
212
|
+
const tokenLines = result.tokens;
|
|
213
|
+
codeChildren = [];
|
|
214
|
+
for (let li = 0; li < tokenLines.length; li++) {
|
|
215
|
+
const line = tokenLines[li];
|
|
216
|
+
const spanCount = line.length;
|
|
217
|
+
// Merge whitespace tokens inline while building spans
|
|
218
|
+
let carry = '';
|
|
219
|
+
const spans = [];
|
|
220
|
+
for (let t = 0; t < spanCount; t++) {
|
|
221
|
+
const tk = line[t];
|
|
222
|
+
const canMerge = !((tk.fontStyle && (tk.fontStyle & 8 /* Strikethrough */ || tk.fontStyle & 4)) /* Underline */);
|
|
223
|
+
if (canMerge && /^\s+$/.test(tk.content) && t + 1 < spanCount) {
|
|
224
|
+
carry += tk.content;
|
|
225
|
+
}
|
|
226
|
+
else if (carry) {
|
|
227
|
+
const style = stringifyTokenStyle(tk.htmlStyle || getTokenStyleObject(tk));
|
|
228
|
+
if (canMerge) {
|
|
229
|
+
spans.push(style ? ['span', { style }, carry + tk.content] : ['span', {}, carry + tk.content]);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
spans.push(['span', {}, carry]);
|
|
233
|
+
spans.push(style ? ['span', { style }, tk.content] : ['span', {}, tk.content]);
|
|
234
|
+
}
|
|
235
|
+
carry = '';
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
const style = stringifyTokenStyle(tk.htmlStyle || getTokenStyleObject(tk));
|
|
239
|
+
spans.push(style ? ['span', { style }, tk.content] : ['span', {}, tk.content]);
|
|
240
|
+
}
|
|
189
241
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
242
|
+
// If trailing whitespace wasn't merged, emit it
|
|
243
|
+
if (carry) {
|
|
244
|
+
spans.push(['span', {}, carry]);
|
|
193
245
|
}
|
|
194
|
-
line
|
|
246
|
+
// eslint-disable-next-line unicorn/no-new-array -- pre-allocated for perf
|
|
247
|
+
const lineNode = new Array(spans.length + 2);
|
|
248
|
+
lineNode[0] = 'span';
|
|
249
|
+
lineNode[1] = { class: 'line' };
|
|
250
|
+
for (let s = 0; s < spans.length; s++)
|
|
251
|
+
lineNode[s + 2] = spans[s];
|
|
252
|
+
codeChildren.push(lineNode);
|
|
253
|
+
if (li < tokenLines.length - 1)
|
|
254
|
+
codeChildren.push('\n');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
classStr = 'shiki';
|
|
260
|
+
codeChildren = [code];
|
|
261
|
+
}
|
|
262
|
+
if (darkClassSuffix)
|
|
263
|
+
classStr += darkClassSuffix;
|
|
264
|
+
// Apply line highlights
|
|
265
|
+
const highlightSet = Array.isArray(preAttrs.highlights) ? new Set(preAttrs.highlights) : null;
|
|
266
|
+
let line = 1;
|
|
267
|
+
for (const child of codeChildren) {
|
|
268
|
+
if (Array.isArray(child)) {
|
|
269
|
+
if (highlightSet !== null && highlightSet.has(line)) {
|
|
270
|
+
child[1].class = `${child[1].class ?? ''} highlight`.trim();
|
|
271
|
+
// TODO: (enforcing default style) once we unify all ecosystem styles we can remove this
|
|
272
|
+
child[1].style = 'display: inline-block';
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
// TODO: (enforcing default style) once we unify all ecosystem styles we can remove this
|
|
276
|
+
child[1].style = 'display: inline';
|
|
195
277
|
}
|
|
278
|
+
line += 1;
|
|
196
279
|
}
|
|
197
280
|
}
|
|
198
281
|
const newPreAttrs = {
|
|
199
282
|
...preAttrs,
|
|
200
|
-
class:
|
|
283
|
+
class: classStr,
|
|
201
284
|
tabindex: '0',
|
|
202
285
|
};
|
|
203
286
|
if (options.preStyles) {
|
|
@@ -222,14 +305,26 @@ export async function highlightCodeBlocks(tree, options = {}) {
|
|
|
222
305
|
}
|
|
223
306
|
const codeEl = node[2];
|
|
224
307
|
const codeAttrs = codeEl[1] || {};
|
|
225
|
-
|
|
308
|
+
// eslint-disable-next-line unicorn/no-new-array -- pre-allocated for perf
|
|
309
|
+
const codeNode = new Array(codeChildren.length + 2);
|
|
310
|
+
codeNode[0] = 'code';
|
|
311
|
+
codeNode[1] = codeAttrs;
|
|
312
|
+
for (let j = 0; j < codeChildren.length; j++)
|
|
313
|
+
codeNode[j + 2] = codeChildren[j];
|
|
314
|
+
const newPreNode = ['pre', newPreAttrs, codeNode];
|
|
226
315
|
if (path.length === 1) {
|
|
227
316
|
newNodes[path[0]] = newPreNode;
|
|
228
317
|
}
|
|
229
318
|
else {
|
|
230
|
-
|
|
319
|
+
// Copy only the spine from root to this node to preserve immutability
|
|
320
|
+
const rootIdx = path[0];
|
|
321
|
+
let current = [...newNodes[rootIdx]];
|
|
322
|
+
newNodes[rootIdx] = current;
|
|
231
323
|
for (let j = 1; j < path.length - 1; j++) {
|
|
232
|
-
|
|
324
|
+
const childSlot = path[j] + 2;
|
|
325
|
+
const next = [...current[childSlot]];
|
|
326
|
+
current[childSlot] = next;
|
|
327
|
+
current = next;
|
|
233
328
|
}
|
|
234
329
|
const childSlot = path[path.length - 1] + 2;
|
|
235
330
|
current[childSlot] = newPreNode;
|
|
@@ -16,13 +16,8 @@ function jsonRenderElementToAst(element, elements) {
|
|
|
16
16
|
if (element.type === 'Text') {
|
|
17
17
|
return String(element.props.content);
|
|
18
18
|
}
|
|
19
|
-
const children = element.children?.map(childName => elements[childName])
|
|
20
|
-
|
|
21
|
-
return [
|
|
22
|
-
element.type,
|
|
23
|
-
element.props,
|
|
24
|
-
...children.map(child => jsonRenderElementToAst(child, elements)),
|
|
25
|
-
];
|
|
19
|
+
const children = element.children?.map((childName) => elements[childName]).filter(Boolean) || [];
|
|
20
|
+
return [element.type, element.props, ...children.map((child) => jsonRenderElementToAst(child, elements))];
|
|
26
21
|
}
|
|
27
22
|
/**
|
|
28
23
|
* Plugin for rendering [JSON Render](https://json-render.dev/) specs as UI components.
|
|
@@ -76,8 +71,9 @@ function jsonRenderElementToAst(element, elements) {
|
|
|
76
71
|
export default defineComarkPlugin((_config = {}) => ({
|
|
77
72
|
name: 'json-render',
|
|
78
73
|
post: async (state) => {
|
|
79
|
-
visit(state.tree, node => node[0] === 'pre' &&
|
|
80
|
-
|
|
74
|
+
visit(state.tree, (node) => node[0] === 'pre' &&
|
|
75
|
+
(node[1].language === 'json-render' ||
|
|
76
|
+
node[1].language === 'yaml-render'), (preNode) => {
|
|
81
77
|
const language = preNode[1].language;
|
|
82
78
|
try {
|
|
83
79
|
let spec = undefined;
|
package/dist/plugins/math.js
CHANGED
|
@@ -70,7 +70,7 @@ function mathInlineDisplayRule(state, silent, _config) {
|
|
|
70
70
|
let pos = start + 2;
|
|
71
71
|
while (pos + 1 < max) {
|
|
72
72
|
// Stop at newline
|
|
73
|
-
if (state.src.charCodeAt(pos) ===
|
|
73
|
+
if (state.src.charCodeAt(pos) === 0x0a /* \n */) {
|
|
74
74
|
return false;
|
|
75
75
|
}
|
|
76
76
|
// Check for $$
|
|
@@ -110,7 +110,7 @@ function mathInlineRule(state, silent, _config) {
|
|
|
110
110
|
while (pos < max) {
|
|
111
111
|
const char = state.src.charCodeAt(pos);
|
|
112
112
|
// Stop at newline - $ must close on same line
|
|
113
|
-
if (char ===
|
|
113
|
+
if (char === 0x0a /* \n */) {
|
|
114
114
|
return false;
|
|
115
115
|
}
|
|
116
116
|
if (char === 0x24 /* $ */) {
|
|
@@ -119,7 +119,7 @@ function mathInlineRule(state, silent, _config) {
|
|
|
119
119
|
// it's not preceded by another $ (which would make it $$),
|
|
120
120
|
// and it's not followed by another $ (which would make it $$)
|
|
121
121
|
const hasContent = pos > start + 1;
|
|
122
|
-
const notEscaped = pos === start + 1 || state.src.charCodeAt(pos - 1) !==
|
|
122
|
+
const notEscaped = pos === start + 1 || state.src.charCodeAt(pos - 1) !== 0x5c; /* \ */
|
|
123
123
|
const notPrecededByDollar = pos === start + 1 || state.src.charCodeAt(pos - 1) !== 0x24;
|
|
124
124
|
const notFollowedByDollar = pos + 1 >= max || state.src.charCodeAt(pos + 1) !== 0x24;
|
|
125
125
|
if (hasContent && notEscaped && notPrecededByDollar && notFollowedByDollar) {
|
|
@@ -256,7 +256,5 @@ function markdownItMath(md, config = {}) {
|
|
|
256
256
|
*/
|
|
257
257
|
export default defineComarkPlugin((config = {}) => ({
|
|
258
258
|
name: 'math',
|
|
259
|
-
markdownItPlugins: [
|
|
260
|
-
((md) => markdownItMath(md, config)),
|
|
261
|
-
],
|
|
259
|
+
markdownItPlugins: [((md) => markdownItMath(md, config))],
|
|
262
260
|
}));
|
package/dist/plugins/mermaid.js
CHANGED
|
@@ -43,7 +43,7 @@ export function searchProps(content, index = 0) {
|
|
|
43
43
|
'(': ')',
|
|
44
44
|
};
|
|
45
45
|
const quotePairs = {
|
|
46
|
-
'
|
|
46
|
+
"'": "'",
|
|
47
47
|
'"': '"',
|
|
48
48
|
'`': '`',
|
|
49
49
|
};
|
|
@@ -67,17 +67,11 @@ export function searchProps(content, index = 0) {
|
|
|
67
67
|
}
|
|
68
68
|
else if (content[index] === '.') {
|
|
69
69
|
index += 1;
|
|
70
|
-
props.push([
|
|
71
|
-
'class',
|
|
72
|
-
searchUntil(' #.}'),
|
|
73
|
-
]);
|
|
70
|
+
props.push(['class', searchUntil(' #.}')]);
|
|
74
71
|
}
|
|
75
72
|
else if (content[index] === '#') {
|
|
76
73
|
index += 1;
|
|
77
|
-
props.push([
|
|
78
|
-
'id',
|
|
79
|
-
searchUntil(' #.}'),
|
|
80
|
-
]);
|
|
74
|
+
props.push(['id', searchUntil(' #.}')]);
|
|
81
75
|
}
|
|
82
76
|
else {
|
|
83
77
|
const start = index;
|
|
@@ -91,16 +85,10 @@ export function searchProps(content, index = 0) {
|
|
|
91
85
|
const key = content.slice(start, index).trim();
|
|
92
86
|
if (char === '=') {
|
|
93
87
|
index += 1;
|
|
94
|
-
props.push([
|
|
95
|
-
key,
|
|
96
|
-
searchValue(),
|
|
97
|
-
]);
|
|
88
|
+
props.push([key, searchValue()]);
|
|
98
89
|
}
|
|
99
90
|
else {
|
|
100
|
-
props.push([
|
|
101
|
-
key,
|
|
102
|
-
'true',
|
|
103
|
-
]);
|
|
91
|
+
props.push([key, 'true']);
|
|
104
92
|
}
|
|
105
93
|
}
|
|
106
94
|
}
|
|
@@ -178,7 +166,5 @@ export function searchProps(content, index = 0) {
|
|
|
178
166
|
*/
|
|
179
167
|
export default defineComarkPlugin((config = {}) => ({
|
|
180
168
|
name: 'mermaid',
|
|
181
|
-
markdownItPlugins: [
|
|
182
|
-
((md) => markdownItMermaid(md, config)),
|
|
183
|
-
],
|
|
169
|
+
markdownItPlugins: [((md) => markdownItMermaid(md, config))],
|
|
184
170
|
}));
|
|
@@ -3,8 +3,7 @@ const DEFAULT_QUOTES = '\u201C\u201D\u2018\u2019'; // ""''
|
|
|
3
3
|
/** Tags whose text content should not be transformed */
|
|
4
4
|
const SKIP_TAGS = new Set(['code', 'pre', 'math', 'script', 'style', 'kbd']);
|
|
5
5
|
function isWhitespaceOrOpener(ch) {
|
|
6
|
-
return ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r'
|
|
7
|
-
|| ch === '(' || ch === '[' || ch === '{';
|
|
6
|
+
return ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r' || ch === '(' || ch === '[' || ch === '{';
|
|
8
7
|
}
|
|
9
8
|
function isLetter(ch) {
|
|
10
9
|
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
|
|
@@ -117,11 +116,11 @@ function applyPunctuation(text, enableQuotes, dashes, ellipsis, symbols, normali
|
|
|
117
116
|
if (enableQuotes) {
|
|
118
117
|
if (ch === '"') {
|
|
119
118
|
const prev = i > 0 ? text[i - 1] : ' ';
|
|
120
|
-
result += text.slice(last, i) + (
|
|
119
|
+
result += text.slice(last, i) + (isWhitespaceOrOpener(prev) || i === 0 ? openDouble : closeDouble);
|
|
121
120
|
last = i + 1;
|
|
122
121
|
continue;
|
|
123
122
|
}
|
|
124
|
-
if (ch === '
|
|
123
|
+
if (ch === "'") {
|
|
125
124
|
const prev = i > 0 ? text[i - 1] : ' ';
|
|
126
125
|
const next = i + 1 < len ? text[i + 1] : '';
|
|
127
126
|
result += text.slice(last, i);
|
|
@@ -130,7 +129,7 @@ function applyPunctuation(text, enableQuotes, dashes, ellipsis, symbols, normali
|
|
|
130
129
|
result += closeSingle;
|
|
131
130
|
}
|
|
132
131
|
else {
|
|
133
|
-
result +=
|
|
132
|
+
result += isWhitespaceOrOpener(prev) || i === 0 ? openSingle : closeSingle;
|
|
134
133
|
}
|
|
135
134
|
last = i + 1;
|
|
136
135
|
continue;
|
|
@@ -181,7 +180,7 @@ function applyPunctuation(text, enableQuotes, dashes, ellipsis, symbols, normali
|
|
|
181
180
|
* ```
|
|
182
181
|
*/
|
|
183
182
|
export default defineComarkPlugin((options = {}) => {
|
|
184
|
-
const { quotes = true, dashes = true, ellipsis = true, symbols = true, normalize = true
|
|
183
|
+
const { quotes = true, dashes = true, ellipsis = true, symbols = true, normalize = true } = options;
|
|
185
184
|
// Resolve quote characters
|
|
186
185
|
let enableQuotes;
|
|
187
186
|
let openDouble;
|
package/dist/plugins/security.js
CHANGED
|
@@ -3,7 +3,7 @@ import { visit } from "../utils/index.js";
|
|
|
3
3
|
import { validateProps } from "../internal/props-validation.js";
|
|
4
4
|
export default defineComarkPlugin((options = {}) => {
|
|
5
5
|
const { blockedTags = [], allowedLinkPrefixes, allowedImagePrefixes, allowedProtocols, defaultOrigin, allowDataImages, } = options;
|
|
6
|
-
const dropSet = new Set(blockedTags.map(t => t.toLowerCase()));
|
|
6
|
+
const dropSet = new Set(blockedTags.map((t) => t.toLowerCase()));
|
|
7
7
|
const propsOptions = {
|
|
8
8
|
allowedLinkPrefixes,
|
|
9
9
|
allowedImagePrefixes,
|
|
@@ -14,7 +14,7 @@ export default defineComarkPlugin((options = {}) => {
|
|
|
14
14
|
return {
|
|
15
15
|
name: 'security',
|
|
16
16
|
post(state) {
|
|
17
|
-
visit(state.tree, node => typeof node !== 'string' && node[0] !== null, (node) => {
|
|
17
|
+
visit(state.tree, (node) => typeof node !== 'string' && node[0] !== null, (node) => {
|
|
18
18
|
const element = node;
|
|
19
19
|
// return false to remove the node from the tree
|
|
20
20
|
if (dropSet.has(element[0].toLowerCase())) {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { MarkdownItPluginWithOptions } from '../types.ts';
|
|
2
|
+
export interface SyntaxOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Enable block component syntax.
|
|
5
|
+
*
|
|
6
|
+
* @see https://comark.dev/syntax/components#block
|
|
7
|
+
* @default true
|
|
8
|
+
*/
|
|
9
|
+
blockComponent?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Enable inline props syntax.
|
|
12
|
+
*
|
|
13
|
+
* @see https://comark.dev/syntax/attributes
|
|
14
|
+
* @default true
|
|
15
|
+
*/
|
|
16
|
+
inlineProps?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Enable inline span syntax.
|
|
19
|
+
*
|
|
20
|
+
* @see https://comark.dev/syntax/attributes#span-attributes
|
|
21
|
+
* @default true
|
|
22
|
+
*/
|
|
23
|
+
inlineSpan?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Enable inline component syntax.
|
|
26
|
+
*
|
|
27
|
+
* @see https://comark.dev/syntax/components#inline
|
|
28
|
+
* @default true
|
|
29
|
+
*/
|
|
30
|
+
inlineComponent?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Enable inline binding syntax (`{{ value }}` and `{{ value || default }}`).
|
|
33
|
+
*
|
|
34
|
+
* Off by default — opt in here, or use the standalone `plugins/binding.ts` plugin.
|
|
35
|
+
*
|
|
36
|
+
* @see https://comark.dev/syntax/components#data-binding
|
|
37
|
+
* @default false
|
|
38
|
+
*/
|
|
39
|
+
inlineBinding?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* The tag name used to render an inline binding.
|
|
42
|
+
*
|
|
43
|
+
* @default 'binding'
|
|
44
|
+
*/
|
|
45
|
+
bindingTag?: string;
|
|
46
|
+
}
|
|
47
|
+
declare const _default: import("../types.ts").ComarkPluginFactory<SyntaxOptions>;
|
|
48
|
+
export default _default;
|
|
49
|
+
export declare const markdownItComark: MarkdownItPluginWithOptions<SyntaxOptions>;
|