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

Requiring electron outside of main.js causes a TypeError #7300

Closed
nukeop opened this issue Sep 21, 2016 · 93 comments
Closed

Requiring electron outside of main.js causes a TypeError #7300

nukeop opened this issue Sep 21, 2016 · 93 comments
Labels
blocked/need-info ❌ Cannot proceed without more information

Comments

@nukeop
Copy link

nukeop commented Sep 21, 2016

  • Electron version: 1.3.5
  • Operating system: Mint 17

Hello

I am using Electron with React.js and babelify, among other plugins (list at the bottom).
Attempting to use require('electron'), for example to gain access to electron's BrowserWindow, results in the console showing the following error:
index.js:4 Uncaught TypeError: fs.readFileSync is not a function

I can, however, use const electron = require('electron'); in main.js. For what it's worth, I am also using watchify to pack everything into a js bundle.

Here is the complete list of dependencies in my package.json:

"devDependencies": {
        "axios": "^0.9.1",
        "babel-preset-es2015": "^6.6.0",
        "babel-preset-react": "^6.5.0",
        "babelify": "^7.2.0",
        "classnames": "^2.2.3",
        "electron": "^1.3.5",
        "electron-reload": "^0.2.0",
        "jquery": "^2.2.3",
        "react": "^0.14.8",
        "react-autocomplete": "^1.0.0-rc2",
        "react-dom": "^0.14.7",
        "react-sound": "^0.4.0",
        "soundmanager2": "^2.97.20150601-a"
    },
@kevinsawicki
Copy link
Contributor

index.js:4 Uncaught TypeError: fs.readFileSync is not a function

Can you include line 4 of index.js here? Or a fuller stack trace?

@kevinsawicki kevinsawicki added the blocked/need-info ❌ Cannot proceed without more information label Sep 21, 2016
@nukeop
Copy link
Author

nukeop commented Sep 21, 2016

I believe it is referring to index.js of electron, which is itself located in node-modules of the project. Here are the contents of this file:


var fs = require('fs')
var path = require('path')

module.exports = path.join(__dirname, fs.readFileSync(path.join(__dirname, 'path.txt'), 'utf-8'))

The entire stack trace suggests a conflict with React.js:

Stack trace

@kevinsawicki
Copy link
Contributor

What happens if you run require('fs').readFileSync from the Console tab of the dev tools?

It looks like the electron-prebuilt node module (the file you pasted) is ending up in your packaged app which I don't think you want since it should use the built-in electron require instead.

@MarshallOfSound
Copy link
Member

@nukeop How are you launching your app. Your `start script should look like.

"scripts": {
  "start": "electron ."
}

I have a feeling you are attempting to run your main.js with node

node main.js

Which won't work

@nukeop
Copy link
Author

nukeop commented Sep 21, 2016

What happens if you run require('fs').readFileSync from the Console tab of the dev tools?

Right after this error is thrown, I am able to run require('fs').existsSync in console to print a definition of the function. It also works before the error.

It looks like the electron-prebuilt node module (the file you pasted) is ending up in your packaged app which I don't think you want since it should use the built-in electron require instead.

I have a watchify instance running in the background as I'm developing which is continually updating my package. I defined it in scripts section of package.json like this:

"watch": "watchify app/app.js -t babelify -o public/js/bundle.js --debug --verbose"

Any advice on avoiding bundling electron-prebuilt?

@MarshallOfSound
Copy link
Member

@nukeop Electron supports require internally so you don't need to use browserify.

As far as I am aware, I'd you want to use browserify, you have to exclude "electron".

@nukeop
Copy link
Author

nukeop commented Sep 21, 2016

Interesting, could it be that watchify/browserify is ruining this? It has worked ok so far.

Now I am not sure how to run the program without it.

@MarshallOfSound
Copy link
Member

Literally just run

electron .

From your main app folder, you don't need to bundle anything when using Electron as it has a full node environment internally.

I'm going to close this out as it is a Browserify issue

@nukeop
Copy link
Author

nukeop commented Sep 21, 2016

That's what I've been doing all along, packing the program into a single .js file, which is included in a simple html file with:

  <script src="public/js/bundle.js">
  </script>

And everything works, except when I use require. This is a problem with interaction between the two modules.

If I do not pack the whole program into a bundle, I have no easy way of running it, as my main.js only starts electron and loads the html file that includes the bundle.

@MarshallOfSound
Copy link
Member

@nukeop The renderer process inside Electron has access to a full node/commonjs environment as well, so you don't need to bundle anything.

If I do not pack the whole program into a bundle, I have no easy way of running it, as my main.js only starts electron and loads the html file that includes the bundle.

I'm not sure I understand here, any script you load in your HTML file has a full commonjs environment and can therefore use require to load in extra files without browserifying anything

@nukeop
Copy link
Author

nukeop commented Sep 21, 2016

For anyone encountering this problem in the future and reading this thread, using window.require instead of require is one possibility of avoiding the conflict between electron's and browserify's require function.

@srinathh
Copy link

srinathh commented Jan 21, 2017

FWIW, I ran into the same issue trying to use electron in the renderer process with create-react-app that uses webpack in the backend instead of browserify. window.require seems to solve it for me too though I'm not entirely clear why.

Edit: I figured out why :-) We want to require electron during runtime from the nodejs environment provided at the runtime rather than the nodejs environment used during compilation by webpack. By default, globals are bound to window and webpack compilation ignores the window global - hence window.require works.

Another way to tell webpack to ignore a global name is to use a comment like /*global Android*/ in the JS file. In another project using CRA built app in an Android WebView, I used the above to get access to a Java object exposed to JS through the JavaScript interface provided by Webview.

@Vpet95
Copy link

Vpet95 commented Mar 2, 2017

@nukeop - thanks for your last post; it helped me a lot. window.require worked for me.

@made-by-chris
Copy link

made-by-chris commented Mar 11, 2017

yep fixed my create react app / webpack issue.
change

import electron, { ipcRenderer } from 'electron'

to

const electron = window.require("electron")

@srinathh how are you exposing/loading your CRA app as renderer in your main? are you building first (and modifying the html static resource paths)

@srinathh
Copy link

srinathh commented Mar 12, 2017

Yes my workflow currently is to basically run npm run build using the CRA scripts and then run the output in the build folder with electron. You don't need to modify the static resource paths by hand. In the package.json for CRA scripts, you just need to set the homepage like this and the paths will be appropriately set.

    "homepage": "./"

Additionally, I have main.js and package.json for the electron app in the public folder. So running the build automatically copies them over & you can just run electron build/ to start your app.

I'd actually like to be able to do the npm start with electron for development but I haven't gotten around to figuring how to make that work. I'm guessing i'll have to do an eject and modify the script by hand.

If you'd like to seen an example of the setup - take a look at https://github.com/srinathh/snippetfu

@sebastian-marinescu
Copy link

I'm not using Electron, but Node-Webkit (nw.js).
Using window.require did also fix my issue. Thanks very much for this!

@Alxmerino
Copy link

@nukeop window.require did the trick for me as well thank you very much! 🎉

@tashxii
Copy link

tashxii commented May 14, 2017

@nukeop I got same error, but it is solved window.require trick, thanks a lot!

@steric85
Copy link

window.require did solve the issue of fs.existsSync is not a function but it lead to another error : window.require is not a function. How shall I solve it?

@Alxmerino
Copy link

Alxmerino commented May 29, 2017

@steric85 are you using browserify, babel or webpack? you might need to transpile your code

@steric85
Copy link

steric85 commented May 29, 2017

@Alxmerino I am using webpack.

@Alxmerino
Copy link

make sure you're compiling your code

@petervojtek
Copy link

@steric85, I faced the window.require is not a function in typescript, I managed to fix it this way:

declare global {
  interface Window {
    require: any;
  }
}

const electron = window.require('electron');

@seanmahan
Copy link

I'm using the above method for accessing the ipcRenderer from electron in my Angular app but Angular change detection is not working when I update an Observable using an ipcRenderer message handler.
Is it because Angular doesn't know that ipcRenderer is an EventEmitter and that it needs to run change detection when ipcRenderer events come in?

@petervojtek
Copy link

in Angular 2 I used to call applicationRef.tick() to explicitly tell angular to refresh its state. https://angular.io/api/core/ApplicationRef

@axelkennedal
Copy link

axelkennedal commented Aug 25, 2017

I'm facing a very similar problem to @nukeop and @srinathh: I set up my Electron + React + Webpack project following this article guide, where the author at the end mentions the trick with window.require. I only require('electron') two places in my project; in the entry point for Electron, and in a JavaScript controller class that is required by some React components. In my Electron entry point file I simply do const electron = require('electron');, since it should be running in the main process (right?), and in my controller class I do const Electron = window.require('electron').remote; followed by const Jsonfile = Electron.require('jsonfile');, which should be the way to do it since it's running in the renderer process. But I get the same error as @nukeop ("TypeError: fs.ExistsSync is not a function") at line six in node_modules/electron/index.js which looks like this:

var fs = require('fs')
var path = require('path')

var pathFile = path.join(__dirname, 'path.txt')

if (fs.existsSync(pathFile)) {
  module.exports = path.join(__dirname, fs.readFileSync(pathFile, 'utf-8'))
} else {
  throw new Error('Electron failed to install correctly, please delete node_modules/electron and try installing again')
}

I've tried deleting node_modules/electron and installing again.
I'm using Electron 1.7.5 on macOS 10.12.6, and start my project using npm run dev as setup in the article.

@truonghongtrieu
Copy link

Thanks to @tumbledwyer for posting the link to a solution that works for me:

Updated:
Actually the 2nd version of how to do the same: https://www.codementor.io/randyfindley/how-to-build-an-electron-app-using-create-react-app-and-electron-builder-ss1k0sfer

The solution from Randy Findley:
Now, if you need to access the fs module like I did, you'll quickly hit the Module not found error

First, install Rescripts.

yarn add @rescripts/cli @rescripts/rescript-env --dev

Then, change the scripts tags in package.json from this...

"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",

to this

"start": "rescripts start",
"build": "rescripts build",
"test": "rescripts test",

Now add a new file called .rescriptsrc.js with the following contents:

module.exports = [require.resolve('./.webpack.config.js')]

Finally add another new file called .webpack.config.js with the following contents:

// define child rescript
module.exports = config => {
  config.target = 'electron-renderer';
  return config;
}

Now you can use the fs module, no worries.

@packetstracer
Copy link

What @ledleds pointed out worked for me, using CRA with typscript and Electron. Then thing is that I declared webPreferences like this:

    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    },

so that seems to be overwriting/setting nodeIntegration to false, so setting it to true and relaunching the app solved the issue (you have to relaunch the app in order to load Electron window with that config)

setting nodeIntegration to true like this

    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: true
    },

For anyone still stuck on this: you'll get the error window.require is not a function unless you explicitly declare nodeIntergation as truewhen you declare your BrowserWindow:

new BrowserWindow({
    webPreferences: {
      nodeIntegration: true,
    }
});

@leognmotta
Copy link

react-app-rewired

This was the only good solution I found in terms of scalability, I do not think anyone should rely on window.require

@nukeop
Copy link
Author

nukeop commented May 10, 2020

Why would window.require not be scalable?

@jahsome
Copy link

jahsome commented Aug 5, 2020

For anyone who happens to be struggling with this or similar issues and is using Vue and vue-electron-builder, see here

@timsamart
Copy link

timsamart commented Aug 11, 2020

For anyone still struggling with this especially regarding quitting the electron application through a react button please read on.

What helped for me was the following:

  1. When you declare your window make sure you set nodeIntegration: true as this is currently by default false for security reasons. Usually that is done in your electron.js file.

  2. As @packetstracer already mentioned: you have to relaunch the app in order to load Electron window with that config

mainWindow = new BrowserWindow({
//...
  webPreferences: {
    nodeIntegration: true,
  },
});
  1. Also in your electron.js put the following statement near the top this ensures that the ipcMain will listen on the event "close-me":
const { ipcMain } = require("electron");
ipcMain.on("close-me", (evt, arg) => {
  app.quit();
});
  1. In the react component where your "close"-button is located add the following window.require statement after your imports. The usual require did not work:
const ipcRenderer = window.require("electron").ipcRenderer;
  1. And in order to close your application call the following statement. It should send the event "close-me" to the ipcMain:
<Button label="close" onClick={(e) => ipcRenderer.send("close-me")} />

Feel free to comment and give feedback I am still new to electron.
I know the context with the quit button is unrelated but I could not find a better suited thread. Please feel free to point me to another more suitable thread if there is one.

My configuration:

"electron": "9.2.0",
"electron-builder": "22.8.0",
"electron-packager": "15.0.0",

@RodrigoStuani
Copy link

solved for me!! Thanks @nukeop

`<script>
const ipcRenderer = window.require('electron')

export default {
...

process() {

ipcRenderer.send('processIPC', 'foi')
ipcRenderer.on('processIPC', (event, resp) => { console.log(resp) })

}

}
</script>
`

@JaviOverflow
Copy link

Does anyone know how to set up the proper type in typescript?

I have the following at the moment

export const electron = window.require('electron').remote

I would like something like

export const electron = window.require('electron').remote as ElectronType

so the IDE knows how to autocomplete the electron API.

@cnscorpions
Copy link

@timsamart it does works. Thank you, it saves me days of work. 🤣

@kunokdev
Copy link

kunokdev commented Dec 2, 2020

After spending many hours trying literally everything above, I missed the part in case you use BrowserView, just like BrowserWindow you need to explicitly enable node.js:

     new BrowserView({ // is a BrowserView not a BrowserWindow
        webPreferences: {
          nodeIntegration: true,
        },
      })

@0501814314
Copy link

m0501814314@icloud.com

@raphael10-collab
Copy link

raphael10-collab commented Dec 11, 2020

I'm using electron-typescript-webpack-react and electron-forge.

This solution #9920 (comment) works only I put the window.api in index.html:

    window.api.receive("fromMain", (data) => {
        console.log(`Received ${data} from main process`);
    });
    window.api.send("toMain", "some data");

But if I put this code in the renderer process: renderer.ts I get the error:

Property 'api' does not exist on type 'Window & typeof globalThis'

I tried also to add

const ipcRenderer = window.require("electron").ipcRenderer; 

and I get

window.require is not a function

@pglezen
Copy link

pglezen commented Mar 15, 2021

One reason some people are still experiencing this problem is that nodeIntegration has been removed from Electron v12. So setting nodeIntegration: true has no affect. Instead, you need to set contextIsolation: false. I suggest reading the justification to understand why and becoming familiar with the contextBridge to make it work safely.

@ikiK-CRO
Copy link

ikiK-CRO commented Mar 16, 2021

Not even last comment helped me. Im trying to follow this article emphasis on simple in title. And then spend two days to resolve errors that should not be there. Its ether fs.existsSync is not a function or window.require is not a function

There are x solutions, and articles and all are different from another, including this topic. This is ridicules.

Today is 16.03.2012, allow me to ask: Does anyone has a suggestion how to modify simple script from article to work?

@pglezen
Copy link

pglezen commented Mar 16, 2021

Not even last comment helped me. Im trying to follow this article emphasis on simple in title.

Just so you know, that article references

 "electron": "^4.1.0",

in the package.json. The behavior of the nodeIntegration property is vastly different between that and Electron 12. If you're really using Electron 4, then the article should work, since nodeIntegration: true was the default back then. But Electron 5 and later breaks this.

@ikiK-CRO
Copy link

Not even last comment helped me. Im trying to follow this article emphasis on simple in title.

Just so you know, that article references

 "electron": "^4.1.0",

in the package.json. The behavior of the nodeIntegration property is vastly different between that and Electron 12. If you're really using Electron 4, then the article should work, since nodeIntegration: true was the default back then. But Electron 5 and later breaks this.

Thank you for the replay, I made rollback of everything to versions from article, still same thing:

electron-filetree-example@0.1.0 /Users/kiki/react/electron-filetree-example
└── electron@4.2.12 
electron-filetree-example@0.1.0 /Users/kiki/react/electron-filetree-example
└── react@16.14.0 

It is reporting window.require is not a functionon line var remote = window.require('electron').remote; in FileTree.js

and if I remove window I get fs.existsSync is not a function on node_modules/electron/index.js:8

@SamheinDM
Copy link

One reason some people are still experiencing this problem is that nodeIntegration has been removed from Electron v12. So setting nodeIntegration: true has no affect. Instead, you need to set contextIsolation: false. I suggest reading the justification to understand why and becoming familiar with the contextBridge to make it work safely.

Thank you very much! That was finally solve my problem.

@pglezen
Copy link

pglezen commented Mar 21, 2021

For @ikiK-CRO and others, I created my own "starter kit" project with Electron/TypeScript/Svelte using the latest (as of this posting) versions of the libraries with contextIsolation=true. It may not be received well. I'm no expert at any of these, much less combining them. It uses the contextBridge to enforce isolation between the renderer process and the main process. The readme.md references other useful discussions on context isolation and the contextBridge, which is crucial to getting your goodies attached to the window object in your renderer process.

https://github.com/pglezen/electron-typescript-svelte-starter

@whenmoon
Copy link

whenmoon commented Oct 24, 2021

After many hours of trying to make this work, I finally got it to work using the contextBridge as other have suggested. I needed to access the electron shell object from my React Native Electron app code to open an external browser window as part of OAuth sign in flow. In electron.js:

const { app, BrowserWindow } = require('electron');
const path = require('path');
...
function createWindow() {
	const mainWindow = new BrowserWindow({
		width: 800,
		height: 600,
		webPreferences: {
			preload: path.join(app.getAppPath(), 'preload.js'), // make sure this path to you preload file is correct
		}
	});

in preload.js

const { contextBridge, shell } = require('electron')

contextBridge.exposeInMainWorld(
	'electron',
	{
		doThing: () => shell.openExternal("http://www.google.com")
	}
)

In the client application:

declare global {
	interface Window {
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		[x: string]: any;
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		require: any;
	}
}
...
const signInWithGoogle = (): void => {
	window.electron.doThing();
};

@pie6k
Copy link

pie6k commented Jan 14, 2022

I need an answer to very simple question:

If I have contextIsolation: true - am I allowed to import ANYTHING in renderer from electron (even 'safe' thing) eg. clipboard?

eg. From clipboard docs https://www.electronjs.org/docs/latest/api/clipboard

Process: Main, Renderer

It suggests I can use it in both processes. But is it 'conditional' on context isolation or not?

Right now I cannot import anything from electron in the renderer, as I'm getting crash. I'm trying to fix it, but I don't know if it is actually fixable.

If importing anything from electron is not possible with contextIsolation - fine, I get it - I simply pass anything I need in preload, done. But I cannot find this information being clear anywhere.

I understand security consequences and I don't want to disable context isolation - can I still import anything from electron in renderer, or should I avoid it like fire?

For several hours I'm trying to fix this crash, not even sure if it is fixable or exepcted

@pglezen
Copy link

pglezen commented Jan 15, 2022

Right now I cannot import anything from electron in the renderer, as I'm getting crash. I'm trying to fix it, but I don't know if it is actually fixable.

If importing anything from electron is not possible with contextIsolation - fine, I get it - I simply pass anything I need in preload, done. But I cannot find this information being clear anywhere.

You should be able to import any renderer API you want from within your preload.js. Then you expose those actions you need via the window object based on your configuration of the ContextBridge.

This stackoverflow answer helped me get a grip on this situation.

@chengwb53
Copy link

it is worked in electron and web
const ipcRenderer = window.require && window.require('electron').ipcRenderer || null;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked/need-info ❌ Cannot proceed without more information
Projects
None yet
Development

No branches or pull requests