// Fills process.env from the gitignored `.config//.env` // files, the local home of every credential the app reads. Deployed code // never sees this directory: Vercel holds the same variables as project // environment variables. // // Called from next.config.ts and drizzle.config.ts. Nothing under src/ // imports it: app code only reads process.env, so no bundle pulls in node:fs. import { existsSync, readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; import { parseEnv } from "node:util"; export const LOCAL_ENV_DIR = ".config"; /** * Every `/.config//.env`, in a stable order. * @param {string} [root] * @returns {string[]} */ export function localEnvFiles(root = process.cwd()) { const dir = join(root, LOCAL_ENV_DIR); if (!existsSync(dir)) return []; /** @type {string[]} */ const files = []; for (const category of readdirSync(dir, { withFileTypes: true })) { if (!category.isDirectory()) continue; for (const entry of readdirSync(join(dir, category.name), { withFileTypes: true, })) { if (entry.isFile() && entry.name.endsWith(".env")) { files.push(join(dir, category.name, entry.name)); } } } return files.sort(); } /** * Loads each file into process.env. A variable already in the environment * wins over the file, so a value exported in the shell or set by Vercel is * never overwritten. An absent `.config/` is a no-op, which is the deployed * case. * @param {string} [root] * @returns {string[]} */ export function loadLocalEnv(root = process.cwd()) { const files = localEnvFiles(root); for (const file of files) { for (const [name, value] of Object.entries( parseEnv(readFileSync(file, "utf8")), )) { if (process.env[name] === undefined) process.env[name] = value; } } return files; }