open-design/scripts/dev-all.ts

88 lines
2.7 KiB
JavaScript

#!/usr/bin/env node
// Launcher for `pnpm run dev:all`.
//
// Probes for free ports for the daemon (OD_PORT, default 7456) and the
// Next.js dev server (NEXT_PORT, default 3000) before spawning the workspace
// apps, so a stray process holding either port doesn't kill the
// whole boot. The resolved ports are exported into the child env, which
// means:
// * the daemon's cli.ts sees the new OD_PORT and binds to it
// * apps/web/next.config.ts reads the same OD_PORT and proxies /api, /artifacts,
// /frames to the daemon's actual port
// * Next.js binds to NEXT_PORT (we pass `-p $NEXT_PORT` to the web package
// dev script so it can stay parameter-free for the common single-process
// case where the user runs just `pnpm dev`)
//
// If a port is busy we walk forward up to PORT_SEARCH_RANGE steps and log
// the switch so the user notices.
import { spawn } from 'node:child_process';
import { findFreePort } from './resolve-dev-ports.ts';
const desiredDaemon = Number(process.env.OD_PORT) || 7456;
const desiredNext = Number(process.env.NEXT_PORT) || 3000;
const strictDaemonPort = process.env.OD_PORT_STRICT === '1';
const strictNextPort = process.env.NEXT_PORT_STRICT === '1';
const daemonPort = strictDaemonPort
? desiredDaemon
: await findFreePort(desiredDaemon, 'daemon');
const nextPort = strictNextPort
? desiredNext
: await findFreePort(desiredNext, 'next');
if (daemonPort !== desiredDaemon) {
console.log(
`[dev:all] daemon port ${desiredDaemon} is busy, switching to ${daemonPort}`,
);
}
if (nextPort !== desiredNext) {
console.log(
`[dev:all] next port ${desiredNext} is busy, switching to ${nextPort}`,
);
}
const env = {
...process.env,
OD_PORT: String(daemonPort),
NEXT_PORT: String(nextPort),
PORT: String(nextPort),
};
const packageManager = process.platform === 'win32' ? 'corepack.cmd' : 'corepack';
const children = [
spawn(packageManager, ['pnpm', '--filter', '@open-design/daemon', 'daemon'], {
env,
stdio: 'inherit',
}),
spawn(packageManager, ['pnpm', '--filter', '@open-design/web', 'dev', '-p', String(nextPort)], {
env,
stdio: 'inherit',
}),
];
let shuttingDown = false;
function stopChildren(signal: NodeJS.Signals = 'SIGTERM'): void {
for (const child of children) {
if (!child.killed) child.kill(signal);
}
}
for (const child of children) {
child.on('exit', (code, signal) => {
if (shuttingDown) return;
shuttingDown = true;
stopChildren(signal || 'SIGTERM');
if (signal) process.kill(process.pid, signal);
else process.exit(code ?? 0);
});
}
for (const sig of ['SIGINT', 'SIGTERM'] as const) {
process.on(sig, () => {
shuttingDown = true;
stopChildren(sig);
});
}