UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

168 lines (167 loc) 5.52 kB
import FSExtra from "fs-extra"; import { setServerGlobals } from "./server/setServerGlobals.mjs"; import { setupBuildInfo } from "./server/setupBuildOptions.mjs"; import { ensureExists } from "./utils/ensureExists.mjs"; process.on("uncaughtException", err => { console.error(`[one] Uncaught exception`, err?.stack || err); }); async function serve(args = {}) { if (args.cluster) { const { cpus, platform } = await import("node:os"); const numWorkers = typeof args.cluster === "number" ? args.cluster : cpus().length; const isBun = typeof process.versions.bun !== "undefined"; const canReusePort = !["win32", "darwin"].includes(platform()) && (isBun || (() => { const [major, minor] = process.versions.node.split(".").map(Number); return major > 22 || major === 22 && minor >= 12 || major >= 23; })()); if (canReusePort) { return await serveWithReusePort(args, numWorkers); } else if (!isBun) { return await serveWithCluster(args, numWorkers); } else { console.warn(`[one] cluster mode not supported on ${platform()} with bun, running single process`); return await startWorker(args); } } return await startWorker(args); } async function serveWithReusePort(args, numWorkers) { const { fork } = await import("node:child_process"); console.info(`[one] cluster: starting ${numWorkers} workers (SO_REUSEPORT)`); const workers = []; let recentCrashes = 0; let lastCrashTime = 0; function spawnWorker() { const child = fork(process.argv[1], process.argv.slice(2).filter(a => !a.startsWith("--cluster")), { env: { ...process.env, ONE_CLUSTER_WORKER: "1" }, stdio: "inherit" }); workers.push(child); child.on("exit", (code, signal) => { const idx = workers.indexOf(child); if (idx >= 0) workers.splice(idx, 1); if (code === 0 || signal === "SIGTERM" || signal === "SIGINT") return; const now = Date.now(); if (now - lastCrashTime < 5e3) { recentCrashes++; } else { recentCrashes = 1; } lastCrashTime = now; if (recentCrashes > numWorkers * 2) { console.error(`[one] too many worker crashes, stopping`); process.exit(1); } console.error(`[one] worker ${child.pid} died (code ${code}, signal ${signal}), restarting`); setTimeout(spawnWorker, Math.min(recentCrashes * 500, 5e3)); }); } for (let i = 0; i < numWorkers; i++) { spawnWorker(); } const shutdown = () => { for (const w of workers) { w.kill("SIGTERM"); } setTimeout(() => process.exit(0), 5e3); }; process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); await new Promise(() => {}); } async function serveWithCluster(args, numWorkers) { const cluster = await import("node:cluster"); if (cluster.default.isPrimary) { console.info(`[one] cluster: starting ${numWorkers} workers (IPC)`); for (let i = 0; i < numWorkers; i++) { cluster.default.fork(); } let recentCrashes = 0; let lastCrashTime = 0; cluster.default.on("exit", (worker, code, signal) => { if (code === 0 || signal === "SIGTERM" || signal === "SIGINT") return; const now = Date.now(); if (now - lastCrashTime < 5e3) { recentCrashes++; } else { recentCrashes = 1; } lastCrashTime = now; if (recentCrashes > numWorkers * 2) { console.error(`[one] too many worker crashes, stopping`); process.exit(1); } console.error(`[one] worker ${worker.process.pid} died (code ${code}, signal ${signal}), restarting`); setTimeout(() => cluster.default.fork(), Math.min(recentCrashes * 500, 5e3)); }); const shutdown = () => { for (const id in cluster.default.workers) { cluster.default.workers[id]?.process.kill("SIGTERM"); } setTimeout(() => process.exit(0), 5e3); }; process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); return; } return await startWorker(args); } async function startWorker(args) { const outDir = args?.outDir || (FSExtra.existsSync("buildInfo.json") ? "." : null) || "dist"; const buildInfo = await FSExtra.readJSON(`${outDir}/buildInfo.json`); const { oneOptions } = buildInfo; setServerGlobals(); setupBuildInfo(buildInfo); ensureExists(oneOptions); const { labelProcess } = await import("./cli/label-process.mjs"); const { removeUndefined } = await import("./utils/removeUndefined.mjs"); const { loadEnv, serve: vxrnServe, serveStaticAssets, compileCacheRules } = await import("vxrn/serve"); const { oneServe } = await import("./server/oneServe.mjs"); labelProcess("serve"); if (args?.loadEnv) { await loadEnv("production"); } const cacheRules = oneOptions.server?.cacheControl ? compileCacheRules(oneOptions.server.cacheControl) : void 0; return await vxrnServe({ outDir: buildInfo.outDir || outDir, app: args?.app, ...oneOptions.server, ...removeUndefined({ port: args?.port ? +args.port : void 0, host: args?.host, compress: args?.compress }), async beforeRegisterRoutes(options, app) { await oneServe(oneOptions, buildInfo, app, { serveStaticAssets: ctx => serveStaticAssets({ ...ctx, cacheRules }) }); }, async afterRegisterRoutes(options, app) {} }); } export { serve }; //# sourceMappingURL=serve.mjs.map