Skip to content

Commit 2bc0325

Browse files
authored
Merge pull request #1993 from zhaomenghuan/feature/electron-dev
feat: add Electron hot-reload development mode
2 parents 4ca535b + 3372510 commit 2bc0325

File tree

3 files changed

+187
-1
lines changed

3 files changed

+187
-1
lines changed

electron/main/ui/window.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ export function createWindow(rendererURL: string) {
99
const bounds = store.get('bounds');
1010
console.log('restored bounds:', bounds);
1111

12+
// preload path
13+
const preloadPath = path.join(isDev ? process.cwd() : app.getAppPath(), 'build', 'electron', 'preload', 'index.cjs');
14+
1215
const win = new BrowserWindow({
1316
...{
1417
width: 1200,
@@ -18,7 +21,7 @@ export function createWindow(rendererURL: string) {
1821
vibrancy: 'under-window',
1922
visualEffectState: 'active',
2023
webPreferences: {
21-
preload: path.join(app.getAppPath(), 'build', 'electron', 'preload', 'index.cjs'),
24+
preload: preloadPath,
2225
},
2326
});
2427

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
"preview": "pnpm run build && pnpm run start",
3131
"prepare": "husky",
3232
"clean": "node scripts/clean.js",
33+
"electron:dev": "node scripts/electron-dev.mjs",
34+
"electron:dev:inspect": "NODE_ENV=development electron --inspect=9229 build/electron/main/index.mjs",
3335
"electron:build:deps": "concurrently \"pnpm electron:build:main\" \"pnpm electron:build:preload\" --kill-others-on-fail",
3436
"electron:build:main": "vite build --config ./electron/main/vite.config.ts",
3537
"electron:build:preload": "vite build --config ./electron/preload/vite.config.ts",

scripts/electron-dev.mjs

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Electron Development Script
5+
*
6+
* This script provides hot-reload development mode for Electron applications.
7+
* It automatically builds Electron dependencies, starts the Remix development server,
8+
* and launches the Electron application with hot-reload capabilities.
9+
*/
10+
11+
import { spawn, exec } from 'node:child_process';
12+
import path from 'node:path';
13+
import fs from 'node:fs';
14+
import { fileURLToPath } from 'node:url';
15+
16+
// Get current file directory
17+
const __filename = fileURLToPath(import.meta.url);
18+
const __dirname = path.dirname(__filename);
19+
20+
// Constants
21+
const REMIX_PORT = 5173;
22+
const CHECK_INTERVAL = 1000;
23+
const MAX_RETRIES = 30;
24+
25+
// Set environment variables
26+
process.env.NODE_ENV = 'development';
27+
28+
console.log('🚀 Starting Electron hot-reload development mode...');
29+
console.log('🔧 Environment:', process.env.NODE_ENV);
30+
31+
let electronProcess = null;
32+
let remixProcess = null;
33+
34+
/**
35+
* Cleanup function to gracefully shutdown all processes
36+
*/
37+
function cleanup() {
38+
console.log('\n🛑 Shutting down all processes...');
39+
40+
if (electronProcess) {
41+
electronProcess.kill('SIGTERM');
42+
}
43+
44+
if (remixProcess) {
45+
remixProcess.kill('SIGTERM');
46+
}
47+
48+
process.exit(0);
49+
}
50+
51+
// Handle process exit signals
52+
process.on('SIGINT', cleanup);
53+
process.on('SIGTERM', cleanup);
54+
55+
/**
56+
* Wait for a server to start on the specified port
57+
* @param {number} port - Port number to check
58+
* @param {string} serverName - Name of the server for logging
59+
* @returns {Promise<void>}
60+
*/
61+
async function waitForServer(port, serverName) {
62+
return new Promise((resolve, reject) => {
63+
let retries = 0;
64+
65+
const checkServer = () => {
66+
exec(`lsof -i :${port}`, (error, stdout) => {
67+
if (stdout) {
68+
console.log(`✅ ${serverName} started`);
69+
resolve();
70+
} else if (retries >= MAX_RETRIES) {
71+
reject(new Error(`Timeout waiting for ${serverName} to start`));
72+
} else {
73+
retries++;
74+
setTimeout(checkServer, CHECK_INTERVAL);
75+
}
76+
});
77+
};
78+
79+
checkServer();
80+
});
81+
}
82+
83+
/**
84+
* Build Electron dependencies
85+
* @returns {Promise<void>}
86+
*/
87+
async function buildElectronDeps() {
88+
return new Promise((resolve, reject) => {
89+
const buildProcess = spawn('pnpm', ['electron:build:deps'], {
90+
stdio: 'inherit',
91+
env: { ...process.env },
92+
});
93+
94+
buildProcess.on('close', (code) => {
95+
if (code === 0) {
96+
console.log('✅ Electron dependencies built successfully');
97+
resolve();
98+
} else {
99+
reject(new Error(`Build failed with exit code: ${code}`));
100+
}
101+
});
102+
103+
buildProcess.on('error', (error) => {
104+
reject(new Error(`Build process error: ${error.message}`));
105+
});
106+
});
107+
}
108+
109+
/**
110+
* Main function to start Electron development mode
111+
* @returns {Promise<void>}
112+
*/
113+
async function startElectronDev() {
114+
try {
115+
// 1. Build Electron dependencies first
116+
console.log('📦 Building Electron dependencies...');
117+
await buildElectronDeps();
118+
119+
// 2. Start Remix development server
120+
console.log('🌐 Starting Remix development server...');
121+
remixProcess = spawn('pnpm', ['dev'], {
122+
stdio: 'pipe',
123+
env: { ...process.env },
124+
});
125+
126+
remixProcess.stdout.on('data', (data) => {
127+
const output = data.toString();
128+
console.log(`[Remix] ${output.trim()}`);
129+
});
130+
131+
remixProcess.stderr.on('data', (data) => {
132+
console.error(`[Remix Error] ${data.toString().trim()}`);
133+
});
134+
135+
// Wait for Remix server to start
136+
await waitForServer(REMIX_PORT, 'Remix development server');
137+
138+
// 3. Start Electron application
139+
console.log('⚡ Starting Electron application...');
140+
141+
const electronPath = path.join(__dirname, '..', 'node_modules', '.bin', 'electron');
142+
const mainPath = path.join(__dirname, '..', 'build', 'electron', 'main', 'index.mjs');
143+
144+
// Check if main process file exists
145+
if (!fs.existsSync(mainPath)) {
146+
throw new Error(`Main process file not found: ${mainPath}`);
147+
}
148+
149+
electronProcess = spawn(electronPath, [mainPath], {
150+
stdio: 'inherit',
151+
env: {
152+
...process.env,
153+
NODE_ENV: 'development',
154+
ELECTRON_IS_DEV: '1',
155+
},
156+
});
157+
158+
electronProcess.on('error', (error) => {
159+
console.error('❌ Failed to start Electron:', error);
160+
cleanup();
161+
});
162+
163+
electronProcess.on('exit', (code) => {
164+
console.log(`📱 Electron process exited with code: ${code}`);
165+
166+
if (code !== 0) {
167+
cleanup();
168+
}
169+
});
170+
171+
console.log('🎉 Electron hot-reload development mode started!');
172+
console.log('💡 Code changes will automatically reload');
173+
console.log('🛑 Press Ctrl+C to exit');
174+
} catch (error) {
175+
console.error('❌ Startup failed:', error.message);
176+
cleanup();
177+
}
178+
}
179+
180+
// Start development mode
181+
startElectronDev();

0 commit comments

Comments
 (0)