linuxelectronviteelectron-forgebetter-sqlite3

Cannot find module 'better-sqlite3' after building Electron Forge Vite app (on Linux)


I'm trying to develop an Electron Forge Vite app that uses better-sqlite3 as database. It works fine in development environment but after I build a .deb package to test it outside development (I'm on Ubuntu) I get following error:

A JavaScript error occurred in the main process
Uncaught Exception:
Error: Cannot find module 'better-sqlite3'
Require stack:
- /usr/lib/babel-gate/resources/app.asar/.vite/build/main.js
- 
    at Module._resolveFilename (node:internal/modules/cjs/loader:1232:15)
    at s._resolveFilename (node:electron/js2c/browser_init:2:124485)
    at Module._load (node:internal/modules/cjs/loader:1058:27)
    at c._load (node:electron/js2c/node_init:2:16955)
    at Module.require (node:internal/modules/cjs/loader:1318:19)
    at require (node:internal/modules/helpers:179:18)
    at Object.<anonymous> (/usr/lib/babel-gate/resources/app.asar/.vite/build/main.js:30:61788)
    at Module._compile (node:internal/modules/cjs/loader:1484:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1564:10)
    at Module.load (node:internal/modules/cjs/loader:1295:32)

My forge.config.js:

const { FusesPlugin } = require("@electron-forge/plugin-fuses");
const { FuseV1Options, FuseVersion } = require("@electron/fuses");

module.exports = {
  packagerConfig: {
    asar: true,
    executableName: "babel-gate",
  },
  rebuildConfig: {
    buildOnly: true,
    force: true,
  },
  makers: [
    {
      name: "@electron-forge/maker-squirrel",
      config: {},
    },
    {
      name: "@electron-forge/maker-zip",
      platforms: ["darwin"],
    },
    {
      name: "@electron-forge/maker-deb",
      config: {},
    },
    {
      name: "@electron-forge/maker-rpm",
      config: {},
    },
  ],
  plugins: [
    {
      name: "@electron-forge/plugin-auto-unpack-natives",
      config: {},
    },
    {
      name: "@electron-forge/plugin-vite",
      config: {
        // `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
        // If you are familiar with Vite configuration, it will look really familiar.
        build: [
          {
            // `entry` is just an alias for `build.lib.entry` in the corresponding file of `config`.
            entry: "src/main.js",
            config: "vite_configs/vite.main.config.mjs",
            target: "main",
          },
          {
            entry: "src/preload.js",
            config: "vite_configs/vite.preload.config.mjs",
            target: "preload",
          },
          {
            entry: "src/overlay/overlay_preload.js",
            config: "vite_configs/vite.overlay_preload.config.mjs",
            target: "preload",
          },
        ],
        renderer: [
          {
            name: "main_window",
            config: "vite_configs/vite.renderer.config.mjs",
          },
        ],
      },
    },
    // Fuses are used to enable/disable various Electron functionality
    // at package time, before code signing the application
    new FusesPlugin({
      version: FuseVersion.V1,
      [FuseV1Options.RunAsNode]: false,
      [FuseV1Options.EnableCookieEncryption]: true,
      [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
      [FuseV1Options.EnableNodeCliInspectArguments]: false,
      [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
      [FuseV1Options.OnlyLoadAppFromAsar]: true,
    }),
  ],
};

My package.json:

{
  "name": "babel-gate",
  "productName": "BabelGate",
  "version": "1.0.0",
  "description": "My Electron application description",
  "main": ".vite/build/main.js",
  "scripts": {
    "start": "electron-forge start",
    "package": "electron-forge package",
    "make": "electron-forge make",
    "publish": "electron-forge publish",
    "lint": "echo \"No linting configured\""
  },
  "keywords": [],
  "author": {
    "name": "fa1ryJack",
    "email": "mymail@gmail.com"
  },
  "license": "MIT",
  "devDependencies": {
    "@electron-forge/cli": "^7.8.0",
    "@electron-forge/maker-deb": "^7.7.0",
    "@electron-forge/maker-rpm": "^7.8.0",
    "@electron-forge/maker-squirrel": "^7.7.0",
    "@electron-forge/maker-zip": "^7.7.0",
    "@electron-forge/plugin-auto-unpack-natives": "^7.8.0",
    "@electron-forge/plugin-fuses": "^7.7.0",
    "@electron-forge/plugin-vite": "^7.7.0",
    "@electron/fuses": "^1.8.0",
    "@electron/rebuild": "^3.7.1",
    "@rollup/plugin-commonjs": "^28.0.3",
    "@vitejs/plugin-vue": "^5.2.1",
    "electron": "34.3.0",
    "electron-forge-maker-appimage": "^26.0.12",
    "vite": "^6.2.2"
  },
  "dependencies": {
    "better-sqlite3": "^11.9.1",
    "deepl-node": "^1.17.3",
    "dotenv": "^16.4.7",
    "electron-squirrel-startup": "^1.0.1",
    "p-queue": "^8.1.0",
    "tesseract.js": "^6.0.0",
    "vue": "^3.5.13",
    "vue-router": "^4.5.0"
  }
}

In my database.js I call better-sqlite3 as:

const Database = require("better-sqlite3");

What I've tried:

packagerConfig: {
  asar: true,
  executableName: "babel-gate",
  extraResource: ["./node_modules/better-sqlite3"],
  asarUnpack: ["**/node_modules/better-sqlite3/**"]
}

Solution

  • The only solution that worked for me is this comment on GitHub under the issue titled as "Forge make combined with vite is creating an incomplete asar".

    I modified my forge.config.js as recommended. I already had plugin-auto-unpack-natives entry in plugins, so I only had to add this ignore to packagerConfig

    ignore:[ /node_modules\/(?!(better-sqlite3|bindings|file-uri-to-path)\/)/, ],