11import type {
2- BundledHighlighterOptions ,
32 BundledLanguage ,
43 CodeOptionsMeta ,
54 CodeOptionsThemes ,
65 CodeToHastOptionsCommon ,
76 BundledTheme ,
7+ HighlighterCore ,
8+ ThemeRegistrationAny ,
9+ LanguageRegistration ,
810} from 'shiki' ;
911import {
1012 type Components ,
@@ -30,7 +32,7 @@ export async function highlightHast(
3032) : Promise < Root > {
3133 const { lang : initialLang , fallbackLanguage, config, ...rest } = options ;
3234 let lang = initialLang ;
33- let themesToLoad : unknown [ ] ;
35+ let themesToLoad : ( ThemeRegistrationAny | string ) [ ] ;
3436
3537 if ( ! ( 'theme' in rest ) && ! ( 'themes' in rest ) ) {
3638 Object . assign ( rest , config . defaultThemes ) ;
@@ -45,14 +47,13 @@ export async function highlightHast(
4547 }
4648
4749 const highlighter = await config . createHighlighter ( ) ;
48- await highlighter . loadTheme ( ...( themesToLoad as never [ ] ) ) ;
49-
50- try {
51- await highlighter . loadLanguage ( lang as never ) ;
52- } catch {
53- lang = fallbackLanguage ?? 'text' ;
54- await highlighter . loadLanguage ( lang as never ) ;
55- }
50+ await Promise . all ( [
51+ loadMissingTheme ( highlighter , ...themesToLoad ) ,
52+ loadMissingLanguage ( highlighter , lang ) . catch ( ( ) => {
53+ lang = fallbackLanguage ?? 'text' ;
54+ if ( fallbackLanguage ) return loadMissingLanguage ( highlighter , fallbackLanguage ) ;
55+ } ) ,
56+ ] ) ;
5657
5758 return highlighter . codeToHast ( code , {
5859 lang,
@@ -61,6 +62,44 @@ export async function highlightHast(
6162 } ) ;
6263}
6364
65+ async function loadMissingTheme (
66+ highlighter : HighlighterCore ,
67+ ...themes : ( ThemeRegistrationAny | string ) [ ]
68+ ) {
69+ const { isSpecialTheme } = await import ( 'shiki/core' ) ;
70+
71+ const missingThemes = themes . filter ( ( theme ) => {
72+ if ( isSpecialTheme ( theme ) ) return false ;
73+ try {
74+ highlighter . getTheme ( theme ) ;
75+ return false ;
76+ } catch {
77+ return true ;
78+ }
79+ } ) ;
80+
81+ if ( missingThemes . length > 0 ) await highlighter . loadTheme ( ...( missingThemes as never [ ] ) ) ;
82+ }
83+
84+ async function loadMissingLanguage (
85+ highlighter : HighlighterCore ,
86+ ...langs : ( LanguageRegistration | string ) [ ]
87+ ) {
88+ const { isSpecialLang } = await import ( 'shiki/core' ) ;
89+
90+ const missingLangs = langs . filter ( ( lang ) => {
91+ if ( isSpecialLang ( lang ) ) return false ;
92+ try {
93+ highlighter . getLanguage ( lang ) ;
94+ return false ;
95+ } catch {
96+ return true ;
97+ }
98+ } ) ;
99+
100+ if ( missingLangs . length > 0 ) await highlighter . loadLanguage ( ...( missingLangs as never [ ] ) ) ;
101+ }
102+
64103export function hastToJsx ( hast : Root , options ?: Partial < ToJsxOptions > ) {
65104 return toJsxRuntime ( hast , {
66105 jsx,
@@ -79,13 +118,16 @@ export function hastToJsx(hast: Root, options?: Partial<ToJsxOptions>) {
79118 */
80119export async function getHighlighter (
81120 config : ResolvedShikiConfig ,
82- options : Omit < BundledHighlighterOptions < BundledLanguage , BundledTheme > , 'engine' | 'langAlias' > ,
121+ options ?: {
122+ langs ?: ( BundledLanguage | LanguageRegistration ) [ ] ;
123+ themes ?: ( BundledTheme | ThemeRegistrationAny ) [ ] ;
124+ } ,
83125) {
84126 const highlighter = await config . createHighlighter ( ) ;
85127
86128 await Promise . all ( [
87- highlighter . loadLanguage ( ...( options . langs as never [ ] ) ) ,
88- highlighter . loadTheme ( ...( options . themes as never [ ] ) ) ,
129+ options ?. langs && loadMissingLanguage ( highlighter , ...options . langs ) ,
130+ options ?. themes && loadMissingTheme ( highlighter , ...options . themes ) ,
89131 ] ) ;
90132
91133 return highlighter ;
0 commit comments