Skip to content

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
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
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
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:
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
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
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):

js
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",
});

Released under the MIT License.