Skip to content

1GoodCod1/env-surgeon

Repository files navigation

env-surgeon

Diff, scan, check and validate .env files — CLI + Node.js library.

npm version npm downloads license


Why

Every Node.js project suffers from the same .env problems:

  • App crashes in production because someone forgot to add a new variable
  • .env.example is months out of date
  • CI passes but deployment fails with Cannot read property of undefined
  • No one knows which variables are required vs optional

env-surgeon solves all of this with a handful of commands.


Install

npx env-surgeon <command>          # run without installing
npm install -D env-surgeon         # dev dependency (recommended)
npm install -g env-surgeon         # global

Commands

diff — Compare two .env files

env-surgeon diff .env .env.production

scan — Scan source code, generate .env.example

env-surgeon scan ./src --output .env.example

Finds process.env.X, import.meta.env.X, Deno.env.get('X'), Bun.env.X, and destructured const { FOO } = process.env.

check — CI check for missing variables

env-surgeon check
env-surgeon check --env .env --template .env.example --strict

validate — Validate types and rules

env-surgeon validate --env .env --schema .env.schema.json

Supports types: string, number, boolean, url, email, json, port, duration, secret, array.

Zod is supported out of the box — point --schema at a .js/.mjs file whose default export is a Zod object schema:

// env.schema.js
import { z } from 'zod'
export default z.object({
  PORT: z.coerce.number().min(1024),
  DATABASE_URL: z.string().url(),
  LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).optional(),
})
env-surgeon validate --env .env --schema env.schema.js

zod is an optional peer dependency — install it only if you use this path.

print — Inspect the effective merged env

env-surgeon print --env .env.local --env .env --expand
env-surgeon print --env .env --reveal          # actual values to stdout

unused — Find dead env keys

env-surgeon unused ./src --env .env

init — Generate schema from existing .env

env-surgeon init --env .env --output .env.schema.json

Infers types (number, boolean, URL, email, JSON, port, duration) from values.


Common options

Option Description
--json Output as JSON
--reporter <fmt> Output format: text, json, junit
--silent Exit code only, no output
--expand Expand ${VAR} references (dotenv-expand semantics)
--watch Re-run on file changes (check, validate)
--strict Fail on extra keys not in template/schema
--auto-env Auto-select .env files by NODE_ENV

Library API

import { diff, scan, check, validate } from 'env-surgeon'

const result = await diff('.env', '.env.production')
const found = await scan('./src')
const status = await check({ template: '.env.example', env: '.env' })
const errors = await validate({
  env: '.env',
  schema: { PORT: { type: 'number', required: true } },
})

Low-level helpers

import {
  parseEnvFile, parseEnvString, readEnvCascade,
  defaultEnvCascade, UserError,
} from 'env-surgeon'

Recommended project setup

your-project/
├── .env              ← local, git-ignored
├── .env.example      ← committed, generated by `scan`
└── .env.schema.json  ← committed, generated by `init`
{
  "scripts": {
    "env:sync":  "env-surgeon scan ./src --output .env.example",
    "env:check": "env-surgeon check",
    "prestart":  "env-surgeon check",
    "prebuild":  "env-surgeon validate"
  }
}

GitHub Action

- uses: env-surgeon/env-surgeon@v1
  with:
    command: check
    env: .env.ci
    template: .env.example
    strict: true

Advanced

See docs/guide.md for:

  • Environment adaptation (stdin, cascades, Windows, Bun/Deno)
  • Config file reference
  • NestJS / dotenv-expand compatibility
  • .env.vault encrypted file support
  • Watch mode
  • CI reporters (JUnit XML)
  • Execution model and pitfalls

Requirements

  • Node.js >= 20

License

MIT

About

readme

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors