NOTE, April 18, 2020: I wrote this document in February 2019 when I was actively investigating Electron. I haven't used it since then and have largely forgotten what this was for or how electron-webpack works. I vaguely remember the problem, but that's it. It would appear based on the comments below that this document is still coming up in web searches but has become out of date and parts of it don't work. If anyone has specific fixes to my instructions, please leave them in the comments and I'll happily incorporate them.
The current version (as of 2019-02-02) of electron-webpack is set up with the assumption that your BrowserWindow instance has nodeIntegration: true
. However, Electron is encouraging users to set nodeIntegration: false
as a security precaution, and in the future BrowserWindows will have this setting set to false by default. Doing so now with electron-webpack throws an error because the index.html template has commonjs require's hardwired into it (which only works with nodeIntegration: true). Furthermore, the renderer script is built by Webpack with commonjs as its libraryTarget, which also throws an error on the browser without nodeIntegration: true.
Modifying your electron-webpack configuration to generate index.html with a custom template instead of the default one that has require's hardwired into it, and to generate the renderer bundle using a browser-compatible libraryTarget.
Note: All of this works with TypeScript, as well, and doesn't require any special modifications to tsconfig.json. Just set up TypeScript as you normally would with electron-webpack.
Bonus: By setting nodeIntegration: false, there's a good chance that you can load your electron-webpack app in the browser by running yarn dev and pointing your browser to http://localhost:9080. Probably doesn't work with Node native modules.
- Add a preload script that will at a minimum load into the global namespace whatever was being loaded by the hardwired require's.
- Point to a partial Webpack config for the renderer bundle that will change its libraryTarget to something that works in browsers (rather than commonjs). This config gets merged with the default one.
- Whitelist any modules that your renderer process imports. By default, electron-webpack generates Webpack configs with all module dependencies listed as externals, and when the libraryTarget is commonjs, this behavior results in your imports becoming require calls. The require calls work with
nodeIntegration: true
, but without require or the Node environment available, we need Webpack to bundle our renderer dependencies.whiteListedModules
prevents those dependencies from being counted as externals. NB: Any dependencies listed in this field are also excluded from externals when compiling the main bundle, which can occasionally result in strange Webpack errors. You can use the undocumentedexternals
field to add back any modules that must be counted as an external.
{
"main": {
"extraEntries": ["@/preload.js"]
},
"renderer": {
"webpackConfig": "webpack.renderer.additions.js"
},
"whiteListedModules": ["a-renderer-module", "another-renderer-module"],
}
module.exports = {
// NB: Allows setting CSP without raising errors. The default setting is eval-source-map, which causes Webpack
// to emit eval(...)'s in the renderer bundle, which makes any CSP you set unnhappy unless you allow unsafe-eval,
// but then Electron gives you a warning. (As it should.) The tradeoff is that inline-source-map is slower.
devtool: "inline-source-map",
output: {
// NB: Can also be "window", etc.
libraryTarget: "var"
},
// NB: Target can be set to "electron-renderer", as well, but that defeats the point since electron-renderer
// is a configuration that's intended to help augment the bundle to work with nodeIntegration: true.
target: "web"
};
If this file exists, electron-webpack will detect and use it as the template for index.html.
<!DOCTYPE html>
<html>
<head>
<!-- NB: Various Webpack settings are exposed in the template through the htmlWebpackPlugin object -->
<!-- The title property maps onto whatever title you set in electron-webpack.json. -->
<title><%= htmlWebpackPlugin.options.title %></title>
<!-- Set your CSP to only load resources from webpack-dev-server in development. -->
<!-- Allow XMLHttpRequest and WebSocket to connect anywhere. (WebSocket needed for HMR.) -->
<!-- Modify to suite your needs: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy -->
<% if (process.env.NODE_ENV === 'development') { %>
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; connect-src *"
/>
<% } else { %>
<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; connect-src *"
/>
<% } %>
</head>
<body>
<!-- HtmlWebpackPlugin will inject a script tag to load your renderer bundle inside here. -->
</body>
</html>
This file will have access to the full Node environment even when nodeIntegration is set to false. We can use this to inject dependencies into our browser environment's global namespace that we would otherwise load as commonjs modules.
import sourceMapSupport from "source-map-support";
window.sourceMapSupport = sourceMapSupport;
Inside whatever function creates your window, configure your BrowserWindow with nodeIntegration: false.
function createMainWindow() {
const window = new BrowserWindow({
webPreferences: {
nodeIntegration: false,
// Below is where we specify our preload script. __dirname points to our source file's path and the preload
// path should point to the Webpack-emitted preload bundle.
// Note that if you're using TypeScript, the emitted preload bundle will be in JS with a .js extension.
preload: path.resolve(__dirname, "..", "..", "dist", "main", "preload.js")
}
});
...
}
Run whatever was previously being require'd inside the default index.html template's script tag
// This is require('source-map-support').install() in the default index.html template.
// window.sourceMapSupport will exist in the browser once preload.js runs.
window.sourceMapSupport.install();
Hey all, I didn't realize how necessary this page would be when I made it! Unfortunately, I haven't worked on an Electron project in years and I've forgotten pretty much everything related to it. I'm happy to update the gist if people have specific fixes to my instructions.
@Deco, any error that says
require is not defined
sounds like it has to do with trying to run Node code in a non-Node context.