|
9 | 9 | import { customAlphabet } from 'nanoid'
|
10 | 10 | import { createRequire } from 'node:module'
|
11 | 11 | import c from 'picocolors'
|
12 |
| -import type { ShikiTransformer } from 'shiki' |
| 12 | +import type { LanguageRegistration, ShikiTransformer } from 'shiki' |
13 | 13 | import { createHighlighter, isSpecialLang } from 'shiki'
|
14 | 14 | import { createSyncFn } from 'synckit'
|
15 | 15 | import type { Logger } from 'vite'
|
@@ -61,22 +61,44 @@ export async function highlight(
|
61 | 61 | logger: Pick<Logger, 'warn'> = console
|
62 | 62 | ): Promise<[(str: string, lang: string, attrs: string) => string, () => void]> {
|
63 | 63 | const {
|
64 |
| - defaultHighlightLang: defaultLang = '', |
| 64 | + defaultHighlightLang: defaultLang = 'txt', |
65 | 65 | codeTransformers: userTransformers = []
|
66 | 66 | } = options
|
67 | 67 |
|
| 68 | + const usingTwoslash = userTransformers.some( |
| 69 | + ({ name }) => name === '@shikijs/vitepress-twoslash' |
| 70 | + ) |
| 71 | + |
68 | 72 | const highlighter = await createHighlighter({
|
69 | 73 | themes:
|
70 | 74 | typeof theme === 'object' && 'light' in theme && 'dark' in theme
|
71 | 75 | ? [theme.light, theme.dark]
|
72 | 76 | : [theme],
|
73 | 77 | langs: [
|
74 | 78 | ...(options.languages || []),
|
75 |
| - ...Object.values(options.languageAlias || {}) |
| 79 | + ...Object.values(options.languageAlias || {}), |
| 80 | + |
| 81 | + // patch for twoslash - https://github.com/vuejs/vitepress/issues/4334 |
| 82 | + ...(usingTwoslash |
| 83 | + ? Object.keys((await import('shiki')).bundledLanguages) |
| 84 | + : []) |
76 | 85 | ],
|
77 | 86 | langAlias: options.languageAlias
|
78 | 87 | })
|
79 | 88 |
|
| 89 | + function loadLanguage(name: string | LanguageRegistration) { |
| 90 | + const lang = typeof name === 'string' ? name : name.name |
| 91 | + if ( |
| 92 | + !isSpecialLang(lang) && |
| 93 | + !highlighter.getLoadedLanguages().includes(lang) |
| 94 | + ) { |
| 95 | + const resolvedLang = resolveLangSync(lang) |
| 96 | + if (resolvedLang.length) highlighter.loadLanguageSync(resolvedLang) |
| 97 | + else return false |
| 98 | + } |
| 99 | + return true |
| 100 | + } |
| 101 | + |
80 | 102 | await options?.shikiSetup?.(highlighter)
|
81 | 103 |
|
82 | 104 | const transformers: ShikiTransformer[] = [
|
@@ -116,23 +138,13 @@ export async function highlight(
|
116 | 138 | .replace(vueRE, '')
|
117 | 139 | .toLowerCase() || defaultLang
|
118 | 140 |
|
119 |
| - if (lang) { |
120 |
| - const langLoaded = highlighter.getLoadedLanguages().includes(lang) |
121 |
| - if (!langLoaded && !isSpecialLang(lang)) { |
122 |
| - const resolvedLang = resolveLangSync(lang) |
123 |
| - if (!resolvedLang.length) { |
124 |
| - logger.warn( |
125 |
| - c.yellow( |
126 |
| - `\nThe language '${lang}' is not loaded, falling back to '${ |
127 |
| - defaultLang || 'txt' |
128 |
| - }' for syntax highlighting.` |
129 |
| - ) |
130 |
| - ) |
131 |
| - lang = defaultLang |
132 |
| - } else { |
133 |
| - highlighter.loadLanguageSync(resolvedLang) |
134 |
| - } |
135 |
| - } |
| 141 | + if (!loadLanguage(lang)) { |
| 142 | + logger.warn( |
| 143 | + c.yellow( |
| 144 | + `\nThe language '${lang}' is not loaded, falling back to '${defaultLang}' for syntax highlighting.` |
| 145 | + ) |
| 146 | + ) |
| 147 | + lang = defaultLang |
136 | 148 | }
|
137 | 149 |
|
138 | 150 | const lineOptions = attrsToLines(attrs)
|
|
0 commit comments