-
-
Notifications
You must be signed in to change notification settings - Fork 538
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Forge make combined with vite is creating an incomplete asar #3738
Comments
I'm not sure about 7.5.0 but in earlier versions it was needed to move |
This works as expected downgrading to 7.4.0. I'll give this a try but no bundles are getting added to the app.asar file at 7.5.0. Well now I'm getting a require is not defined error after making the project. :/ |
@steveoh I use electron-log and it works fine after downgrading. |
I've had this happen to ubuntu as well, but found a way to get the trick: |
Ran into a similar issue with other packages. Found a work-around and modified it to work for me. Seems // forge.config.js
const { spawn } = require('node:child_process');
module.exports = {
hooks: {
packageAfterPrune: async (config, build_path) => {
const vite_config = await import('./vite.preload.config.mjs');
const external = vite_config?.default?.build?.rollupOptions?.external || [];
const commands = [
'install',
'--no-package-lock',
'--no-save',
...external,
];
return new Promise((resolve, reject) => {
npm = spawn('npm', commands, {
cwd: build_path,
stdio: 'inherit',
shell: true,
});
npm.on('close', (code) => {
if (0 === code) {
resolve();
return;
}
reject(`Process exited with code: ${code}`);
});
npm.on('error', reject);
});
},
},
// ...
}; |
same issue here, happens when trying to load a json file
|
temp workaround is to disable asar by modifying the following properties in const config: ForgeConfig = {
packagerConfig: {
asar: false,
},
plugins: [
new FusesPlugin({
[FuseV1Options.OnlyLoadAppFromAsar]: false,
}),
],
}; |
This error is not related to ASAR. Even if ASAR is disabled, the error is still reported. I think it is a vite plug-in problem. It works normally in 7.4.0 |
I'm getting this issue on 7.4.0, I can't find any errors in the logs to understand what's causing it. |
Still having these same problems with forge 7.6. The docs state (see screenshot below) that native node modules should work "out of the box", which they do not. But also trying any of the "workarounds" is not working either... added to build.rollupOptions.external etc. and still not getting any node_modules included in the package. This is starting to become very frustrating. Last year spent months figuring out workarounds to make 7.3/7.4 work with vite to properly package the correct node modules... finally got it working, but now to update to the current stable version of electron (v34) need to update forge as well and now everything is broken again with native modules. I hate to make a negative post but this is becoming a pattern and a real issue any time we have to consider updating anything with electron forge (for vite). cc @erickzhao |
I would like to know if I am also affected by the issue at hand. - So that I can put my mind to rest, after having put in far too many hours to get the release build of my project to run with better-sqlite3 marked as an external lib. I have scrolled through docs, forums and bug reports looking for a solution. But I am still stuck. I am using a MacBook (arm64 with M2 chip). I have setup my project with create-electron-app . --template=vite-typescript After running the electron forge package command yarn run package and starting the bundled app open out/test-darwin-arm64/test.app I am running into this error: ![]() package.json{
"name": "test",
"productName": "test",
"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"
},
"devDependencies": {
"@electron-forge/cli": "^7.6.0",
"@electron-forge/maker-deb": "^7.6.0",
"@electron-forge/maker-rpm": "^7.6.0",
"@electron-forge/maker-squirrel": "^7.6.0",
"@electron-forge/maker-zip": "^7.6.0",
"@electron-forge/plugin-auto-unpack-natives": "^7.6.0",
"@electron-forge/plugin-fuses": "^7.6.0",
"@electron-forge/plugin-vite": "^7.6.0",
"@electron/asar": "^3.2.18",
"@electron/fuses": "^1.8.0",
"@types/bootstrap": "^5",
"@types/electron-squirrel-startup": "^1",
"@types/node": "^22.10.5",
"@vitejs/plugin-vue": "^5.2.1",
"electron": "33.3.1",
"ts-node": "^10.9.2",
"typescript": "^5.7.2",
"vite": "^5.0.12",
"vue": "^3.5.13",
"vue-tsc": "^2.2.0"
},
"keywords": [],
"author": {
"name": "***",
"email": "***"
},
"license": "MIT",
"packageManager": "[email protected]+sha512.5383cc12567a95f1d668fbe762dfe0075c595b4bfff433be478dbbe24e05251a8e8c3eb992a986667c1d53b6c3a9c85b8398c35a960587fbd9fa3a0915406728",
"dependencies": {
"@popperjs/core": "^2.11.8",
"@types/better-sqlite3": "^7.6.12",
"apexcharts": "^4.3.0",
"axios": "^1.7.9",
"better-sqlite3": "^11.8.1",
"bootstrap": "^5.3.3",
"bootstrap-icons": "^1.11.3",
"electron-squirrel-startup": "^1.0.1",
"pinia": "^2.3.0",
"vue-router": "^4.5.0",
"vue3-apexcharts": "^1.8.0"
}
} vite.main.config.tsimport path from 'path';
import { defineConfig } from 'vite';
// https://vitejs.dev/config
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
},
build: {
rollupOptions: {
external: [
'better-sqlite3'
],
},
}
}); forge.config.tsimport 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 { 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: {
unpack: "**/node_modules/better-sqlite3/**/*"
},
},
rebuildConfig: {
onlyModules: ['better-sqlite3'],
buildFromSource: true,
force: true,
},
makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerRpm({}), new MakerDeb({})],
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; Note that leaving out the AutoUnpackNatviesPlugin or the rebuildConfig does not change the behaviour. Cheers! |
again for anyone willing to forgo asar, disabling it fixed everything in my case |
@lorenzjosten yes the problem is the vite plugin for forge is not packaging any node_modules which means if you use any node native libs like better-sqlite it will not work. It will work in dev because forge/vite uses the external package config option to know to leverage your local node_modules for those native libs like better-SQLite. But then when packaged for production those required node_modules are missing so everything breaks. |
This comment has been minimized.
This comment has been minimized.
I ended up recreating my app from the newest forge template and everything started working again. You might give that a try. |
@steveoh a few follow-up questions:
I have also tried from scratch from the current 7.6 template and native modules like sqlite do not get packaged (as described in earlier comments). |
It's amazing that I worked normally in version 7.4, but I cloned a 7.4 version forge from a previous GitHub project to write the project. At first, it worked normally, but after a period of time, the package results still reported errors. When the 7.5 and 7.6 versions were updated, I tried to solve the problem. Occasionally, the package was fine, but most of the time it was not executed correctly. I have tried using type=module, but there are still problems. The current forge cannot be used out of the box. The simplest temporary solution: When using the package, use the hook to copy package.json to the path after the package, and then use JavaScript to call npm i to download the module. This is my whimsical idea, I haven't tried it yet, and I can't use the computer recently. Please help me try it, lol |
In addition, it is not just the vite template, even if you do not set up a template or use webpack, this problem may be caused by the package |
@lorenzjosten i've got a similar issue, with another native library (node-rfc), and was able to package succesfully using the following:
Originally posted by @mtharrison in #3734 |
I got the same issue. My electron vite application that I'm trying to make using electron-forge + the vite plugin throws an error on open that it "cannot find module I tried everything that was mentioned in the answers to this issue to fix it, but no luck so far. |
I'm running into this issue as well. Native packages (those marked as external in the Vite config) just don't get packaged. The hack linked in #3734 (comment) might kinda work but for me it messed with codesigning and is causing other issues. |
Ok I debated whether to post this here because of how YMMV and who knows what corner cases may be missing. But this is working for us now for the time being. I really hope the electron team is able to get the vite plugin working as expected for external dependencies, but until then, this will do. Hope this helps others! I tried to "clean it up" and generalize it as much as possible (given there are several things specific to our application that we have to do a bit differently) but if nothing else this should hopefully give you a baseline to build from. A few key notes:
// forge.config.ts
import type { ForgeConfig } from '@electron-forge/shared-types';
import { readdirSync, rmdirSync, statSync } from 'node:fs';
import { join, normalize } from 'node:path';
// Use flora-colossus for finding all dependencies of EXTERNAL_DEPENDENCIES
// flora-colossus is maintained by MarshallOfSound (a top electron-forge contributor)
// already included as a dependency of electron-packager/galactus (so we do NOT have to add it to package.json)
// grabs nested dependencies from tree
import { Walker, DepType, type Module } from 'flora-colossus';
let nativeModuleDependenciesToPackage: string[] = [];
export const EXTERNAL_DEPENDENCIES = [
'realm',
'electron-squirrel-startup',
'better-sqlite3',
];
const config: ForgeConfig = {
hooks: {
prePackage: async () => {
const projectRoot = normalize(__dirname);
const getExternalNestedDependencies = async (
nodeModuleNames: string[],
includeNestedDeps = true
) => {
const foundModules = new Set(nodeModuleNames);
if (includeNestedDeps) {
for (const external of nodeModuleNames) {
type MyPublicClass<T> = {
[P in keyof T]: T[P];
};
type MyPublicWalker = MyPublicClass<Walker> & {
modules: Module[];
walkDependenciesForModule: (
moduleRoot: string,
depType: DepType
) => Promise<void>;
};
const moduleRoot = join(projectRoot, 'node_modules', external);
const walker = new Walker(moduleRoot) as unknown as MyPublicWalker;
walker.modules = [];
await walker.walkDependenciesForModule(moduleRoot, DepType.PROD);
walker.modules
.filter((dep) => (dep.nativeModuleType as number) === DepType.PROD)
// for a package like '@realm/fetch', need to split the path and just take the first part
.map((dep) => dep.name.split('/')[0])
.forEach((name) => foundModules.add(name));
}
}
return foundModules;
};
const nativeModuleDependencies =
await getExternalNestedDependencies(EXTERNAL_DEPENDENCIES);
nativeModuleDependenciesToPackage = Array.from(nativeModuleDependencies);
},
packageAfterPrune: async (_forgeConfig, buildPath) => {
function getItemsFromFolder(
path: string,
totalCollection: {
path: string;
type: 'directory' | 'file';
empty: boolean;
}[] = []
) {
try {
const normalizedPath = normalize(path);
const childItems = readdirSync(normalizedPath);
const getItemStats = statSync(normalizedPath);
if (getItemStats.isDirectory()) {
totalCollection.push({
path: normalizedPath,
type: 'directory',
empty: childItems.length === 0,
});
}
childItems.forEach((childItem) => {
const childItemNormalizedPath = join(normalizedPath, childItem);
const childItemStats = statSync(childItemNormalizedPath);
if (childItemStats.isDirectory()) {
getItemsFromFolder(childItemNormalizedPath, totalCollection);
} else {
totalCollection.push({
path: childItemNormalizedPath,
type: 'file',
empty: false,
});
}
});
} catch {
return;
}
return totalCollection;
}
const getItems = getItemsFromFolder(buildPath) ?? [];
for (const item of getItems) {
const DELETE_EMPTY_DIRECTORIES = true;
if (item.empty === true) {
if (DELETE_EMPTY_DIRECTORIES) {
const pathToDelete = normalize(item.path);
// one last check to make sure it is a directory and is empty
const stats = statSync(pathToDelete);
if (!stats.isDirectory()) {
// SKIPPING DELETION: pathToDelete is not a directory
return;
}
const childItems = readdirSync(pathToDelete);
if (childItems.length !== 0) {
// SKIPPING DELETION: pathToDelete is not empty
return;
}
rmdirSync(pathToDelete);
}
}
}
},
},
packagerConfig: {
prune: true,
asar: { unpackDir: '' },
ignore: (file) => {
const filePath = file.toLowerCase();
const KEEP_FILE = {
keep: false,
log: true,
};
// NOTE: must return false for empty string or nothing will be packaged
if (filePath === '') KEEP_FILE.keep = true;
if (!KEEP_FILE.keep && filePath === '/package.json') KEEP_FILE.keep = true;
if (!KEEP_FILE.keep && filePath === '/node_modules') KEEP_FILE.keep = true;
if (!KEEP_FILE.keep && filePath === '/.vite') KEEP_FILE.keep = true;
if (!KEEP_FILE.keep && filePath.startsWith('/.vite/')) KEEP_FILE.keep = true;
if (!KEEP_FILE.keep && filePath.startsWith('/node_modules/')) {
// check if matches any of the external dependencies
for (const dep of nativeModuleDependenciesToPackage) {
if (
filePath === `/node_modules/${dep}/` ||
filePath === `/node_modules/${dep}`
) {
KEEP_FILE.keep = true;
break;
}
if (filePath === `/node_modules/${dep}/package.json`) {
KEEP_FILE.keep = true;
break;
}
if (filePath.startsWith(`/node_modules/${dep}/`)) {
KEEP_FILE.keep = true;
KEEP_FILE.log = false;
break;
}
}
}
if (KEEP_FILE.keep) {
if (KEEP_FILE.log) console.log('Keeping:', file);
return false;
}
return true;
},
// if applicable, your other config / options / app info
overwrite: true,
// osxSign: {
// // if applicable, your codesigning configuration here
// },
// osxNotarize: {
// // if applicable, your notarization configuration here
// },
},
rebuildConfig: {},
makers: [
// if applicable, your maker code here
],
publishers: [
// if applicable, your publisher code here
],
};
export default config; |
@MarshallOfSound given I am utilizing your flora-colossus dependency walker, if you think I am using it wrong or could improve how I am using it, please don't hesitate to critique my code! cc @erickzhao hopefully this can help with coming up with a solution to fix the problems with the forge vite plugin/template out of the box. Thanks! |
Also I would be curious to hear from the electron team how the It feels like there is an opportunity here to solve this once and for all! :-) But I attempted to use the |
i had a similar issue to this when running
this issue occurs when building. i've tried with plugin https://www.electronforge.io/config/plugins/auto-unpack-natives but it still doesn't work. How to work around this issue while i include packagerConfig: {
asar: true,
extraResource: [
'node_modules'
],
}, However this was a bad idea, my app tripled in size. |
Yes you definitely do not want to include all your In a perfect world, the vite / forge plugin/template would allow us to add a config item for the names of the node modules that we want included / packaged with our app (basically external / native node modules) and then would handle for us the dependency graph to make sure the dependencies of those named dependencies are also included (similar to my code a few messages above). |
yup, i tried this approach (#3738 (comment)), and it worked fine, app size reduced by -+ 500Mb. |
Glad to hear you got something working! But be aware if you are doing any code signing (especially notarization for macos) you will likely run into issues. But if you do not need that then should be fine. If you decide to give my solution a try (#3738 (comment)) please let me know if you have any questions or issues and/or if you get it to work for your situation. |
This is a very good solution, you reduced my app size by 56Mb smaller than the solution I mentioned before. I will use this method for the time being until the official bug fix is released. Thank you @GitMurf |
I also encountered the same problem. My electron-forge version is 7.6.1. After I packaged and ran the program, I got an error that better-sqlite3 could not be found. I checked for a long time until I tried to parse the asar file and found that there was no node_modules. I tried to disable asar, but the problem still exists. According to @SupianIDz 's answer, we only need to copy node_modules to solve this problem. In order to copy only the required modules, I wrote a hook to implement a custom copy operation, as shown below // forge.config.js
const path = require('path')
const fs = require('fs-extra')
module.exports = {
packagerConfig: {
asar: false,
},
// ...
plugins: [
// ...
new FusesPlugin({
// ...
[FuseV1Options.OnlyLoadAppFromAsar]: false,
}),
],
hooks: {
packageAfterCopy: async (config, buildPath, electronVersion, platform, arch) => {
await fs.copy(path.join(__dirname, 'node_modules', 'better-sqlite3'), path.join(buildPath, 'node_modules', 'better-sqlite3'))
await fs.copy(path.join(__dirname, 'node_modules', 'bindings'), path.join(buildPath, 'node_modules', 'bindings'))
await fs.copy(path.join(__dirname, 'node_modules', 'file-uri-to-path'), path.join(buildPath, 'node_modules', 'file-uri-to-path'))
},
},
} Although this method can solve the problem, it is only a stopgap measure. You still need to disable asar, and analyze which modules need to be copied, including its dependencies. I hope this problem can be solved as soon as possible. |
@laa-1 checkout my comment above: #3738 (comment) You are absolutely correct on your findings. My comment above shows how we are handling the nested dependencies and also how to keep using asar. I'm hopeful that an official solution to this is added to forge / vite plugin in the future, but in the meantime the solution on my comment is working for us 🤞 |
thank @GitMurf you save my life. Struggle with this issue 1 months then this is absolutely good peace of code |
Pre-flight checklist
Electron Forge version
7.5.0
Electron version
33.0.1
Operating system
windows 11
Last known working Electron Forge version
7.4.0
Expected behavior
npm run make creates an asar file that includes the node_modules and the app can run on windows successfully.
Actual behavior
node_modules aren't present and install fails with ERR_MODULE_NOT_FOUND
Steps to reproduce
Additional information
This is only happening on windows as I believe that is the only place that asar is used.
We are using vite instead of webpack.
The main.js file from within the asar in the error message imports the following
Where electron-window-state and update-electron-app and I assume electron-squirrel-startup are not available to be imported.
The text was updated successfully, but these errors were encountered: