import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; const repoRoot = process.cwd(); const examplePath = path.join(repoRoot, ".env.example"); const targetPath = path.join(repoRoot, ".env.local"); function parseEnv(text) { const map = new Map(); const order = []; for (const rawLine of text.split(/\r?\n/)) { const line = rawLine.trim(); if (!line || line.startsWith("#")) { continue; } const eqIndex = line.indexOf("="); if (eqIndex === -1) { continue; } const key = line.slice(0, eqIndex).trim(); const value = line.slice(eqIndex + 1); if (!map.has(key)) { order.push(key); } map.set(key, value); } return { map, order }; } function readFileIfExists(filePath) { return fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : ""; } if (!fs.existsSync(examplePath)) { throw new Error(".env.example was not found."); } const example = parseEnv(readFileIfExists(examplePath)); const existing = parseEnv(readFileIfExists(targetPath)); const merged = new Map(example.map); for (const [key, value] of existing.map.entries()) { merged.set(key, value); } if (!merged.get("DEV_ORGANIZATION_ID")) { merged.set("DEV_ORGANIZATION_ID", crypto.randomUUID()); } if (!merged.get("DEV_USER_ID")) { merged.set("DEV_USER_ID", crypto.randomUUID()); } if (!merged.get("DEV_AUTO_BOOTSTRAP")) { merged.set("DEV_AUTO_BOOTSTRAP", "1"); } const orderedKeys = [...example.order]; for (const key of existing.order) { if (!orderedKeys.includes(key)) { orderedKeys.push(key); } } const output = [ "# Generated by scripts/setup-env.mjs", "# Fill required secrets before running db:migrate and API flows.", ...orderedKeys.map((key) => `${key}=${merged.get(key) ?? ""}`), "" ].join("\n"); fs.writeFileSync(targetPath, output, "utf8"); console.log(`Wrote ${path.relative(repoRoot, targetPath)}`); console.log("Set defaults for DEV_ORGANIZATION_ID, DEV_USER_ID, and DEV_AUTO_BOOTSTRAP.");