|
1 | 1 | import { dirname } from 'node:path'
|
2 | 2 | import { createShikiHighlighter, rehypeHighlight } from '@nuxtjs/mdc/runtime'
|
3 | 3 | import { hash } from 'ohash'
|
4 |
| -import type { Highlighter, MdcConfig, ModuleOptions as MDCModuleOptions } from '@nuxtjs/mdc' |
| 4 | +import type { Highlighter, MdcConfig, ModuleOptions as MDCModuleOptions, MdcThemeOptions } from '@nuxtjs/mdc' |
5 | 5 | import type { Nuxt } from '@nuxt/schema'
|
6 | 6 | import { resolveAlias } from '@nuxt/kit'
|
7 | 7 | import type { LanguageRegistration } from 'shiki'
|
@@ -35,69 +35,75 @@ let highlightPlugin: {
|
35 | 35 | }
|
36 | 36 | let highlightPluginPromise: Promise<typeof highlightPlugin>
|
37 | 37 | async function getHighlightPluginInstance(options: HighlighterOptions) {
|
| 38 | + const key = hash(JSON.stringify(options || {})) |
| 39 | + |
| 40 | + // If the key is different, reset the plugin |
| 41 | + if (highlightPlugin && highlightPlugin.key !== key) { |
| 42 | + highlightPlugin = undefined |
| 43 | + highlightPluginPromise = undefined |
| 44 | + } |
| 45 | + |
| 46 | + // If the plugin is not initialized, initialize it |
38 | 47 | if (!highlightPlugin) {
|
39 |
| - highlightPluginPromise = highlightPluginPromise || _getHighlightPlugin(options) |
| 48 | + highlightPluginPromise = highlightPluginPromise || _getHighlightPlugin(key, options) |
40 | 49 | await highlightPluginPromise
|
41 | 50 | }
|
42 | 51 |
|
43 | 52 | return highlightPlugin
|
44 | 53 | }
|
45 |
| -async function _getHighlightPlugin(options: HighlighterOptions) { |
46 |
| - const key = hash(JSON.stringify(options || {})) |
47 |
| - if (!highlightPlugin || highlightPlugin.key !== key) { |
48 |
| - const langs = Array.from(new Set(['bash', 'html', 'mdc', 'vue', 'yml', 'scss', 'ts', 'typescript', ...(options.langs || [])])) |
49 |
| - const themesObject = typeof options.theme === 'string' ? { default: options.theme } : options.theme || { default: 'material-theme-palenight' } |
50 |
| - const bundledThemes = await Promise.all(Object.entries(themesObject) |
51 |
| - .map(async ([name, theme]) => [ |
52 |
| - name, |
53 |
| - typeof theme === 'string' ? (await import(`shiki/themes/${theme}.mjs`).then(m => m.default || m)) : theme, |
54 |
| - ])) |
55 |
| - const bundledLangs = await Promise.all(langs.map(async lang => [ |
56 |
| - typeof lang === 'string' ? lang : (lang as unknown as LanguageRegistration).name, |
57 |
| - typeof lang === 'string' ? await import(`@shikijs/langs/${lang}`).then(m => m.default || m) : lang, |
| 54 | +async function _getHighlightPlugin(key: string, options: HighlighterOptions) { |
| 55 | + const langs = Array.from(new Set(['bash', 'html', 'mdc', 'vue', 'yml', 'scss', 'ts', 'typescript', ...(options.langs || [])])) |
| 56 | + const themesObject = typeof options.theme === 'string' ? { default: options.theme } : options.theme || { default: 'material-theme-palenight' } |
| 57 | + const bundledThemes = await Promise.all(Object.entries(themesObject) |
| 58 | + .map(async ([name, theme]) => [ |
| 59 | + name, |
| 60 | + typeof theme === 'string' ? (await import(`shiki/themes/${theme}.mjs`).then(m => m.default || m)) : theme, |
58 | 61 | ]))
|
59 |
| - |
60 |
| - const highlighter = createShikiHighlighter({ |
61 |
| - bundledThemes: Object.fromEntries(bundledThemes), |
62 |
| - // Configure the bundled languages |
63 |
| - bundledLangs: Object.fromEntries(bundledLangs), |
64 |
| - engine: createOnigurumaEngine(import('shiki/wasm')), |
65 |
| - getMdcConfigs: () => Promise.resolve(parserOptions.mdcConfigs), |
66 |
| - }) |
67 |
| - |
68 |
| - highlightPlugin = { |
69 |
| - key, |
70 |
| - instance: rehypeHighlight, |
71 |
| - ...options, |
72 |
| - options: { |
73 |
| - highlighter: async (code, lang, theme, opts) => { |
74 |
| - const result = await highlighter(code, lang, theme, opts) |
75 |
| - const visitTree = { |
76 |
| - type: 'element', |
77 |
| - children: result.tree as Array<unknown>, |
78 |
| - } |
79 |
| - if (options.compress) { |
80 |
| - const stylesMap: Record<string, string> = {} |
81 |
| - visit( |
82 |
| - visitTree, |
83 |
| - node => !!(node as HighlightedNode).properties?.style, |
84 |
| - (_node) => { |
85 |
| - const node = _node as HighlightedNode |
86 |
| - const style = node.properties!.style! |
87 |
| - stylesMap[style] = stylesMap[style] || 's' + hash(style).substring(0, 4) |
88 |
| - node.properties!.class = `${node.properties!.class || ''} ${stylesMap[style]}`.trim() |
89 |
| - node.properties!.style = undefined |
90 |
| - }, |
91 |
| - ) |
92 |
| - |
93 |
| - result.style = Object.entries(stylesMap).map(([style, cls]) => `html pre.shiki code .${cls}, html code.shiki .${cls}{${style}}`).join('') + result.style |
94 |
| - } |
95 |
| - |
96 |
| - return result |
97 |
| - }, |
98 |
| - theme: Object.fromEntries(bundledThemes), |
| 62 | + const bundledLangs = await Promise.all(langs.map(async lang => [ |
| 63 | + typeof lang === 'string' ? lang : (lang as unknown as LanguageRegistration).name, |
| 64 | + typeof lang === 'string' ? await import(`@shikijs/langs/${lang}`).then(m => m.default || m) : lang, |
| 65 | + ])) |
| 66 | + |
| 67 | + const highlighter = createShikiHighlighter({ |
| 68 | + bundledThemes: Object.fromEntries(bundledThemes), |
| 69 | + // Configure the bundled languages |
| 70 | + bundledLangs: Object.fromEntries(bundledLangs), |
| 71 | + engine: createOnigurumaEngine(import('shiki/wasm')), |
| 72 | + getMdcConfigs: () => Promise.resolve(parserOptions.mdcConfigs), |
| 73 | + }) |
| 74 | + |
| 75 | + highlightPlugin = { |
| 76 | + key, |
| 77 | + instance: rehypeHighlight, |
| 78 | + ...options, |
| 79 | + options: { |
| 80 | + highlighter: async (code, lang, theme, opts) => { |
| 81 | + const result = await highlighter(code, lang, theme, opts) |
| 82 | + const visitTree = { |
| 83 | + type: 'element', |
| 84 | + children: result.tree as Array<unknown>, |
| 85 | + } |
| 86 | + if (options.compress) { |
| 87 | + const stylesMap: Record<string, string> = {} |
| 88 | + visit( |
| 89 | + visitTree, |
| 90 | + node => !!(node as HighlightedNode).properties?.style, |
| 91 | + (_node) => { |
| 92 | + const node = _node as HighlightedNode |
| 93 | + const style = node.properties!.style! |
| 94 | + stylesMap[style] = stylesMap[style] || 's' + hash(style).substring(0, 4) |
| 95 | + node.properties!.class = `${node.properties!.class || ''} ${stylesMap[style]}`.trim() |
| 96 | + node.properties!.style = undefined |
| 97 | + }, |
| 98 | + ) |
| 99 | + |
| 100 | + result.style = Object.entries(stylesMap).map(([style, cls]) => `html pre.shiki code .${cls}, html code.shiki .${cls}{${style}}`).join('') + result.style |
| 101 | + } |
| 102 | + |
| 103 | + return result |
99 | 104 | },
|
100 |
| - } |
| 105 | + theme: Object.fromEntries(bundledThemes), |
| 106 | + }, |
101 | 107 | }
|
102 | 108 | return highlightPlugin
|
103 | 109 | }
|
|
0 commit comments