Skip to content

Commit e1eb3c7

Browse files
authored
Merge pull request #260 from electron-vite/v0.29.0
V0.29.0
2 parents 4aae557 + e6787c7 commit e1eb3c7

File tree

5 files changed

+133
-4
lines changed

5 files changed

+133
-4
lines changed

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
## 0.29.0 (2024-11-16)
2+
3+
- 638d0f3 feat: support main process control hot-reload
4+
- c58fbf9 feat: mock index.html for support use the main process only
5+
6+
**Hot Reload**
7+
8+
Since `v0.29.0`, when preload scripts are rebuilt, they will send an `electron-vite&type=hot-reload` event to the main process.
9+
If your App doesn't need a renderer process, this will give you **hot-reload**.
10+
11+
```js
12+
// electron/main.ts
13+
14+
process.on('message', (msg) => {
15+
if (msg === 'electron-vite&type=hot-reload') {
16+
for (const win of BrowserWindow.getAllWindows()) {
17+
// Hot reload preload scripts
18+
win.webContents.reload()
19+
}
20+
}
21+
})
22+
```
23+
124
## 0.28.8 (2024-09-19)
225

326
- 3239718 fix: better exit app #251

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,24 @@ build({
246246
})
247247
```
248248

249+
**Hot Reload**
250+
251+
Since `v0.29.0`, when preload scripts are rebuilt, they will send an `electron-vite&type=hot-reload` event to the main process.
252+
If your App doesn't need a renderer process, this will give you **hot-reload**.
253+
254+
```js
255+
// electron/main.ts
256+
257+
process.on('message', (msg) => {
258+
if (msg === 'electron-vite&type=hot-reload') {
259+
for (const win of BrowserWindow.getAllWindows()) {
260+
// Hot reload preload scripts
261+
win.webContents.reload()
262+
}
263+
}
264+
})
265+
```
266+
249267
## How to work
250268

251269
It just executes the `electron .` command in the Vite build completion hook and then starts or restarts the Electron App.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vite-plugin-electron",
3-
"version": "0.28.8",
3+
"version": "0.29.0",
44
"description": "Electron 🔗 Vite",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",
@@ -26,7 +26,7 @@
2626
"type": "git",
2727
"url": "git+https://github.com/electron-vite/vite-plugin-electron.git"
2828
},
29-
"author": "草鞋没号 <[email protected]>",
29+
"author": "Leo Wang(草鞋没号) <[email protected]>",
3030
"license": "MIT",
3131
"packageManager": "[email protected]",
3232
"scripts": {

src/index.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
import {
88
resolveServerUrl,
99
resolveViteConfig,
10+
resolveInput,
11+
mockIndexHtml,
1012
withExternalBuiltins,
1113
treeKillSync,
1214
} from './utils'
@@ -51,6 +53,7 @@ export default function electron(options: ElectronOptions | ElectronOptions[]):
5153
const optionsArray = Array.isArray(options) ? options : [options]
5254
let userConfig: UserConfig
5355
let configEnv: ConfigEnv
56+
let mockdInput: Awaited<ReturnType<typeof mockIndexHtml>> | undefined
5457

5558
return [
5659
{
@@ -89,11 +92,14 @@ export default function electron(options: ElectronOptions | ElectronOptions[]):
8992
options.onstart.call(this, {
9093
startup,
9194
// Why not use Vite's built-in `/@vite/client` to implement Hot reload?
92-
// Because Vite only inserts `/@vite/client` into the `*.html` entry file.
95+
// Because Vite only inserts `/@vite/client` into the `*.html` entry file, the preload scripts are usually a `*.js` file.
9396
// @see - https://github.com/vitejs/vite/blob/v5.2.11/packages/vite/src/node/server/middlewares/indexHtml.ts#L399
9497
reload() {
9598
if (process.electronApp) {
9699
(server.hot || server.ws).send({ type: 'full-reload' })
100+
101+
// For Electron apps that don't need to use the renderer process.
102+
startup.send('electron-vite&type=hot-reload')
97103
} else {
98104
startup()
99105
}
@@ -120,7 +126,15 @@ export default function electron(options: ElectronOptions | ElectronOptions[]):
120126
// Make sure that Electron can be loaded into the local file using `loadFile` after packaging.
121127
config.base ??= './'
122128
},
129+
async configResolved(config) {
130+
const input = resolveInput(config)
131+
if (input == null) {
132+
mockdInput = await mockIndexHtml(config)
133+
}
134+
},
123135
async closeBundle() {
136+
mockdInput?.remove()
137+
124138
for (const options of optionsArray) {
125139
options.vite ??= {}
126140
options.vite.mode ??= configEnv.mode
@@ -154,7 +168,10 @@ export async function startup(
154168
await startup.exit()
155169

156170
// Start Electron.app
157-
process.electronApp = spawn(electronPath, argv, { stdio: 'inherit', ...options })
171+
process.electronApp = spawn(electronPath, argv, {
172+
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
173+
...options,
174+
})
158175

159176
// Exit command after Electron.app exits
160177
process.electronApp.once('exit', process.exit)
@@ -164,6 +181,14 @@ export async function startup(
164181
process.once('exit', startup.exit)
165182
}
166183
}
184+
185+
startup.send = (message: string) => {
186+
if (process.electronApp) {
187+
// Based on { stdio: [,,, 'ipc'] }
188+
process.electronApp.send?.(message)
189+
}
190+
}
191+
167192
startup.hookedProcessExit = false
168193
startup.exit = async () => {
169194
if (process.electronApp) {

src/utils.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { AddressInfo } from 'node:net'
55
import { builtinModules } from 'node:module'
66
import {
77
type InlineConfig,
8+
type ResolvedConfig,
89
type ViteDevServer,
910
mergeConfig,
1011
} from 'vite'
@@ -137,6 +138,68 @@ export function resolvePackageJson(root = process.cwd()): {
137138
}
138139
}
139140

141+
/** @see https://github.com/vitejs/vite/blob/v5.4.9/packages/vite/src/node/build.ts#L489-L504 */
142+
export function resolveInput(config: ResolvedConfig) {
143+
const options = config.build
144+
const { root } = config
145+
const libOptions = options.lib
146+
147+
const resolve = (p: string) => path.resolve(root, p)
148+
const input = libOptions
149+
? options.rollupOptions?.input ||
150+
(typeof libOptions.entry === 'string'
151+
? resolve(libOptions.entry)
152+
: Array.isArray(libOptions.entry)
153+
? libOptions.entry.map(resolve)
154+
: Object.fromEntries(
155+
Object.entries(libOptions.entry).map(([alias, file]) => [
156+
alias,
157+
resolve(file),
158+
]),
159+
))
160+
: options.rollupOptions?.input
161+
162+
if (input) return input
163+
164+
const indexHtml = resolve('index.html')
165+
return fs.existsSync(indexHtml) ? indexHtml : undefined
166+
}
167+
168+
/**
169+
* When run the `vite build` command, there must be an entry file.
170+
* If the user does not need Renderer, we need to create a temporary entry file to avoid Vite throw error.
171+
* @inspired https://github.com/vitejs/vite/blob/v5.4.9/packages/vite/src/node/config.ts#L1234-L1236
172+
*/
173+
export async function mockIndexHtml(config: ResolvedConfig) {
174+
const { root, build } = config
175+
const output = path.resolve(root, build.outDir)
176+
const content = `
177+
<!doctype html>
178+
<html lang="en">
179+
<head>
180+
<title>vite-plugin-electron</title>
181+
</head>
182+
<body>
183+
<div>An entry file for electron renderer process.</div>
184+
</body>
185+
</html>
186+
`.trim()
187+
const index = 'index.html'
188+
const filepath = path.join(root, index)
189+
const distpath = path.join(output, index)
190+
191+
await fs.promises.writeFile(filepath, content)
192+
193+
return {
194+
async remove() {
195+
await fs.promises.unlink(filepath)
196+
await fs.promises.unlink(distpath)
197+
},
198+
filepath,
199+
distpath,
200+
}
201+
}
202+
140203
/**
141204
* Inspired `tree-kill`, implemented based on sync-api. #168
142205
* @see https://github.com/pkrumins/node-tree-kill/blob/v1.2.2/index.js

0 commit comments

Comments
 (0)