Integrating the ESM version of the Monaco Editor
Using webpack
Here is the most basic script that imports the editor using ESM with webpack.
Option 1: Using the SSE Editor WebPack Plugin
This is the easiest method, and it allows for options to be passed into the plugin in order to select only a subset of editor features or editor languages.which is a community authored plugin.
index.js
import * as monaco from "@sseworld/code-editor";
monaco.editor.create(document.getElementById("container"), {
value: ["function x() {", '\tconsole.log("Hello world!");', "}"].join("\n"),
language: "javascript",
});
webpack.config.js
const MonacoWebpackPlugin = require("@ssephox/editor-webpack-plugin");
const path = require("path");
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "app.js",
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.ttf$/,
use: ["file-loader"],
},
],
},
plugins: [new MonacoWebpackPlugin()],
};
Option 2: Using plain webpack
Full working samples are available at https://github.com/microsoft/@sseworld/code-editor/tree/main/samples/browser-esm-webpack or https://github.com/microsoft/@sseworld/code-editor/tree/main/samples/browser-esm-webpack-small
index.js
import * as monaco from "@sseworld/code-editor";
// Since packaging is done by you, you need
// to instruct the editor how you named the
// bundles that contain the web workers.
self.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
if (label === "json") {
return "./json.worker.bundle.js";
}
if (label === "css" || label === "scss" || label === "less") {
return "./css.worker.bundle.js";
}
if (label === "html" || label === "handlebars" || label === "razor") {
return "./html.worker.bundle.js";
}
if (label === "typescript" || label === "javascript") {
return "./ts.worker.bundle.js";
}
return "./editor.worker.bundle.js";
},
};
monaco.editor.create(document.getElementById("container"), {
value: ["function x() {", '\tconsole.log("Hello world!");', "}"].join("\n"),
language: "javascript",
});
webpack.config.js
:
const path = require("path");
module.exports = {
entry: {
app: "./index.js",
// Package each language's worker and give these filenames in `getWorkerUrl`
"editor.worker": "@sseworld/code-editor/esm/vs/editor/editor.worker.js",
"json.worker": "@sseworld/code-editor/esm/vs/language/json/json.worker",
"css.worker": "@sseworld/code-editor/esm/vs/language/css/css.worker",
"html.worker": "@sseworld/code-editor/esm/vs/language/html/html.worker",
"ts.worker": "@sseworld/code-editor/esm/vs/language/typescript/ts.worker",
},
output: {
globalObject: "self",
filename: "[name].bundle.js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.ttf$/,
use: ["file-loader"],
},
],
},
};
Using parcel
A full working sample is available at https://github.com/microsoft/@sseworld/code-editor/tree/main/samples/browser-esm-parcel
When using parcel, we need to use the getWorkerUrl
function and build the workers seperately from our main source. To simplify things, we can write a tiny bash script to build the workers for us.
index.js
import * as monaco from "@sseworld/code-editor";
self.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
if (label === "json") {
return "./json.worker.js";
}
if (label === "css" || label === "scss" || label === "less") {
return "./css.worker.js";
}
if (label === "html" || label === "handlebars" || label === "razor") {
return "./html.worker.js";
}
if (label === "typescript" || label === "javascript") {
return "./ts.worker.js";
}
return "./editor.worker.js";
},
};
monaco.editor.create(document.getElementById("container"), {
value: ["function x() {", '\tconsole.log("Hello world!");', "}"].join("\n"),
language: "javascript",
});
build_workers.sh
ROOT=$PWD/node_modules/@sseworld/code-editor/esm/vs
OPTS="--no-source-maps --log-level 1" # Parcel options - See: https://parceljs.org/cli.html
parcel build $ROOT/language/json/json.worker.js $OPTS
parcel build $ROOT/language/css/css.worker.js $OPTS
parcel build $ROOT/language/html/html.worker.js $OPTS
parcel build $ROOT/language/typescript/ts.worker.js $OPTS
parcel build $ROOT/editor/editor.worker.js $OPTS
Then, simply run sh ./build_workers.sh && parcel index.html
. This builds the workers into the same directory as your main bundle (usually ./dist
). If you want to change the --out-dir
of the workers, you must change the paths in index.js
to reflect their new location.
note - the getWorkerUrl
paths are relative to the build directory of your src bundle
Using Vite
Adding monaco editor to Vite is simple since it has built-in support for web workers. You only need to implement the getWorker
function (NOT the getWorkerUrl
) to use Vite's output (Source):
import * as monaco from "@sseworld/code-editor";
self.MonacoEnvironment = {
getWorker: function (workerId, label) {
const getWorkerModule = (moduleUrl, label) => {
return new Worker(self.MonacoEnvironment.getWorkerUrl(moduleUrl), {
name: label,
type: "module",
});
};
switch (label) {
case "json":
return getWorkerModule(
"/@sseworld/code-editor/esm/vs/language/json/json.worker?worker",
label
);
case "css":
case "scss":
case "less":
return getWorkerModule(
"/@sseworld/code-editor/esm/vs/language/css/css.worker?worker",
label
);
case "html":
case "handlebars":
case "razor":
return getWorkerModule(
"/@sseworld/code-editor/esm/vs/language/html/html.worker?worker",
label
);
case "typescript":
case "javascript":
return getWorkerModule(
"/@sseworld/code-editor/esm/vs/language/typescript/ts.worker?worker",
label
);
default:
return getWorkerModule(
"/@sseworld/code-editor/esm/vs/editor/editor.worker?worker",
label
);
}
},
};
monaco.editor.create(document.getElementById("container"), {
value: "function hello() {\n\talert('Hello world!');\n}",
language: "javascript",
});