dotenv-diff 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -8
- package/dist/cli.js +74 -43
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# dotenv-diff
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Easily compare your `.env` and `.env.example` files in Node.js projects to detect missing, extra, empty, or mismatched environment variables.
|
|
3
|
+
Easily compare your .env, .env.example, and other environment files (like .env.local, .env.production) to detect missing, extra, empty, or mismatched variables — and ensure they’re properly ignored by Git.
|
|
6
4
|
|
|
7
5
|
[](https://www.npmjs.com/package/dotenv-diff)
|
|
8
6
|
[](https://www.npmjs.com/package/dotenv-diff)
|
|
@@ -26,11 +24,12 @@ pnpm add -g dotenv-diff
|
|
|
26
24
|
```bash
|
|
27
25
|
dotenv-diff
|
|
28
26
|
```
|
|
29
|
-
##
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
27
|
+
## What it checks
|
|
28
|
+
dotenv-diff will automatically compare all matching .env* files in your project against .env.example, including:
|
|
29
|
+
- `.env`
|
|
30
|
+
- `.env.local`
|
|
31
|
+
- `.env.production`
|
|
32
|
+
- Any other .env.* file
|
|
34
33
|
|
|
35
34
|
## Optional: Check values too
|
|
36
35
|
|
package/dist/cli.js
CHANGED
|
@@ -23,13 +23,21 @@ program
|
|
|
23
23
|
const options = program.opts();
|
|
24
24
|
const checkValues = options.checkValues ?? false;
|
|
25
25
|
const cwd = process.cwd();
|
|
26
|
-
const
|
|
27
|
-
|
|
26
|
+
const envFiles = fs
|
|
27
|
+
.readdirSync(cwd)
|
|
28
|
+
.filter((f) => f.startsWith('.env') && !f.startsWith('.env.example'))
|
|
29
|
+
.sort((a, b) => (a === '.env' ? -1 : b === '.env' ? 1 : a.localeCompare(b)));
|
|
30
|
+
// Brug første .env* fil som "main" hvis flere findes
|
|
31
|
+
// (resten håndteres senere i et loop)
|
|
32
|
+
const primaryEnv = envFiles.includes('.env') ? '.env' : envFiles[0] || '.env';
|
|
33
|
+
const primaryExample = '.env.example';
|
|
34
|
+
const envPath = path.resolve(cwd, primaryEnv);
|
|
35
|
+
const examplePath = path.resolve(cwd, primaryExample);
|
|
28
36
|
const envExists = fs.existsSync(envPath);
|
|
29
37
|
const exampleExists = fs.existsSync(examplePath);
|
|
30
|
-
// Case 1:
|
|
31
|
-
if (
|
|
32
|
-
console.log(chalk.yellow('⚠️ No .env or .env.example file found. Skipping comparison.'));
|
|
38
|
+
// Case 1: No env files and no .env.example → nothing to do
|
|
39
|
+
if (envFiles.length === 0 && !exampleExists) {
|
|
40
|
+
console.log(chalk.yellow('⚠️ No .env* or .env.example file found. Skipping comparison.'));
|
|
33
41
|
process.exit(0);
|
|
34
42
|
}
|
|
35
43
|
// Case 2: .env is missing but .env.example exists
|
|
@@ -41,9 +49,9 @@ if (!envExists && exampleExists) {
|
|
|
41
49
|
message: '❓ Do you want to create a new .env file from .env.example?',
|
|
42
50
|
choices: [
|
|
43
51
|
{ title: 'Yes', value: true },
|
|
44
|
-
{ title: 'No', value: false }
|
|
52
|
+
{ title: 'No', value: false },
|
|
45
53
|
],
|
|
46
|
-
initial: 0
|
|
54
|
+
initial: 0,
|
|
47
55
|
});
|
|
48
56
|
if (!response.createEnv) {
|
|
49
57
|
console.log(chalk.gray('🚫 Skipping .env creation.'));
|
|
@@ -63,15 +71,16 @@ if (envExists && !exampleExists) {
|
|
|
63
71
|
message: '❓ Do you want to create a new .env.example file from .env?',
|
|
64
72
|
choices: [
|
|
65
73
|
{ title: 'Yes', value: true },
|
|
66
|
-
{ title: 'No', value: false }
|
|
74
|
+
{ title: 'No', value: false },
|
|
67
75
|
],
|
|
68
|
-
initial: 0
|
|
76
|
+
initial: 0,
|
|
69
77
|
});
|
|
70
78
|
if (!response.createExample) {
|
|
71
79
|
console.log(chalk.gray('🚫 Skipping .env.example creation.'));
|
|
72
80
|
process.exit(0);
|
|
73
81
|
}
|
|
74
|
-
const envContent = fs
|
|
82
|
+
const envContent = fs
|
|
83
|
+
.readFileSync(envPath, 'utf-8')
|
|
75
84
|
.split('\n')
|
|
76
85
|
.map((line) => {
|
|
77
86
|
const trimmed = line.trim();
|
|
@@ -89,38 +98,60 @@ if (!fs.existsSync(envPath) || !fs.existsSync(examplePath)) {
|
|
|
89
98
|
console.error(chalk.red('❌ Error: .env or .env.example is missing after setup.'));
|
|
90
99
|
process.exit(1);
|
|
91
100
|
}
|
|
92
|
-
// Case 5:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (diff.extra.length > 0) {
|
|
113
|
-
console.log(chalk.yellow('\n⚠️ Extra keys in .env (not defined in .env.example):'));
|
|
114
|
-
diff.extra.forEach((key) => console.log(chalk.yellow(` - ${key}`)));
|
|
115
|
-
}
|
|
116
|
-
if (emptyKeys.length > 0) {
|
|
117
|
-
console.log(chalk.yellow('\n⚠️ The following keys in .env have no value (empty):'));
|
|
118
|
-
emptyKeys.forEach((key) => console.log(chalk.yellow(` - ${key}`)));
|
|
119
|
-
}
|
|
120
|
-
if (checkValues && diff.valueMismatches.length > 0) {
|
|
121
|
-
console.log(chalk.yellow('\n⚠️ The following keys have different values:'));
|
|
122
|
-
diff.valueMismatches.forEach(({ key, expected, actual }) => {
|
|
123
|
-
console.log(chalk.yellow(` - ${key}: expected '${expected}', but got '${actual}'`));
|
|
101
|
+
// Case 5: Compare all found .env* files
|
|
102
|
+
let exitWithError = false;
|
|
103
|
+
for (const envName of envFiles.length > 0 ? envFiles : [primaryEnv]) {
|
|
104
|
+
const suffix = envName === '.env' ? '' : envName.replace('.env', '');
|
|
105
|
+
const exampleName = suffix ? `.env.example${suffix}` : primaryExample;
|
|
106
|
+
const envPathCurrent = path.resolve(cwd, envName);
|
|
107
|
+
const examplePathCurrent = fs.existsSync(path.resolve(cwd, exampleName))
|
|
108
|
+
? path.resolve(cwd, exampleName)
|
|
109
|
+
: examplePath;
|
|
110
|
+
if (!fs.existsSync(envPathCurrent) || !fs.existsSync(examplePathCurrent)) {
|
|
111
|
+
console.log(chalk.bold(`🔍 Comparing ${envName} ↔ ${path.basename(examplePathCurrent)}...`));
|
|
112
|
+
console.log(chalk.yellow(' ⚠️ Skipping: missing matching example file.'));
|
|
113
|
+
console.log();
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
console.log(chalk.bold(`🔍 Comparing ${envName} ↔ ${path.basename(examplePathCurrent)}...`));
|
|
117
|
+
warnIfEnvNotIgnored({
|
|
118
|
+
cwd,
|
|
119
|
+
envFile: envName,
|
|
120
|
+
log: (msg) => console.log(msg.replace(/^/gm, ' ')),
|
|
124
121
|
});
|
|
122
|
+
const current = parseEnvFile(envPathCurrent);
|
|
123
|
+
const example = parseEnvFile(examplePathCurrent);
|
|
124
|
+
const diff = diffEnv(current, example, checkValues);
|
|
125
|
+
const emptyKeys = Object.entries(current)
|
|
126
|
+
.filter(([, value]) => (value ?? '').trim() === '')
|
|
127
|
+
.map(([key]) => key);
|
|
128
|
+
if (diff.missing.length === 0 &&
|
|
129
|
+
diff.extra.length === 0 &&
|
|
130
|
+
emptyKeys.length === 0 &&
|
|
131
|
+
diff.valueMismatches.length === 0) {
|
|
132
|
+
console.log(chalk.green(' ✅ All keys match.'));
|
|
133
|
+
console.log();
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (diff.missing.length > 0) {
|
|
137
|
+
exitWithError = true;
|
|
138
|
+
console.log(chalk.red(' ❌ Missing keys:'));
|
|
139
|
+
diff.missing.forEach((key) => console.log(chalk.red(` - ${key}`)));
|
|
140
|
+
}
|
|
141
|
+
if (diff.extra.length > 0) {
|
|
142
|
+
console.log(chalk.yellow(' ⚠️ Extra keys (not in example):'));
|
|
143
|
+
diff.extra.forEach((key) => console.log(chalk.yellow(` - ${key}`)));
|
|
144
|
+
}
|
|
145
|
+
if (emptyKeys.length > 0) {
|
|
146
|
+
console.log(chalk.yellow(' ⚠️ Empty values:'));
|
|
147
|
+
emptyKeys.forEach((key) => console.log(chalk.yellow(` - ${key}`)));
|
|
148
|
+
}
|
|
149
|
+
if (checkValues && diff.valueMismatches.length > 0) {
|
|
150
|
+
console.log(chalk.yellow(' ⚠️ Value mismatches:'));
|
|
151
|
+
diff.valueMismatches.forEach(({ key, expected, actual }) => {
|
|
152
|
+
console.log(chalk.yellow(` - ${key}: expected '${expected}', but got '${actual}'`));
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
console.log(); // blank line efter sektionen med findings
|
|
125
156
|
}
|
|
126
|
-
process.exit(
|
|
157
|
+
process.exit(exitWithError ? 1 : 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dotenv-diff",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A small CLI and library to find differences between .env and .env.example files.",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dev": "vitest --watch",
|
|
19
19
|
"test": "vitest",
|
|
20
20
|
"lint": "eslint ./src --ext .ts",
|
|
21
|
-
"format": "prettier --write
|
|
21
|
+
"format": "prettier --write \"src/**/*.ts\" \"src/*.ts\"",
|
|
22
22
|
"start": "node dist/cli.js"
|
|
23
23
|
},
|
|
24
24
|
"author": "Chrilleweb",
|