14

I just created a new application using the following command:

npx create-electron-app my-new-app --template=typescript-webpack

Inside the renderer.ts I added the following code

import "./index.css";
import { ipcRenderer } from "electron";

But when I run npm run start I have the following error in Browser Console

Uncaught ReferenceError: require is not defined

Update What I've tried:

webpack.plugins.js

const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
const webpack = require("webpack");
module.exports = [
  new ForkTsCheckerWebpackPlugin(),
  new webpack.ExternalsPlugin("commonjs", ["electron"]),
];

But it still doesn't work.

Georgian Stan
  • 1,865
  • 3
  • 13
  • 27

4 Answers4

40

Found Solution

The solution is to use ipcRenderer in a preload script.

preload.ts

import { ipcRenderer } from "electron";

index.ts

declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: any;

const mainWindow = new BrowserWindow({
  height: 600,
  width: 800,
  webPreferences: {
    preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
  },
});

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.ts",
              "name": "main_window",
              "preload": {
                "js": "./src/preload.ts"
              }
            }
          ]
        }
      }
    ]
  ]
Sagnik Pradhan
  • 509
  • 1
  • 5
  • 16
Georgian Stan
  • 1,865
  • 3
  • 13
  • 27
  • 2
    George, you may want to add " declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: any;" as a note for Typescript users or they'll get an error as the TS compiler will not know about this Global. This can be defined in the main index.ts or you can move these "declare" declarations including the default into "src/typings.d.ts" after creating a declare global { // your declare const....here }. If you wish I can edit this for you :) – Blujedis Mar 27 '21 at 20:20
  • 1
    How do you then reference ipcRenderer in the application? (a React component for example?) – Blue Waters May 08 '21 at 05:28
  • 1
    @BlueWaters to send IPC message from React component, you must enhance the preload.ts script: import { contextBridge, ipcRenderer } from 'electron'; contextBridge.exposeInMainWorld('dog', { bark(arg1, arg2) { ipcRenderer.invoke('dog.bark', arg1, arg2); } }); Now there is a `window.dog.bark()` function available in your React component. – srigi Jul 19 '21 at 12:44
  • I'm close to implementing this solution. I've defined my function in the context bridge and I'm able to call it from the debug console like `window.api.get()`. However, in my react typescript component, I keep getting `window.api is not a function`. How do I declare the new function? – Evan Lalo Dec 23 '21 at 12:27
  • Hi @EvanLalo! If you use `window.api` I assume you have something like this in your preload script: `contextBridge.exposeInMainWorld("api",{})` correct? – Georgian Stan Jan 03 '22 at 15:39
6

The solution is using the ContextBridge API from electron for electron-forge react+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"
              }
            }
          ]
        }
      }
    ]
  ]

app.jsx

import * as React from "react";
import * as ReactDOM from "react-dom";

class App extends React.Component {
  componentDidMount() {
    electron.notificationApi.sendNotification("Finally!");
  }
  render() {
    return <h1>contextBridge</h1>;
  }
}

ReactDOM.render(<App />, document.body);
Sathishkumar Rakkiyasamy
  • 3,509
  • 2
  • 30
  • 34
  • 1
    this is exactly what i'm looking for.. thank you so much to answering this bro. appreciate it – anztrax Aug 20 '21 at 15:45
0

If you are using typescript, you need to add this to your renderer.ts file:

declare global {
  interface Window {
    electron: {
      funcName: () => void;
    };
  }
}

Then you can use window.electron.funcName().

ouflak
  • 2,458
  • 10
  • 44
  • 49
-1

In index.js when creating new window use

win = new BrowserWindow({
        webPreferences: {
            preload: path.join(__dirname, 'app', 'assets', 'js', 'preloader.js'),
            nodeIntegration: true,
            contextIsolation: false
        },
    })

Simply add nodeIntegration: true to webPreferences.Then you would be able to use require

dommilosz
  • 127
  • 13