/vite-electron-plugin

Fast, Electron plugin for Vite

Primary LanguageTypeScriptMIT LicenseMIT

vite-electron-plugin

Fast, Electron plugin for Vite

NPM version NPM Downloads

  • 🚀 Fast (Not Bundle, based on esbuild)
  • 🎯 Plugin (Like Vite's plugin)
  • 🔥 Hot reload
  • 📦 Out of the box
  • 🌱 What you see is what you get

Install

npm i vite-electron-plugin -D

Examples

Recommend structure

Let's use the official template-vanilla-ts created based on create vite as an example

+ ├─┬ electron
+ │ └── main.ts
  ├─┬ src
  │ ├── main.ts
  │ ├── style.css
  │ └── vite-env.d.ts
  ├── .gitignore
  ├── favicon.svg
  ├── index.html
  ├── package.json
  ├── tsconfig.json
+ └── vite.config.ts
  • 🚨 Any files ending with preload.ext are considered Preload-Scripts(e.g. preload.ts, foo.preload.js), and when they change, they will trigger the Electron-Renderer process to reload instead of restarting the entire Electron App.
  • 🚨 By default, the files in electron folder will be built into the dist-electron
  • 🚨 Currently, "type": "module" is not supported in Electron

Usage

vite.config.ts

import electron from 'vite-electron-plugin'

export default {
  plugins: [
    electron({
      include: [
        // The Electron source codes directory
        'electron',
      ],
    }),
  ],
}

electron/main.ts

import { app, BrowserWindow } from 'electron'

app.whenReady().then(() => {
  const win = new BrowserWindow()

  if (app.isPackaged) {
    win.loadFile('your-build-output-index.html')
  } else {
    win.loadURL(process.env.VITE_DEV_SERVER_URL)
  }
})

Plugin API

The design of plugin is similar to Vite's plugin. But simpler, only 4 hooks in total.

configResolved

  • Type: (config: ResolvedConfig) => void | Promise<void>
  • Kind: async, sequential

You can freely modify the config argument in ths hooks or use.

onwatch serve only

  • Type: (envet: 'add' | 'change' | 'addDir' | 'unlink' | 'unlinkDir', path: string) => void
  • Kind: async, parallel

Triggered by include file changes. You can emit some files in this hooks. Even restart the Electron App.

transform

  • Type: (args: { filename: string, code: string, done: () => void }) => string | import('esbuild').TransformResult | void | Promise<string | import('esbuild').TransformResult | void>
  • Kind: async, sequential

Triggered by changes in extensions files in include.

ondone

  • Type: (args: { filename: string, distname: string }) => void
  • Kind: async, parallel

Triggered when transform() ends or a file in extensions is removed.

Builtin Plugin

import path from 'path'
import electron from 'vite-electron-plugin'
import {
  alias,
  copy,
  customStart,
} from 'vite-electron-plugin/plugin'

export default {
  plugins: [
    electron({
      plugins: [
        alias([
          // Absolute path are recommended for alias, which will automatically calculate relative path
          { find: '@', replacement: path.join(__dirname, 'src') },
        ]),

        copy([
          // Support glob
          { from: 'foo/*.ext', to: 'dest' },
        ]),

        customStart(({ startup }) => {
          // If you want to control the launch of Electron App yourself
          startup()
        }),
      ],
    }),
  ],
}

API (Define)

electron(config: Configuration)
export interface Configuration {
  /** Like Vite's plugin */
  plugins?: {
    name: string
    configResolved?: (config: ResolvedConfig) => void | Promise<void>
    /** Triggered by `include` file changes. You can emit some files in this hooks. */
    onwatch?: (envet: 'add' | 'change' | 'addDir' | 'unlink' | 'unlinkDir', path: string) => void
    /** Triggered by changes in `extensions` files in include */
    transform?: (args: {
      /** Raw filename */
      filename: string
      code: string
      /** Skip subsequent transform hooks */
      done: () => void
    }) => string | null | void | import('esbuild').TransformResult | Promise<string | null | void | import('esbuild').TransformResult>
    /** Triggered when `transform()` ends or a file in `extensions` is removed */
    ondone?: (args: {
      /** Raw filename */
      filename: string
      /** Dist filename */
      distname: string
    }) => void
  }[],
  /** @default process.cwd() */
  root?: string
  /** Electron-Main, Preload-Scripts */
  include: string[]
  /** @default 'dist-electron' */
  outDir?: string
  /** Options of `esbuild.transform()` */
  transformOptions?: import('esbuild').TransformOptions
}
ResolvedConfig
export interface ResolvedConfig {
  plugins: Required<Configuration>['plugins']
  /** @default process.cwd() */
  root: string
  /** Relative path */
  include: string[]
  /** Absolute path */
  outDir: string
  /** Options of `esbuild.transform()` */
  transformOptions: import('esbuild').TransformOptions

  config: Configuration
  /** Vite's command */
  command: 'build' | 'serve',
  /** @default ['.ts', '.tsx', '.js', '.jsx'] */
  extensions: string[]
  /** The value is `null` at build time */
  watcher: import('chokidar').FSWatcher | null
  /** The value is `null` at build time */
  viteDevServer: import('vite').ViteDevServer | null,
  /** Internal functions (🚨 Experimental) */
  _fn: {
    /** Electron App startup function */
    startup: (args?: string[]) => void
    include2files: (config: ResolvedConfig, include?: string[]) => string[]
    include2globs: (config: ResolvedConfig, include?: string[]) => string[]
    replace2dist: (filename: string, replace2js?: boolean) => string
  }
}