我正在为 Meshtastic 开发一个开源固件更新应用程序 (https://github.com/medentem/electron-flasher/)。该应用程序是与 Vite for Electron 捆绑在一起的 ReactJS/TS 应用程序。当在 OSX (
electron-forge start
) 上本地运行时,它运行完美,并且本机依赖项(串行端口和驱动器列表)似乎被正确引用。但是,在捆绑 OSX 应用程序进行分发 (electron-forge make
) 后,生成的应用程序启动时会出现以下错误:
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)
我确认
serialport
和 drivelist
包在 vite 中配置为外部(参见 https://github.com/medentem/electron-flasher/blob/main/vite.main.config.ts),以及 electron-forge
配置 AutoUnpackNativesPlugin
以确保两个包都被重建并包含在 asar 包之外。但这似乎不起作用。
我还尝试使用
electron-builder
生成 OSX 应用程序,但在这种情况下,该应用程序甚至不会启动。
预先感谢您的帮助!
经过很多个小时的尝试和失败,我能够获得正确的配置来构建项目。
主要学习内容:
external
汇总选项中。base
路径才能正确解析路径。emptyOutDir
配置值是......冒险的,因为它取决于 forge 构建包的顺序。我发现在 vite.main.config.ts
配置中关闭它是有效的,否则新的子目录在构建后就会被吹走。这是配置的快照 - 有关最新配置,请查看问题中链接的存储库。
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;