@nuxtjs/mdc 0.4.0 → 0.6.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 +145 -1
- package/dist/module.d.mts +9 -1
- package/dist/module.d.ts +9 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +36 -17
- package/dist/runtime/components/MDC.vue +2 -1
- package/dist/runtime/components/MDCRenderer.vue +14 -11
- package/dist/runtime/components/MDCRenderer.vue.d.ts +1 -0
- package/dist/runtime/components/prose/ProseScript.vue +1 -3
- package/dist/runtime/highlighter/rehype-nuxt.d.ts +3 -0
- package/dist/runtime/highlighter/rehype-nuxt.mjs +33 -0
- package/dist/runtime/highlighter/rehype.d.ts +1 -1
- package/dist/runtime/highlighter/rehype.mjs +2 -26
- package/dist/runtime/highlighter/shiki.mjs +2 -2
- package/dist/runtime/index.d.ts +3 -1
- package/dist/runtime/index.mjs +3 -1
- package/dist/runtime/parser/compiler.d.ts +2 -1
- package/dist/runtime/parser/compiler.mjs +7 -1
- package/dist/runtime/parser/index.d.ts +3 -7
- package/dist/runtime/parser/index.mjs +32 -23
- package/dist/runtime/types/parser.d.ts +9 -0
- package/dist/runtime/types/tree.d.ts +6 -2
- package/dist/runtime/utils/ast.mjs +3 -0
- package/dist/types.d.mts +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +33 -21
package/README.md
CHANGED
|
@@ -23,6 +23,11 @@ MDC supercharges regular Markdown to write documents interacting deeply with any
|
|
|
23
23
|
|
|
24
24
|
Learn more about the MDC syntax on https://content.nuxtjs.org/guide/writing/mdc
|
|
25
25
|
|
|
26
|
+
> [!Note]
|
|
27
|
+
> You may utilize this package inside of your Nuxt project (standard configuration) or within any Vue project.
|
|
28
|
+
>
|
|
29
|
+
> See [Rendering in your Vue project](#rendering-in-your-vue-project) below for more information.
|
|
30
|
+
|
|
26
31
|
## Install
|
|
27
32
|
|
|
28
33
|
```bash
|
|
@@ -44,7 +49,7 @@ export default defineNuxtConfig({
|
|
|
44
49
|
})
|
|
45
50
|
```
|
|
46
51
|
|
|
47
|
-
That's it! You can start writing and rendering markdown files ✨
|
|
52
|
+
That's it! You can start writing and rendering markdown files in your Nuxt project ✨
|
|
48
53
|
|
|
49
54
|
## Rendering
|
|
50
55
|
|
|
@@ -262,6 +267,145 @@ export default defineNuxtConfig({
|
|
|
262
267
|
|
|
263
268
|
Checkout [`ModuleOptions` types↗︎](https://github.com/nuxt-modules/mdc/blob/main/src/types.ts).
|
|
264
269
|
|
|
270
|
+
## Rendering in your Vue project
|
|
271
|
+
|
|
272
|
+
The `<MDCRenderer>` component in combination with a few exported package utilities may also be utilized inside a normal (non-Nuxt) Vue project.
|
|
273
|
+
|
|
274
|
+
To implement in your standard Vue project, follow the instructions below.
|
|
275
|
+
|
|
276
|
+
### Install the package
|
|
277
|
+
|
|
278
|
+
Follow the [install instructions above](#install), ignoring the step of adding the Nuxt module to a `nuxt.config.ts` file.
|
|
279
|
+
|
|
280
|
+
### Stub Nuxt module imports
|
|
281
|
+
|
|
282
|
+
Since you're not using Nuxt, you'll need to stub a few of the module's imports in your Vue projects's Vite config file. This is necessary to avoid errors when the module tries to access Nuxt-specific imports.
|
|
283
|
+
|
|
284
|
+
Create a new file in your Vue project's root directory, such as `stub-mdc-imports.js`, and add the following content:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
// stub-mdc-imports.js
|
|
288
|
+
export default {}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Next, update your Vue project's Vite config file (e.g. `vite.config.ts`) to alias the module's imports to the stub file:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
import { defineConfig } from 'vite'
|
|
295
|
+
import path from 'path'
|
|
296
|
+
|
|
297
|
+
export default defineConfig({
|
|
298
|
+
resolve: {
|
|
299
|
+
alias: {
|
|
300
|
+
'#mdc-imports': path.resolve(__dirname, './stub-mdc-imports.js'),
|
|
301
|
+
'#mdc-configs': path.resolve(__dirname, './stub-mdc-imports.js'),
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
})
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Usage
|
|
308
|
+
|
|
309
|
+
Next, let's create a new [Vue composable](https://vuejs.org/guide/reusability/composables.html) to handle parsing the markdown content, as well as adding syntax highlighting to code blocks with [Shiki](https://shiki.style/).
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
// composables/useMarkdownParser.ts
|
|
313
|
+
// Import package exports
|
|
314
|
+
import {
|
|
315
|
+
createMarkdownParser,
|
|
316
|
+
rehypeHighlight,
|
|
317
|
+
createShikiHighlighter,
|
|
318
|
+
} from '@nuxtjs/mdc/runtime'
|
|
319
|
+
// Import desired Shiki themes and languages
|
|
320
|
+
import MaterialThemePalenight from 'shiki/themes/material-theme-palenight.mjs'
|
|
321
|
+
import HtmlLang from 'shiki/langs/html.mjs'
|
|
322
|
+
import MdcLang from 'shiki/langs/mdc.mjs'
|
|
323
|
+
import TsLang from 'shiki/langs/typescript.mjs'
|
|
324
|
+
import VueLang from 'shiki/langs/vue.mjs'
|
|
325
|
+
import ScssLang from 'shiki/langs/scss.mjs'
|
|
326
|
+
import YamlLang from 'shiki/langs/yaml.mjs'
|
|
327
|
+
|
|
328
|
+
export default function useMarkdownParser() {
|
|
329
|
+
let parser: Awaited<ReturnType<typeof createMarkdownParser>>
|
|
330
|
+
|
|
331
|
+
const parse = async (markdown: string) => {
|
|
332
|
+
if (!parser) {
|
|
333
|
+
parser = await createMarkdownParser({
|
|
334
|
+
rehype: {
|
|
335
|
+
plugins: {
|
|
336
|
+
highlight: {
|
|
337
|
+
instance: rehypeHighlight,
|
|
338
|
+
options: {
|
|
339
|
+
// Pass in your desired theme(s)
|
|
340
|
+
theme: 'material-theme-palenight',
|
|
341
|
+
// Create the Shiki highlighter
|
|
342
|
+
highlighter: createShikiHighlighter({
|
|
343
|
+
bundledThemes: {
|
|
344
|
+
'material-theme-palenight': MaterialThemePalenight,
|
|
345
|
+
},
|
|
346
|
+
// Configure the bundled languages
|
|
347
|
+
bundledLangs: {
|
|
348
|
+
html: HtmlLang,
|
|
349
|
+
mdc: MdcLang,
|
|
350
|
+
vue: VueLang,
|
|
351
|
+
yml: YamlLang,
|
|
352
|
+
scss: ScssLang,
|
|
353
|
+
ts: TsLang,
|
|
354
|
+
typescript: TsLang,
|
|
355
|
+
},
|
|
356
|
+
}),
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
})
|
|
362
|
+
}
|
|
363
|
+
return parser(markdown)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return parse
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Now import the `useMarkdownParser` composable we just created along with an exported type interface into your host project's Vue component, and utilize them to process the raw markdown and initialize the `<MDCRenderer>` component.
|
|
371
|
+
|
|
372
|
+
```vue
|
|
373
|
+
<script setup lang="ts">
|
|
374
|
+
import { onBeforeMount, ref, watch } from 'vue'
|
|
375
|
+
// Import package exports
|
|
376
|
+
import { MDCRenderer } from '@nuxtjs/mdc/runtime/components/MDCRenderer'
|
|
377
|
+
import type { MDCParserResult } from '@nuxtjs/mdc/runtime/types/index'
|
|
378
|
+
import { useMarkdownParser } from './composables/useMarkdownParser';
|
|
379
|
+
|
|
380
|
+
const md = ref(`
|
|
381
|
+
# Just a Vue app
|
|
382
|
+
|
|
383
|
+
This is markdown content rendered via the \`<MDCRenderer>\` component, including MDC below.
|
|
384
|
+
|
|
385
|
+
::alert
|
|
386
|
+
Hello MDC
|
|
387
|
+
::
|
|
388
|
+
|
|
389
|
+
\`\`\`ts
|
|
390
|
+
const a = 1;
|
|
391
|
+
\`\`\`
|
|
392
|
+
`);
|
|
393
|
+
|
|
394
|
+
const ast = ref<MDCParserResult | null>(null)
|
|
395
|
+
const parse = useMarkdownParser()
|
|
396
|
+
|
|
397
|
+
onBeforeMount(async () => {
|
|
398
|
+
ast.value = await parse(md.value)
|
|
399
|
+
})
|
|
400
|
+
</script>
|
|
401
|
+
|
|
402
|
+
<template>
|
|
403
|
+
<Suspense>
|
|
404
|
+
<MDCRenderer v-if="ast?.body" :body="ast.body" :data="ast.data" />
|
|
405
|
+
</Suspense>
|
|
406
|
+
</template>
|
|
407
|
+
```
|
|
408
|
+
|
|
265
409
|
## Contributing
|
|
266
410
|
|
|
267
411
|
You can contribute to this module online with CodeSandbox:
|
package/dist/module.d.mts
CHANGED
|
@@ -32,6 +32,12 @@ interface ModuleOptions {
|
|
|
32
32
|
theme?: MdcThemeOptions;
|
|
33
33
|
/**
|
|
34
34
|
* Languages to be bundled loaded by Shiki
|
|
35
|
+
*
|
|
36
|
+
* All languages used has to be included in this list at build time, to create granular bundles.
|
|
37
|
+
*
|
|
38
|
+
* Unlike the `preload` option, when this option is provided, it will override the default languages.
|
|
39
|
+
*
|
|
40
|
+
* @default ['js','jsx','json','ts','tsx','vue','css','html','vue','bash','md','mdc','yaml']
|
|
35
41
|
*/
|
|
36
42
|
langs?: (BundledLanguage | LanguageRegistration)[];
|
|
37
43
|
/**
|
|
@@ -56,12 +62,14 @@ interface ModuleOptions {
|
|
|
56
62
|
[heading in 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6']?: boolean;
|
|
57
63
|
};
|
|
58
64
|
};
|
|
65
|
+
keepComments?: boolean;
|
|
59
66
|
components?: {
|
|
60
67
|
prose?: boolean;
|
|
61
68
|
map?: Record<string, string>;
|
|
62
69
|
};
|
|
63
70
|
}
|
|
64
71
|
|
|
72
|
+
declare const DefaultHighlightLangs: BundledLanguage[];
|
|
65
73
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
|
|
66
74
|
|
|
67
75
|
declare module '@nuxt/schema' {
|
|
@@ -92,4 +100,4 @@ declare module '@nuxt/schema' {
|
|
|
92
100
|
}
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
export { _default as default };
|
|
103
|
+
export { DefaultHighlightLangs, _default as default };
|
package/dist/module.d.ts
CHANGED
|
@@ -32,6 +32,12 @@ interface ModuleOptions {
|
|
|
32
32
|
theme?: MdcThemeOptions;
|
|
33
33
|
/**
|
|
34
34
|
* Languages to be bundled loaded by Shiki
|
|
35
|
+
*
|
|
36
|
+
* All languages used has to be included in this list at build time, to create granular bundles.
|
|
37
|
+
*
|
|
38
|
+
* Unlike the `preload` option, when this option is provided, it will override the default languages.
|
|
39
|
+
*
|
|
40
|
+
* @default ['js','jsx','json','ts','tsx','vue','css','html','vue','bash','md','mdc','yaml']
|
|
35
41
|
*/
|
|
36
42
|
langs?: (BundledLanguage | LanguageRegistration)[];
|
|
37
43
|
/**
|
|
@@ -56,12 +62,14 @@ interface ModuleOptions {
|
|
|
56
62
|
[heading in 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6']?: boolean;
|
|
57
63
|
};
|
|
58
64
|
};
|
|
65
|
+
keepComments?: boolean;
|
|
59
66
|
components?: {
|
|
60
67
|
prose?: boolean;
|
|
61
68
|
map?: Record<string, string>;
|
|
62
69
|
};
|
|
63
70
|
}
|
|
64
71
|
|
|
72
|
+
declare const DefaultHighlightLangs: BundledLanguage[];
|
|
65
73
|
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
|
|
66
74
|
|
|
67
75
|
declare module '@nuxt/schema' {
|
|
@@ -92,4 +100,4 @@ declare module '@nuxt/schema' {
|
|
|
92
100
|
}
|
|
93
101
|
}
|
|
94
102
|
|
|
95
|
-
export { _default as default };
|
|
103
|
+
export { DefaultHighlightLangs, _default as default };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -55,7 +55,8 @@ async function mdcConfigs({ options }) {
|
|
|
55
55
|
async function mdcHighlighter({
|
|
56
56
|
options: {
|
|
57
57
|
shikiPath,
|
|
58
|
-
options
|
|
58
|
+
options,
|
|
59
|
+
useWasmAssets
|
|
59
60
|
}
|
|
60
61
|
}) {
|
|
61
62
|
if (!options || !options.highlighter)
|
|
@@ -67,7 +68,14 @@ async function mdcHighlighter({
|
|
|
67
68
|
].find((file2) => existsSync(file2));
|
|
68
69
|
if (!file)
|
|
69
70
|
throw new Error(`[@nuxtjs/mdc] Could not find shiki highlighter: ${shikiPath}`);
|
|
70
|
-
|
|
71
|
+
let code = await fs.readFile(file, "utf-8");
|
|
72
|
+
if (useWasmAssets) {
|
|
73
|
+
code = code.replace(
|
|
74
|
+
/import\((['"])shiki\/wasm\1\)/,
|
|
75
|
+
// We can remove the .client condition once Vite supports WASM ESM import
|
|
76
|
+
"import.meta.client ? import('shiki/wasm') : import('shiki/onig.wasm')"
|
|
77
|
+
);
|
|
78
|
+
}
|
|
71
79
|
const { bundledLanguagesInfo } = await import('shiki/langs');
|
|
72
80
|
const langsMap = /* @__PURE__ */ new Map();
|
|
73
81
|
options.langs?.forEach((lang) => {
|
|
@@ -149,16 +157,32 @@ function processUnistPlugins(plugins) {
|
|
|
149
157
|
const imports = [];
|
|
150
158
|
const definitions = [];
|
|
151
159
|
Object.entries(plugins).forEach(([name, plugin]) => {
|
|
152
|
-
|
|
160
|
+
const instanceName = `_${pascalCase(name).replace(/\W/g, "")}`;
|
|
161
|
+
imports.push(`import ${instanceName} from '${plugin.src || name}'`);
|
|
153
162
|
if (Object.keys(plugin).length) {
|
|
154
|
-
definitions.push(` '${name}': { instance: ${
|
|
163
|
+
definitions.push(` '${name}': { instance: ${instanceName}, options: ${JSON.stringify(plugin.options || plugin)} },`);
|
|
155
164
|
} else {
|
|
156
|
-
definitions.push(` '${name}': { instance: ${
|
|
165
|
+
definitions.push(` '${name}': { instance: ${instanceName} },`);
|
|
157
166
|
}
|
|
158
167
|
});
|
|
159
168
|
return { imports, definitions };
|
|
160
169
|
}
|
|
161
170
|
|
|
171
|
+
const DefaultHighlightLangs = [
|
|
172
|
+
"js",
|
|
173
|
+
"jsx",
|
|
174
|
+
"json",
|
|
175
|
+
"ts",
|
|
176
|
+
"tsx",
|
|
177
|
+
"vue",
|
|
178
|
+
"css",
|
|
179
|
+
"html",
|
|
180
|
+
"vue",
|
|
181
|
+
"bash",
|
|
182
|
+
"md",
|
|
183
|
+
"mdc",
|
|
184
|
+
"yaml"
|
|
185
|
+
];
|
|
162
186
|
const module = defineNuxtModule({
|
|
163
187
|
meta: {
|
|
164
188
|
name: "@nuxtjs/mdc",
|
|
@@ -179,6 +203,7 @@ const module = defineNuxtModule({
|
|
|
179
203
|
h6: false
|
|
180
204
|
}
|
|
181
205
|
},
|
|
206
|
+
keepComments: false,
|
|
182
207
|
components: {
|
|
183
208
|
prose: true,
|
|
184
209
|
map: {}
|
|
@@ -223,7 +248,7 @@ const module = defineNuxtModule({
|
|
|
223
248
|
});
|
|
224
249
|
options.rehypePlugins ||= {};
|
|
225
250
|
options.rehypePlugins.highlight ||= {};
|
|
226
|
-
options.rehypePlugins.highlight.src ||= await resolver.resolvePath("./runtime/highlighter/rehype");
|
|
251
|
+
options.rehypePlugins.highlight.src ||= await resolver.resolvePath("./runtime/highlighter/rehype-nuxt");
|
|
227
252
|
options.rehypePlugins.highlight.options ||= {};
|
|
228
253
|
}
|
|
229
254
|
const registerTemplate = (options2) => {
|
|
@@ -266,7 +291,9 @@ const module = defineNuxtModule({
|
|
|
266
291
|
getContents: mdcHighlighter,
|
|
267
292
|
options: {
|
|
268
293
|
shikiPath: resolver.resolve("../dist/runtime/highlighter/shiki"),
|
|
269
|
-
options: options.highlight
|
|
294
|
+
options: options.highlight,
|
|
295
|
+
// When WASM support enabled in Nitro, we could use the .wasm file directly for Cloudflare Workers
|
|
296
|
+
useWasmAssets: !nuxt.options.dev && !!nuxt.options.nitro.experimental?.wasm
|
|
270
297
|
}
|
|
271
298
|
});
|
|
272
299
|
registerTemplate({
|
|
@@ -354,19 +381,11 @@ function resolveOptions(options) {
|
|
|
354
381
|
default: "github-light",
|
|
355
382
|
dark: "github-dark"
|
|
356
383
|
};
|
|
357
|
-
options.highlight.langs ||=
|
|
358
|
-
"js",
|
|
359
|
-
"ts",
|
|
360
|
-
"vue",
|
|
361
|
-
"css",
|
|
362
|
-
"html",
|
|
363
|
-
"vue",
|
|
364
|
-
"shell"
|
|
365
|
-
];
|
|
384
|
+
options.highlight.langs ||= DefaultHighlightLangs;
|
|
366
385
|
if (options.highlight.preload) {
|
|
367
386
|
options.highlight.langs.push(...options.highlight.preload || []);
|
|
368
387
|
}
|
|
369
388
|
}
|
|
370
389
|
}
|
|
371
390
|
|
|
372
|
-
export { module as default };
|
|
391
|
+
export { DefaultHighlightLangs, module as default };
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
:body="data?.body"
|
|
5
5
|
:toc="data?.toc"
|
|
6
6
|
:excerpt="data?.excerpt"
|
|
7
|
+
:error="error"
|
|
7
8
|
>
|
|
8
9
|
<MDCRenderer
|
|
9
10
|
v-if="body"
|
|
@@ -59,7 +60,7 @@ const props = defineProps({
|
|
|
59
60
|
|
|
60
61
|
const key = computed(() => hash(props.value))
|
|
61
62
|
|
|
62
|
-
const { data, refresh } = await useAsyncData(key.value, async () => {
|
|
63
|
+
const { data, refresh, error } = await useAsyncData(key.value, async () => {
|
|
63
64
|
if (typeof props.value !== 'string') {
|
|
64
65
|
return props.value
|
|
65
66
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { h, resolveComponent, Text, defineComponent, toRaw, computed } from "vue";
|
|
2
|
+
import { h, resolveComponent, Text, Comment, defineComponent, toRaw, computed, getCurrentInstance } from "vue";
|
|
3
3
|
import destr from "destr";
|
|
4
4
|
import { kebabCase, pascalCase } from "scule";
|
|
5
5
|
import { find, html } from "property-information";
|
|
6
|
-
import { useRoute, useRuntimeConfig } from "#imports";
|
|
7
6
|
import htmlTags from "../parser/utils/html-tags-list";
|
|
8
7
|
const DEFAULT_SLOT = "default";
|
|
9
8
|
const rxOn = /^@|^v-on:/;
|
|
@@ -51,10 +50,12 @@ export default defineComponent({
|
|
|
51
50
|
}
|
|
52
51
|
},
|
|
53
52
|
async setup(props) {
|
|
54
|
-
const
|
|
53
|
+
const $nuxt = getCurrentInstance()?.appContext?.app?.$nuxt;
|
|
54
|
+
const route = $nuxt?.$route;
|
|
55
|
+
const { mdc } = $nuxt?.$config?.public || {};
|
|
55
56
|
const tags = {
|
|
56
|
-
...mdc
|
|
57
|
-
...mdc
|
|
57
|
+
...mdc?.components?.prose && props.prose !== false ? proseComponentMap : {},
|
|
58
|
+
...mdc?.components?.map || {},
|
|
58
59
|
...toRaw(props.data?.mdc?.components || {}),
|
|
59
60
|
...props.components
|
|
60
61
|
};
|
|
@@ -63,14 +64,14 @@ export default defineComponent({
|
|
|
63
64
|
return Array.from(new Set(components)).sort().join(".");
|
|
64
65
|
});
|
|
65
66
|
await resolveContentComponents(props.body, { tags });
|
|
66
|
-
return { tags, contentKey };
|
|
67
|
+
return { tags, contentKey, route };
|
|
67
68
|
},
|
|
68
69
|
render(ctx) {
|
|
69
|
-
const { tags, tag, body, data, contentKey } = ctx;
|
|
70
|
+
const { tags, tag, body, data, contentKey, route } = ctx;
|
|
70
71
|
if (!body) {
|
|
71
72
|
return null;
|
|
72
73
|
}
|
|
73
|
-
const meta = { ...data, tags };
|
|
74
|
+
const meta = { ...data, tags, $route: route };
|
|
74
75
|
const component = tag !== false ? resolveVueComponent(tag || meta.component?.name || meta.component || "div") : void 0;
|
|
75
76
|
const childrenRenderer = renderSlots(body, h, meta, meta);
|
|
76
77
|
return component ? h(component, { ...meta.component?.props, ...this.$attrs, key: contentKey }, childrenRenderer) : childrenRenderer.default?.();
|
|
@@ -80,6 +81,9 @@ function renderNode(node, h2, documentMeta, parentScope = {}) {
|
|
|
80
81
|
if (node.type === "text") {
|
|
81
82
|
return h2(Text, node.value);
|
|
82
83
|
}
|
|
84
|
+
if (node.type === "comment") {
|
|
85
|
+
return h2(Comment, null, node.value);
|
|
86
|
+
}
|
|
83
87
|
const originalTag = node.tag;
|
|
84
88
|
const renderTag = findMappedTag(node, documentMeta.tags);
|
|
85
89
|
if (node.tag === "binding") {
|
|
@@ -99,7 +103,6 @@ function renderNode(node, h2, documentMeta, parentScope = {}) {
|
|
|
99
103
|
function renderBinding(node, h2, documentMeta, parentScope = {}) {
|
|
100
104
|
const data = {
|
|
101
105
|
...parentScope,
|
|
102
|
-
$route: () => useRoute(),
|
|
103
106
|
$document: documentMeta,
|
|
104
107
|
$doc: documentMeta
|
|
105
108
|
};
|
|
@@ -207,7 +210,7 @@ function propsToDataRxBind(key, value, data, documentMeta) {
|
|
|
207
210
|
return data;
|
|
208
211
|
}
|
|
209
212
|
const resolveVueComponent = (component) => {
|
|
210
|
-
if (!htmlTags.includes(component) && !component?.render) {
|
|
213
|
+
if (!htmlTags.includes(component) && !component?.render && !component?.ssrRender) {
|
|
211
214
|
const componentFn = resolveComponent(pascalCase(component), false);
|
|
212
215
|
if (typeof componentFn === "object") {
|
|
213
216
|
return componentFn;
|
|
@@ -261,7 +264,7 @@ async function resolveContentComponents(body, meta) {
|
|
|
261
264
|
}));
|
|
262
265
|
function loadComponents(node, documentMeta) {
|
|
263
266
|
const tag = node.tag;
|
|
264
|
-
if (node.type === "text" || tag === "binding") {
|
|
267
|
+
if (node.type === "text" || tag === "binding" || node.type === "comment") {
|
|
265
268
|
return [];
|
|
266
269
|
}
|
|
267
270
|
const renderTag = findMappedTag(node, documentMeta.tags);
|
|
@@ -39,6 +39,7 @@ declare const _default: DefineComponent<{
|
|
|
39
39
|
}, {
|
|
40
40
|
tags: any;
|
|
41
41
|
contentKey: import("vue").ComputedRef<string>;
|
|
42
|
+
route: any;
|
|
42
43
|
}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
43
44
|
/**
|
|
44
45
|
* Content to render
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { rehypeHighlight as rehypeHighlightUniversal } from "./rehype.mjs";
|
|
2
|
+
const defaults = {
|
|
3
|
+
theme: {},
|
|
4
|
+
async highlighter(code, lang, theme, options) {
|
|
5
|
+
try {
|
|
6
|
+
if (process.browser && window.sessionStorage.getItem("mdc-shiki-highlighter") === "browser") {
|
|
7
|
+
return import("#mdc-highlighter").then((h) => h.default(code, lang, theme, options)).catch(() => ({}));
|
|
8
|
+
}
|
|
9
|
+
return await $fetch("/api/_mdc/highlight", {
|
|
10
|
+
params: {
|
|
11
|
+
code,
|
|
12
|
+
lang,
|
|
13
|
+
theme: JSON.stringify(theme),
|
|
14
|
+
options: JSON.stringify(options)
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
} catch (e) {
|
|
18
|
+
if (process.browser && e?.response?.status === 404) {
|
|
19
|
+
window.sessionStorage.setItem("mdc-shiki-highlighter", "browser");
|
|
20
|
+
return this.highlighter?.(code, lang, theme, options);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return Promise.resolve({ tree: [{ type: "text", value: code }], className: "", style: "" });
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export default rehypeHighlight;
|
|
27
|
+
export function rehypeHighlight(opts = {}) {
|
|
28
|
+
const options = { ...defaults, ...opts };
|
|
29
|
+
if (typeof options.highlighter !== "function") {
|
|
30
|
+
options.highlighter = defaults.highlighter;
|
|
31
|
+
}
|
|
32
|
+
return rehypeHighlightUniversal(options);
|
|
33
|
+
}
|
|
@@ -5,4 +5,4 @@ export interface RehypeHighlightOption {
|
|
|
5
5
|
highlighter?: Highlighter;
|
|
6
6
|
}
|
|
7
7
|
export default rehypeHighlight;
|
|
8
|
-
export declare function rehypeHighlight(opts
|
|
8
|
+
export declare function rehypeHighlight(opts: RehypeHighlightOption): (tree: Root) => Promise<void>;
|
|
@@ -1,32 +1,8 @@
|
|
|
1
1
|
import { visit } from "unist-util-visit";
|
|
2
2
|
import { toString } from "hast-util-to-string";
|
|
3
|
-
const defaults = {
|
|
4
|
-
theme: {},
|
|
5
|
-
async highlighter(code, lang, theme, options) {
|
|
6
|
-
if (process.browser && window.sessionStorage.getItem("mdc-shiki-highlighter") === "browser") {
|
|
7
|
-
return import("#mdc-highlighter").then((h) => h.default(code, lang, theme, options));
|
|
8
|
-
}
|
|
9
|
-
try {
|
|
10
|
-
return await $fetch("/api/_mdc/highlight", {
|
|
11
|
-
params: {
|
|
12
|
-
code,
|
|
13
|
-
lang,
|
|
14
|
-
theme: JSON.stringify(theme),
|
|
15
|
-
options: JSON.stringify(options)
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
} catch (e) {
|
|
19
|
-
if (process.browser && e?.response?.status === 404) {
|
|
20
|
-
window.sessionStorage.setItem("mdc-shiki-highlighter", "browser");
|
|
21
|
-
return this.highlighter?.(code, lang, theme, options);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return Promise.resolve({ tree: [{ type: "text", value: code }], className: "", style: "" });
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
3
|
export default rehypeHighlight;
|
|
28
|
-
export function rehypeHighlight(opts
|
|
29
|
-
const options =
|
|
4
|
+
export function rehypeHighlight(opts) {
|
|
5
|
+
const options = opts;
|
|
30
6
|
return async (tree) => {
|
|
31
7
|
const tasks = [];
|
|
32
8
|
const styles = [];
|
|
@@ -53,7 +53,7 @@ export function createShikiHighlighter({
|
|
|
53
53
|
if (bundledLangs[lang]) {
|
|
54
54
|
await shiki2.loadLanguage(bundledLangs[lang]);
|
|
55
55
|
} else {
|
|
56
|
-
if (
|
|
56
|
+
if (import.meta.env) {
|
|
57
57
|
console.warn(`[@nuxtjs/mdc] Language "${lang}" is not loaded to the Shiki highlighter, fallback to plain text. Add the language to "mdc.highlight.langs" to fix this.`);
|
|
58
58
|
}
|
|
59
59
|
lang = "text";
|
|
@@ -64,7 +64,7 @@ export function createShikiHighlighter({
|
|
|
64
64
|
if (bundledThemes[theme2]) {
|
|
65
65
|
await shiki2.loadTheme(bundledThemes[theme2]);
|
|
66
66
|
} else {
|
|
67
|
-
if (
|
|
67
|
+
if (import.meta.env) {
|
|
68
68
|
console.warn(`[@nuxtjs/mdc] Theme "${theme2}" is not loaded to the Shiki highlighter. Add the theme to "mdc.highlight.themes" to fix this.`);
|
|
69
69
|
}
|
|
70
70
|
themesObject[color] = "none";
|
package/dist/runtime/index.d.ts
CHANGED
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
export { parseMarkdown } from "./parser/index.mjs";
|
|
1
|
+
export { parseMarkdown, createMarkdownParser } from "./parser/index.mjs";
|
|
2
|
+
export { rehypeHighlight } from "./highlighter/rehype.mjs";
|
|
3
|
+
export { createShikiHighlighter } from "./highlighter/shiki.mjs";
|
|
2
4
|
export * from "./utils/node.mjs";
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import type { MDCParseOptions } from '../types';
|
|
2
|
+
export declare function compileHast(this: any, options?: MDCParseOptions): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { toString } from "hast-util-to-string";
|
|
2
2
|
import Slugger from "github-slugger";
|
|
3
3
|
import { validateProps } from "./utils/props.mjs";
|
|
4
|
-
export function compileHast() {
|
|
4
|
+
export function compileHast(options = {}) {
|
|
5
5
|
const slugs = new Slugger();
|
|
6
6
|
function compileToJSON(node, parent) {
|
|
7
7
|
if (node.type === "root") {
|
|
@@ -55,6 +55,12 @@ export function compileHast() {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
+
if (options.keepComments && node.type === "comment") {
|
|
59
|
+
return {
|
|
60
|
+
type: "comment",
|
|
61
|
+
value: node.value
|
|
62
|
+
};
|
|
63
|
+
}
|
|
58
64
|
return null;
|
|
59
65
|
}
|
|
60
66
|
this.Compiler = (tree) => {
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare const
|
|
3
|
-
|
|
4
|
-
body: MDCRoot;
|
|
5
|
-
excerpt: MDCRoot | undefined;
|
|
6
|
-
toc: Toc | undefined;
|
|
7
|
-
}>;
|
|
1
|
+
import type { MDCParseOptions, MDCParserResult, MDCRoot } from '../types';
|
|
2
|
+
export declare const createMarkdownParser: (inlineOptions?: MDCParseOptions) => Promise<(md: string) => Promise<MDCParserResult>>;
|
|
3
|
+
export declare const parseMarkdown: (md: string, inlineOptions?: MDCParseOptions) => Promise<MDCParserResult>;
|
|
8
4
|
export declare function contentHeading(body: MDCRoot): {
|
|
9
5
|
title: string;
|
|
10
6
|
description: string;
|
|
@@ -10,7 +10,7 @@ import { generateToc } from "./toc.mjs";
|
|
|
10
10
|
import { nodeTextContent } from "../utils/node.mjs";
|
|
11
11
|
let moduleOptions;
|
|
12
12
|
let generatedMdcConfigs;
|
|
13
|
-
export const
|
|
13
|
+
export const createMarkdownParser = async (inlineOptions = {}) => {
|
|
14
14
|
if (!moduleOptions) {
|
|
15
15
|
moduleOptions = await import(
|
|
16
16
|
"#mdc-imports"
|
|
@@ -27,7 +27,7 @@ export const parseMarkdown = async (md, inlineOptions = {}) => {
|
|
|
27
27
|
...generatedMdcConfigs || [],
|
|
28
28
|
...inlineOptions.configs || []
|
|
29
29
|
];
|
|
30
|
-
if (inlineOptions.highlight != null && inlineOptions.highlight != false && typeof inlineOptions.highlight.highlighter !== "function") {
|
|
30
|
+
if (inlineOptions.highlight != null && inlineOptions.highlight != false && inlineOptions.highlight.highlighter !== void 0 && typeof inlineOptions.highlight.highlighter !== "function") {
|
|
31
31
|
if (import.meta.dev)
|
|
32
32
|
console.warn("[@nuxtjs/mdc] `highlighter` passed to `parseMarkdown` is should be a function, but got " + JSON.stringify(inlineOptions.highlight.highlighter) + ", ignored.");
|
|
33
33
|
inlineOptions = {
|
|
@@ -44,7 +44,10 @@ export const parseMarkdown = async (md, inlineOptions = {}) => {
|
|
|
44
44
|
highlight: moduleOptions?.highlight
|
|
45
45
|
}, defaults);
|
|
46
46
|
if (options.rehype?.plugins?.highlight) {
|
|
47
|
-
options.rehype.plugins.highlight.options =
|
|
47
|
+
options.rehype.plugins.highlight.options = {
|
|
48
|
+
...options.rehype.plugins.highlight.options || {},
|
|
49
|
+
...options.highlight || {}
|
|
50
|
+
};
|
|
48
51
|
}
|
|
49
52
|
let processor = unified();
|
|
50
53
|
for (const config of mdcConfigs) {
|
|
@@ -60,34 +63,40 @@ export const parseMarkdown = async (md, inlineOptions = {}) => {
|
|
|
60
63
|
processor = await config.unified?.rehype?.(processor) || processor;
|
|
61
64
|
}
|
|
62
65
|
await useProcessorPlugins(processor, options.rehype?.plugins);
|
|
63
|
-
processor.use(compileHast);
|
|
66
|
+
processor.use(compileHast, options);
|
|
64
67
|
for (const config of mdcConfigs) {
|
|
65
68
|
processor = await config.unified?.post?.(processor) || processor;
|
|
66
69
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
return async (md) => {
|
|
71
|
+
const { content, data: frontmatter } = await parseFrontMatter(md);
|
|
72
|
+
const processedFile = await processor.process({ value: content, data: frontmatter });
|
|
73
|
+
const result = processedFile.result;
|
|
74
|
+
const data = Object.assign(
|
|
75
|
+
contentHeading(result.body),
|
|
76
|
+
frontmatter,
|
|
77
|
+
processedFile?.data || {}
|
|
78
|
+
);
|
|
79
|
+
let toc;
|
|
80
|
+
if (data.toc !== false) {
|
|
81
|
+
const tocOption = defu(data.toc || {}, options.toc);
|
|
82
|
+
toc = generateToc(result.body, tocOption);
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
data,
|
|
86
|
+
body: result.body,
|
|
87
|
+
excerpt: result.excerpt,
|
|
88
|
+
toc
|
|
89
|
+
};
|
|
85
90
|
};
|
|
86
91
|
};
|
|
92
|
+
export const parseMarkdown = async (md, inlineOptions = {}) => {
|
|
93
|
+
const parser = await createMarkdownParser(inlineOptions);
|
|
94
|
+
return parser(md);
|
|
95
|
+
};
|
|
87
96
|
export function contentHeading(body) {
|
|
88
97
|
let title = "";
|
|
89
98
|
let description = "";
|
|
90
|
-
const children = body.children.filter((node) => node.type
|
|
99
|
+
const children = body.children.filter((node) => node.type === "element" && node.tag !== "hr");
|
|
91
100
|
if (children.length && children[0].tag === "h1") {
|
|
92
101
|
const node = children.shift();
|
|
93
102
|
title = nodeTextContent(node);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Options as RehypeOption } from 'remark-rehype';
|
|
2
2
|
import type { RehypeHighlightOption } from '../highlighter/rehype';
|
|
3
3
|
import type { MdcConfig } from './config';
|
|
4
|
+
import type { MDCData, MDCRoot } from './tree';
|
|
5
|
+
import type { Toc } from './toc';
|
|
4
6
|
export interface RemarkPlugin {
|
|
5
7
|
instance?: any;
|
|
6
8
|
options?: Array<any> | Record<string, any>;
|
|
@@ -25,8 +27,15 @@ export interface MDCParseOptions {
|
|
|
25
27
|
depth?: number;
|
|
26
28
|
searchDepth?: number;
|
|
27
29
|
};
|
|
30
|
+
keepComments?: boolean;
|
|
28
31
|
/**
|
|
29
32
|
* Inline mdc.config.ts
|
|
30
33
|
*/
|
|
31
34
|
configs?: MdcConfig[];
|
|
32
35
|
}
|
|
36
|
+
export interface MDCParserResult {
|
|
37
|
+
data: MDCData;
|
|
38
|
+
body: MDCRoot;
|
|
39
|
+
excerpt: MDCRoot | undefined;
|
|
40
|
+
toc: Toc | undefined;
|
|
41
|
+
}
|
|
@@ -2,13 +2,17 @@ export type MDCText = {
|
|
|
2
2
|
type: 'text';
|
|
3
3
|
value: string;
|
|
4
4
|
};
|
|
5
|
+
export type MDCComment = {
|
|
6
|
+
type: 'comment';
|
|
7
|
+
value: string;
|
|
8
|
+
};
|
|
5
9
|
export type MDCElement = {
|
|
6
10
|
type: 'element';
|
|
7
11
|
tag: string;
|
|
8
12
|
props: Record<string, any> | undefined;
|
|
9
|
-
children: Array<MDCElement | MDCText>;
|
|
13
|
+
children: Array<MDCElement | MDCText | MDCComment>;
|
|
10
14
|
};
|
|
11
|
-
export type MDCNode = MDCElement | MDCText;
|
|
15
|
+
export type MDCNode = MDCElement | MDCText | MDCComment;
|
|
12
16
|
export type MDCRoot = {
|
|
13
17
|
type: 'root';
|
|
14
18
|
children: Array<MDCNode>;
|
package/dist/types.d.mts
CHANGED
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxtjs/mdc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Nuxt MDC module",
|
|
5
5
|
"repository": "nuxt-modules/mdc",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,10 +16,22 @@
|
|
|
16
16
|
"import": "./dist/config.mjs",
|
|
17
17
|
"require": "./dist/config.cjs"
|
|
18
18
|
},
|
|
19
|
-
"./runtime":
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
"./runtime": {
|
|
20
|
+
"types": "./dist/runtime/index.d.ts",
|
|
21
|
+
"import": "./dist/runtime/index.mjs"
|
|
22
|
+
},
|
|
23
|
+
"./dist/runtime": {
|
|
24
|
+
"types": "./dist/runtime/index.d.ts",
|
|
25
|
+
"import": "./dist/runtime/index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./runtime/*": {
|
|
28
|
+
"types": "./dist/runtime/*.d.ts",
|
|
29
|
+
"import": "./dist/runtime/*.mjs"
|
|
30
|
+
},
|
|
31
|
+
"./dist/runtime/*": {
|
|
32
|
+
"types": "./dist/runtime/*.d.ts",
|
|
33
|
+
"import": "./dist/runtime/*.mjs"
|
|
34
|
+
}
|
|
23
35
|
},
|
|
24
36
|
"main": "./dist/module.cjs",
|
|
25
37
|
"types": "./dist/types.d.ts",
|
|
@@ -50,15 +62,15 @@
|
|
|
50
62
|
"test:watch": "vitest watch"
|
|
51
63
|
},
|
|
52
64
|
"dependencies": {
|
|
53
|
-
"@nuxt/kit": "^3.10.
|
|
54
|
-
"@shikijs/transformers": "^1.
|
|
65
|
+
"@nuxt/kit": "^3.10.3",
|
|
66
|
+
"@shikijs/transformers": "^1.1.7",
|
|
55
67
|
"@types/hast": "^3.0.4",
|
|
56
68
|
"@types/mdast": "^4.0.3",
|
|
57
|
-
"@vue/compiler-core": "^3.4.
|
|
69
|
+
"@vue/compiler-core": "^3.4.21",
|
|
58
70
|
"consola": "^3.2.3",
|
|
59
71
|
"debug": "^4.3.4",
|
|
60
72
|
"defu": "^6.1.4",
|
|
61
|
-
"destr": "^2.0.
|
|
73
|
+
"destr": "^2.0.3",
|
|
62
74
|
"detab": "^3.0.2",
|
|
63
75
|
"github-slugger": "^2.0.0",
|
|
64
76
|
"hast-util-to-string": "^3.0.0",
|
|
@@ -75,12 +87,12 @@
|
|
|
75
87
|
"rehype-sort-attributes": "^5.0.0",
|
|
76
88
|
"remark-emoji": "^4.0.1",
|
|
77
89
|
"remark-gfm": "^4.0.0",
|
|
78
|
-
"remark-mdc": "^3.0
|
|
90
|
+
"remark-mdc": "^3.1.0",
|
|
79
91
|
"remark-parse": "^11.0.0",
|
|
80
92
|
"remark-rehype": "^11.1.0",
|
|
81
|
-
"scule": "^1.
|
|
82
|
-
"shiki": "^1.
|
|
83
|
-
"ufo": "^1.
|
|
93
|
+
"scule": "^1.3.0",
|
|
94
|
+
"shiki": "^1.1.7",
|
|
95
|
+
"ufo": "^1.4.0",
|
|
84
96
|
"unified": "^11.0.4",
|
|
85
97
|
"unist-builder": "^4.0.0",
|
|
86
98
|
"unist-util-visit": "^5.0.0",
|
|
@@ -90,19 +102,19 @@
|
|
|
90
102
|
"@nuxt/devtools": "latest",
|
|
91
103
|
"@nuxt/eslint-config": "^0.2.0",
|
|
92
104
|
"@nuxt/module-builder": "^0.5.5",
|
|
93
|
-
"@nuxt/schema": "^3.10.
|
|
105
|
+
"@nuxt/schema": "^3.10.3",
|
|
94
106
|
"@nuxt/test-utils": "^3.11.0",
|
|
95
|
-
"@nuxt/ui": "^2.
|
|
107
|
+
"@nuxt/ui": "^2.14.1",
|
|
96
108
|
"@types/mdurl": "^1.0.5",
|
|
97
|
-
"@types/node": "^20.11.
|
|
109
|
+
"@types/node": "^20.11.24",
|
|
98
110
|
"changelogen": "^0.5.5",
|
|
99
|
-
"eslint": "^8.
|
|
100
|
-
"nuxt": "^3.10.
|
|
111
|
+
"eslint": "^8.57.0",
|
|
112
|
+
"nuxt": "^3.10.3",
|
|
101
113
|
"rehype": "^13.0.1",
|
|
102
|
-
"release-it": "^17.
|
|
103
|
-
"vitest": "^1.
|
|
114
|
+
"release-it": "^17.1.1",
|
|
115
|
+
"vitest": "^1.3.1"
|
|
104
116
|
},
|
|
105
|
-
"packageManager": "pnpm@8.15.
|
|
117
|
+
"packageManager": "pnpm@8.15.4",
|
|
106
118
|
"release-it": {
|
|
107
119
|
"git": {
|
|
108
120
|
"commitMessage": "chore(release): release v${version}"
|