Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run the Monaco editor from a CDN like cdnjs?

I am struggling a bit to find a minimal runnable example that just runs from the CDN, as opposed to the existing in-tree examples which mostly use local servers.


2 Answers

After Googling a bit I found https://jsfiddle.net/developit/bwgkr6uq/ which just works with unpkg.com, and so I mostly just adapted it to cdnjs, here is a single working HTML file:

index.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Monaco editor</title>
<link rel="stylesheet" data-name="vs/editor/editor.main" href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/editor/editor.main.min.css">
</head>
<body>
<div id="container" style="height:400px;border:1px solid black;"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs/loader.min.js"></script>
<script>
// require is provided by loader.min.js.
require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs' }});
require(["vs/editor/editor.main"], () => {
  monaco.editor.create(document.getElementById('container'), {
    value: `function x() {
  console.log("Hello world!");
}`,
    language: 'javascript',
    theme: 'vs-dark',
  });
});
</script>
</body>
</html>

Live: http://cirosantilli.com/web-cheat/monaco-editor.html

https://jsfiddle.net/developit/bwgkr6uq was also setting window.MonacoEnvironment to some hacky looking stuff, but things now seem to work without it.

Using less loader magic

Inspired from: how to use monaco editor without node.js

less-loader.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Monaco editor no loader</title>
<link rel="stylesheet" data-name="vs/editor/editor.main" href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs/editor/editor.main.min.css">
</head>
<body>
<div id="container" style="height:400px;border:1px solid black;"></div>
<script>var require = { paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs' } }</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs/loader.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs/editor/editor.main.nls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs/editor/editor.main.js"></script>
<script>
monaco.editor.create(document.getElementById('container'), {
  value: `function x() {
  console.log("Hello world!");
}`,
  language: 'javascript',
  theme: 'vs-dark',
});
</script>
</body>
</html>

Live: http://cirosantilli.com/web-cheat/monaco-editor-no-loader.html

Do it without the loader

Dedicated question: how to use monaco editor without node.js

I don't know how to do it entirely without the ugly loader thing.

By looking at the Network inspector tab in Chrome, we see that the following are loaded when opening the above HTML:

  • https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/loader.min.js
  • https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/editor/editor.main.js
  • https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/editor/editor.main.nls.js
  • https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/language/typescript/tsMode.js
  • https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/basic-languages/javascript/javascript.js

so we might try:

no-loader.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Monaco editor no loader TODO</title>
    <link rel="stylesheet" data-name="vs/editor/editor.main" href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/editor/editor.main.min.css">
</head>
<body>
<div id="container" style="height:400px;border:1px solid black;"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/editor/editor.main.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/editor/editor.main.nls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/language/typescript/tsMode.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/basic-languages/javascript/javascript.js"></script>
<script>
let editor = monaco.editor.create(document.getElementById('container'), {
    value: `function x() {
  console.log("Hello world!");
}`,
    language: 'javascript',
    theme: 'vs-dark',
});
</script>
</body>
</html>

but that fails because all .js files rely on a global define( function being present, which is defined in the loader.

If we add the loader back to the above, the editor seems to work, but a bunch of errors show on the JavaScript console, as it appears that some of those files are trying to dynamically load other files in the local filesystem.

Webpack integration

For anything remotely serious, this will be the way to go.

Since their library distribution is so complex, they have this Webpack plugin to help: https://github.com/microsoft/monaco-editor-webpack-plugin

Minimal example that seems to work fine, just note that there is a JavaScript error "Unexpected usage at EditorSimpleWorker.loadForeignModule" when running from file://: https://github.com/microsoft/monaco-editor-webpack-plugin/issues/157 so presumably the loader is still used for dynamic resources. Build and view:

npm install
npm run build
python3 -m http.server

and then visit http://localhost:8080

package.json

{
  "name": "monaco-editor-webpack-plugin-demo",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "css-loader": "5.2.4",
    "file-loader": "^6.2.0",
    "monaco-editor": "0.26.1",
    "monaco-editor-webpack-plugin": "4.1.1",
    "style-loader": "2.0.0",
    "webpack": "5.36.1",
    "webpack-cli": "4.6.0"
  },
  "scripts": {
    "build": "webpack",
    "start": "webpack serve --open --config webpack.config.js"
  }
}

index.js

import * as monaco from 'monaco-editor'
// or import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
// if shipping only a subset of the features & languages is desired

monaco.editor.create(document.getElementById('container'), {
  value: `function x() {
  console.log("Hello world!");
}`,
  language: 'javascript',
  theme: 'vs-dark',
});

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Monaco editor webpack plugin</title>
</head>
<body>
<div id="container" style="height:400px;border:1px solid black;"></div>
<script src="dist/index.js"></script>
</body>
</html>

This example could also be somewhat extracted from their upstream test under: https://github.com/microsoft/monaco-editor-webpack-plugin/tree/2459e4a023f9026ae5796a6e92f584c5d38e583e/test That test uses the package.json from the package itself however, and you have to extract some devDependencies from their into your own package.json, since they are not really devDependencies. Full run with:

git clone https://github.com/microsoft/monaco-editor-webpack-plugin
cd monaco-editor-webpack-plugin
npm install
npm run prepublishOnly
npm test
chromium test/dist/index.html

A mode: 'production' build is extremely slow, about 50 seconds on my Lenovo P51.

'development' is much faster, only 5s, so the problem is presumably the optimization of all the assets.

The plugin offers options to select a subset of features to speed things up and make the bundle smaller, some I've tried:

  • new MonacoWebpackPlugin({
      languages: ['javascript', 'typescript'],
    })
    

    Might save a few seconds, not very noticeable. As mentioned in the docs, typescript is mandatory when using javascript.

  • languages: [],: 22s

  • languages: [], features: [],: also 22s, features: [] made no difference

Get it to work with Next.js

Just when I thought the fight was over... but no! The above webpack setup did not work with Next.js 11, was failing as asked at: Monaco editor with nextjs but I managed to work around by using: https://github.com/suren-atoyan/monaco-react


Official Examples

  • https://github.com/microsoft/monaco-editor/tree/main/docs
  • https://github.com/Microsoft/monaco-editor-samples

Specifically, you probably want https://github.com/microsoft/monaco-editor/blob/main/docs/integrate-amd-cross.md

Example using Unpkg

<script type="text/javascript" src="https://unpkg.com/monaco-editor@latest/min/vs/loader.js"></script>
<script>
  require.config({ paths: { 'vs': 'https://unpkg.com/monaco-editor@latest/min/vs' }});

  // Before loading vs/editor/editor.main, define a global MonacoEnvironment that overwrites
  // the default worker url location (used when creating WebWorkers). The problem here is that
  // HTML5 does not allow cross-domain web workers, so we need to proxy the instantiation of
  // a web worker through a same-domain script
  window.MonacoEnvironment = {
    getWorkerUrl: function(workerId, label) {
      return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
        self.MonacoEnvironment = {
          baseUrl: 'https://unpkg.com/monaco-editor@latest/min/'
        };
        importScripts('https://unpkg.com/monaco-editor@latest/min/vs/base/worker/workerMain.js');`
      )}`;
    }
  };

  require(["vs/editor/editor.main"], function () {
    monaco.editor.create(document.querySelector('.monaco-editor-container'), {
      value: `function x() {
  console.log("Hello world!");
}`,
      language: 'javascript',
      theme: 'vs-dark',
    });
  });
</script>
like image 30
coolaj86 Avatar answered Oct 17 '22 15:10

coolaj86