Skip to content

Commit

Permalink
Merge pull request #260 from electron-vite/v0.29.0
Browse files Browse the repository at this point in the history
V0.29.0
  • Loading branch information
caoxiemeihao authored Nov 16, 2024
2 parents 4aae557 + e6787c7 commit e1eb3c7
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 4 deletions.
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
## 0.29.0 (2024-11-16)

- 638d0f3 feat: support main process control hot-reload
- c58fbf9 feat: mock index.html for support use the main process only

**Hot Reload**

Since `v0.29.0`, when preload scripts are rebuilt, they will send an `electron-vite&type=hot-reload` event to the main process.
If your App doesn't need a renderer process, this will give you **hot-reload**.

```js
// electron/main.ts

process.on('message', (msg) => {
if (msg === 'electron-vite&type=hot-reload') {
for (const win of BrowserWindow.getAllWindows()) {
// Hot reload preload scripts
win.webContents.reload()
}
}
})
```

## 0.28.8 (2024-09-19)

- 3239718 fix: better exit app #251
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,24 @@ build({
})
```

**Hot Reload**

Since `v0.29.0`, when preload scripts are rebuilt, they will send an `electron-vite&type=hot-reload` event to the main process.
If your App doesn't need a renderer process, this will give you **hot-reload**.

```js
// electron/main.ts

process.on('message', (msg) => {
if (msg === 'electron-vite&type=hot-reload') {
for (const win of BrowserWindow.getAllWindows()) {
// Hot reload preload scripts
win.webContents.reload()
}
}
})
```

## How to work

It just executes the `electron .` command in the Vite build completion hook and then starts or restarts the Electron App.
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vite-plugin-electron",
"version": "0.28.8",
"version": "0.29.0",
"description": "Electron 🔗 Vite",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand All @@ -26,7 +26,7 @@
"type": "git",
"url": "git+https://github.com/electron-vite/vite-plugin-electron.git"
},
"author": "草鞋没号 <308487730@qq.com>",
"author": "Leo Wang(草鞋没号) <308487730@qq.com>",
"license": "MIT",
"packageManager": "pnpm@8.0.0",
"scripts": {
Expand Down
29 changes: 27 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
import {
resolveServerUrl,
resolveViteConfig,
resolveInput,
mockIndexHtml,
withExternalBuiltins,
treeKillSync,
} from './utils'
Expand Down Expand Up @@ -51,6 +53,7 @@ export default function electron(options: ElectronOptions | ElectronOptions[]):
const optionsArray = Array.isArray(options) ? options : [options]
let userConfig: UserConfig
let configEnv: ConfigEnv
let mockdInput: Awaited<ReturnType<typeof mockIndexHtml>> | undefined

return [
{
Expand Down Expand Up @@ -89,11 +92,14 @@ export default function electron(options: ElectronOptions | ElectronOptions[]):
options.onstart.call(this, {
startup,
// Why not use Vite's built-in `/@vite/client` to implement Hot reload?
// Because Vite only inserts `/@vite/client` into the `*.html` entry file.
// Because Vite only inserts `/@vite/client` into the `*.html` entry file, the preload scripts are usually a `*.js` file.
// @see - https://github.com/vitejs/vite/blob/v5.2.11/packages/vite/src/node/server/middlewares/indexHtml.ts#L399
reload() {
if (process.electronApp) {
(server.hot || server.ws).send({ type: 'full-reload' })

// For Electron apps that don't need to use the renderer process.
startup.send('electron-vite&type=hot-reload')
} else {
startup()
}
Expand All @@ -120,7 +126,15 @@ export default function electron(options: ElectronOptions | ElectronOptions[]):
// Make sure that Electron can be loaded into the local file using `loadFile` after packaging.
config.base ??= './'
},
async configResolved(config) {
const input = resolveInput(config)
if (input == null) {
mockdInput = await mockIndexHtml(config)
}
},
async closeBundle() {
mockdInput?.remove()

for (const options of optionsArray) {
options.vite ??= {}
options.vite.mode ??= configEnv.mode
Expand Down Expand Up @@ -154,7 +168,10 @@ export async function startup(
await startup.exit()

// Start Electron.app
process.electronApp = spawn(electronPath, argv, { stdio: 'inherit', ...options })
process.electronApp = spawn(electronPath, argv, {
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
...options,
})

// Exit command after Electron.app exits
process.electronApp.once('exit', process.exit)
Expand All @@ -164,6 +181,14 @@ export async function startup(
process.once('exit', startup.exit)
}
}

startup.send = (message: string) => {
if (process.electronApp) {
// Based on { stdio: [,,, 'ipc'] }
process.electronApp.send?.(message)
}
}

startup.hookedProcessExit = false
startup.exit = async () => {
if (process.electronApp) {
Expand Down
63 changes: 63 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { AddressInfo } from 'node:net'
import { builtinModules } from 'node:module'
import {
type InlineConfig,
type ResolvedConfig,
type ViteDevServer,
mergeConfig,
} from 'vite'
Expand Down Expand Up @@ -137,6 +138,68 @@ export function resolvePackageJson(root = process.cwd()): {
}
}

/** @see https://github.com/vitejs/vite/blob/v5.4.9/packages/vite/src/node/build.ts#L489-L504 */
export function resolveInput(config: ResolvedConfig) {
const options = config.build
const { root } = config
const libOptions = options.lib

const resolve = (p: string) => path.resolve(root, p)
const input = libOptions
? options.rollupOptions?.input ||
(typeof libOptions.entry === 'string'
? resolve(libOptions.entry)
: Array.isArray(libOptions.entry)
? libOptions.entry.map(resolve)
: Object.fromEntries(
Object.entries(libOptions.entry).map(([alias, file]) => [
alias,
resolve(file),
]),
))
: options.rollupOptions?.input

if (input) return input

const indexHtml = resolve('index.html')
return fs.existsSync(indexHtml) ? indexHtml : undefined
}

/**
* When run the `vite build` command, there must be an entry file.
* If the user does not need Renderer, we need to create a temporary entry file to avoid Vite throw error.
* @inspired https://github.com/vitejs/vite/blob/v5.4.9/packages/vite/src/node/config.ts#L1234-L1236
*/
export async function mockIndexHtml(config: ResolvedConfig) {
const { root, build } = config
const output = path.resolve(root, build.outDir)
const content = `
<!doctype html>
<html lang="en">
<head>
<title>vite-plugin-electron</title>
</head>
<body>
<div>An entry file for electron renderer process.</div>
</body>
</html>
`.trim()
const index = 'index.html'
const filepath = path.join(root, index)
const distpath = path.join(output, index)

await fs.promises.writeFile(filepath, content)

return {
async remove() {
await fs.promises.unlink(filepath)
await fs.promises.unlink(distpath)
},
filepath,
distpath,
}
}

/**
* Inspired `tree-kill`, implemented based on sync-api. #168
* @see https://github.com/pkrumins/node-tree-kill/blob/v1.2.2/index.js
Expand Down

0 comments on commit e1eb3c7

Please sign in to comment.