Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always load when used in electron #48

Closed
vaemc opened this issue Dec 25, 2019 · 32 comments
Closed

Always load when used in electron #48

vaemc opened this issue Dec 25, 2019 · 32 comments

Comments

@vaemc
Copy link

vaemc commented Dec 25, 2019

Hi brother ,Always load when used in electron,Can't find the reason

image

@suren-atoyan
Copy link
Owner

Hello @vaemc, sure, we will try!

Could you please show the console?

@vaemc
Copy link
Author

vaemc commented Dec 28, 2019

I'm sorry to have kept you waiting for a long time and found that the console has no important information

image

@suren-atoyan
Copy link
Owner

Hello @vaemc. Sorry for the late answer.

Could you please provide me a minimal code snippet, so I can reproduce your problem.

I have already created an electron app with minimal config and used this library and it works very well so far. Also, we have "users" of this library who are using electron and there were no issues.

Thank you!

@LiamRiddell
Copy link

LiamRiddell commented Jan 7, 2020

@SurenAt93 I've got the same problem and I'm using this electron base.
Github

Steps:

  1. Include @monaco-editor/react in package.json
  2. Run yarn install
  3. Include the sample code
  4. Run yarn dev

Edit:
Looks like monaco.init() promise is never returned and it return removeEditor

@suren-atoyan
Copy link
Owner

Thank you @LiamRiddell. I will check it right now.

@suren-atoyan
Copy link
Owner

@LiamRiddell you are completely right, monaco.init() never ends. And the problem is that it can't load sources from CDN. The only thing is missing here is error handling during loading sources from cdn, which I will add in the next version.

@vaemc @LiamRiddell So, we have already faced this, and there is a solution for that, please read this discussion and let me know if there are still questions.

Thank you for your support!

@LiamRiddell
Copy link

@SurenAt93 Thanks for taking a look so fast!

I was close to solving last night then... since I changed loader config using monaco.config() but I was still using CDN.

Nonetheless this is by far cleanest implementation of Monaco, Thanks :)

@suren-atoyan
Copy link
Owner

Glad to hear that, will keep it open yet; to wait also @vaemc.

@LiamRiddell
Copy link

@SurenAt93 Just testing now. I still can't get this working. Here's the config below:

monaco.config({
  urls: {
    monacoLoader: '../node_modules/monaco-editor/min/vs/loader.js',
    monacoBase: '../node_modules/monaco-editor/min/vs'
  }
});

It finds the loader JS file and then just sits loading still. I've tried copying the file into the app

@LiamRiddell
Copy link

Okay, so I've managed to find the issue. It HAS to be an absolute data URI or it will fail to load. See below:

import Editor, { monaco } from '@monaco-editor/react';
const path = require('path')

// https://github.com/microsoft/monaco-editor-samples/blob/master/electron-amd-nodeIntegration/electron-index.html
function uriFromPath(_path) {
  let pathName = path.resolve(_path).replace(/\\/g, '/');

  if (pathName.length > 0 && pathName.charAt(0) !== '/') {
    pathName = `/${pathName}`;
  }
  return encodeURI(`file://${pathName}`);
}

monaco.config({
  urls: {
    monacoLoader: uriFromPath(
      path.join(__dirname, '../node_modules/monaco-editor/min/vs/loader.js')
    ),
    monacoBase: uriFromPath(
      path.join(__dirname, '../node_modules/monaco-editor/min/vs')
    )
  }
});

export default function MonacoEditorComponent() {
  const [isEditorReady, setIsEditorReady] = useState(false);
  const valueGetter = useRef(null);

  function handleEditorDidMount(_valueGetter) {
    setIsEditorReady(true);
    valueGetter.current = _valueGetter;
  }

  return (
    <>
      <Editor
        height="100%"
        width="100%"
        language="javascript"
        theme="dark"
        value="// write your code here"
        editorDidMount={handleEditorDidMount}
      />
    </>
  );
}

@suren-atoyan
Copy link
Owner

Ah, I see. Huh, it takes a long, but now everything is clear.

So, two things I need to do in the near future;

  1. Add a section about possible problems while using this package with electron.
  2. Handle error while loading monaco files from CDN or other places.

@LiamRiddell Again, thank you for your support.

@LiamRiddell
Copy link

@SurenAt93 I think your two action points are good idea.

I'm currently struggling to try to get underlying Monaco instance from Editor component. We can use the monaco.init() utility but then I have to handle the creation of the whole component.

Editor.js

// Original   
editorDidMount(editorRef.current.getValue.bind(editorRef.current), editorRef.current);
// Extended
editorDidMount(editorRef.current.getValue.bind(editorRef.current), monacoRef.current, editorRef.current);

The reason behind it is so people can register themes and languages without having to do lots of heavy lifting.

If you're happy I could make PR?

@suren-atoyan
Copy link
Owner

  1. It's not a right from a logical point of view. Let me explain; editorDidMount is in single editor instance, maximum it can give you should be editorInstance. It isn't allowed to give you access to monacoInstance, you shouldn't be able to change global things from inside of a single instance <- technically you can do it (I'll show it bellow) as you can do something with window object, but things should be clear, it's out of responsibilities of editorDidMount.
  2. We can use the monaco.init() utility but then I have to handle the creation of the whole component <- maybe there is a misunderstanding? You do not need to handle the creation of component. For example, if you need to do something with monaco instance:
import React from "react";
import ReactDOM from "react-dom";

import Editor, { monaco } from "@monaco-editor/react";

monaco
  .config({
    // ...
  })
  .init()
  .then(monacoInstance => {
    /* here is the instance of monaco, so you can use the `monaco.languages` or whatever you want */
    console.log("Log ::: Monaco ::: ", monacoInstance);
  })
  .catch(error =>
    console.error("An error occurred during initialization of Monaco: ", error)
  );

const App = _ => <Editor height="90vh" language="javascript" />;

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

or, if you really need to use it from inside of component, it's also possible:

import React, { useEffect } from "react";
import ReactDOM from "react-dom";

import Editor, { monaco } from "@monaco-editor/react";

const App = _ => {
  useEffect(_ => {
    monaco
      .init()
      .then(monacoInstance => {
        /* here is the instance of monaco, so you can use the `monaco.languages` or whatever you want */
        console.log("Log ::: Monaco ::: ", monacoInstance);
      });
  }, []);

  return <Editor height="90vh" language="javascript" />;
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
  1. Themes and languages are global things, they can be made separately;
import { monaco } from "@monaco-editor/react";

monaco
  .init()
  .then(monacoInstance => {
    monacoInstance.editor.defineTheme('my-super-theme', {
      // ...
    });
  });

The same for language creation.

Note that monaco.init() will work once and will return the same instance in further calls.

@LiamRiddell
Copy link

@SurenAt93 Yep, I've misunderstood the utility. Thanks for clarifying, I've fixed it in my code too :)

@5cript
Copy link

5cript commented Jan 13, 2020

LiamRiddell's Solution #48 (comment) only works (for me), if I disable webSecurity in Electron, or it refuses to load the resources.
"Not allowed to load local resource".
I dont know how to feel about that.

mainWindow = new BrowserWindow({
	webPreferences: {
		webSecurity: false, // <- here
		nodeIntegration: true,
	},
})

@suren-atoyan
Copy link
Owner

@5cript are you trying to load Monaco from node_modules? Could you please provide the part of your code where you configure the Monaco?

@5cript
Copy link

5cript commented Jan 14, 2020

electron version 7.1.8 btw.

This is basically just copy paste.

// in global scope of one of my files.
import Editor, { monaco } from '@monaco-editor/react';

function uriFromPath(_path) {
    let pathName = path.resolve(_path).replace(/\\/g, '/');

    if (pathName.length > 0 && pathName.charAt(0) !== '/') {
        pathName = `/${pathName}`;
    }
    return encodeURI(`file://${pathName}`);
}

monaco.config({
    urls: {
        monacoLoader: uriFromPath(
            // same result from node_modules. Does not do anything, if i get the path wrong.
            path.join(__dirname, '../public/vs/loader.js')
        ),
        monacoBase: uriFromPath(
            path.join(__dirname, '../public/vs')
        )
    }
});

I never tried deploying the application yet.

@suren-atoyan
Copy link
Owner

@5cript one more question: do you also use this boilerplate? I just want to reproduce your case in my side.

@5cript
Copy link

5cript commented Jan 14, 2020

@vaemc
Copy link
Author

vaemc commented Jan 19, 2020

Awesome @LiamRiddell method solved my problem! thank you all @suren-atoyan

@richard-livingston
Copy link

I was having the same problem and managed to fix it while also keeping webSecurity: true.

The issue is that Monaco uses an AMD loader, and Electron uses CJS (when nodeIntegration: true).

I found that the easiest solution is to delete the references to all CJS related properties from the window before attempting to use Monaco:

delete window.require;
delete window.exports;
delete window.module;

// Optionally change the loader paths, it will work regardless
// In my case I wanted to copy these files into a local directory
monaco.config({
    urls: {
        monacoLoader: '/monaco-editor/min/vs/loader.js',
        monacoBase: '/monaco-editor/min/vs'
    }
});

const App = <Editor />

I found this solution by chance on the Electron website: https://www.electronjs.org/docs/faq#i-can-not-use-jqueryrequirejsmeteorangularjs-in-electron

@suren-atoyan
Copy link
Owner

Thank you for reply @richard-livingston. The problem is that the behavior is different with different electron setups. Some of them work without a hitch, some of them work with additional config (with changed urls), some of them give strange errors. I am wondering what setup/boilerplate do you use? And BTW, @5cript does it help you?

@5cript
Copy link

5cript commented Feb 11, 2020

I have to test that later when I have the chance, but I'm confident that it will.

@richard-livingston
Copy link

Hi @suren-atoyan

I am using create-react-app, electron ({webSecurity: true, nodeIntegration: true}) rescripts (allows rewrite of webpack.config so that node built-ins are not "empty").

I followed this guide: https://www.codementor.io/@randyfindley/how-to-build-an-electron-app-using-create-react-app-and-electron-builder-ss1k0sfer

@5cript good luck.

@siva-kranthi
Copy link

siva-kranthi commented Apr 21, 2020

I was having the same problem and managed to fix it while also keeping webSecurity: true.

The issue is that Monaco uses an AMD loader, and Electron uses CJS (when nodeIntegration: true).

I found that the easiest solution is to delete the references to all CJS related properties from the window before attempting to use Monaco:

delete window.require;
delete window.exports;
delete window.module;

// Optionally change the loader paths, it will work regardless
// In my case I wanted to copy these files into a local directory
monaco.config({
    urls: {
        monacoLoader: '/monaco-editor/min/vs/loader.js',
        monacoBase: '/monaco-editor/min/vs'
    }
});

const App = <Editor />

I found this solution by chance on the Electron website: https://www.electronjs.org/docs/faq#i-can-not-use-jqueryrequirejsmeteorangularjs-in-electron

Worked for me with electron-webpack. But for local files loading, I still disabled webSecurity: false. It was not allowing without that

@siva-kranthi
Copy link

With electron-webpack it is working fine in dev mode & production on windows. On Ubuntu, it is not working in Dev mode. But working in prod mode

monaco.config({ paths: { vs: path.join(__static, "/vs") } });

The difference what I found in dev mode was in windows it is trying to load with file:/// URL. But on ubuntu, it is trying to load with localhost:9080. Hence it was failing.

The output of path.join(__static, "/vs") is pointing to absolute paths in both Ubuntu& windows

ubuntu - /home/user/workspace/Git/bla/static/vs
windows - D:\Git\bla\static\vs

I don't know why in ubuntu monaco is trying to load the resources using localhost server instead of file:/// URI

@wangzongming
Copy link

wangzongming commented Jul 15, 2020

TypeError: Cannot set property 'configScriptSrc' of undefined

Did you ever make that mistake? In the manner of @LiamRiddell

use: electron + typescript

const path: any = require('path')
function uriFromPath(_path: string) {
    let pathName: string = path.resolve(_path).replace(/\\/g, '/');

    if (pathName.length > 0 && pathName.charAt(0) !== '/') {
        pathName = `/${pathName}`;
    }
    return encodeURI(`file://${pathName}`);
}
const monacoConfig: (obj: any) => any = monaco.config; 
monacoConfig({
    urls: {
        monacoLoader: uriFromPath(
            path.join(__dirname, '../../../node_modules/monaco-editor/min/vs/loader.js')
        ),
        monacoBase: uriFromPath(
            path.join(__dirname, '../../../node_modules/monaco-editor/min/vs')
        )
    }
});

@suren-atoyan
Copy link
Owner

TypeError: Cannot set property 'configScriptSrc' of undefined

Did you ever make that mistake? In the manner of @LiamRiddell

use: electron + typescript

const path: any = require('path')
function uriFromPath(_path: string) {
    let pathName: string = path.resolve(_path).replace(/\\/g, '/');

    if (pathName.length > 0 && pathName.charAt(0) !== '/') {
        pathName = `/${pathName}`;
    }
    return encodeURI(`file://${pathName}`);
}
const monacoConfig: (obj: any) => any = monaco.config; 
monacoConfig({
    urls: {
        monacoLoader: uriFromPath(
            path.join(__dirname, '../../../node_modules/monaco-editor/min/vs/loader.js')
        ),
        monacoBase: uriFromPath(
            path.join(__dirname, '../../../node_modules/monaco-editor/min/vs')
        )
    }
});

hmm, strange. BTW; monaco.config({ urls: ... }) is deprecated, use monaco.config({ paths: ... }) instead. It's described here

@wangzongming
Copy link

TypeError: Cannot set property 'configScriptSrc' of undefined
Did you ever make that mistake? In the manner of @LiamRiddell
use: electron + typescript

const path: any = require('path')
function uriFromPath(_path: string) {
    let pathName: string = path.resolve(_path).replace(/\\/g, '/');

    if (pathName.length > 0 && pathName.charAt(0) !== '/') {
        pathName = `/${pathName}`;
    }
    return encodeURI(`file://${pathName}`);
}
const monacoConfig: (obj: any) => any = monaco.config; 
monacoConfig({
    urls: {
        monacoLoader: uriFromPath(
            path.join(__dirname, '../../../node_modules/monaco-editor/min/vs/loader.js')
        ),
        monacoBase: uriFromPath(
            path.join(__dirname, '../../../node_modules/monaco-editor/min/vs')
        )
    }
});

hmm, strange. BTW; monaco.config({ urls: ... }) is deprecated, use monaco.config({ paths: ... }) instead. It's described here

thank you .
image

I don't know why this is undefind

@suren-atoyan
Copy link
Owner

TypeError: Cannot set property 'configScriptSrc' of undefined
Did you ever make that mistake? In the manner of @LiamRiddell
use: electron + typescript

const path: any = require('path')
function uriFromPath(_path: string) {
    let pathName: string = path.resolve(_path).replace(/\\/g, '/');

    if (pathName.length > 0 && pathName.charAt(0) !== '/') {
        pathName = `/${pathName}`;
    }
    return encodeURI(`file://${pathName}`);
}
const monacoConfig: (obj: any) => any = monaco.config; 
monacoConfig({
    urls: {
        monacoLoader: uriFromPath(
            path.join(__dirname, '../../../node_modules/monaco-editor/min/vs/loader.js')
        ),
        monacoBase: uriFromPath(
            path.join(__dirname, '../../../node_modules/monaco-editor/min/vs')
        )
    }
});

hmm, strange. BTW; monaco.config({ urls: ... }) is deprecated, use monaco.config({ paths: ... }) instead. It's described here

thank you .
image

I don't know why this is undefind

could you please change the version of the library?

@wangzongming
Copy link

TypeError: Cannot set property 'configScriptSrc' of undefined
Did you ever make that mistake? In the manner of @LiamRiddell
use: electron + typescript

const path: any = require('path')
function uriFromPath(_path: string) {
    let pathName: string = path.resolve(_path).replace(/\\/g, '/');

    if (pathName.length > 0 && pathName.charAt(0) !== '/') {
        pathName = `/${pathName}`;
    }
    return encodeURI(`file://${pathName}`);
}
const monacoConfig: (obj: any) => any = monaco.config; 
monacoConfig({
    urls: {
        monacoLoader: uriFromPath(
            path.join(__dirname, '../../../node_modules/monaco-editor/min/vs/loader.js')
        ),
        monacoBase: uriFromPath(
            path.join(__dirname, '../../../node_modules/monaco-editor/min/vs')
        )
    }
});

hmm, strange. BTW; monaco.config({ urls: ... }) is deprecated, use monaco.config({ paths: ... }) instead. It's described here

thank you .
image
I don't know why this is undefind

could you please change the version of the library?

I'm using @monaco-editor/react@3.4.1.
Ha ha, I've used the monaco-editor as an alternative and I'm not going to struggle. Thank you for your reply.

@ShallowGreen
Copy link

dev env

I am using absolute URL node_modules/monaco-editor/min/vs/ to loader dependence,
But how can I loader dependence in application after build, which used in other computor
is there a way to build the dependence into application?

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants