UNPKG

atikin-env

Version:

Ultra-fast, zero-dependency .env loader with type safety for Node/TS. Created by: Atikin Verse.

57 lines (49 loc) 1.74 kB
import fs from 'fs'; import path from 'path'; type EnvSchema = Record<string, 'string' | 'number' | 'boolean'>; interface LoadOptions { path?: string; allowPartial?: boolean; defaults?: Record<string, any>; } function parseValue(value: string, type: string) { switch (type) { case 'number': const n = Number(value); if (isNaN(n)) throw new Error(`Invalid number: ${value}`); return n; case 'boolean': if (value === 'true') return true; if (value === 'false') return false; throw new Error(`Invalid boolean: ${value}`); case 'string': default: return value; } } export function loadEnv(schema: EnvSchema, options: LoadOptions = {}) { const envPath = options.path || path.resolve(process.cwd(), '.env'); const allowPartial = options.allowPartial ?? false; const defaults = options.defaults || {}; if (!fs.existsSync(envPath)) throw new Error(`.env file not found at ${envPath}`); const raw = fs.readFileSync(envPath, 'utf8'); const lines = raw.split('\n'); const envRaw: Record<string, string> = {}; for (const line of lines) { const trimmed = line.trim(); if (!trimmed || trimmed.startsWith('#')) continue; const [key, ...rest] = trimmed.split('='); envRaw[key] = rest.join('=').trim(); } const finalEnv: Record<string, any> = {}; for (const key in schema) { const expectedType = schema[key]; const rawValue = envRaw[key] ?? defaults[key]; if (rawValue === undefined) { if (!allowPartial) throw new Error(`Missing required env variable: ${key}`); continue; } finalEnv[key] = parseValue(rawValue, expectedType); } return finalEnv; }