feat: enhance Electron app packaging and server preparation

- Added afterPack script in package.json to rebuild native modules for the server bundle.
- Improved icon handling in main.js to support cross-platform formats and verify icon existence.
- Updated startStaticServer function to return a promise for better error handling.
- Introduced a new script, rebuild-server-natives.js, to rebuild native modules based on the target architecture.
- Enhanced prepare-server.js to include native module rebuilding step for improved terminal functionality.
This commit is contained in:
Kacper
2025-12-14 01:08:35 +01:00
parent 223fff9ef9
commit 8dc3bdde67
4 changed files with 135 additions and 12 deletions

View File

@@ -26,17 +26,34 @@ let staticServer = null;
const SERVER_PORT = 3008;
const STATIC_PORT = 3007;
// Get icon path - works in both dev and production
// Get icon path - works in both dev and production, cross-platform
function getIconPath() {
return app.isPackaged
? path.join(process.resourcesPath, "app", "public", "logo.png")
: path.join(__dirname, "../public/logo.png");
// Different icon formats for different platforms
let iconFile;
if (process.platform === "win32") {
iconFile = "icon.ico";
} else if (process.platform === "darwin") {
iconFile = "logo_larger.png";
} else {
// Linux
iconFile = "logo_larger.png";
}
const iconPath = path.join(__dirname, "../public", iconFile);
// Verify the icon exists
if (!fs.existsSync(iconPath)) {
console.warn(`[Electron] Icon not found at: ${iconPath}`);
return null;
}
return iconPath;
}
/**
* Start static file server for production builds
*/
function startStaticServer() {
async function startStaticServer() {
const staticPath = path.join(__dirname, "../out");
staticServer = http.createServer((request, response) => {
@@ -89,8 +106,15 @@ function startStaticServer() {
});
});
staticServer.listen(STATIC_PORT, () => {
console.log(`[Electron] Static server running at http://localhost:${STATIC_PORT}`);
return new Promise((resolve, reject) => {
staticServer.listen(STATIC_PORT, (err) => {
if (err) {
reject(err);
} else {
console.log(`[Electron] Static server running at http://localhost:${STATIC_PORT}`);
resolve();
}
});
});
}
@@ -233,12 +257,12 @@ async function waitForServer(maxAttempts = 30) {
* Create the main window
*/
function createWindow() {
mainWindow = new BrowserWindow({
const iconPath = getIconPath();
const windowOptions = {
width: 1400,
height: 900,
minWidth: 1024,
minHeight: 700,
icon: getIconPath(),
webPreferences: {
preload: path.join(__dirname, "preload.js"),
contextIsolation: true,
@@ -246,7 +270,14 @@ function createWindow() {
},
titleBarStyle: "hiddenInset",
backgroundColor: "#0a0a0a",
});
};
// Only set icon if it exists
if (iconPath) {
windowOptions.icon = iconPath;
}
mainWindow = new BrowserWindow(windowOptions);
// Load Next.js dev server in development or static server in production
const isDev = !app.isPackaged;
@@ -270,13 +301,20 @@ function createWindow() {
app.whenReady().then(async () => {
// Set app icon (dock icon on macOS)
if (process.platform === "darwin" && app.dock) {
app.dock.setIcon(getIconPath());
const iconPath = getIconPath();
if (iconPath) {
try {
app.dock.setIcon(iconPath);
} catch (error) {
console.warn("[Electron] Failed to set dock icon:", error.message);
}
}
}
try {
// Start static file server in production
if (app.isPackaged) {
startStaticServer();
await startStaticServer();
}
// Start backend server

View File

@@ -95,6 +95,7 @@
"appId": "com.automaker.app",
"productName": "Automaker",
"artifactName": "${productName}-${version}-${arch}.${ext}",
"afterPack": "./scripts/rebuild-server-natives.js",
"directories": {
"output": "dist"
},
@@ -113,6 +114,10 @@
"from": "server-bundle/node_modules",
"to": "server/node_modules"
},
{
"from": "server-bundle/package.json",
"to": "server/package.json"
},
{
"from": "../../.env",
"to": ".env",

View File

@@ -64,4 +64,18 @@ execSync('npm install --omit=dev', {
}
});
// Step 6: Rebuild native modules for current architecture
// This is critical for modules like node-pty that have native bindings
console.log('🔨 Rebuilding native modules for current architecture...');
try {
execSync('npm rebuild', {
cwd: BUNDLE_DIR,
stdio: 'inherit'
});
console.log('✅ Native modules rebuilt successfully');
} catch (error) {
console.warn('⚠️ Warning: Failed to rebuild native modules. Terminal functionality may not work.');
console.warn(' Error:', error.message);
}
console.log('\n✅ Server prepared for bundling at:', BUNDLE_DIR);

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env node
/**
* Electron-builder afterPack hook
* Rebuilds native modules in the server bundle for the target architecture
*/
const { exec } = require('child_process');
const { promisify } = require('util');
const path = require('path');
const execAsync = promisify(exec);
exports.default = async function(context) {
const { appOutDir, electronPlatformName, arch, packager } = context;
const electronVersion = packager.config.electronVersion;
// Convert arch to string if it's a number (electron-builder sometimes passes indices)
const archNames = ['ia32', 'x64', 'armv7l', 'arm64', 'universal'];
const archStr = typeof arch === 'number' ? archNames[arch] : arch;
console.log(`\n🔨 Rebuilding server native modules for ${electronPlatformName}-${archStr}...`);
// Path to server node_modules in the packaged app
let serverNodeModulesPath;
if (electronPlatformName === 'darwin') {
serverNodeModulesPath = path.join(
appOutDir,
`${packager.appInfo.productName}.app`,
'Contents',
'Resources',
'server',
'node_modules'
);
} else if (electronPlatformName === 'win32') {
serverNodeModulesPath = path.join(
appOutDir,
'resources',
'server',
'node_modules'
);
} else {
serverNodeModulesPath = path.join(
appOutDir,
'resources',
'server',
'node_modules'
);
}
try {
// Rebuild native modules for the target architecture
const rebuildCmd = `npx --yes @electron/rebuild --version=${electronVersion} --arch=${archStr} --force --module-dir="${serverNodeModulesPath}/.."`;
console.log(` Command: ${rebuildCmd}`);
const { stdout, stderr } = await execAsync(rebuildCmd);
if (stdout) console.log(stdout);
if (stderr) console.error(stderr);
console.log(`✅ Server native modules rebuilt successfully for ${archStr}\n`);
} catch (error) {
console.error(`❌ Failed to rebuild server native modules:`, error.message);
// Don't fail the build, just warn
}
};