Skip to content

Instantly share code, notes, and snippets.

@ersinakinci
Last active January 25, 2023 19:03
Show Gist options
  • Star 36 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ersinakinci/053470a04defc6d7dfaacd5e5a073b15 to your computer and use it in GitHub Desktop.
Save ersinakinci/053470a04defc6d7dfaacd5e5a073b15 to your computer and use it in GitHub Desktop.
Making electron-webpack work with nodeIntegration: false

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.

Using electron-webpack without Node integration

Overview

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.

Approach

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.

electron-webpack.json

  1. Add a preload script that will at a minimum load into the global namespace whatever was being loaded by the hardwired require's.
  2. 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.
  3. 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 undocumented externals 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"],
}

webpack.renderer.additions.js

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"
};

src/index.ejs

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>

src/main/preload.js

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;

src/main/index.js

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")
    }
  });
  ...
}

src/renderer/index.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();
@olzzon
Copy link

olzzon commented Dec 3, 2019

Thanks a lot for the description that and this link helped me solve the noteIntegration.
quasarframework/quasar#5763

@jmaha
Copy link

jmaha commented Dec 14, 2019

I followed these instructions to a tee but Webpack will not copy src/main/preload.js into dist/. Is there something missing from these instructions? If I manually copy src/main/preload.js into dist/ after running 'npm run development', the preload works fine upon starting the app.

@francoiscolas
Copy link

The index.ejs template is not executed, it is copied as is.

Also, electron-webpack keeps adding the following code which is causing an error :

<script>require('module').globalPaths.push(".../electron-webpack-quick-start/node_modules")</script>
<script>require("source-map-support/source-map-support.js").install()</script></head>

@jeel2308
Copy link

I am also facing above issue

@Deco
Copy link

Deco commented Apr 18, 2020

Pasting the actual error to help other poor souls afflicted by this issue find this wonderful page via web search:

Uncaught ReferenceError: require is not defined

@ersinakinci
Copy link
Author

ersinakinci commented Apr 18, 2020

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.

@byte47
Copy link

byte47 commented Jul 9, 2020

Pasting the actual error to help other poor souls afflicted by this issue find this wonderful page via web search:

Uncaught ReferenceError: require is not defined

😄😄😄 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment