javascriptnode.jselectronelectron-forgenode.js-fs

Error packaging electron app that uses local files


I created an App using electron that interacts with local files using IPC. When i run it using npm start, éverything works as excpected, but when i try to package the app into an .exe file using the steps provided in the docs (https://www.electronjs.org/docs/latest/tutorial/tutorial-packaging), the App wont start and i get this error:

Error
× A JavaScript error occurred in the main process
Uncaught Exception:
X
Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file and data are supported by the default ESM loader. Received protocol 'electron:'
at getSourceSync (node:internal/modules/esm/load:74:11)
at defaultLoadSync (node:internal/modules/esm/load:172:32)
at ModuleLoader.getModuleJobForRequire (node:internal/modules/esm/loader:410:24)
at new ModuleJobSync (node:internal/modules/esm/module_job:341:34)
at
ModuleLoader.importSyncForRequire (node:internal/modules/esm/loader:357:11)
at loadESMFromCJS (node:internal/modules/cjs/loader:1392:24)
at Module._compile (node:internal/modules/cjs/loader: 1543:5)
at Module._extensions..js (node:internal/modules/cjs/loader:1722:10)
at Module.load (node:internal/modules/cjs/loader: 1296:32)
at Module._load (node:internal/modules/cjs/loader:1115:12)
OK

Electron version: 35.0.1
Node version: v22.14.0

I suspect it has to do something with the way i handle paths, so here are some example codes

// main.js
const { app, BrowserWindow, dialog, ipcMain, shell} = require('electron');
const path = require('path'); 
const fs = require('fs'); 
const pdf = require('pdf-parse'); 
const ElectronStore = require('electron-store'); 
const store = new ElectronStore.default();

function createWindow() {
    let win = new BrowserWindow({
        width: 1200,
        height: 700,
        icon: path.join(__dirname,'assets/icon.png'),
        webPreferences: {
            nodeIntegration: false, // Security best practice
            contextIsolation: true, // Isolate main and renderer processes
            enableRemoteModule: false, 
            devTools: true,
            preload: path.join(__dirname, 'preload.js') // Use a preload script
        }
    });
    // win.removeMenu()
    win.loadFile(path.join(__dirname, 'index.html'));
}
ipcMain.handle("fs:readdir",(event, path) =>{
    return fs.promises.readdir(path)
});


ipcMain.handle("fs:isDir", (event, filePath, document) =>{
    return fs.lstatSync(path.join(filePath,document)).isDirectory()
});

//preload.js
contextBridge.exposeInMainWorld('fs',{
    readdir: (path) => ipcRenderer.invoke("fs:readdir",path),
    isDir: (path, document) => ipcRenderer.invoke("fs:isDir",path,document)
})

I already updated my Node.js version


Solution

  • As I noted in the comments I was having the same issue as you. I removed my includes one-by-one until I narrowed it down to electron-store. I don't think it's compatible with require() syntax when bundled for some reason (yet, it works in dev mode).

    I found this gist which describes the problem: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c

    The package that linked you here is now pure ESM. It cannot be require()'d from CommonJS.

    This means you have the following choices:

    1. Use ESM yourself. (preferred)
      Use import foo from 'foo' instead of const foo = require('foo') to import the package. You also need to put "type": "module" in your package.json and more. Follow the below guide.
    2. If the package is used in an async context, you could use await import(…) from CommonJS instead of require(…).
    3. Stay on the existing version of the package until you can move to ESM.
    4. Since Node.js 22, you may be able to require() ESM modules. However, I strongly recommend moving to ESM instead.

    You also need to make sure you're on the latest minor version of Node.js. At minimum Node.js 16.

    I would strongly recommend moving to ESM. ESM can still import CommonJS packages, but CommonJS packages cannot import ESM packages synchronously.

    So, stubborn devs lol. I fixed this by doing:

        "electron-store": "v8.2.0"
    

    in my package.json and reinstalling the packages. 8.2.0 seems to be the last release before 9.0.0 which forces ESM modules.

    Hope this helps.