Reputation: 31
I am working on an open source firmware update app for Meshtastic (https://github.com/medentem/electron-flasher/). The app is a ReactJS/TS app bundled with Vite for Electron. When running locally on OSX (electron-forge start
), it functions perfectly and the native dependencies (serialport and drivelist) seem to be referenced properly. However, after bundling the OSX app for distribution (electron-forge make
), the resulting app launches with this error:
Uncaught Exception:
Error: Cannot find module 'serialport'
Require stack:
- /Users/medentem/Dev/electron-flasher/out/electron-flasher-darwin-arm64/electron-flasher.app/Contents/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:124038)
at Module._load (node:internal/modules/cjs/loader:1058:27)
at c._load (node:electron/js2c/node_init:2:17025)
at Module.require (node:internal/modules/cjs/loader:1318:19)
at require (node:internal/modules/helpers:179:18)
at Object.<anonymous> (/Users/medentem/Dev/electron-flasher/out/electron-flasher-darwin-arm64/electron-flasher.app/Contents/Resources/app.asar/.vite/build/main.js:1:214)
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)
I confirmed that the serialport
and drivelist
packages are configured in vite as external (see https://github.com/medentem/electron-flasher/blob/main/vite.main.config.ts), and that the electron-forge
configuration AutoUnpackNativesPlugin
to ensure both packages are rebuilt and included outside of the asar bundle. But that does not seem to work.
I've also tried to use electron-builder
to generate the OSX app, but in that case, the app will not even launch.
Thank you in advance for any help!
Upvotes: 2
Views: 292
Reputation: 31
After many many hours of trying and failing, I was able to get the right configuration to build the project.
Key learnings:
external
rollup options.base
path to resolve paths correctly.emptyOutDir
config value is ... dicey because it depends on what order forge builds the package. I found that turning that off in the vite.main.config.ts
config works, otherwise the new subdirectories get blown away right after they were built.Here is a snapshot of the config - for the latest, view the repository linked in the question.
vite.main.config.ts
import { defineConfig } from "vite";
import { builtinModules } from "node:module";
export default defineConfig({
build: {
sourcemap: true,
outDir: ".vite", // Output directory set to .vite
lib: {
entry: "src/main.ts",
formats: ["cjs"],
},
rollupOptions: {
external: ["electron", ...builtinModules, "serialport", "drivelist"],
},
},
});
vite.preload.config.ts
import { defineConfig } from "vite";
import { builtinModules } from "node:module";
export default defineConfig({
build: {
sourcemap: true,
outDir: ".vite/preload", // Output directory set to .vite
emptyOutDir: true,
lib: {
entry: "src/preload.ts",
formats: ["cjs"],
},
rollupOptions: {
external: ["electron", ...builtinModules],
output: {
entryFileNames: "[name].js",
},
},
},
});
vite.renderer.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
base: "./",
build: {
sourcemap: true,
outDir: ".vite/renderer", // Output directory set to .vite
emptyOutDir: true,
},
});
forge.config.ts
import type { ForgeConfig } from "@electron-forge/shared-types";
import { AutoUnpackNativesPlugin } from "@electron-forge/plugin-auto-unpack-natives";
import { MakerSquirrel } from "@electron-forge/maker-squirrel";
import { MakerZIP } from "@electron-forge/maker-zip";
import { MakerDeb } from "@electron-forge/maker-deb";
import { MakerDMG } from "@electron-forge/maker-dmg";
import { MakerRpm } from "@electron-forge/maker-rpm";
import { VitePlugin } from "@electron-forge/plugin-vite";
import { FusesPlugin } from "@electron-forge/plugin-fuses";
import { FuseV1Options, FuseVersion } from "@electron/fuses";
const config: ForgeConfig = {
packagerConfig: {
asar: true,
ignore: [/\/\.(?!vite)/],
},
makers: [
new MakerSquirrel({}),
new MakerZIP({}, ["darwin"]),
new MakerRpm({}),
new MakerDeb({}),
new MakerDMG(),
],
rebuildConfig: {
force: true,
onlyModules: ["serialport", "drivelist"],
},
plugins: [
new AutoUnpackNativesPlugin({}),
new VitePlugin({
// `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.ts",
config: "vite.main.config.ts",
target: "main",
},
{
entry: "src/preload.ts",
config: "vite.preload.config.ts",
target: "preload",
},
],
renderer: [
{
name: "main_window",
config: "vite.renderer.config.ts",
},
],
}),
// 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,
}),
],
};
export default config;
Upvotes: 1