Electron Forge with Webpack getting "require is not defined" when importing 'events'

Viewed 1537

I've recently started a new project with electron forge and the webpack template. I'm creating a simple class for authentication and want to use the event system in nodejs. I'm using the import syntax but getting an error saying that require is undefined. I do not wish to turn on nodeIntegration and I thought webpack would automatically use the browserfy version of the nodejs event system:

You usually do not have to install events yourself! If your code runs in Node.js, events is built in. If your code runs in the browser, bundlers like browserify or webpack also include the events module.

Installing the browserify event module does not fix the issue either. I also tried excluding the node_modules/events folder to hope webpack would use its own events module but that didn't work for me. Here is my code:

import { EventEmitter } from 'events';

export default class Auth {
  constructor(socket) {
  }

  setToken(token) {
    this.token = token;
    localStorage.setItem('token', token);
    this.emit('token', token);
  }

  checkToken() {
    if(localStorage.getItem("token") !== null) {
      this.setToken(localStorage.getItem("token"));
    } else {
      this.emit('false', false);
    }
  }

  getToken() {
    return localStorage.getItem("token");
  }

  setAuth(auth) {
    this.emit('auth', auth);
    this.isAuth = auth;
  }
  
}

Here is my webpack renderer config:

const rules = require('./webpack.rules');
const webpack = require('webpack');
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');

rules.push({
  test: /\.css$/,
  use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
});

const assets = [ 'assets' ];
const copyPlugins = assets.map((asset) => {
  return new CopyWebpackPlugin({
    patterns: [{ from: path.resolve(__dirname, 'src/renderer/', asset), to: asset }],
  });
});

module.exports = {
  // Put your normal webpack config below here
  module: {
    rules,
  },
  plugins: [
      new webpack.ProvidePlugin({
          $: 'jquery',
      }),
      ...copyPlugins
  ],
  resolve: {
    alias: {
      'components': path.resolve(__dirname, './src/renderer/components'),
      'pages': path.resolve(__dirname, './src/renderer/pages'),
      'core': path.resolve(__dirname, './src/renderer/core'),
    },
    extensions: ['.js']
  },
};

Here is my webpack rules config:

module.exports = [
  // Add support for native node modules
  {
    test: /\.node$/,
    use: 'node-loader',
  },
  {
    test: /\.(m?js|node)$/,
    parser: { amd: false },
    use: {
      loader: '@marshallofsound/webpack-asset-relocator-loader',
      options: {
        outputAssetBase: 'native_modules',
      },
    },
  },
];

And here is the error:

external "events":
Uncaught ReferenceError: require is not defined
    at Object.events (external "events":1)
    at __webpack_require__ (bootstrap:832)
    at fn (bootstrap:129)
    at Module../src/renderer/core/Auth.js (Auth.js:1)
    at __webpack_require__ (bootstrap:832)
    at fn (bootstrap:129)
    at Module../src/renderer/index.js (index.js:1)
    at __webpack_require__ (bootstrap:832)
    at fn (bootstrap:129)
    at Object.0 (routes.js:40)
2 Answers

After taking a break from it, I realized the solution was simple:

I just needed to add the following to my renderer config:

target: 'web'

The solution is using the ContextBridge API from electron for the electron-forge webpack template

preload.js

import { ipcRenderer, contextBridge } from "electron";

contextBridge.exposeInMainWorld("electron", {
  notificationApi: {
    sendNotification(message) {
      ipcRenderer.send("notify", message);
    },
  },
  batteryApi: {},
  fileApi: {},
});

main.js

const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      worldSafeExecuteJavaScript: true,
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
    },
 });

ipcMain.on("notify", (_, message) => {
   new Notification({ title: "Notification", body: message }).show();
});

package.json

 "plugins": [
    [
      "@electron-forge/plugin-webpack",
      {
        "mainConfig": "./webpack.main.config.js",
        "renderer": {
          "config": "./webpack.renderer.config.js",
          "entryPoints": [
            {
              "html": "./src/index.html",
              "js": "./src/renderer.js",
              "name": "main_window",
              "preload": {
                "js": "./src/preload.js"
              }
            }
          ]
        }
      }
    ]
  ]

then using the below code will invoke the native notification message

electron.notificationApi.sendNotification("Finally!");
  
Related