@@ -5,7 +5,11 @@ import type {
55 NegatedEditorFeature ,
66 IFeatureDefinition ,
77} from 'monaco-editor/esm/metadata.js'
8+ import type { BundledLanguage , BundledTheme } from 'shiki'
9+
810import { features , languages } from 'monaco-editor/esm/metadata.js'
11+ import { codegen } from 'shiki-codegen'
12+ import { bundledLanguages } from 'shiki'
913
1014type Options = {
1115 /**
@@ -59,6 +63,38 @@ type Options = {
5963 * ```
6064 */
6165 features ?: Array < EditorFeature | NegatedEditorFeature >
66+
67+ /**
68+ * Shiki configuration options.
69+ * @type {{ themes?: BundledTheme[]; langs?: BundledLanguage[] } }
70+ */
71+ shiki ?: {
72+ /**
73+ * Lanuages to include for Shiki syntax highlighting.
74+ *
75+ * @type {BundledLanguage[] }
76+ * @defaultValue Languages shipped with monaco-editor according to the `languages` option.
77+ * @example
78+ * ```ts
79+ * // Only include JavaScript and JSON support
80+ * langs: ['javascript', 'json']
81+ * ```
82+ */
83+ langs ?: BundledLanguage [ ]
84+
85+ /**
86+ * Themes to include for Shiki syntax highlighting.
87+ *
88+ * @type {BundledTheme[] }
89+ * @defaultValue ['catppuccin-latte', 'catppuccin-mocha']
90+ * @example
91+ * ```ts
92+ * // Include the 'nord' theme
93+ * themes: ['nord']
94+ * ```
95+ */
96+ themes ?: BundledTheme [ ]
97+ }
6298}
6399
64100// Some languages share the same worker; define aliases here
@@ -70,12 +106,21 @@ const WORKER_ALIASES: Record<string, string> = {
70106 razor : 'html' ,
71107}
72108
73- const VIRTUAL_MODULE_ID = '\0virtual:monaco-editor'
109+ const VIRTUAL_MODULE_MONACO_ID = '\0virtual:monaco-editor'
110+ const VIRTUAL_MODULE_SHIKI_ID = '\0virtual:shiki'
74111
75112// Generate import statements for Monaco Editor feature entries
76113function generateImports ( entries : string | string [ ] ) : string [ ] {
77114 const entryArray = Array . isArray ( entries ) ? entries : [ entries ]
78- return entryArray . map ( ( entry ) => `import 'monaco-editor/esm/${ entry } '` )
115+ return entryArray . map ( ( entry ) => {
116+ // Start from monaco v0.55.1, languages with monaco.contribution needed to be reexported
117+ // https://github.com/microsoft/monaco-editor/issues/5133
118+ if ( entry . endsWith ( 'monaco.contribution' ) ) {
119+ const lang = entry . split ( '/' ) . at ( - 2 ) !
120+ return `export * as ${ lang } from 'monaco-editor/esm/${ entry } '`
121+ }
122+ return `import 'monaco-editor/esm/${ entry } '`
123+ } )
79124}
80125
81126// Resolve which editor features to include based on user options
@@ -161,73 +206,86 @@ function generateWorkerCode(
161206 ]
162207}
163208
164- export default function ( options ?: Options ) : Plugin {
209+ export default function plugin ( options ?: Options ) : Plugin {
165210 return {
166211 name : 'vite-plugin-monaco' ,
167212 enforce : 'pre' ,
168213
169214 resolveId ( id ) {
170215 if ( id === 'monaco-editor' ) {
171- return VIRTUAL_MODULE_ID
216+ return VIRTUAL_MODULE_MONACO_ID
217+ } else if ( id === 'shiki' ) {
218+ return VIRTUAL_MODULE_SHIKI_ID
172219 }
173220 } ,
174221
175222 load ( id ) {
176- if ( id !== VIRTUAL_MODULE_ID ) {
177- return
178- }
223+ if ( id === VIRTUAL_MODULE_MONACO_ID ) {
224+ const languagesDict = Object . fromEntries (
225+ languages . map ( ( lang ) => [ lang . label , lang ] ) ,
226+ )
179227
180- const languagesDict = Object . fromEntries (
181- languages . map ( ( lang ) => [ lang . label , lang ] ) ,
182- )
228+ const featuresDict = Object . fromEntries (
229+ features . map ( ( feat ) => [ feat . label , feat ] ) ,
230+ )
183231
184- const featuresDict = Object . fromEntries (
185- features . map ( ( feat ) => [ feat . label , feat ] ) ,
186- )
232+ const featuresIds = resolveFeatures (
233+ options ?. features ,
234+ Object . keys ( featuresDict ) as EditorFeature [ ] ,
235+ )
187236
188- const featuresIds = resolveFeatures (
189- options ?. features ,
190- Object . keys ( featuresDict ) as EditorFeature [ ] ,
191- )
237+ const featureImports = featuresIds . flatMap ( ( featureId ) => {
238+ const feature = featuresDict [ featureId ]
239+ if ( ! feature ?. entry ) {
240+ return [ ]
241+ }
242+ return generateImports ( feature . entry )
243+ } )
192244
193- const featureImports = featuresIds . flatMap ( ( featureId ) => {
194- const feature = featuresDict [ featureId ]
195- if ( ! feature ?. entry ) {
196- return [ ]
197- }
198- return generateImports ( feature . entry )
199- } )
245+ const languageIds =
246+ options ?. languages || ( Object . keys ( languagesDict ) as EditorLanguage [ ] )
200247
201- const languageIds =
202- options ?. languages || ( Object . keys ( languagesDict ) as EditorLanguage [ ] )
248+ const languageImports = languageIds . flatMap ( ( langId ) => {
249+ const lang = languagesDict [ langId ]
250+ if ( ! lang ?. entry ) {
251+ return [ ]
252+ }
253+ return generateImports ( lang . entry )
254+ } )
203255
204- const languageImports = languageIds . flatMap ( ( langId ) => {
205- const lang = languagesDict [ langId ]
206- if ( ! lang ?. entry ) {
207- return [ ]
208- }
209- return generateImports ( lang . entry )
210- } )
256+ const customLanguageImports = ( options ?. customLanguages || [ ] ) . map (
257+ ( { entry } ) => `import '${ entry } '` ,
258+ )
211259
212- const customLanguageImports = ( options ?. customLanguages || [ ] ) . map (
213- ( { entry } ) => `import '${ entry } '` ,
214- )
260+ const workerCode = generateWorkerCode (
261+ languageIds ,
262+ languagesDict ,
263+ options ?. customLanguages ,
264+ )
215265
216- const workerCode = generateWorkerCode (
217- languageIds ,
218- languagesDict ,
219- options ?. customLanguages ,
220- )
266+ return [
267+ "import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'" ,
268+ ...featureImports ,
269+ ...languageImports ,
270+ ...customLanguageImports ,
271+ ...workerCode ,
272+ "export * from 'monaco-editor/esm/vs/editor/editor.api'" ,
273+ 'export default monaco' ,
274+ ] . join ( '\n' )
275+ } else if ( id === VIRTUAL_MODULE_SHIKI_ID ) {
276+ const languageIds =
277+ options ?. shiki ?. langs ||
278+ ( options ?. languages || languages . map ( ( lang ) => lang . label ) )
279+ // Only include languages that are bundled with shiki
280+ . filter ( ( lang ) : lang is BundledLanguage => lang in bundledLanguages )
221281
222- return [
223- "import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'" ,
224- ...featureImports ,
225- ...languageImports ,
226- ...customLanguageImports ,
227- ...workerCode ,
228- "export * from 'monaco-editor/esm/vs/editor/editor.api'" ,
229- 'export default monaco' ,
230- ] . join ( '\n' )
282+ return codegen ( {
283+ themes : options ?. shiki ?. themes || [ 'catppuccin-latte' , 'catppuccin-mocha' ] ,
284+ engine : 'javascript' ,
285+ langs : languageIds ,
286+ typescript : false ,
287+ } )
288+ }
231289 } ,
232290 }
233291}
0 commit comments