DEV Community 👩‍💻👨‍💻

Lawrence Chen
Lawrence Chen

Posted on • Updated on

Monaco Editor + Svelte Kit

Here's the gist:

<script lang="ts">
    import type monaco from 'monaco-editor';
    import { onMount } from 'svelte';
    import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
    import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
    import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
    import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
    import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';

    let divEl: HTMLDivElement = null;
    let editor: monaco.editor.IStandaloneCodeEditor;
    let Monaco;

    onMount(async () => {
        // @ts-ignore
        self.MonacoEnvironment = {
            getWorker: function (_moduleId: any, label: string) {
                if (label === 'json') {
                    return new jsonWorker();
                }
                if (label === 'css' || label === 'scss' || label === 'less') {
                    return new cssWorker();
                }
                if (label === 'html' || label === 'handlebars' || label === 'razor') {
                    return new htmlWorker();
                }
                if (label === 'typescript' || label === 'javascript') {
                    return new tsWorker();
                }
                return new editorWorker();
            }
        };

        Monaco = await import('monaco-editor');
        editor = Monaco.editor.create(divEl, {
            value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
            language: 'javascript'
        });

        return () => {
            editor.dispose();
        };
    });
</script>

<div bind:this={divEl} class="h-screen" />

Enter fullscreen mode Exit fullscreen mode

Key points

Top comments (8)

Collapse
dan1ve profile image
Daniel Veihelmann • Edited on

As it turned out, the code snippet of the original post (and the official documentation) causes subtle bugs Firefox (e.g. the highlighting in monaco's diff editor did not work).
I think this happens because Firefox needs an explicit type: module for registering the worker, otherwise an error happens: import declarations may only appear at top level of a module

Here is the version that works in Chrome + Firefox. Maybe you want to update your post accordingly, @lawrencecchen ?

onMount(async () => {
        // @ts-ignore
        self.MonacoEnvironment = {
            getWorker: function (workerId: string, label: string) {
                const getWorkerModule = (moduleUrl: string, label: string): Worker => {
                    // @ts-ignore
                    return new Worker(self.MonacoEnvironment.getWorkerUrl(moduleUrl), {
                        name: label,
                        type: 'module'
                    });
                };

                switch (label) {
                    case 'json':
                        return getWorkerModule('/monaco-editor/esm/vs/language/json/json.worker?worker', label);
                    case 'css':
                    case 'scss':
                    case 'less':
                        return getWorkerModule('/monaco-editor/esm/vs/language/css/css.worker?worker', label);
                    case 'html':
                    case 'handlebars':
                    case 'razor':
                        return getWorkerModule('/monaco-editor/esm/vs/language/html/html.worker?worker', label);
                    case 'typescript':
                    case 'javascript':
                        return getWorkerModule(
                            '/monaco-editor/esm/vs/language/typescript/ts.worker?worker',
                            label
                        );
                    default:
                        return getWorkerModule('/monaco-editor/esm/vs/editor/editor.worker?worker', label);
                }
            }
        };

        Monaco = await import('monaco-editor');
    });
Enter fullscreen mode Exit fullscreen mode

In addition, setting let Monaco: typeof monaco; will help TypeScript to figure stuff out ;-)

Collapse
samfilip profile image
Sam Filip

I've been struggling with monaco and svelte in electron. What does the ?worker at the end of the import statement do? It's required for me to build the app buit throws and Uncaught TypeError: Relative references must start with either "/", "./", or "../". in the console and doesn't run. Thanks for any additional insight.

Collapse
artemis69 profile image
Artemis

Nice

Collapse
dan1ve profile image
Daniel Veihelmann

Nice, thanks man! I can confirm this works for me :)

Collapse
alexvdvalk profile image
Alex v

I wrap my on onMount function with:

if (browser) {
onMount...
}

It usually helps with compile errors.

Is there any way to optimize this further? The compiled bundle is over 2MB.

Collapse
wtho profile image
wtho

Does not work for me :(
On npm run build && npm run preview I get the error:
self is not defined

Collapse
donpedro profile image
donpedro

This is great, thanks!

I had to add a min-height to the div so it wouldn't have a zero-height:

<div bind:this={divEl} class="h-screen" style="min-height: 300px;" />
Enter fullscreen mode Exit fullscreen mode
Collapse
donpedro profile image
donpedro

I just added an answer on StackOverflow which shows how to create a SvelteKit project from scratch around this code.

🌚 Browsing with dark mode makes you a better developer.

It's a scientific fact.