diff --git a/apps/app/.gitignore b/apps/app/.gitignore index cefc9348..cb9812cb 100644 --- a/apps/app/.gitignore +++ b/apps/app/.gitignore @@ -48,3 +48,4 @@ next-env.d.ts # Electron /dist/ +/server-bundle/ diff --git a/apps/app/electron/main.js b/apps/app/electron/main.js index 0fd95d97..4e746ce3 100644 --- a/apps/app/electron/main.js +++ b/apps/app/electron/main.js @@ -63,18 +63,31 @@ async function startServer() { command = "node"; serverPath = path.join(process.resourcesPath, "server", "index.js"); args = [serverPath]; + + // Verify server files exist + if (!fs.existsSync(serverPath)) { + throw new Error(`Server not found at: ${serverPath}`); + } } // Set environment variables for server + const serverNodeModules = app.isPackaged + ? path.join(process.resourcesPath, "server", "node_modules") + : path.join(__dirname, "../../server/node_modules"); + const env = { ...process.env, PORT: SERVER_PORT.toString(), DATA_DIR: app.getPath("userData"), + NODE_PATH: serverNodeModules, }; console.log("[Electron] Starting backend server..."); + console.log("[Electron] Server path:", serverPath); + console.log("[Electron] NODE_PATH:", serverNodeModules); serverProcess = spawn(command, args, { + cwd: path.dirname(serverPath), env, stdio: ["ignore", "pipe", "pipe"], }); @@ -92,6 +105,11 @@ async function startServer() { serverProcess = null; }); + serverProcess.on("error", (err) => { + console.error(`[Server] Failed to start server process:`, err); + serverProcess = null; + }); + // Wait for server to be ready await waitForServer(); } diff --git a/apps/app/package.json b/apps/app/package.json index 98a8b0b4..eb9e6c4b 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -20,7 +20,10 @@ "dev:electron": "concurrently \"next dev -p 3007\" \"wait-on http://localhost:3007 && electron .\"", "dev:electron:debug": "concurrently \"next dev -p 3007\" \"wait-on http://localhost:3007 && OPEN_DEVTOOLS=true electron .\"", "build": "next build", - "build:electron": "next build && electron-builder", + "build:electron": "node scripts/prepare-server.js && next build && electron-builder", + "build:electron:win": "node scripts/prepare-server.js && next build && electron-builder --win", + "build:electron:mac": "node scripts/prepare-server.js && next build && electron-builder --mac", + "build:electron:linux": "node scripts/prepare-server.js && next build && electron-builder --linux", "start": "next start", "lint": "eslint", "test": "playwright test", @@ -79,13 +82,13 @@ "@types/react": "^19", "@types/react-dom": "^19", "concurrently": "^9.2.1", - "electron": "^39.2.6", + "electron": "39.2.7", "electron-builder": "^26.0.12", "eslint": "^9", "eslint-config-next": "16.0.7", "tailwindcss": "^4", "tw-animate-css": "^1.4.0", - "typescript": "^5", + "typescript": "5.9.3", "wait-on": "^9.0.3" }, "build": { @@ -103,11 +106,17 @@ ], "extraResources": [ { - "from": ".env", + "from": "server-bundle/dist", + "to": "server" + }, + { + "from": "server-bundle/node_modules", + "to": "server/node_modules" + }, + { + "from": "../../.env", "to": ".env", - "filter": [ - "**/*" - ] + "filter": ["**/*"] } ], "mac": { diff --git a/apps/app/scripts/prepare-server.js b/apps/app/scripts/prepare-server.js new file mode 100644 index 00000000..3487db71 --- /dev/null +++ b/apps/app/scripts/prepare-server.js @@ -0,0 +1,67 @@ +#!/usr/bin/env node + +/** + * This script prepares the server for bundling with Electron. + * It copies the server dist and installs production dependencies + * in a way that works with npm workspaces. + */ + +import { execSync } from 'child_process'; +import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync, readFileSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const APP_DIR = join(__dirname, '..'); +const SERVER_DIR = join(APP_DIR, '..', 'server'); +const BUNDLE_DIR = join(APP_DIR, 'server-bundle'); + +console.log('šŸ”§ Preparing server for Electron bundling...\n'); + +// Step 1: Clean up previous bundle +if (existsSync(BUNDLE_DIR)) { + console.log('šŸ—‘ļø Cleaning previous server-bundle...'); + rmSync(BUNDLE_DIR, { recursive: true }); +} +mkdirSync(BUNDLE_DIR, { recursive: true }); + +// Step 2: Build the server TypeScript +console.log('šŸ“¦ Building server TypeScript...'); +execSync('npm run build', { cwd: SERVER_DIR, stdio: 'inherit' }); + +// Step 3: Copy server dist +console.log('šŸ“‹ Copying server dist...'); +cpSync(join(SERVER_DIR, 'dist'), join(BUNDLE_DIR, 'dist'), { recursive: true }); + +// Step 4: Create a minimal package.json for the server +console.log('šŸ“ Creating server package.json...'); +const serverPkg = JSON.parse(readFileSync(join(SERVER_DIR, 'package.json'), 'utf-8')); + +const bundlePkg = { + name: '@automaker/server-bundle', + version: serverPkg.version, + type: 'module', + main: 'dist/index.js', + dependencies: serverPkg.dependencies +}; + +writeFileSync( + join(BUNDLE_DIR, 'package.json'), + JSON.stringify(bundlePkg, null, 2) +); + +// Step 5: Install production dependencies +console.log('šŸ“„ Installing server production dependencies...'); +execSync('npm install --omit=dev', { + cwd: BUNDLE_DIR, + stdio: 'inherit', + env: { + ...process.env, + // Prevent npm from using workspace resolution + npm_config_workspace: '' + } +}); + +console.log('\nāœ… Server prepared for bundling at:', BUNDLE_DIR); diff --git a/package.json b/package.json index 5ef7a387..724d9e23 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,9 @@ "build": "npm run build --workspace=apps/app", "build:server": "npm run build --workspace=apps/server", "build:electron": "npm run build:electron --workspace=apps/app", + "build:electron:win": "npm run build:electron:win --workspace=apps/app", + "build:electron:mac": "npm run build:electron:mac --workspace=apps/app", + "build:electron:linux": "npm run build:electron:linux --workspace=apps/app", "start": "npm run start --workspace=apps/app", "start:server": "npm run start --workspace=apps/server", "lint": "npm run lint --workspace=apps/app",