Skip to content

Commit 79252d0

Browse files
authored
Merge pull request #902 from PolicyEngine/fix/make-dev-next-apps
Use Next apps for root dev workflow
2 parents ec07ab3 + 320d6fa commit 79252d0

5 files changed

Lines changed: 152 additions & 4 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
help:
44
@echo "Available commands:"
55
@echo " make install - Install dependencies"
6-
@echo " make dev - Start development server"
6+
@echo " make dev - Start Next.js website + calculator dev servers"
77
@echo " make build - Build production version"
88
@echo " make typecheck - Run type checks"
99
@echo " make test - Run tests"

calculator-app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"private": true,
44
"version": "0.1.0",
55
"scripts": {
6-
"dev": "next dev --port 3001",
6+
"dev": "sh -c 'next dev --port ${PORT:-3001}'",
77
"build": "next build",
88
"start": "next start",
99
"typecheck": "tsc --noEmit"

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"calculator-app"
99
],
1010
"scripts": {
11-
"dev": "bun run design-system:build && bun run --filter=policyengine-app-v2 dev",
11+
"dev": "bun run design-system:build && node scripts/dev-server-next.mjs",
12+
"dev:legacy": "bun run design-system:build && bun run --filter=policyengine-app-v2 dev",
1213
"build": "turbo run build",
1314
"test": "turbo run test",
1415
"lint": "turbo run lint",

scripts/dev-server-next.mjs

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { spawn } from "child_process";
2+
import net from "net";
3+
4+
function tryConnect(port, host) {
5+
return new Promise((resolve) => {
6+
const socket = new net.Socket();
7+
const timeout = setTimeout(() => {
8+
socket.destroy();
9+
resolve(false);
10+
}, 1000);
11+
12+
socket.once("connect", () => {
13+
clearTimeout(timeout);
14+
socket.destroy();
15+
resolve(true);
16+
});
17+
18+
socket.once("error", () => {
19+
clearTimeout(timeout);
20+
socket.destroy();
21+
resolve(false);
22+
});
23+
24+
socket.connect(port, host);
25+
});
26+
}
27+
28+
function tryListen(port, host) {
29+
return new Promise((resolve) => {
30+
const server = net.createServer();
31+
const timeout = setTimeout(() => {
32+
server.close(() => resolve(false));
33+
}, 1000);
34+
35+
server.once("error", () => {
36+
clearTimeout(timeout);
37+
resolve(false);
38+
});
39+
40+
server.once("listening", () => {
41+
clearTimeout(timeout);
42+
server.close(() => resolve(true));
43+
});
44+
45+
server.listen({
46+
exclusive: true,
47+
host,
48+
port,
49+
});
50+
});
51+
}
52+
53+
async function isPortAvailable(port) {
54+
const [ipv4InUse, ipv6InUse] = await Promise.all([
55+
tryConnect(port, "127.0.0.1"),
56+
tryConnect(port, "::1"),
57+
]);
58+
59+
if (ipv4InUse || ipv6InUse) {
60+
return false;
61+
}
62+
63+
return tryListen(port, "127.0.0.1");
64+
}
65+
66+
async function findAvailablePort(start) {
67+
let port = start;
68+
69+
while (port < start + 100) {
70+
if (await isPortAvailable(port)) {
71+
return port;
72+
}
73+
74+
port += 1;
75+
}
76+
77+
throw new Error(`No available port found in range ${start}-${start + 99}`);
78+
}
79+
80+
function parsePort(value, fallback) {
81+
const parsed = Number(value ?? fallback);
82+
83+
if (!Number.isInteger(parsed) || parsed <= 0) {
84+
throw new Error(`Invalid port value: ${value}`);
85+
}
86+
87+
return parsed;
88+
}
89+
90+
async function main() {
91+
const requestedWebsitePort = parsePort(process.env.WEBSITE_PORT, 3000);
92+
const requestedCalculatorPort = parsePort(
93+
process.env.CALCULATOR_PORT,
94+
requestedWebsitePort + 1,
95+
);
96+
97+
const websitePort = process.env.WEBSITE_PORT
98+
? requestedWebsitePort
99+
: await findAvailablePort(requestedWebsitePort);
100+
const calculatorPort = process.env.CALCULATOR_PORT
101+
? requestedCalculatorPort
102+
: await findAvailablePort(Math.max(requestedCalculatorPort, websitePort + 1));
103+
104+
if (websitePort === calculatorPort) {
105+
throw new Error("Website and calculator ports must be different");
106+
}
107+
108+
const websiteUrl = `http://localhost:${websitePort}`;
109+
const calculatorUrl = `http://localhost:${calculatorPort}`;
110+
111+
console.log(`\n Dev servers: Website :${websitePort}, Calculator :${calculatorPort}\n`);
112+
113+
const env = {
114+
...process.env,
115+
WEBSITE_PORT: String(websitePort),
116+
CALCULATOR_PORT: String(calculatorPort),
117+
};
118+
119+
const command = [
120+
"npx concurrently",
121+
"--names website,calculator",
122+
"--prefix-colors blue,green",
123+
`"cd website && PORT=${websitePort} NEXT_PUBLIC_CALCULATOR_URL=${calculatorUrl} bun --bun run dev"`,
124+
`"cd calculator-app && PORT=${calculatorPort} NEXT_PUBLIC_WEBSITE_URL=${websiteUrl} NEXT_PUBLIC_CALCULATOR_URL=${calculatorUrl} bun --bun run dev"`,
125+
].join(" ");
126+
127+
const child = spawn(command, [], {
128+
cwd: process.cwd(),
129+
env,
130+
shell: true,
131+
stdio: "inherit",
132+
});
133+
134+
child.on("error", (error) => {
135+
console.error("Failed to start Next.js dev servers:", error);
136+
process.exit(1);
137+
});
138+
139+
child.on("close", (code) => {
140+
process.exit(code ?? 0);
141+
});
142+
}
143+
144+
main().catch((error) => {
145+
console.error(error);
146+
process.exit(1);
147+
});

website/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"private": true,
44
"version": "0.1.0",
55
"scripts": {
6-
"dev": "next dev --turbopack",
6+
"dev": "sh -c 'next dev --turbopack --port ${PORT:-3000}'",
77
"dev:full": "node scripts/dev-server.mjs",
88
"build": "bash scripts/build.sh",
99
"build:next": "next build",

0 commit comments

Comments
 (0)