I'm building a small application with electron.js and react (my first app of this kind).
I use electron-packager for builds. The problem is that my builds contain all my node modules. I use my built react app in production though.
I thought, maybe I should use separate node_modules for my react part and node part. I mean, in this case it would copy only the packages which are used in node. It's just a few small packages like dotenv.
Here you can see the npm scripts:
"scripts": {
"start": "react-scripts start",
"electron": "electron index.js",
"release-win": "electron-packager ./ mailer --platform=win32 --arch=x64 --overwrite",
"release-darwin": "electron-packager ./ mailer --platform=darwin --arch=x64 --overwrite",
"release-linux": "electron-packager ./ mailer --platform=linux --arch=x64 --overwrite",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
That's how the window is created:
function createWindow() {
// Build desktop window
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
preload: path.join(__dirname, 'preload.js')
}
});
// Add developer tools in dev mode only
if(isDev)
mainWindow.webContents.openDevTools()
// Remove top menu with dev-actions in production
if(!isDev)
mainWindow.removeMenu();
// Load environment variables form .env
dotenv.config({
path: path.join(__dirname, './.env')
});
// Load url:
// - In dev it's loaded from localhost - react dev server with hot reloads and other features
// - In prod it's loads built application
mainWindow.loadURL(
isDev
? 'http://localhost:3000' // Development server URL
: `file://${path.join(__dirname, './build/index.html')}` // Production build path
);
mainWindow.on('closed', () => {
mainWindow = null;
});
}
Since you use React, and more specifically in your case CRA, you have webpack that can bundle the files for you, and save you from packging
node_modulescompletely. Given this, one solution for your issue is to change the webpack configuration to include your Electron files in the bundle, and then to configureelectron-packagerto only package the bundled files.1. Configure
webpackto bundle your Electron codeBecause they use Node.js, Electron files can't be bundled with the same configuration as for React (browser) files. So to include them in the bundle, you can't just add an entry point, you actually need to add an extra configuration for your main process code (and you can also do the same for the preload, if you have one).
When not ejected, CRA offers little or no possibility of editing the bundler's configuration. So to edit it without ejecting, you can use a tool such as
cracoorreact-app-rewired. I never used Craco, so I can only give you an example of configuration for Rewired:config-overrides.js
On this example,
main.jsis inelectron/main.jsand it will be bundled asbuild/static/js/electron.main.js. In developement, the default original config is used.The important part is the
target: for the main processelectron-maincan be used, for the preload it would beelectron-preload. Unlike thebrowserslisttarget (which is used for your renderer, React code), these targets are compatible with the Node.js code of your Electron files.Note that on build you will get the error:
This is because CRA was not configured to handle multiple webpack configurations. It will not break the process so it can be ignored (for reference).
Now, to load the app in the window you can use:
With this configuration, all your files should be neatly bundled inside your
buildfolder.2. Configure
electron-packagerto only package the bundled filesNow that you have a
buildfolder containing everything you need, you want to tellelectron-packagerthat it should only package this folder (and thepackage.json, which I'm pretty sure is mandatory). As I gathered,electron-packageronly provide anignoreoption for the files you don't want to package. In command line, it should look something like:I can't test this so I'm not 100% sure about the syntax, but this is based on a configuration shared here.
3. Use the correct
mainentry point in productionThe main entry point is the one specified on your
package.json, as required by Electron. In production, you want it to be the path to your main file in thebuildfolder. This part is a little tricky becauseelectron-packagerdoesn't seem to provide an option to change it while packaging (or I have not found it), so it means you either need to do it manually every time you want to package, or use a script to do it for you. Here is a small script I've used when I needed to do this.An example of usage with the script in
bin/change-entry-point.js, your main file inelectron/main.js, the modulecross-envand yarn:The
exit 0on thebuildscript is to prevent the process to stop when it encounters the error I mentioned on step 1.This command will bundle your files for production and then package them using your command
release-win:Alternative
If you're looking for a simplier way to package your app without the need to add a custom script, I would suggest to replace
electron-packagerwithelectron-builder, this way you can do steps 2 and 3 with the following configuration:See the official docs for more info on this configuration.