Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Scan your codebase to detect which environment variables are used in your code.
Optimized for SvelteKit and Next.js. </br>
Also works well in modern JavaScript/TypeScript projects and frameworks like Node.js, Nuxt, and Vue — or any other setup where you want reliable .env file comparison / scanning.

![CI](https://github.com/chrilleweb/dotenv-diff/actions/workflows/ci.yml/badge.svg)
[![npm version](https://img.shields.io/npm/v/dotenv-diff.svg)](https://www.npmjs.com/package/dotenv-diff)
[![npm downloads](https://img.shields.io/npm/dt/dotenv-diff.svg)](https://www.npmjs.com/package/dotenv-diff)

Expand Down Expand Up @@ -347,12 +348,19 @@ This makes it quick to set up environment files without manually copying or rety
`dotenv-diff` will warn you if your `.env` file is **not** ignored by Git.
This helps prevent accidentally committing sensitive environment variables.

## Exit codes

- `0` → No errors (warnings may be present unless `--strict`)
- `1` → Errors found (or warnings when using `--strict`)

## 🤝 Contributing

Contributions are welcome! Feel free to open an issue or a pull request.
Issues, feature requests and pull requests are welcome.

If you plan a larger change, please open an issue first to discuss scope and approach.

## License

MIT
Licensed under the [MIT](LICENSE) license.

### Created by [chrilleweb](https://github.com/chrilleweb)
5 changes: 5 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Security Policy

If you discover a security vulnerability, please report it responsibly.

Send us an email to [email protected] and **don't** open a public issue.
16 changes: 11 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "dotenv-diff",
"version": "2.3.11",
"type": "module",
"description": "Scan your codebase to find environment variables in use.",
"description": "Detects environment variable issues, usage, and potential security risks.",
"bin": {
"dotenv-diff": "dist/bin/dotenv-diff.js"
},
Expand Down Expand Up @@ -40,11 +40,17 @@
"dotenv",
"env",
"dotenv-diff",
"env-check",
"env-validate",
"env-scan",
"cli",
"compare",
"environment variables",
"dotenv validator",
"scan codebase"
"config",
"security",
"ci",
"monorepo",
"sveltekit",
"nextjs",
"node"
],
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion src/cli/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from 'fs';
import path from 'path';
import { normalizeOptions } from '../config/options.js';
import { discoverEnvFiles } from '../services/envDiscovery.js';
import { pairWithExample } from '../services/envPairing.js';
import { pairWithExample } from '../core/envPairing.js';
import { ensureFilesOrPrompt } from '../services/ensureFilesOrPrompt.js';
import { compareMany } from '../commands/compare.js';
import {
Expand Down
19 changes: 15 additions & 4 deletions src/core/determineComparisonFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,26 @@ import path from 'path';
import { type ScanUsageOptions } from '../config/types.js';
import { resolveFromCwd } from './helpers/resolveFromCwd.js';

type ComparisonFile = {
path: string;
name: string;
};

const DEFAULT_ENV_FILES = [
'.env',
'.env.example',
'.env.local',
'.env.production',
] as const;

/**
* Determines which file to use for comparison based on provided options
* @param {ScanUsageOptions} opts - Scan configuration options
* @returns {Object|null} - The comparison file information or undefined if not found
* @returns Comparison file info with absolute path and basename, or undefined if not found
*/
export function determineComparisonFile(
opts: ScanUsageOptions,
): { path: string; name: string } | undefined {
): ComparisonFile | undefined {
// Priority: explicit flags first, then auto-discovery

if (opts.examplePath) {
Expand All @@ -28,8 +40,7 @@ export function determineComparisonFile(
}

// Auto-discovery: look for common env files relative to cwd
const candidates = ['.env', '.env.example', '.env.local', '.env.production'];
for (const candidate of candidates) {
for (const candidate of DEFAULT_ENV_FILES) {
const fullPath = path.resolve(opts.cwd, candidate);
if (fs.existsSync(fullPath)) {
return { path: fullPath, name: candidate };
Expand Down
13 changes: 4 additions & 9 deletions src/services/envPairing.ts → src/core/envPairing.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import fs from 'fs';
import path from 'path';
import type { Discovery } from './envDiscovery.js';
import type { Discovery } from '../services/envDiscovery.js';
import type { FilePair } from '../config/types.js';

/**
* Pairs each environment file with its corresponding example file.
* @param d - The discovery object containing environment and example file information.
* @returns An array of objects containing the environment name, path, and example path.
*/
export function pairWithExample(
d: Discovery,
): Array<{ envName: string; envPath: string; examplePath: string }> {
const pairs: Array<{
envName: string;
envPath: string;
examplePath: string;
}> = [];
export function pairWithExample(d: Discovery): Array<FilePair> {
const pairs: Array<FilePair> = [];
const list = d.envFiles.length > 0 ? d.envFiles : [d.primaryEnv];

for (const envName of list) {
Expand Down
7 changes: 3 additions & 4 deletions src/services/duplicates.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from 'fs';
import type { Duplicate } from '../config/types.js';

/**
* Scan a .env-like file for duplicate keys.
Expand All @@ -9,9 +10,7 @@ import fs from 'fs';
* @param filePath - Path to the .env file to scan
* @returns An array of objects representing duplicate keys and their counts.
*/
export function findDuplicateKeys(
filePath: string,
): Array<{ key: string; count: number }> {
export function findDuplicateKeys(filePath: string): Array<Duplicate> {
if (!fs.existsSync(filePath)) return [];

const raw = fs.readFileSync(filePath, 'utf8');
Expand All @@ -32,7 +31,7 @@ export function findDuplicateKeys(
counts.set(key, (counts.get(key) ?? 0) + 1);
}

const duplicates: Array<{ key: string; count: number }> = [];
const duplicates: Array<Duplicate> = [];
for (const [key, count] of counts) {
if (count > 1) duplicates.push({ key, count });
}
Expand Down
12 changes: 9 additions & 3 deletions src/services/fileWalker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import {
DEFAULT_EXCLUDE_PATTERNS,
} from '../core/patterns.js';

interface FindFilesOptions {
include?: string[];
exclude?: string[];
files?: string[];
}

/**
* Recursively finds all files in the given directory matching the include patterns,
* while excluding files and directories that match the exclude patterns.
Expand All @@ -15,7 +21,7 @@ import {
*/
export async function findFiles(
rootDir: string,
opts: { include: string[]; exclude: string[]; files?: string[] },
opts: FindFilesOptions,
): Promise<string[]> {
// If --files provided, keep existing replacement behavior
if (opts.files && opts.files.length > 0) {
Expand All @@ -24,7 +30,7 @@ export async function findFiles(

const defaultPatterns = getDefaultPatterns();
const rawInclude =
opts.include.length > 0
opts.include && opts.include.length > 0
? [...defaultPatterns, ...opts.include]
: defaultPatterns;
const includePatterns = rawInclude.flatMap(expandBraceSets);
Expand Down Expand Up @@ -69,7 +75,7 @@ export async function findFiles(
if (
shouldExclude(entry.name, relativeToRoot, [
...DEFAULT_EXCLUDE_PATTERNS,
...opts.exclude,
...(opts.exclude ?? []),
])
) {
continue;
Expand Down
14 changes: 8 additions & 6 deletions src/services/scanOutputToConsole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ import { printHealthScore } from '../ui/scan/printHealthScore.js';
import { printExpireWarnings } from '../ui/scan/printExpireWarnings.js';
import { printInconsistentNamingWarning } from '../ui/scan/printInconsistentNamingWarning.js';

interface FixContext {
fixApplied: boolean;
removedDuplicates: string[];
addedEnv: string[];
gitignoreUpdated: boolean;
}

/**
* Outputs the scan results to the console.
* @param scanResult - The result of the scan.
Expand All @@ -35,12 +42,7 @@ export function outputToConsole(
scanResult: ScanResult,
opts: ScanUsageOptions,
comparedAgainst: string,
fixContext?: {
fixApplied: boolean;
removedDuplicates: string[];
addedEnv: string[];
gitignoreUpdated: boolean;
},
fixContext?: FixContext,
): { exitWithError: boolean } {
let exitWithError = false;

Expand Down
Loading