diff --git a/.gitignore b/.gitignore index 874f897..97a2ed4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea node_modules *.log* dist diff --git a/src/env.ts b/src/env.ts index fada504..56b584b 100644 --- a/src/env.ts +++ b/src/env.ts @@ -1,41 +1,82 @@ -const _envShim = Object.create(null); - export type EnvObject = Record; -const _getEnv = (useShim?: boolean) => +const _getEnv = (): Record | undefined => globalThis.process?.env || import.meta.env || globalThis.Deno?.env.toObject() || - globalThis.__env__ || - (useShim ? _envShim : globalThis); + globalThis.__env__; -export const env = new Proxy(_envShim, { - get(_, prop) { - const env = _getEnv(); - return env[prop as any] ?? _envShim[prop]; - }, - has(_, prop) { - const env = _getEnv(); - return prop in env || prop in _envShim; - }, - set(_, prop, value) { - const env = _getEnv(true); - env[prop as any] = value; - return true; - }, - deleteProperty(_, prop) { - if (!prop) { - return false; - } - const env = _getEnv(true); - delete env[prop as any]; - return true; - }, - ownKeys() { - const env = _getEnv(true); - return Object.keys(env); +export const env = new Proxy( + {}, + { + get(target, prop) { + const env = _getEnv(); + if (env && Reflect.has(env, prop)) { + return env[prop as any]; + } + + return target[prop as any]; + }, + has(target, prop) { + const env = _getEnv(); + if (env && Reflect.has(env, prop)) { + return Reflect.has(env, prop); + } + + return Reflect.has(target, prop); + }, + set(target, prop, value) { + const env = _getEnv(); + if (env) { + env[prop as any] = value; + } else { + target[prop as any] = value; + } + + return true; + }, + deleteProperty(target, prop) { + if (!prop) { + return false; + } + + const env = _getEnv(); + if (env && Reflect.has(env, prop)) { + delete env[prop as any]; + } + + delete target[prop as any]; + return true; + }, + ownKeys(target) { + const env = _getEnv(); + return [ + ...new Set([ + ...Reflect.ownKeys(target), + ...(env ? Reflect.ownKeys(env) : []), + ]), + ]; + }, + getOwnPropertyDescriptor( + target: EnvObject, + p: string | symbol, + ): PropertyDescriptor | undefined { + if (Reflect.has(target, p)) { + return Reflect.getOwnPropertyDescriptor(target, p); + } + + const env = _getEnv(); + if (env && Reflect.has(env, p)) { + return { + ...Reflect.getOwnPropertyDescriptor(env, p), + enumerable: true, + }; + } + + return undefined; + }, }, -}); +); export const nodeENV = (typeof process !== "undefined" && process.env && process.env.NODE_ENV) || ""; diff --git a/test/env.spec.ts b/test/env.spec.ts new file mode 100644 index 0000000..00216f2 --- /dev/null +++ b/test/env.spec.ts @@ -0,0 +1,21 @@ +import { it, describe, expect } from "vitest"; +import { env } from "../src"; + +const set = (key: string, value: string | undefined) => { + if (value === undefined) { + delete env[key]; + return; + } + + env[key] = value; +}; + +describe("std-env", () => { + it("should read many env vars", () => { + set("foo", "bar"); + + expect(Object.keys(env).length).toBeGreaterThanOrEqual(1); + + set("foo", undefined); + }); +});