comark 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/internal/stringify/handlers/a.js +1 -1
- package/dist/internal/stringify/handlers/code.js +1 -1
- package/dist/internal/stringify/handlers/emphesis.js +1 -1
- package/dist/internal/stringify/handlers/html.js +2 -2
- package/dist/internal/stringify/handlers/img.js +1 -1
- package/dist/internal/stringify/handlers/mermaid.js +1 -1
- package/dist/internal/stringify/handlers/ol.js +1 -1
- package/dist/internal/stringify/handlers/strong.js +1 -1
- package/dist/internal/stringify/handlers/ul.js +1 -1
- package/dist/plugins/highlight.d.ts +14 -6
- package/dist/plugins/highlight.js +38 -39
- package/dist/plugins/security.d.ts +1 -1
- package/dist/plugins/security.js +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://comark.dev)
|
|
9
9
|
[](https://github.com/comarkdown/comark/blob/main/LICENSE)
|
|
10
10
|
|
|
11
|
-
A high-performance markdown parser and renderer with Vue, React
|
|
11
|
+
A high-performance markdown parser and renderer with Vue, React, Svelte, HTML and ANSI terminal.
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { htmlAttributes } from
|
|
2
|
-
import { indent } from
|
|
1
|
+
import { htmlAttributes } from "../attributes.js";
|
|
2
|
+
import { indent } from "../indent.js";
|
|
3
3
|
const textBlocks = new Set(['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'td', 'th']);
|
|
4
4
|
const selfCloseTags = new Set(['br', 'hr', 'img', 'input', 'link', 'meta', 'source', 'track', 'wbr']);
|
|
5
5
|
const inlineTags = new Set(['strong', 'em', 'code', 'a', 'br', 'span', 'img']);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { LanguageRegistration } from 'shiki';
|
|
2
2
|
import type { ComarkNode, ComarkTree, ComarkPlugin } from 'comark';
|
|
3
3
|
import type { ShikiPrimitive, ThemeRegistration } from '@shikijs/primitive';
|
|
4
|
+
import type { ShikiTransformer } from '@shikijs/types';
|
|
4
5
|
export interface HighlightOptions {
|
|
5
6
|
/**
|
|
6
7
|
* Whether to use the default language definitions
|
|
@@ -25,12 +26,23 @@ export interface HighlightOptions {
|
|
|
25
26
|
light?: ThemeRegistration;
|
|
26
27
|
dark?: ThemeRegistration;
|
|
27
28
|
};
|
|
29
|
+
/**
|
|
30
|
+
* Transformers to apply to the code blocks
|
|
31
|
+
* @default undefined
|
|
32
|
+
*/
|
|
33
|
+
transformers?: ShikiTransformer[];
|
|
28
34
|
/**
|
|
29
35
|
* Whether to add pre styles to the code blocks
|
|
30
36
|
* @default false
|
|
31
37
|
*/
|
|
32
38
|
preStyles?: boolean;
|
|
33
39
|
}
|
|
40
|
+
export interface CodeBlockAttributes {
|
|
41
|
+
language?: string;
|
|
42
|
+
class?: string;
|
|
43
|
+
highlights?: number[];
|
|
44
|
+
meta?: string;
|
|
45
|
+
}
|
|
34
46
|
/**
|
|
35
47
|
* Get or create the Shiki highlighter instance
|
|
36
48
|
* Uses a singleton pattern to avoid creating multiple highlighters
|
|
@@ -38,13 +50,9 @@ export interface HighlightOptions {
|
|
|
38
50
|
export declare function getHighlighter(options?: HighlightOptions): Promise<ShikiPrimitive>;
|
|
39
51
|
/**
|
|
40
52
|
* Highlight code using Shiki with codeToTokens
|
|
41
|
-
* Returns comark nodes built from
|
|
53
|
+
* Returns comark nodes built from hast
|
|
42
54
|
*/
|
|
43
|
-
export declare function highlightCode(code: string, attrs: {
|
|
44
|
-
language?: string;
|
|
45
|
-
class?: string;
|
|
46
|
-
highlights?: number[];
|
|
47
|
-
}, options?: HighlightOptions): Promise<{
|
|
55
|
+
export declare function highlightCode(code: string, attrs: CodeBlockAttributes, options?: HighlightOptions): Promise<{
|
|
48
56
|
nodes: ComarkNode[];
|
|
49
57
|
language: string;
|
|
50
58
|
bgColor?: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { createShikiPrimitive
|
|
1
|
+
import { createShikiPrimitive } from '@shikijs/primitive';
|
|
2
2
|
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';
|
|
3
|
+
import { codeToHast } from 'shiki/core';
|
|
3
4
|
let highlighter = null;
|
|
4
5
|
let highlighterPromise = null;
|
|
5
6
|
const loadedThemes = new Set();
|
|
@@ -45,20 +46,6 @@ export async function getHighlighter(options = {}) {
|
|
|
45
46
|
throw error;
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
|
-
/**
|
|
49
|
-
* Convert color to inline style
|
|
50
|
-
*/
|
|
51
|
-
function colorToStyle(token) {
|
|
52
|
-
if (!token)
|
|
53
|
-
return undefined;
|
|
54
|
-
const variants = token.variants;
|
|
55
|
-
if (!variants)
|
|
56
|
-
return `color:${token.color}`;
|
|
57
|
-
const { light: lc, dark: dc } = variants;
|
|
58
|
-
if (!lc?.color || !dc?.color)
|
|
59
|
-
return undefined;
|
|
60
|
-
return lc.color === dc.color ? `color:${lc.color}` : `color:${lc.color};--shiki-dark:${dc.color}`;
|
|
61
|
-
}
|
|
62
49
|
async function registerDefaults(options) {
|
|
63
50
|
const themes = Object.values(options.themes || {});
|
|
64
51
|
const languages = options.languages || [];
|
|
@@ -94,7 +81,7 @@ async function loadLanguage(hl, language) {
|
|
|
94
81
|
}
|
|
95
82
|
/**
|
|
96
83
|
* Highlight code using Shiki with codeToTokens
|
|
97
|
-
* Returns comark nodes built from
|
|
84
|
+
* Returns comark nodes built from hast
|
|
98
85
|
*/
|
|
99
86
|
export async function highlightCode(code, attrs, options = {}) {
|
|
100
87
|
// Extract language from attributes
|
|
@@ -102,28 +89,21 @@ export async function highlightCode(code, attrs, options = {}) {
|
|
|
102
89
|
try {
|
|
103
90
|
const hl = await getHighlighter(options);
|
|
104
91
|
const { themes = { light: 'material-theme-lighter', dark: 'material-theme-palenight' } } = options;
|
|
92
|
+
const lightTheme = themes.light || themes.dark || 'material-theme-lighter';
|
|
93
|
+
const darkTheme = themes.dark || themes.light || 'material-theme-palenight';
|
|
105
94
|
// Use codeToTokens to get raw tokens
|
|
106
|
-
const result =
|
|
95
|
+
const result = await codeToHast(hl, code, {
|
|
107
96
|
lang: language,
|
|
97
|
+
transformers: options.transformers,
|
|
108
98
|
themes: {
|
|
109
|
-
light:
|
|
110
|
-
dark:
|
|
99
|
+
light: lightTheme,
|
|
100
|
+
dark: lightTheme !== darkTheme ? darkTheme : undefined,
|
|
101
|
+
},
|
|
102
|
+
meta: {
|
|
103
|
+
__raw: attrs.meta,
|
|
111
104
|
},
|
|
112
105
|
});
|
|
113
|
-
|
|
114
|
-
const allTokens = Array.from({ length: result.length });
|
|
115
|
-
const highlights = attrs.highlights;
|
|
116
|
-
for (let i = 0; i < result.length; i++) {
|
|
117
|
-
const lineTokens = result[i];
|
|
118
|
-
const children = Array.from({ length: lineTokens.length });
|
|
119
|
-
for (let j = 0; j < lineTokens.length; j++) {
|
|
120
|
-
const token = lineTokens[j];
|
|
121
|
-
const style = colorToStyle(token);
|
|
122
|
-
children[j] = style ? ['span', { style }, token.content] : token.content;
|
|
123
|
-
}
|
|
124
|
-
const lineClass = 'line' + (highlights?.includes(i + 1) ? ' highlight' : '');
|
|
125
|
-
allTokens[i] = ['span', { class: lineClass }, ...children];
|
|
126
|
-
}
|
|
106
|
+
const allTokens = result.children.map(hastToMinimarkNode);
|
|
127
107
|
return {
|
|
128
108
|
nodes: allTokens,
|
|
129
109
|
language,
|
|
@@ -137,6 +117,20 @@ export async function highlightCode(code, attrs, options = {}) {
|
|
|
137
117
|
language,
|
|
138
118
|
};
|
|
139
119
|
}
|
|
120
|
+
function hastToMinimarkNode(input) {
|
|
121
|
+
const props = input.properties || {};
|
|
122
|
+
if (input.type === 'comment')
|
|
123
|
+
return [null, {}, input.value];
|
|
124
|
+
if (input.type === 'text')
|
|
125
|
+
return input.value;
|
|
126
|
+
if (input.tag === 'code' && props?.className && props.className.length === 0)
|
|
127
|
+
delete props.className;
|
|
128
|
+
return [
|
|
129
|
+
input.tagName,
|
|
130
|
+
props,
|
|
131
|
+
...(input.children || []).map(hastToMinimarkNode),
|
|
132
|
+
];
|
|
133
|
+
}
|
|
140
134
|
}
|
|
141
135
|
/**
|
|
142
136
|
* Apply syntax highlighting to all code blocks in a Comark tree
|
|
@@ -164,19 +158,24 @@ export async function highlightCodeBlocks(tree, options = {}) {
|
|
|
164
158
|
if (codeBlocks.length === 0)
|
|
165
159
|
return tree;
|
|
166
160
|
const highlightedResults = await Promise.all(codeBlocks.map(({ node }) => {
|
|
167
|
-
|
|
168
|
-
const codeContent = node[2][2];
|
|
169
|
-
return highlightCode(codeContent, attrs, options);
|
|
161
|
+
return highlightCode(node[2][2], node[1], options);
|
|
170
162
|
}));
|
|
171
163
|
const newNodes = JSON.parse(JSON.stringify(tree.nodes));
|
|
172
164
|
for (let i = 0; i < codeBlocks.length; i++) {
|
|
173
165
|
const { node, path } = codeBlocks[i];
|
|
174
166
|
const result = highlightedResults[i];
|
|
175
|
-
const
|
|
167
|
+
const preNode = result.nodes[0];
|
|
168
|
+
const preNodeClasses = typeof preNode === 'string'
|
|
169
|
+
? ['shiki', options.themes?.light?.name]
|
|
170
|
+
: (Array.isArray(preNode[1].class)
|
|
171
|
+
? preNode[1].class
|
|
172
|
+
: String(preNode[1].class).split(' '));
|
|
173
|
+
const codeChildren = preNode[2].slice(2);
|
|
174
|
+
const children = typeof preNode === 'string' ? preNode : codeChildren.filter(element => element !== '\n');
|
|
176
175
|
const preAttrs = node[1];
|
|
177
176
|
const newPreAttrs = {
|
|
178
177
|
...preAttrs,
|
|
179
|
-
class: [
|
|
178
|
+
class: [...preNodeClasses, options.themes?.dark?.name ? `dark:${options.themes?.dark?.name}` : ''].filter(Boolean).join(' '),
|
|
180
179
|
tabindex: '0',
|
|
181
180
|
};
|
|
182
181
|
if (options.preStyles) {
|
|
@@ -201,7 +200,7 @@ export async function highlightCodeBlocks(tree, options = {}) {
|
|
|
201
200
|
}
|
|
202
201
|
const codeEl = node[2];
|
|
203
202
|
const codeAttrs = codeEl[1] || {};
|
|
204
|
-
const newPreNode = ['pre', newPreAttrs, ['code', codeAttrs, ...
|
|
203
|
+
const newPreNode = ['pre', newPreAttrs, ['code', codeAttrs, ...children]];
|
|
205
204
|
if (path.length === 1) {
|
|
206
205
|
newNodes[path[0]] = newPreNode;
|
|
207
206
|
}
|
package/dist/plugins/security.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { visit } from 'comark/utils';
|
|
2
2
|
import { validateProps } from '../internal/props-validation';
|
|
3
|
-
export default function security(options) {
|
|
3
|
+
export default function security(options = {}) {
|
|
4
4
|
const { blockedTags = [], allowedLinkPrefixes, allowedImagePrefixes, allowedProtocols, defaultOrigin, allowDataImages, } = options;
|
|
5
5
|
const dropSet = new Set(blockedTags.map(t => t.toLowerCase()));
|
|
6
6
|
const propsOptions = {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "comark",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.1",
|
|
5
5
|
"description": "Components in Markdown (Comark) parser with streaming support for Vue, React, Svelte and HTML",
|
|
6
6
|
"author": "",
|
|
7
7
|
"license": "MIT",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@nuxt/kit": "^4.4.2",
|
|
61
61
|
"@shikijs/primitive": "^4.0.2",
|
|
62
|
+
"@shikijs/twoslash": "^4.0.2",
|
|
62
63
|
"@types/js-yaml": "^4.0.9",
|
|
63
64
|
"github-slugger": "^2.0.0",
|
|
64
65
|
"hast-util-to-string": "^3.0.1",
|
|
@@ -66,13 +67,14 @@
|
|
|
66
67
|
"minimark": "0.2.0",
|
|
67
68
|
"mitata": "^1.0.34",
|
|
68
69
|
"tsx": "^4.21.0",
|
|
70
|
+
"twoslash": "^0.3.6",
|
|
69
71
|
"vitest": "^4.1.1"
|
|
70
72
|
},
|
|
71
73
|
"dependencies": {
|
|
72
74
|
"@comark/markdown-it": "^0.3.2",
|
|
73
75
|
"entities": "^8.0.0",
|
|
74
|
-
"js-yaml": "^4.1.1",
|
|
75
76
|
"htmlparser2": "^12.0.0",
|
|
77
|
+
"js-yaml": "^4.1.1",
|
|
76
78
|
"markdown-exit": "1.0.0-beta.9"
|
|
77
79
|
}
|
|
78
80
|
}
|