@md-plugins/md-plugin-codeblocks 0.1.0-beta.2 → 0.1.0-beta.20
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 +25 -4
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +210 -29
- package/package.json +9 -7
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# md-plugin-codeblocks
|
|
2
2
|
|
|
3
|
-
A **Markdown-It** plugin that enhances code block rendering by providing syntax highlighting, line numbering, and support for advanced features like tabbed code blocks. It integrates with
|
|
3
|
+
A **Markdown-It** plugin that enhances code block rendering by providing syntax highlighting, line numbering, and support for advanced features like tabbed code blocks. It integrates with Shiki for syntax highlighting and allows customization for various use cases.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Syntax Highlighting**: Automatically highlights code blocks using **
|
|
7
|
+
- **Syntax Highlighting**: Automatically highlights code blocks using **Shiki**.
|
|
8
|
+
- **TwoSlash Hovers**: Opt in to TypeScript-powered hover and query output for richer examples.
|
|
8
9
|
- **Line Numbering**: Optionally adds line numbers to code blocks.
|
|
9
10
|
- **Magic Comments**: Supports special comments like `[[! highlight]]`, `[[! add]]`, and `[[! rem]]` for inline code annotations.
|
|
10
11
|
- **Tabbed Code Blocks**: Enables the creation of tabbed code blocks for multi-language or multi-file examples.
|
|
@@ -18,6 +19,8 @@ Install the plugin via your preferred package manager:
|
|
|
18
19
|
```bash
|
|
19
20
|
# with pnpm:
|
|
20
21
|
pnpm add @md-plugins/md-plugin-codeblocks
|
|
22
|
+
# with bun:
|
|
23
|
+
bun add @md-plugins/md-plugin-codeblocks
|
|
21
24
|
# with Yarn:
|
|
22
25
|
yarn add @md-plugins/md-plugin-codeblocks
|
|
23
26
|
# with npm:
|
|
@@ -38,8 +41,8 @@ md.use(codeblocksPlugin, {
|
|
|
38
41
|
copyButtonComponent: '<MarkdownCopyButton',
|
|
39
42
|
preClass: 'markdown-code',
|
|
40
43
|
pageScripts: [
|
|
41
|
-
"import MarkdownPrerender from '
|
|
42
|
-
"import MarkdownCopyButton from '
|
|
44
|
+
"import MarkdownPrerender from '@/.q-press/components/MarkdownPrerender'",
|
|
45
|
+
"import MarkdownCopyButton from '@/.q-press/components/MarkdownCopyButton.vue'",
|
|
43
46
|
],
|
|
44
47
|
})
|
|
45
48
|
```
|
|
@@ -99,6 +102,17 @@ console.log('Line 3')
|
|
|
99
102
|
```
|
|
100
103
|
````
|
|
101
104
|
|
|
105
|
+
### TwoSlash Type Hovers
|
|
106
|
+
|
|
107
|
+
Add the `twoslash` attribute to TypeScript or JavaScript examples when you want inferred type information, compiler diagnostics, or `^?` query output.
|
|
108
|
+
|
|
109
|
+
````markup
|
|
110
|
+
```ts [twoslash]
|
|
111
|
+
const count = 1
|
|
112
|
+
// ^?
|
|
113
|
+
```
|
|
114
|
+
````
|
|
115
|
+
|
|
102
116
|
### Line Highlighting and Annotations
|
|
103
117
|
|
|
104
118
|
````markup
|
|
@@ -177,6 +191,13 @@ pnpm test
|
|
|
177
191
|
|
|
178
192
|
In case this README falls out of date, please refer to the [documentation](https://md-plugins.netlify.app/md-plugins/codeblocks/overview) for the latest information.
|
|
179
193
|
|
|
194
|
+
## Support
|
|
195
|
+
|
|
196
|
+
If md-plugin-codeblocks is useful in your workflow and you want to support ongoing maintenance:
|
|
197
|
+
|
|
198
|
+
GitHub Sponsors: https://github.com/sponsors/hawkeye64
|
|
199
|
+
PayPal: https://paypal.me/hawkeye64
|
|
200
|
+
|
|
180
201
|
## License
|
|
181
202
|
|
|
182
203
|
This project is licensed under the MIT License. See the [LICENSE](LICENSE.md) file for details.
|
package/dist/index.d.mts
CHANGED
|
@@ -46,7 +46,7 @@ interface CodeblockPluginOptions {
|
|
|
46
46
|
*/
|
|
47
47
|
pageScripts?: string[];
|
|
48
48
|
/**
|
|
49
|
-
* Optional
|
|
49
|
+
* Optional Shiki languages configuration array. This allows you to override or add custom language definitions.
|
|
50
50
|
* Each item can have a `name`, optional `aliases`, and `customCopy` boolean.
|
|
51
51
|
*/
|
|
52
52
|
langList?: Lang[];
|
package/dist/index.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ interface CodeblockPluginOptions {
|
|
|
46
46
|
*/
|
|
47
47
|
pageScripts?: string[];
|
|
48
48
|
/**
|
|
49
|
-
* Optional
|
|
49
|
+
* Optional Shiki languages configuration array. This allows you to override or add custom language definitions.
|
|
50
50
|
* Each item can have a `name`, optional `aliases`, and `customCopy` boolean.
|
|
51
51
|
*/
|
|
52
52
|
langList?: Lang[];
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { addClassToHast } from 'shiki/core';
|
|
2
|
+
import { createHighlighter } from 'shiki';
|
|
3
|
+
import { transformerNotationHighlight, transformerNotationDiff, transformerNotationFocus, transformerNotationErrorLevel, transformerNotationWordHighlight } from '@shikijs/transformers';
|
|
3
4
|
|
|
4
5
|
function resolvePluginOptions(options, key, defaults) {
|
|
5
6
|
if (options && typeof options === "object" && key in options) {
|
|
@@ -8,11 +9,175 @@ function resolvePluginOptions(options, key, defaults) {
|
|
|
8
9
|
return { ...defaults, ...options };
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
const shikiLangs = [
|
|
13
|
+
"bash",
|
|
14
|
+
"css",
|
|
15
|
+
"diff",
|
|
16
|
+
"html",
|
|
17
|
+
"javascript",
|
|
18
|
+
"json",
|
|
19
|
+
"nginx",
|
|
20
|
+
"sass",
|
|
21
|
+
"scss",
|
|
22
|
+
"typescript",
|
|
23
|
+
"vue",
|
|
24
|
+
"xml",
|
|
25
|
+
"yaml"
|
|
26
|
+
];
|
|
27
|
+
const shikiLangAlias = {
|
|
28
|
+
js: "javascript",
|
|
29
|
+
markup: "html",
|
|
30
|
+
shell: "bash",
|
|
31
|
+
sh: "bash",
|
|
32
|
+
ts: "typescript",
|
|
33
|
+
yml: "yaml"
|
|
34
|
+
};
|
|
35
|
+
const highlighter = await createHighlighter({
|
|
36
|
+
themes: ["github-light", "github-dark"],
|
|
37
|
+
langs: shikiLangs,
|
|
38
|
+
langAlias: shikiLangAlias
|
|
39
|
+
});
|
|
40
|
+
const debugEnvValue = process.env.DEBUG;
|
|
41
|
+
delete process.env.DEBUG;
|
|
42
|
+
const twoslashModule = await import('@shikijs/twoslash').finally(() => {
|
|
43
|
+
if (debugEnvValue !== void 0) {
|
|
44
|
+
process.env.DEBUG = debugEnvValue;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const { rendererRich, transformerTwoslash } = twoslashModule;
|
|
48
|
+
const supportedLangSet = new Set(highlighter.getLoadedLanguages());
|
|
49
|
+
const themeOptions = {
|
|
50
|
+
themes: {
|
|
51
|
+
light: "github-light",
|
|
52
|
+
dark: "github-dark"
|
|
53
|
+
},
|
|
54
|
+
defaultColor: false
|
|
55
|
+
};
|
|
56
|
+
function normalizeShikiLang(lang) {
|
|
57
|
+
return supportedLangSet.has(lang) ? lang : "text";
|
|
58
|
+
}
|
|
59
|
+
function buildCodeBlockTransformers({
|
|
60
|
+
codeClass,
|
|
61
|
+
lineList,
|
|
62
|
+
maxheight,
|
|
63
|
+
preClass,
|
|
64
|
+
twoslash
|
|
65
|
+
}) {
|
|
66
|
+
return [
|
|
67
|
+
preClassTransformer(preClass, maxheight),
|
|
68
|
+
codeClassTransformer(codeClass),
|
|
69
|
+
...twoslash === true ? [twoslashTransformer] : [],
|
|
70
|
+
transformerNotationHighlight(),
|
|
71
|
+
transformerNotationDiff(),
|
|
72
|
+
transformerNotationFocus(),
|
|
73
|
+
transformerNotationErrorLevel(),
|
|
74
|
+
transformerNotationWordHighlight(),
|
|
75
|
+
lineDecorTransformer(lineList)
|
|
76
|
+
];
|
|
77
|
+
}
|
|
78
|
+
const twoslashTransformer = transformerTwoslash({
|
|
79
|
+
renderer: rendererRich({
|
|
80
|
+
queryRendering: "line"
|
|
81
|
+
}),
|
|
82
|
+
twoslashOptions: {
|
|
83
|
+
compilerOptions: {
|
|
84
|
+
traceResolution: false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
function preClassTransformer(preClass, maxheight) {
|
|
89
|
+
return {
|
|
90
|
+
name: "md-plugins:pre-class",
|
|
91
|
+
pre(node) {
|
|
92
|
+
addClassToHast(node, preClass);
|
|
93
|
+
if (maxheight !== void 0) {
|
|
94
|
+
const style = typeof node.properties?.style === "string" ? node.properties.style : "";
|
|
95
|
+
node.properties = {
|
|
96
|
+
...node.properties,
|
|
97
|
+
style: `${style}${style.length > 0 ? ";" : ""}max-height:${maxheight}`
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function codeClassTransformer(codeClass) {
|
|
104
|
+
return {
|
|
105
|
+
name: "md-plugins:code-class",
|
|
106
|
+
code(node) {
|
|
107
|
+
if (codeClass !== void 0 && codeClass.length > 0) {
|
|
108
|
+
addClassToHast(node, codeClass);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function lineDecorTransformer(lineList) {
|
|
114
|
+
return {
|
|
115
|
+
name: "md-plugins:line-decor",
|
|
116
|
+
code(codeNode) {
|
|
117
|
+
const lines = codeNode.children.filter(
|
|
118
|
+
(child) => child.type === "element" && child.tagName === "span" && lineHasClass(child, "line")
|
|
119
|
+
);
|
|
120
|
+
lines.forEach((line, index) => {
|
|
121
|
+
const target = lineList[index];
|
|
122
|
+
const classList = [...target?.classList ?? []];
|
|
123
|
+
const prefix = target?.prefix ?? [];
|
|
124
|
+
if (this.options.lang === "diff") {
|
|
125
|
+
const first = lineTextContent(line).charAt(0);
|
|
126
|
+
if (first === "+") {
|
|
127
|
+
classList.push("line-add");
|
|
128
|
+
} else if (first === "-") {
|
|
129
|
+
classList.push("line-rem");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (classList.length > 0) {
|
|
133
|
+
line.children?.unshift({
|
|
134
|
+
type: "element",
|
|
135
|
+
tagName: "span",
|
|
136
|
+
properties: { class: ["c-line", ...classList] },
|
|
137
|
+
children: []
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (prefix.length > 0) {
|
|
141
|
+
line.children?.unshift({
|
|
142
|
+
type: "element",
|
|
143
|
+
tagName: "span",
|
|
144
|
+
properties: { class: "c-lpref" },
|
|
145
|
+
children: [{ type: "text", value: prefix.join(" ") }]
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function lineHasClass(line, className) {
|
|
153
|
+
const classValue = line.properties?.class;
|
|
154
|
+
if (typeof classValue === "string") {
|
|
155
|
+
return classValue.split(/\s+/).includes(className);
|
|
156
|
+
}
|
|
157
|
+
if (Array.isArray(classValue)) {
|
|
158
|
+
return classValue.includes(className);
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
function lineTextContent(line) {
|
|
163
|
+
let acc = "";
|
|
164
|
+
const walk = (node) => {
|
|
165
|
+
if (node.type === "text") {
|
|
166
|
+
acc += node.value ?? "";
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
node.children?.forEach(walk);
|
|
170
|
+
};
|
|
171
|
+
line.children?.forEach(walk);
|
|
172
|
+
return acc;
|
|
173
|
+
}
|
|
174
|
+
|
|
11
175
|
const defaultLangList = [
|
|
12
176
|
{ name: "markup" },
|
|
13
177
|
{ name: "bash", customCopy: true },
|
|
14
178
|
{ name: "javascript", aliases: "javascript|js" },
|
|
15
179
|
{ name: "typescript", aliases: "typescript|ts" },
|
|
180
|
+
{ name: "yaml", aliases: "yaml|yml" },
|
|
16
181
|
{ name: "sass" },
|
|
17
182
|
{ name: "scss" },
|
|
18
183
|
{ name: "css" },
|
|
@@ -20,6 +185,7 @@ const defaultLangList = [
|
|
|
20
185
|
{ name: "xml" },
|
|
21
186
|
{ name: "nginx" },
|
|
22
187
|
{ name: "html" },
|
|
188
|
+
{ name: "vue" },
|
|
23
189
|
{ name: "diff" }
|
|
24
190
|
// special grammars
|
|
25
191
|
];
|
|
@@ -32,8 +198,8 @@ const DEFAULT_CODEBLOCK_PLUGIN_OPTIONS = {
|
|
|
32
198
|
tabPanelTagName: "q-tab-panel",
|
|
33
199
|
tabPanelTagClass: "q-pa-none",
|
|
34
200
|
pageScripts: [
|
|
35
|
-
"import MarkdownPrerender from '
|
|
36
|
-
"import MarkdownCopyButton from '
|
|
201
|
+
"import MarkdownPrerender from '@/.q-press/components/MarkdownPrerender'",
|
|
202
|
+
"import MarkdownCopyButton from '@/.q-press/components/MarkdownCopyButton.vue'"
|
|
37
203
|
],
|
|
38
204
|
langList: defaultLangList
|
|
39
205
|
};
|
|
@@ -54,7 +220,6 @@ const codeblocksPlugin = (md, options) => {
|
|
|
54
220
|
pageScripts = DEFAULT_CODEBLOCK_PLUGIN_OPTIONS.pageScripts,
|
|
55
221
|
langList = defaultLangList
|
|
56
222
|
} = resolvedOptions;
|
|
57
|
-
loadLanguages(langList.map((l) => l.name));
|
|
58
223
|
const customCopyLangList = langList.filter((l) => l.customCopy === true).map((l) => l.name);
|
|
59
224
|
const langMatch = langList.map((l) => l.aliases || l.name).join("|");
|
|
60
225
|
const definitionLineRE = new RegExp(
|
|
@@ -71,11 +236,13 @@ const codeblocksPlugin = (md, options) => {
|
|
|
71
236
|
const tabsMatch = line.match(tabsLineRE);
|
|
72
237
|
if (tabsMatch !== null) {
|
|
73
238
|
const { lang, attrs, title } = tabsMatch.groups || {};
|
|
74
|
-
|
|
239
|
+
const bareAttrs = extractBareAttrs(title?.trim() || null);
|
|
240
|
+
currentTabName = bareAttrs.title ?? `Tab ${list.length + 1}`;
|
|
75
241
|
list.push(currentTabName);
|
|
76
242
|
tabMap[currentTabName] = {
|
|
77
243
|
attrs: {
|
|
78
244
|
...parseAttrs(attrs?.trim() || null),
|
|
245
|
+
...bareAttrs.attrs,
|
|
79
246
|
lang
|
|
80
247
|
},
|
|
81
248
|
content: []
|
|
@@ -94,6 +261,7 @@ const codeblocksPlugin = (md, options) => {
|
|
|
94
261
|
};
|
|
95
262
|
}
|
|
96
263
|
const magicCommentList = ["highlight", "rem", "add"];
|
|
264
|
+
const bareAttrList = ["twoslash"];
|
|
97
265
|
const magicCommentRE = new RegExp(` *\\[\\[! (?<type>(${magicCommentList.join("|")}))\\]\\] *`);
|
|
98
266
|
const magicCommentGlobalRE = new RegExp(magicCommentRE, "g");
|
|
99
267
|
function extractCodeLineProps(lines, attrs) {
|
|
@@ -152,35 +320,25 @@ const codeblocksPlugin = (md, options) => {
|
|
|
152
320
|
}
|
|
153
321
|
return acc;
|
|
154
322
|
}
|
|
155
|
-
function renderCodeBlock(html, codeClass2) {
|
|
156
|
-
return `<code${codeClass2 ? ` class="${codeClass2}"` : ""}>${html}</code>`;
|
|
157
|
-
}
|
|
158
|
-
function getPrismHighlightedContent(rawContent, lang) {
|
|
159
|
-
const content = rawContent.trim();
|
|
160
|
-
return prism.highlight(content, prism.languages[lang], lang);
|
|
161
|
-
}
|
|
162
323
|
function getHighlightedContent(rawContent, attrs) {
|
|
163
|
-
const { lang, maxheight } = attrs;
|
|
324
|
+
const { lang, maxheight, twoslash } = attrs;
|
|
164
325
|
let content = rawContent.trim();
|
|
165
326
|
const lineList = parseCodeLine(content, attrs);
|
|
166
327
|
if (lang !== "markup") {
|
|
167
328
|
content = content.trim().replace(magicCommentGlobalRE, "");
|
|
168
329
|
}
|
|
169
|
-
const html = getPrismHighlightedContent(content, lang).split("\n").map((line, lineIndex) => {
|
|
170
|
-
const target = lineList[lineIndex];
|
|
171
|
-
if (target === void 0) return line;
|
|
172
|
-
let lineHtml = "";
|
|
173
|
-
lineHtml += target.classList.length !== 0 ? `<span class="c-line ${target.classList.join(" ")}"></span>` : "";
|
|
174
|
-
lineHtml += target.prefix.length !== 0 ? `<span class="c-lpref">${target.prefix.join(" ")}</span>` : "";
|
|
175
|
-
lineHtml += line;
|
|
176
|
-
return lineHtml;
|
|
177
|
-
}).join("\n");
|
|
178
|
-
const preAttrs = maxheight !== void 0 ? ` style="max-height:${maxheight}"` : "";
|
|
179
330
|
const langProp = customCopyLangList.includes(lang) === true ? ` lang="${lang}"` : "";
|
|
180
|
-
return (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
331
|
+
return highlighter.codeToHtml(content, {
|
|
332
|
+
lang: normalizeShikiLang(lang),
|
|
333
|
+
...themeOptions,
|
|
334
|
+
transformers: buildCodeBlockTransformers({
|
|
335
|
+
codeClass,
|
|
336
|
+
lineList,
|
|
337
|
+
maxheight,
|
|
338
|
+
preClass: preClass ?? "markdown-code",
|
|
339
|
+
twoslash: twoslash === true || twoslash === "true"
|
|
340
|
+
})
|
|
341
|
+
}).replace("<pre ", "<pre v-pre ") + `<${copyButtonComponent}${langProp} />`;
|
|
184
342
|
}
|
|
185
343
|
function parseAttrs(rawAttrs) {
|
|
186
344
|
if (rawAttrs === null) return {};
|
|
@@ -192,6 +350,27 @@ const codeblocksPlugin = (md, options) => {
|
|
|
192
350
|
}
|
|
193
351
|
return acc;
|
|
194
352
|
}
|
|
353
|
+
function extractBareAttrs(title) {
|
|
354
|
+
if (title === null) {
|
|
355
|
+
return {
|
|
356
|
+
attrs: {},
|
|
357
|
+
title: null
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
const attrs = {};
|
|
361
|
+
const remainingTitle = [];
|
|
362
|
+
for (const chunk of title.split(/\s+/)) {
|
|
363
|
+
if (remainingTitle.length === 0 && bareAttrList.includes(chunk)) {
|
|
364
|
+
attrs[chunk] = true;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
remainingTitle.push(chunk);
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
attrs,
|
|
371
|
+
title: remainingTitle.length > 0 ? remainingTitle.join(" ") : null
|
|
372
|
+
};
|
|
373
|
+
}
|
|
195
374
|
function parseDefinitionLine(token) {
|
|
196
375
|
const match = token.info.trim().match(definitionLineRE);
|
|
197
376
|
if (match === null) {
|
|
@@ -201,10 +380,12 @@ const codeblocksPlugin = (md, options) => {
|
|
|
201
380
|
};
|
|
202
381
|
}
|
|
203
382
|
const { lang, attrs, title } = match.groups || {};
|
|
383
|
+
const bareAttrs = extractBareAttrs(title?.trim() || null);
|
|
204
384
|
const acc = {
|
|
205
385
|
...parseAttrs(attrs?.trim() || null),
|
|
386
|
+
...bareAttrs.attrs,
|
|
206
387
|
lang,
|
|
207
|
-
title: title
|
|
388
|
+
title: bareAttrs.title
|
|
208
389
|
};
|
|
209
390
|
if (acc.lang === "tabs") {
|
|
210
391
|
acc.tabs = extractTabs(token.content);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@md-plugins/md-plugin-codeblocks",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.20",
|
|
4
4
|
"description": "A markdown-it plugin for code blocks.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown-it",
|
|
@@ -35,16 +35,18 @@
|
|
|
35
35
|
"publishConfig": {
|
|
36
36
|
"access": "public"
|
|
37
37
|
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@shikijs/transformers": "^4.1.0",
|
|
40
|
+
"@shikijs/twoslash": "^4.1.0",
|
|
41
|
+
"shiki": "^4.1.0"
|
|
42
|
+
},
|
|
38
43
|
"devDependencies": {
|
|
39
44
|
"@types/markdown-it": "^14.1.2",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"prismjs": "^1.30.0",
|
|
43
|
-
"@md-plugins/shared": "0.1.0-beta.2"
|
|
45
|
+
"markdown-it": "^14.2.0",
|
|
46
|
+
"@md-plugins/shared": "0.1.0-beta.20"
|
|
44
47
|
},
|
|
45
48
|
"peerDependencies": {
|
|
46
|
-
"markdown-it": "^14.
|
|
47
|
-
"prismjs": "^1.29.0"
|
|
49
|
+
"markdown-it": "^14.2.0"
|
|
48
50
|
},
|
|
49
51
|
"scripts": {
|
|
50
52
|
"build": "unbuild",
|