import { spawn } from 'node:child_process'; export function createDevProcessSpecs({ nodeCommand = process.execPath, env = process.env, } = {}) { return { roda: { command: nodeCommand, args: ['groups/RODA/server.js'], options: { env: { ...env, HOST: env.RODA_HOST || '127.0.0.1', PORT: env.RODA_PORT || '3000', }, stdio: ['ignore', 'ignore', 'pipe'], }, }, vite: { command: nodeCommand, args: ['node_modules/vite/bin/vite.js'], options: { env: { ...env }, stdio: 'inherit', }, }, }; } export function startDevServers({ spawnProcess = spawn, specs = createDevProcessSpecs(), } = {}) { const roda = spawnProcess(specs.roda.command, specs.roda.args, specs.roda.options); const vite = spawnProcess(specs.vite.command, specs.vite.args, specs.vite.options); let shuttingDown = false; let rodaErrorOutput = ''; roda.stderr?.on('data', (chunk) => { const message = String(chunk); rodaErrorOutput += message; if (!message.includes('EADDRINUSE')) { process.stderr.write(`[roda] ${message}`); } }); function stopChildren() { shuttingDown = true; if (!roda.killed) { roda.kill(); } if (!vite.killed) { vite.kill(); } } vite.on('exit', (code, signal) => { shuttingDown = true; if (!roda.killed) { roda.kill(); } if (signal) { process.exitCode = 1; return; } process.exitCode = code ?? 0; }); roda.on('exit', (code, signal) => { if (shuttingDown) { return; } if (rodaErrorOutput.includes('EADDRINUSE')) { process.stderr.write('[roda] Port 3000 is already in use; continuing with the existing RODA server.\n'); return; } const reason = signal ? `signal ${signal}` : `exit code ${code}`; process.stderr.write(`[roda] server stopped unexpectedly (${reason})\n`); if (!vite.killed) { vite.kill(); } process.exitCode = code || 1; }); process.once('SIGINT', stopChildren); process.once('SIGTERM', stopChildren); return { roda, vite }; }