🔨
Vue3 + Vite で Monaco Editor の Web Worker を設定する
はじめに
monaco-editorをインストールし、monacoEditor.editor.create()
を行うとエディターがレンダリングされます。
一見正しいように見えますが、コンソールを開くと Web Worker に関するエラーが発生しています。
今回は Vue + Viteの構成における Web Worker の設定方法をまとめます。
サンプルコードに関して
今回のプロジェクトは create-vue を利用して作成されることを想定しています。
作成直後のプロジェクトに Monaco Editor に関する以下の変更を加えたソースコードに対して変更を加える想定でサンプルコードを記載します。
- monaco-editor のインストール
- 任意のSFCにおいて、
monacoEditor.editor.create()
を実行する
エラーの詳細
発生するエラーは以下のようなものです。
chunk-2APFOYHI.js?v=62e343f8:40838 Could not create web worker(s). Falling back to loading web worker code in main thread, which might cause UI freezes. Please see https://github.com/microsoft/monaco-editor#faq
logOnceWebWorkerWarning @ chunk-2APFOYHI.js?v=62e343f8:40838
Show 1 more frame
Show less
chunk-2APFOYHI.js?v=62e343f8:40840 You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker
logOnceWebWorkerWarning @ chunk-2APFOYHI.js?v=62e343f8:40840
Show 1 more frame
Show less
chunk-2APFOYHI.js?v=62e343f8:9832 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'toUrl')
at _FileAccessImpl.toUri (chunk-2APFOYHI.js?v=62e343f8:9832:40)
at _FileAccessImpl.asBrowserUri (chunk-2APFOYHI.js?v=62e343f8:9788:26)
at chunk-2APFOYHI.js?v=62e343f8:45899:32
at new Promise (<anonymous>)
at EditorSimpleWorker.$loadForeignModule (chunk-2APFOYHI.js?v=62e343f8:45891:12)
at chunk-2APFOYHI.js?v=62e343f8:83747:22
at async tsMode-HJUKZYNU.js?v=62e343f8:81:16
at async WorkerManager.getLanguageServiceWorker (tsMode-HJUKZYNU.js?v=62e343f8:87:20)
at async DiagnosticsAdapter._doValidate (tsMode-HJUKZYNU.js?v=62e343f8:368:20)
要約すると以下のようなことが書かれています。
- Monaco Editor が Web Worker を作成しようとしたが失敗し、処理がメインスレッドで実行されている
-
MonacoEnvironment.getWorkerUrl
またはMonacoEnvironment.getWorker
が未定義のため Web Worker を作成できない -
toUrl
関数の呼び出しでundefined
を読み取ろうとしている
対応
Viteのエントリーポイントのファイルに MonacoEnvironment.getWorker
を追加する処理を記述することで対応できます。
MonacoEnvironment.getWorker
を追加する
monaco-editor のGitHubリポジトリに React + Vite での実装例があります。
これを拝借します。
src/userWorker.ts
import * as monaco from 'monaco-editor';
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';
// @ts-ignore
self.MonacoEnvironment = {
getWorker(_: 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.languages.typescript.typescriptDefaults.setEagerModelSync(true);
Viteのエントリーポイントのファイルに追加する
先ほどの userWorker.ts
を main.ts
にインポートします。
side-effects import を利用し、モジュールごとインポートします。
src/main.ts
import "./assets/main.css";
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import "@/userWorker"
const app = createApp(App);
app.use(router);
app.mount("#app");
以上で対応は完了です。
コンソールに発生していた警告、エラーがすべて消えているはずです。
Discussion