/** * Load the workspace .env into process.env. * * The API gets this from node's own --env-file-if-exists, but astro is launched by its * own binary, so the web side needs it done in-process. astro.config.mjs imports this * first, before it reads anything off process.env, and the build-time consumers * (web/src/lib/api.ts, web/src/lib/nostr-build.ts) run in the same process afterwards. * * Matching node's semantics: a variable already present in the real environment is left * alone, so `PORT=9000 pnpm dev` and systemd Environment= still win over the file. */ import { readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const webRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); /** Repo root first, then a web-local override if someone wants one. */ const files = [path.join(webRoot, '..', '.env'), path.join(webRoot, '.env')]; const LINE = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/; function unquote(value) { const quote = value[0]; if ((quote === '"' || quote === "'") && value.at(-1) === quote && value.length > 1) { const inner = value.slice(1, -1); return quote === '"' ? inner.replace(/\\n/g, '\n').replace(/\\"/g, '"') : inner; } // Unquoted: an inline comment ends the value. return value.split(/\s+#/)[0].trim(); } for (const file of files) { let text; try { text = readFileSync(file, 'utf8'); } catch { continue; // No .env is a supported setup — every variable has a default. } for (const line of text.split('\n')) { if (!line.trim() || line.trim().startsWith('#')) continue; const match = LINE.exec(line); if (!match) continue; const [, name, raw] = match; if (name in process.env) continue; process.env[name] = unquote(raw); } }