Always load when used in electron
vaemc opened this issue · comments
Hello @vaemc, sure, we will try!
Could you please show the console?
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!
@SurenAt93 I've got the same problem and I'm using this electron base.
Github
Steps:
- Include @monaco-editor/react in package.json
- Run yarn install
- Include the sample code
- Run yarn dev
Edit:
Looks like monaco.init()
promise is never returned and it return removeEditor
Thank you @LiamRiddell. I will check it right now.
@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!
@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 :)
Glad to hear that, will keep it open yet; to wait also @vaemc.
@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
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}
/>
</>
);
}
Ah, I see. Huh, it takes a long, but now everything is clear.
So, two things I need to do in the near future;
- Add a section about possible problems while using this package with electron.
- Handle error while loading monaco files from CDN or other places.
@LiamRiddell Again, thank you for your support.
@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?
- 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.
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);
- 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.
@SurenAt93 Yep, I've misunderstood the utility. Thanks for clarifying, I've fixed it in my code too :)
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,
},
})
@5cript are you trying to load Monaco from node_modules? Could you please provide the part of your code where you configure the Monaco?
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.
I used this tutorial for setup
https://dev.to/jsmanifest/create-your-first-react-desktop-application-in-electron-with-hot-reload-4jj5
Awesome @LiamRiddell method solved my problem! thank you all @suren-atoyan
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
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?
I have to test that later when I have the chance, but I'm confident that it will.
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.
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
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
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')
)
}
});
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
TypeError: Cannot set property 'configScriptSrc' of undefined
Did you ever make that mistake? In the manner of @LiamRiddell
use: electron + typescriptconst 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, usemonaco.config({ paths: ... })
instead. It's described here
I don't know why this is undefind
TypeError: Cannot set property 'configScriptSrc' of undefined
Did you ever make that mistake? In the manner of @LiamRiddell
use: electron + typescriptconst 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, usemonaco.config({ paths: ... })
instead. It's described hereI don't know why this is undefind
could you please change the version of the library?
TypeError: Cannot set property 'configScriptSrc' of undefined
Did you ever make that mistake? In the manner of @LiamRiddell
use: electron + typescriptconst 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, usemonaco.config({ paths: ... })
instead. It's described herecould 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.
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?