UNPKG

datacops-cms

Version:

A modern, extensible CMS built with Next.js and Prisma.

110 lines (97 loc) 3.41 kB
const { spawn, execSync } = require("child_process"); const chokidar = require("chokidar"); const detect = require("detect-port").default; const path = require("path"); const os = require("os"); // The dev server port (change if you use a different port) const DEV_PORT = 3000; let devServer = null; function startDev() { devServer = spawn("pnpm", ["dev-turbo"], { stdio: "inherit", shell: true }); devServer.on("exit", code => { if (code !== 0 && code !== null) { console.error(`Dev server exited with code ${code}`); } }); } async function stopDev() { if (!devServer) return; await new Promise(resolve => { devServer.on("exit", resolve); devServer.kill(); devServer = null; }); await waitForPortToBeFree(DEV_PORT, 20000); // Wait up to 20s } // On Windows, force-kill any process listening on the port async function killProcessOnPort(port) { if (os.platform() !== "win32") return; // Only do this on Windows try { const output = execSync(`netstat -ano | findstr :${port}`).toString(); // Look for a LISTENING entry const match = output.match(/LISTENING\s+(\d+)/); if (match) { const pid = match[1]; console.log(`⚠️ Forcibly killing process ${pid} on port ${port}...`); execSync(`taskkill /PID ${pid} /F`); } } catch (err) { // No process found, that's fine } } async function waitForPortToBeFree(port, timeout = 20000) { const start = Date.now(); let killed = false; while (Date.now() - start < timeout) { const freePort = await detect(port); if (freePort === port) return; // If not free after 10s, force kill (Windows only) if (!killed && Date.now() - start > 10000) { await killProcessOnPort(port); killed = true; } await new Promise(res => setTimeout(res, 250)); } throw new Error(`Port ${port} not free after ${timeout}ms!`); } function runCmd(cmd, args) { return new Promise((resolve, reject) => { const p = spawn(cmd, args, { stdio: "inherit", shell: true }); p.on("close", code => (code === 0 ? resolve() : reject(code))); }); } async function updatePrisma() { console.log("🛠️ Updating Prisma schema..."); await runCmd("npx", ["tsx", "scripts/generate-prisma-schema.ts"]); await runCmd("pnpm", ["prisma", "generate"]); await runCmd("pnpm", ["prisma", "db", "push", "--accept-data-loss"]); console.log("✅ Prisma updated!"); } // Debounce/lock for rebuilds let rebuilding = false; function safeRebuild(eventType, file) { if (rebuilding) return; rebuilding = true; (async () => { console.log(`🔄 Detected content-type ${eventType} in:`, file); await stopDev(); try { await updatePrisma(); } catch (e) { console.error("❌ Error updating Prisma!", e); } startDev(); // Wait 5s before next allowed rebuild setTimeout(() => { rebuilding = false }, 5000); })(); } // Watch only for real file events const watcher = chokidar.watch(path.join(process.cwd(), "content-types"), { ignoreInitial: true, awaitWriteFinish: { stabilityThreshold: 500 }, }); ["add", "change", "unlink"].forEach(eventType => { watcher.on(eventType, file => safeRebuild(eventType, file)); }); console.log("👀 Watching /content-types for add/change/unlink events..."); startDev();