Files
Chris Tate 176305530c Add Node 24 environment-file support (#628)
* Add Node 24 environment-file support

* Update runtime harnesses for environment-file URL paths

* Link URL dependencies in weak metadata runtime tests
2026-10-03 04:44:57 -05:00

82 lines
4.4 KiB
JavaScript

import { parseEnv } from "node:util";
import { loadEnvFile } from "node:process";
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { pathToFileURL } from "node:url";
export function readEnv(key) { return process.env[key]; }
export function writeEnv(key, value) { process.env[key] = value; }
export function reportEnv() {
const samples = [
"Z=last\nA=first\nZ=replaced\nEMPTY=\n",
" # heading\nexport NAME = world\nINLINE=value#comment\nSPACES = two words \n",
"SINGLE='a # b\\n c'\nDOUBLE=\"a # b\\n c\"\nTICK=`a # b\\n c`\n",
"MULTI=\"first\nsecond\" trailing ignored\nNEXT=ok\n",
"UNCLOSED=\"one\nNEXT=two\nLAST=three",
"export X=\nexport Y= \n=\n = value\nexport =foo",
"X= \nY=foo",
"__proto__=ignored\nconstructor=own\ntoString=value\n2=two\n10=ten\n1=one",
"UNICODE=é 😀 你好\n\uFEFFBOM=kept\nNULL=va\0lue\nLONE=\ud800\nn\0ame=value",
"A=${OTHER}\nB=$OTHER\nC=one=two\nD=\"\\t\\r\\n\"", "",
];
for (const sample of samples) {
const result = parseEnv(sample);
console.log(JSON.stringify(result), JSON.stringify(Object.keys(result)));
console.log(Object.getPrototypeOf(result) === Object.prototype);
}
for (const value of [undefined, null, 42, true, {}, [], Symbol("x"), 1n, () => {}, new Date(0), Object.create(null), Object.assign(Object.create(null), { X: 1 })]) {
try { parseEnv(value); }
catch (e) { console.log(e.name, e.code, e.message); }
}
const fresh = parseEnv("FRESH=one");
fresh.FRESH = "changed";
console.log(fresh.FRESH, parseEnv("FRESH=one").FRESH);
const directory = mkdtempSync(tmpdir() + "/scriptc-env-island-");
const previous = process.cwd();
const prefix = "SCRIPTC_ENV_ISLAND_";
const symbol = Symbol("env");
console.log("symbols", process.env[symbol], symbol in process.env, delete process.env[symbol]);
const names = ["NEW", "KEEP", "EMPTY", "SECOND", "RAW", "NUL", "DEFINED", "NUMBER"];
for (const name of names) delete process.env[prefix + name];
process.env[prefix + "KEEP"] = "existing";
process.env[prefix + "EMPTY"] = "";
process.env[prefix + "NUMBER"] = 42;
Object.defineProperty(process.env, prefix + "DEFINED", { value: "defined", configurable: true, writable: true, enumerable: true });
try {
console.log("chdir", process.chdir(directory), process.cwd() === directory);
for (const value of [undefined, 42, {}]) {
try { process.chdir(value); }
catch (e) { console.log(e.name, e.code, e.message); }
}
writeFileSync(".env", prefix + "NEW=default\n" + prefix + "KEEP=replaced\n" + prefix + "EMPTY=replaced\n");
writeFileSync("second.env", prefix + "NEW=second\n" + prefix + "SECOND=\"two\\nlines\"\n");
console.log("default", loadEnvFile(), process.env[prefix + "NEW"], process.env[prefix + "KEEP"], JSON.stringify(process.env[prefix + "EMPTY"]));
loadEnvFile("second.env");
console.log("second", process.env[prefix + "NEW"], JSON.stringify(process.env[prefix + "SECOND"]));
for (const path of [undefined, null, Buffer.from(".env"), new Uint8Array(Buffer.from(".env")), pathToFileURL(directory + "/.env")]) {
delete process.env[prefix + "NEW"];
console.log("path", loadEnvFile(path), process.env[prefix + "NEW"]);
}
writeFileSync("raw.env", Buffer.concat([Buffer.from(prefix + "RAW="), Buffer.from([0xff, 0xe2, 0x82]), Buffer.from("\n" + prefix + "NUL=va\0lue")]));
loadEnvFile("raw.env");
console.log("raw", JSON.stringify(process.env[prefix + "RAW"]), process.env[prefix + "NUL"]);
console.log("keys", Object.keys(process.env).filter(k => k.startsWith(prefix)).sort().join(","));
console.log("descriptor", JSON.stringify(Object.getOwnPropertyDescriptor(process.env, prefix + "DEFINED")), process.env[prefix + "NUMBER"]);
for (const path of [42, false, {}, [], Symbol("path"), "a\0b", Buffer.from("a\0b"), new URL("https://example.com/"), new URL("file:///tmp/a%2fb"), "missing.env"]) {
try { loadEnvFile(path); }
catch (e) { console.log(e.name, e.code, e.message); }
}
try { Object.defineProperty(process.env, prefix + "BAD", { value: "bad" }); }
catch (e) { console.log(e.name, e.code, e.message); }
delete process.env[prefix + "KEEP"];
loadEnvFile();
console.log("reload", process.env[prefix + "KEEP"]);
} finally {
process.chdir(previous);
rmSync(directory, { recursive: true, force: true });
for (const name of names) delete process.env[prefix + name];
}
}