DEV Community

Cover image for How to build An Electron App by esbuild with hot reload functionality?
sprout2000
sprout2000

Posted on • Edited on

How to build An Electron App by esbuild with hot reload functionality?

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();
Enter fullscreen mode Exit fullscreen mode

Create a type definition file

  • src/@types/Debug.d.ts
declare const DEBUG: boolean;
Enter fullscreen mode Exit fullscreen mode

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 ."
  }
}
Enter fullscreen mode Exit fullscreen mode

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"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
naucode profile image
Al - Naucode

Hey, that was a nice read, you got my follow, keep writing 😉