build-electron
Use ES modules in Electron now!
build-electron
is a simple build tool for main and preload code of your Electron app, so you don’t have to setup a webpack build system yourself. The aim is to make it easier to get started building Electron apps, like it used to be.
Background
Because Electron does not support ES Modules, and the Node ecosystem is shifting to ESM we are lacking an easy way to simply use ESM Node.js code and modules in our Electron projects. A boilerplate is possible, but it can get outdated quickly and hard to maintain with new updates. So I created this project in the spirit of Create React App. When Electron supports ESM in the future ?, build-electron
can simply be unplugged from the project and it will hopefully “just work”.
Features
- Simple – Doesn’t build frontend; less hairy logic
- Supports Webpack 5
- Customizable
- Building for production or watching
How to use
Note that this project provides Node.js code building only (main, preload) – not renderer code due to there being so many different languages and frameworks for that, and there are already excellent tools for building those. For React based Electron apps, I recommend pairing with Create React App (CRA).
yarn add -D build-electron concurrently wait-on
Put your Electron main ESM source code in src/main/index.js
and preload source in src/preload/index.js
.
Now create a configuration file in your project root build-electron.config.js
:
module.exports = {
mainEntry: 'src/main/index.js',
preloadEntry: 'src/preload/index.js',
outDir: 'build',
mainTarget: 'electron16.0-main',
preloadTarget: 'electron16.0-preload',
}
Add to your package.json
:
{
"main": "build/main.js",
"build": {
"files": [
"build/**/*"
]
},
"scripts": {
"start": "concurrently -k \"build-electron -d\" \"wait-on build/.build-electron-done && electron .\"",
"build": "build-electron"
}
Optionally add your frontend builder like CRA (see below for example).
Now you can start developing:
npm run start
And to build your production app:
npm run build && npm exec electron-builder --mac
Using with Create React App
In this example we will use CRA and electron-builder, although it should be similar with any other framework too.
yarn create react-app my-awesome-app
cd my-awesome-app
yarn add -D build-electron electron electron-builder concurrently wait-on
Now let’s create the project structure. Because CRA uses a particular directory structure (and src
is reserved for the frontend source), we have to adapt to that. The relevant structure will be like this:
? my-awesome-app
? src-main
? index.js
? src-preload
? index.js
? src
[React source]
? public
? main.js [generated by build-electron]
? preload.js [generated by build-electron]
? build [generated by CRA]
? dist [generated by electron-builder]
? package.json
? .gitignore
? src-main
– Source for the electron main process (where you create yourBrowserWindow
). Entry pointindex.js
bundles topublic/main.js
.? src-preload
– Source for the electron preload script. Entry pointindex.js
bundles topublic/preload.js
.? src
– Static frontend React files processed by CRA and outputted intobuild
, along with files frompublic
.? public
– Output ofbuild-electron
‘s entry points (src-main
andsrc-preload
), and is the input of the CRA build’s static files.? dist
– Final production app output generated byelectron-builder
.
Now create a configuration file in your project root build-electron.config.js
:
module.exports = {
mainEntry: 'src-main/index.js',
preloadEntry: 'src-preload/index.js',
outDir: 'public',
mainTarget: 'electron16.0-main',
preloadTarget: 'electron16.0-preload',
}
Relevant bits to add to your package.json
:
"main": "public/main.js",
"build": {
"extraMetadata": {
"main": "build/main.js"
},
"files": [
"build/**/*"
]
},
"scripts": {
"start": "concurrently -k \"BROWSER=none react-scripts start\" \"build-electron -d\" \"wait-on public/.build-electron-done http://localhost:3000 && electron .\"",
"build": "build-electron && react-scripts build",
"postinstall": "electron-builder install-app-deps"
}
Add to .gitignore
:
/build
/dist
/icon-build
/public/main.js
/public/preload.js
Now you can start developing:
npm run start
And to build your production app:
npm run build && npm exec electron-builder --mac
build-electron.config.js
options
Note that paths can be relative to project root or absolute.
mainEntry
– Electron’s main entrypoint.preloadEntry
– Electron’s preload entrypoint.mainExtraEntries
,preloadExtraEntries
– Include additional main or preload entrypoints:- Key-value pairs, example:
{ 'name': 'path/to/source' }
- Key-value pairs, example:
outDir
– Output files to this path.externals
– Use this to exclude certain modules from being bundled, like native dependencies.customConfig
– Customize Webpack config for bothmain.js
andpreload.js
customMainConfig
– Customize Webpack config just formain.js
customPreloadConfig
– Customize Webpack config just forpreload.js
mainTarget
– Should useelectronX.Y-main
. See Webpack targetpreloadTarget
– Should useelectronX.Y-preload
. See Webpack target
build-electron
CLI
--config
,-c
– Override path tobuild-electron.config.js
(default is in project root)--dev
,-d
– Run in development mode with file watcher (default is production build + exit after finish)
Size optimizations
Make sure you follow these guidelines:
- Any npm dependencies that have binaries (native Node.js modules) must be in
dependencies
and included in theexternals
options so they don’t get bundled in - All other dependencies should be
devDependencies
to prevent them from being included by electron-builder.
Source Maps
yarn add -D source-map-support
Add to the top of src-main/index.js
:
import 'source-map-support/register';
TODO
- Code splitting
- Figure out Source Maps for preload
- Create example project
- Environment variables
Alternatives / inspiration
- https://github.com/cawa-93/vite-electron-builder – High maintenance to keep up-to-date with boilerplates.
- https://github.com/electron-userland/electron-compile unmaintained.
- https://github.com/electron-userland/electron-webpack – Somewhat outdated and unmaintained.
- Using Vite instead of Webpack, however Vite is less popular and I’ve experienced issues with some npm modules. As Webpack is the de-facto standard way of building Javascript and used by so many developers, it was the best choice.
Development
To test in a consuming project (yarn v2+), add to its package.json
:
"resolutions": {
"build-electron": "portal:/path/to/build-electron"
}
then run yarn
.