UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

93 lines (87 loc) 3.27 kB
import { $inject } from "alepha"; import { $logger } from "alepha/logger"; import { FileSystemProvider } from "alepha/system"; export class EnvUtils { protected readonly log = $logger(); protected readonly fs = $inject(FileSystemProvider); /** * Load environment variables from .env files into process.env. * * Variables that already exist in process.env are NOT overwritten, * matching the standard dotenv convention where the shell environment * takes precedence over .env file values. * * By default, it loads from ".env" and ".env.local". * You can specify additional files to load, e.g. [".env", ".env.production"]. */ public async loadEnv( root: string, files: string[] = [".env"], ): Promise<void> { const vars = await this.parseEnv(root, files); for (const [key, value] of Object.entries(vars)) { if (process.env[key] === undefined) { process.env[key] = value; } } } /** * Parse environment variables from .env files without mutating process.env. * * Returns a merged record from all files (later files override earlier ones). * For each file, also tries the `.local` variant (e.g. `.env.production.local`). */ public async parseEnv( root: string, files: string[] = [".env"], ): Promise<Record<string, string>> { const result: Record<string, string> = {}; for (const it of files) { for (const file of [it, `${it}.local`]) { const envPath = this.fs.join(root, file); try { const buffer = await this.fs.readFile(envPath); const envContent = buffer.toString("utf8"); for (const line of envContent.split("\n")) { const [key, ...rest] = line.split("="); if (key) { const trimmedKey = key.trim(); if (trimmedKey && !trimmedKey.startsWith("#")) { let value = rest.join("=").trim(); const last = value.length - 1; if ( value.length >= 2 && value[0] === '"' && value[last] === '"' ) { // Double-quoted: JSON-decode so escapes round-trip with a // `JSON.stringify`-based writer (e.g. Rocket's // `.env.<env>.local` overrides, which write JSON values like // CLUB_CONFIG_JSON). Falls back to a naive strip when the // body isn't valid JSON (e.g. a Windows path with `\`), so // simple `KEY="value"` still behaves as before. try { value = JSON.parse(value); } catch { value = value.slice(1, -1); } } else if ( value.length >= 2 && value[0] === "'" && value[last] === "'" ) { value = value.slice(1, -1); } result[trimmedKey] = value; } } } this.log.debug(`Parsed environment variables from ${envPath}`); } catch { this.log.debug(`No ${file} file found at ${envPath}, skipping.`); } } } return result; } }