/** * Shared helpers for monorepo-root `.env` (never commit secrets). */ import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; export const repoRoot = path.resolve( path.dirname(fileURLToPath(import.meta.url)), "..", ); export function dotenvPath() { return process.env.DOTENV_PATH ? path.resolve(process.env.DOTENV_PATH) : path.join(repoRoot, ".env"); } /** Parse KEY=VAL lines; does not expand shell. */ export function parseEnvFile(text) { const out = {}; for (const raw of text.split(/\r?\n/)) { let line = raw.trim(); if (!line || line.startsWith("#")) continue; if (line.startsWith("export ")) line = line.slice(7).trim(); const eq = line.indexOf("="); if (eq <= 0) continue; const key = line.slice(0, eq).trim(); if (!key) continue; let val = line.slice(eq + 1).trim(); if ( (val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'")) ) { val = val.slice(1, -1); } out[key] = val; } return out; } /** Merge root `.env` into env (does not override existing keys). */ export function loadRootEnv(env = process.env) { const file = dotenvPath(); let text; try { text = fs.readFileSync(file, "utf8"); } catch { return { ...env }; } const parsed = parseEnvFile(text); const out = { ...env }; for (const [key, val] of Object.entries(parsed)) { if (Object.prototype.hasOwnProperty.call(out, key)) continue; out[key] = val; } return out; } export function readRootEnvFile() { const file = dotenvPath(); try { return fs.readFileSync(file, "utf8"); } catch { return null; } } export function writeRootEnvFile(text) { fs.writeFileSync(dotenvPath(), text, "utf8"); }