import { spawn } from "node:child_process"; import { existsSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; import { parse } from "dotenv"; const [environment = "development", command = "dev", ...args] = process.argv.slice(2); if ( !["development", "test", "production"].includes(environment) || !["dev", "build", "start"].includes(command) ) { throw new Error( "Usage: tsx scripts/next-env.ts development|test|production dev|build|start", ); } const config = (file: string) => (existsSync(file) ? parse(readFileSync(file)) : {}); const childEnv: NodeJS.ProcessEnv = { ...config(resolve(`config/${environment}.env`)), ...config(resolve(`config/${environment}.local.env`)), ...process.env, APP_ENV: environment, NODE_ENV: command === "dev" ? "development" : "production", }; const nodeOptions = [childEnv.NODE_OPTIONS, "--dns-result-order=ipv4first"] .filter(Boolean) .join(" "); childEnv.NODE_OPTIONS = nodeOptions; if (command !== "build") { const api = childEnv.API_PROXY_TARGET?.trim() || childEnv.NEXT_PUBLIC_API_BASE_URL?.trim(); if (!api) { throw new Error( `API_PROXY_TARGET 或 NEXT_PUBLIC_API_BASE_URL 必填(${environment})。可写在 config/${environment}.local.env`, ); } } const nextBin = resolve("node_modules/next/dist/bin/next"); // CLI 参数比 NODE_OPTIONS 更稳:Next 子进程有时会改写 NODE_OPTIONS,导致仍走 IPv6。 const child = spawn( process.execPath, [ "--dns-result-order=ipv4first", nextBin, command, ...(command === "build" ? [] : ["--port", childEnv.PORT || "3000"]), ...args, ], { stdio: "inherit", env: childEnv }, ); for (const signal of ["SIGINT", "SIGTERM"] as const) { process.on(signal, () => child.kill(signal)); } child.on("exit", (code) => process.exit(code ?? 1));