Electron Forge with React and Typescript gives Module not found: Error: Can't resolve 'fs'

728 views Asked by At

I'm new to Electron.js and I'm trying to use it to build an app with React and trying to send custom desktop notifications for my app. I have tried a lot of things but been running into this error

ERROR in ./node_modules/electron/index.js 1:11-24 Module not found: Error: Can't resolve 'fs' in '<pathToProject>/Desktop/Projects/todo-list/node_modules/electron'.
ERROR in ./node_modules/electron/index.js 2:13-28
Module not found: Error: Can't resolve 'path' in '<pathToProject>/Desktop/Projects/todo-list/node_modules/electron'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
    - add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
    - install 'path-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
    resolve.fallback: { "path": false }

console error: Console error

This occurs when I try using the ipcRenderer as follows just to create dynamic notifications as follows:

App.tsx ->

import React from 'react';
import NavBar from "./components/Navbar";
import {ipcRenderer} from "electron";


const App: React.FC = () => {
    const notificationData = {
        notificationTitle: 'Notification Title',
        notificationBody: 'Notification Body'
    }
    return (
        <>
            <NavBar/>
            <h1>Hello there!</h1>
            <button onClick={() => ipcRenderer.send('show-notification', notificationData)}>Click me for notification</button>
        </>
    )
}

export default App;

index.ts

import { app, BrowserWindow,ipcMain } from 'electron';
import CreateNotification from "./components/CreateNotification";
// This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Webpack
// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on
// whether you're running in development or production).
import path from "path";

declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
  app.quit();
}

const createWindow = (): void => {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    height: 600,
    width: 800,
    webPreferences: {
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
      nodeIntegration: true
    },
  });

  // and load the index.html of the app.
  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);

  // Open the DevTools.
  mainWindow.webContents.openDevTools();

  ipcMain.on('show-notification', (event, data) => {
    CreateNotification(data);
  });
};

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.

CreateNotification.ts

import {Notification} from "electron";

type NotificationData = {
    notificationTitle: string;
    notificationBody: string
}
const CreateNotification = ({notificationTitle, notificationBody}: NotificationData) => {

    new Notification({
        title: notificationTitle,
        body: notificationBody
    }).show()
}

export default CreateNotification;

So far are the things I have tried:

  • Adding a custom webpack.config.ts
  • Adding the follwing in package.json
"browser": {
  "fs": false
}

  • I also tried various preload.ts and tsconfig configuration changes from all the resources of other people running into the same issue but I have had no luck so far. I would greatly appreciate any insights in fixing this error, thanks in advance!
1

There are 1 answers

1
Arkellys On BEST ANSWER

The error you get happens when you try to compile NodeJS code with webpack (on an Electron app). For security reasons, your should not use NodeJS on your renderer process.

To use IPC correctly, you should use a preload file to expose what you need, and then use the exposed function on your renderer process:

preload.js

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  showNotification: (notificationData) => {
    ipcRenderer.send('show-notification', notificationData);
  }
});

As a reminder, you should never expose the whole ipcRenderer API directly for security reasons.

App.tsx

const { electronAPI } = window;

[...]

<button onClick={() => electronAPI.showNotification(notificationData)}>
  Click me for notification
</button>

And if you care about security, you should also disable nodeIntegration on the webPreferences of your window:

webPreferences: {
  preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
  nodeIntegration: false // This is the default value
}