40 lines
1.4 KiB
TypeScript
40 lines
1.4 KiB
TypeScript
|
|
/**
|
||
|
|
* Loads .env / .env.local into process.env BEFORE any other import runs.
|
||
|
|
*
|
||
|
|
* ES module imports are hoisted — so any `import { x } from "./y"`
|
||
|
|
* at the top of a script runs BEFORE the body. If `./y` reads
|
||
|
|
* process.env at module-init or call time, the env vars need to be
|
||
|
|
* set during the import phase itself, which means this file has to
|
||
|
|
* be the FIRST import in any smoke script that needs PB / Anthropic
|
||
|
|
* credentials.
|
||
|
|
*
|
||
|
|
* Import order matters:
|
||
|
|
* import "./_env"; // ← FIRST, side-effect only
|
||
|
|
* import { … } from "../../src/…"; // ← can now safely read env
|
||
|
|
*
|
||
|
|
* No exports — this file's only purpose is the side effect of reading
|
||
|
|
* .env / .env.local into process.env.
|
||
|
|
*/
|
||
|
|
import { readFileSync } from "node:fs";
|
||
|
|
import { resolve } from "node:path";
|
||
|
|
|
||
|
|
function loadEnvFile(name: string): void {
|
||
|
|
try {
|
||
|
|
const raw = readFileSync(resolve(process.cwd(), name), "utf8");
|
||
|
|
for (const line of raw.split("\n")) {
|
||
|
|
const trimmed = line.trim();
|
||
|
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
||
|
|
const m = trimmed.match(/^([A-Z0-9_]+)\s*=\s*(.+)$/i);
|
||
|
|
if (m && !process.env[m[1]]) {
|
||
|
|
// Strip surrounding quotes if the .env used them.
|
||
|
|
process.env[m[1]] = m[2].replace(/^["']|["']$/g, "").trim();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} catch {
|
||
|
|
// missing file — fine, rely on shell env
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
loadEnvFile(".env");
|
||
|
|
loadEnvFile(".env.local");
|