Monaco-editor: Monaco Editor in Electron (optional: with AngularJS)

Created on 3 Aug 2016  路  24Comments  路  Source: microsoft/monaco-editor

Hi,

I want to get the monaco editor up and running in Electron. I found the electron examples for monaco but they won't work in my app.

All I get are errors like:

"loader.js:1817 Uncaught Error: Unrecognized require call"

"angular.js:13920 Error: Check dependency list! Synchronous require cannot resolve module 'fs'. This is the first mention of this module! at e.synchronousRequire (loader.js:1063)"

All is working fine if the loader.js of monaco isn't included as a script file.

The error appears in one of my AngularJS services at the beginning of the file when I try to include the "fs" module:

var fs = require("fs");

But as said: without the monaco loader.js file included this is working fine.

Any suggestions how I can fix that? I want to include the monaco editor in my Electron app and in best cases access it in my AngularJS directives and/or services. Would simplify my app compared to the ACE editor.

Thanks for help.

integration

Most helpful comment

Hello all,

We have built an Angular Component that wraps around the Monaco Editor. It uses a "webview" within Electron to sandbox the amdRequires so that they don't interfere with the requires inside Electron. The component also can be run in the browser without issue as well. When you use it you don't have to worry about this and can just do a:

<td-code-editor theme="vs" language="sql" flex style="height: 200px"></td-code-editor>

Feel free to check it out:
https://github.com/Teradata/covalent-code-editor

Also checkout Covalent Electron to see the editor running in action:
https://github.com/Teradata/covalent-electron

All 24 comments

@alexandrudima It is for a new Electron application. The Problem is, that this demo is a bit short.

For example it states, that you should load all node.js modules before the loader.js comes into play. When a framework like AngularJS is involved and other JavaScript modules are loaded _after_ the loader.js, then it is not working.

And that's my problem: The AngularJS code is loaded after the Electron pages are loaded and then the loader.js is failing.

Haven't found a working example of Electron, Monaco and some framework like AngularJS yet.

@alexandrudima Hi, I faced with the same issue. It seems monaco override electron require and dynamic require not working anymore. It's really showstopper to use monaco in my application. Is there any workaround to not change original require?

I haven't tried it, but you could do the following:

  • save var nodeRequire = global.require
  • load up loader.js
  • save var amdRequire = global.require.
  • restore global.require = nodeRequire
  • use amdRequire(['vs/editor/editor.main'], function() { ... }) to load the editor.

Alternatively, you could load the editor "sync" and also restore node's require: https://github.com/Microsoft/monaco-editor-samples/blob/master/sample-sync/index.html

If these tricks don't work, then we need to change something on our side to avoid the "require" conflict

Many thanks @alexandrudima. It works for me. It would be great if you could fix it in future.

Late reply, sorry. Thanks @alexandrudima. This works for me, too.

But maybe it is a good idea to fix this in future releases? This loader thing breaks a whole lot of projects where Monaco would be a good fit. Maybe not everyone will search for a solution because there are many other web-based code editors out there.

@fdeitelhoff I agree. I want to change the packaging of the editor to remove the external exposure of the AMD modules that make up the editor. (similar to how jquery does it -- they use AMD in source form and then they bundle and manage to remove the AMD modules -- e.g. https://github.com/jquery/jquery/blob/master/src/attributes.js).

Then, the responsibility of creating the script tag will move to the integrators instead of being to an AMD loader.

Already on this path, the upcoming 0.7.x contains some good refactorings that reduce the number of .js files we bundle in the core of the editor to (editor.main, workerMain, and the nls files), and I hope this is something that will make it easier to go down this path.

TL;DR not quite there yet, but on the path. All other issues tagged with integration would basically be fixed by this... Although it will be a breaking change in how to load the editor.

@alexandrudima That sounds really good! Looking forward to the changes and to try them out. :)

I am getting this error while loading monaco sync code from https://github.com/Microsoft/monaco-editor-samples/blob/master/sample-sync/index.html

I am using

<link rel="stylesheet" data-name="vs/editor/editor.main" href="lib/monaco-editor/min/vs/editor/editor.main.css">

<script>var require = { paths: { 'vs': './lib/monaco-editor/min/vs' } };</script>
<script src="lib/monaco-editor/min/vs/loader.js"></script>
<script src="lib/monaco-editor/min/vs/editor/editor.main.nls.js"></script>
<script src="lib/monaco-editor/min/vs/editor/editor.main.js"></script>

the error is e is not a function from _amdgloballoader
here the some code from where error is raised

var _amdLoaderGlobal = this, define, AMDLoader;
!function(e) {
    function t() {
        return !!("undefined" != typeof navigator && navigator.userAgent && navigator.userAgent.indexOf("Windows") >= 0) || "undefined" != typeof process && "win32" === process.platform
    }
    function n() {
        return O ? A.performance.now() : Date.now()
    }
    function o() {
        v = "function" == typeof A.importScripts,
        m = "undefined" != typeof process && "undefined" != typeof process.versions && "undefined" != typeof process.versions.electron && "renderer" === process.type,
        M = "undefined" != typeof process && "undefined" != typeof process.versions && "undefined" != typeof process.versions.electron && "browser" === process.type,
        E = "undefined" != typeof module && !!module.exports,
        P = new b(v ? new k : E ? new I : new L),
        S = new y(P),
        P.setModuleManager(S)
    }
    function r() {
        E || (A.console || (A.console = {}),
        A.console.log || (A.console.log = function() {}
        ),
        A.console.warn || (A.console.warn = A.console.log),
        A.console.error || (A.console.error = A.console.log))
    }
    function i() {
        v || E || (window.onload = function() {
            var e, t, n, o = document.getElementsByTagName("script");
            for (e = 0,
            t = o.length; e < t && !(n = o[e].getAttribute("data-main")); e++)
                ;
            n && S.defineModule(u.generateAnonymousModule(), [n], null , null , null , new c(new d,""))
        }
        )
    }
    function s() {
        if (o(),
        r(),
        i(),
        E) {
            var e = A.require || require
              , t = function(t) {
                S.getRecorder().record(p.NodeBeginNativeRequire, t);
                var n = e(t); // here e is not a function

@fdeitelhoff @kieferrm Is there a working example to make Electron and monaco-editor woking together? I still encounter some errors.

@naumanumer this is a wrong example, if you try to replace the subpath min to dev, you can see the un-uglified files, the value of e variable is the require you assigned in var require { paths: { 'vs': './lib/monaco-editor/min/vs' } };, so it's an object, not a function.

@PinkyJie So, have you any idea how to load monaco synchronously

@naumanumer No, I can't make it work whatever in synchronous or asynchronous mode.

@alexandrudima your solution for restoring native require again helped me tremendously. I'd previously given up trying to figure it out before stumbling on to this issue. I know you plan to eventually update monaco to not need this work around -- but until then would you accept a PR to the relevant example with your notes? Something like:

  <script>
    // Monaco uses a custom amd loader that over-rides node's require. 
    // Keep a reference to node's require so we can restore it after executing the amd loader file.
    var nodeRequire = global.require;
  </script>
  <script src="../../node_modules/monaco-editor/min/vs/loader.js"></script>
  <script>
    // Save Monaco's amd require and restore Node's require
    var amdRequire = global.require;
    global.require = nodeRequire;
       // ... remainder of example below
  </script>

Thank you for the work on this project!

Hi all, even with described fix, I get "define is not defined" when used from Electron?

I have used "sample-electron" code.

@mi5ha Can you post some sample code? I've done it exactly like the samples above and it's working fine in Electron for me.

This is error:

screen shot 2016-11-18 at 10 32 20

It happens in this part of Monaco code:

define("vs/css",new s(e))}()}(c||(c={}));

And here is the code that produce it, its entered in Angular state template:

<!-- ELECTRON MONACO EDITOR -->
<div id="container" style="width:500px;height:300px;border:1px solid #ccc"></div>

<script>
   // require node modules before loader.js comes in
   var path = require('path');
</script>
<script>
   // Monaco uses a custom amd loader that over-rides node's require.
   // Keep a reference to node's require so we can restore it after executing the amd loader file.
   var nodeRequire = global.require;
</script>
<script src="assets/angular/other_components/monaco-editor/min/vs/loader.js"></script>
<script>
   // Save Monaco's amd require and restore Node's require
   var amdRequire = global.require;
   global.require = nodeRequire;
</script>
<script>
   function uriFromPath(_path) {
       var pathName = path.resolve(_path).replace(/\\/g, '/');
       if (pathName.length > 0 && pathName.charAt(0) !== '/') {
           pathName = '/' + pathName;
       }
       return encodeURI('file://' + pathName);
   }

   amdRequire.config({
       baseUrl: uriFromPath(path.join(__dirname, 'assets/angular/other_components/monaco-editor/min'))
   });

   // workaround monaco-css not understanding the environment
   self.module = undefined;
   // workaround monaco-typescript not understanding the environment
   self.process.browser = true;
   amdRequire(['vs/editor/editor.main'], function() {
       var editor = monaco.editor.create(document.getElementById('container'), {
           value: [
               'function x() {',
               '\tconsole.log("Hello world!");',
               '}'
           ].join('\n'),
           language: 'javascript'
       });
   });
</script>

Ok, I solved the problem. I divided the previous code between Electron index.html and Angular state template.

I only left this in Angular state template:

window.amdRequire(['vs/editor/editor.main'], function() {
   var editor = monaco.editor.create(document.getElementById('container'), {
       value: [
           'function x() {',
           '\tconsole.log("Hello world!");',
           '}'
       ].join('\n'),
       language: 'javascript'
   });
});

Everything else is moved to index.html

@alexandrudima When Electron is loading files from disk via file://, the loader.js and amdRequire hacks work.

But when Electron is loading files from a local web server on http://localhost:3000 it does not.

I am configuring it with

amdRequire.config({
    baseUrl: 'http://localhost:3000'
});

And then calling amdRequire(['vs/editor/editor.main'], function() { ... gives me an error:

screenshot 2017-04-09 16 53 31

Even though loading http://localhost:3000/vs/editor/editor.main.js in a browser does return the file.

What can I do for loader.js to work with http:// as well, not just file://?

Thank you!

I am also trying to get Monaco running with electron, but for some reason it breaks whenever it tries to load the loader.js file. I've tried to implement the way mentioned above of using the AMD loader but still doesn't work. What can I try to get it to work?

Hello all,

We have built an Angular Component that wraps around the Monaco Editor. It uses a "webview" within Electron to sandbox the amdRequires so that they don't interfere with the requires inside Electron. The component also can be run in the browser without issue as well. When you use it you don't have to worry about this and can just do a:

<td-code-editor theme="vs" language="sql" flex style="height: 200px"></td-code-editor>

Feel free to check it out:
https://github.com/Teradata/covalent-code-editor

Also checkout Covalent Electron to see the editor running in action:
https://github.com/Teradata/covalent-electron

Sad to see this problem still relevant in 2019. Monaco loader.js stops working completely as soon as SystemJS is imported into the app. Tried dozens of workarounds and still nothing

Was this page helpful?
0 / 5 - 0 ratings

Related issues

gynet picture gynet  路  22Comments

akosyakov picture akosyakov  路  16Comments

CitrusFruits picture CitrusFruits  路  15Comments

spahnke picture spahnke  路  26Comments

tylerlong picture tylerlong  路  17Comments