Finally found how to do it ! As Maneet pointed in their answer, better-sqlite3 wasn't packaged, and thus the required call would fail.
I found here and there mentions to packagerConfig.extraRessource
property in forge.config.ts file, which will copy asked files outside the asar archive. However, I needed better-sqlite3 files to be inside the asar archive.
After an astronomic amount of research, I found the answer on this page. I had to adapt it a little, but it works fine. The idea is to use an Electron-forge hook to copy what we want in a temp file, before it gets archived int app.asar. This solution only requires changes on forge.config.ts file :
import type { ForgeConfig } from "@electron-forge/shared-types";
import { MakerSquirrel } from "@electron-forge/maker-squirrel";
import { VitePlugin } from "@electron-forge/plugin-vite";
import { FusesPlugin } from "@electron-forge/plugin-fuses";
import { FuseV1Options, FuseVersion } from "@electron/fuses";
import { resolve, join, dirname } from "path";
import { copy, mkdirs } from "fs-extra";
const config: ForgeConfig = {
packagerConfig: {
asar: true
},
rebuildConfig: {},
hooks: {
// The call to this hook is mandatory for better-sqlite3 to work once the app built
async packageAfterCopy(_forgeConfig, buildPath) {
const requiredNativePackages = ["better-sqlite3", "bindings", "file-uri-to-path"];
// __dirname isn't accessible from here
const dirnamePath: string = ".";
const sourceNodeModulesPath = resolve(dirnamePath, "node_modules");
const destNodeModulesPath = resolve(buildPath, "node_modules");
// Copy all asked packages in /node_modules directory inside the asar archive
await Promise.all(
requiredNativePackages.map(async (packageName) => {
const sourcePath = join(sourceNodeModulesPath, packageName);
const destPath = join(destNodeModulesPath, packageName);
await mkdirs(dirname(destPath));
await copy(sourcePath, destPath, {
recursive: true,
preserveTimestamps: true
});
})
);
}
},
makers: [new MakerSquirrel({})],
plugins: [
new VitePlugin({
build: [
{
entry: "src/main.ts",
config: "vite.config.ts",
target: "main"
},
{
entry: "src/preload.ts",
config: "vite.config.ts",
target: "preload"
}
],
renderer: [
{
name: "main_window",
config: "vite.config.ts"
}
]
}),
new FusesPlugin({
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false,
[FuseV1Options.EnableCookieEncryption]: true,
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
[FuseV1Options.EnableNodeCliInspectArguments]: false,
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
[FuseV1Options.OnlyLoadAppFromAsar]: true
})
]
};
export default config;
For each string in requiredNativePackages
list, the hook will look for a directory with the same name in node_modules, and copy this in a directory named node_modules inside the temp directory which will be turned into an archive right after.
We need bindings
and file-uri-to-path
packages in top of better-sqlite3
because they're direct dependencies.