How to build An Electron App by esbuild with hot reload functionality?
sprout2000
Posted on January 30, 2023
- sample repo: https://github.com/sprout2000/electron-react-esbuild
A script to run esbuild
-
esbuild.ts
import type { BuildOptions } from 'esbuild';
import { build, context } from 'esbuild';
import { htmlPlugin } from '@craftamap/esbuild-plugin-html';
/**
* Process according to the value of
* the NODE_ENV environment variable
*/
const isDev = process.env.NODE_ENV === 'development';
// Common settings for build or watch
const common: BuildOptions = {
outdir: 'dist', // output destination
bundle: true, // bundle, of course.
minify: !isDev,
sourcemap: isDev,
define: {
// Requires a type declaration file (later mention)
DEBUG: isDev ? 'true' : 'false',
},
};
// Configuration for main process
const main: BuildOptions = {
// Import common settings
...common,
// main process and preload script
entryPoints: ['src/main.ts', 'src/preload.ts'],
// Build for Node.js environment
platform: 'node',
// you will get run-time error without the following:
external: ['electron'],
};
// Configuration for renderer process
const renderer: BuildOptions = {
...common,
// Entry file for React app
entryPoints: ['src/web/index.tsx'],
// Build for Web
platform: 'browser',
// Required for htmlPlugin
metafile: true,
plugins: [
htmlPlugin({
files: [
{
entryPoints: ['src/web/index.tsx'],
filename: 'index.html',
htmlTemplate: 'src/web/index.html',
},
],
}),
],
};
// Function to execute during development
const watch = async () => {
await (await context({ ...main })).watch();
await (await context({ ...renderer })).watch();
};
// Function to execute during production build
const prod = async () => {
build({ ...main });
build({ ...renderer });
};
// Executing scripts
isDev ? watch() : prod();
Create a type definition file
-
src/@types/Debug.d.ts
declare const DEBUG: boolean;
Update your package.json
Use electronmon to restart the Electron app when the main process code changes, and reload WebView for the renderer process.
{
// Specify the main process JavaScript file output by esbuild.
"main": "dist/main.js",
"scripts": {
"dev": "rimraf dist && run-p dev:esbuild dev:electron",
// Run `tsc` without output before build as esbuild does not type check.
"build": "tsc && cross-env NODE_ENV=\"production\" ts-node ./esbuild.ts",
"dev:esbuild": "cross-env NODE_ENV=\"development\" ts-node ./esbuild.ts",
"dev:electron": "wait-on dist/main.js dist/index.html && electronmon ."
}
}
You will need to install rimraf, npm-run-all, cross-env and wait-on.
- A sample for
tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"esModuleInterop": true,
"isolatedModules": true, // <-- required
"resolveJsonModule": true,
"lib": ["DOM", "ESNext"],
"jsx": "react-jsx",
"strict": true,
"noEmit": true // <-- required
},
"include": ["src"],
"ts-node": {
"compilerOptions": {
"module": "CommonJS"
}
}
}
Assumed directory structure
% tree
.
├── esbuild.ts
├── package.json
├── src
│ ├── @types
│ │ └── Debug.d.ts
│ ├── main.ts
│ ├── preload.ts
│ └── web
│ ├── App.css
│ ├── App.tsx
│ ├── index.html
│ └── index.tsx
└── tsconfig.json
4 directories, 10 files
💖 💪 🙅 🚩
sprout2000
Posted on January 30, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.