dotenv-diff 1.2.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/LICENSE +21 -0
- package/README.md +21 -8
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +85 -45
- package/dist/diffEnv.d.ts +5 -0
- package/dist/diffEnv.js +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/lib/checkGitignore.d.ts +23 -0
- package/dist/lib/checkGitignore.js +73 -0
- package/dist/lib/diffEnv.d.ts +18 -0
- package/dist/lib/diffEnv.js +1 -1
- package/dist/lib/parseEnv.d.ts +10 -0
- package/dist/parseEnv.d.ts +1 -0
- package/dist/parseEnv.js +16 -0
- package/package.json +6 -7
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Chrilleweb
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
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
|
|
|
@@ -40,6 +39,20 @@ dotenv-diff --check-values
|
|
|
40
39
|
|
|
41
40
|
When using the `--check-values` option, the tool will also compare the actual values of the variables in `.env` and `.env.example`. It will report any mismatches found and it also compares values if .env.example defines a non-empty expected value.
|
|
42
41
|
|
|
42
|
+
## Automatic file creation prompts
|
|
43
|
+
|
|
44
|
+
If one of the files is missing, `dotenv-diff` will ask if you want to create it from the other:
|
|
45
|
+
|
|
46
|
+
- **`.env` missing** → prompts to create it from `.env.example`
|
|
47
|
+
- **`.env.example` missing** → prompts to create it from `.env` *(keys only, no values)*
|
|
48
|
+
|
|
49
|
+
This makes it quick to set up environment files without manually copying or retyping variable names.
|
|
50
|
+
|
|
51
|
+
## Also checks your .gitignore
|
|
52
|
+
|
|
53
|
+
`dotenv-diff` will warn you if your `.env` file is **not** ignored by Git.
|
|
54
|
+
This helps prevent accidentally committing sensitive environment variables.
|
|
55
|
+
|
|
43
56
|
## 🤝 Contributing
|
|
44
57
|
|
|
45
58
|
Contributions are welcome! Feel free to open an issue or a pull request.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,7 @@ import chalk from 'chalk';
|
|
|
13
13
|
import prompts from 'prompts';
|
|
14
14
|
import { parseEnvFile } from './lib/parseEnv.js';
|
|
15
15
|
import { diffEnv } from './lib/diffEnv.js';
|
|
16
|
+
import { warnIfEnvNotIgnored } from './lib/checkGitignore.js';
|
|
16
17
|
const program = new Command();
|
|
17
18
|
program
|
|
18
19
|
.name('dotenv-diff')
|
|
@@ -22,13 +23,21 @@ program
|
|
|
22
23
|
const options = program.opts();
|
|
23
24
|
const checkValues = options.checkValues ?? false;
|
|
24
25
|
const cwd = process.cwd();
|
|
25
|
-
const
|
|
26
|
-
|
|
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);
|
|
27
36
|
const envExists = fs.existsSync(envPath);
|
|
28
37
|
const exampleExists = fs.existsSync(examplePath);
|
|
29
|
-
// Case 1:
|
|
30
|
-
if (
|
|
31
|
-
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.'));
|
|
32
41
|
process.exit(0);
|
|
33
42
|
}
|
|
34
43
|
// Case 2: .env is missing but .env.example exists
|
|
@@ -40,9 +49,9 @@ if (!envExists && exampleExists) {
|
|
|
40
49
|
message: '❓ Do you want to create a new .env file from .env.example?',
|
|
41
50
|
choices: [
|
|
42
51
|
{ title: 'Yes', value: true },
|
|
43
|
-
{ title: 'No', value: false }
|
|
52
|
+
{ title: 'No', value: false },
|
|
44
53
|
],
|
|
45
|
-
initial: 0
|
|
54
|
+
initial: 0,
|
|
46
55
|
});
|
|
47
56
|
if (!response.createEnv) {
|
|
48
57
|
console.log(chalk.gray('🚫 Skipping .env creation.'));
|
|
@@ -51,6 +60,7 @@ if (!envExists && exampleExists) {
|
|
|
51
60
|
const exampleContent = fs.readFileSync(examplePath, 'utf-8');
|
|
52
61
|
fs.writeFileSync(envPath, exampleContent);
|
|
53
62
|
console.log(chalk.green('✅ .env file created successfully from .env.example.\n'));
|
|
63
|
+
warnIfEnvNotIgnored();
|
|
54
64
|
}
|
|
55
65
|
// Case 3: .env exists, but .env.example is missing
|
|
56
66
|
if (envExists && !exampleExists) {
|
|
@@ -61,15 +71,25 @@ if (envExists && !exampleExists) {
|
|
|
61
71
|
message: '❓ Do you want to create a new .env.example file from .env?',
|
|
62
72
|
choices: [
|
|
63
73
|
{ title: 'Yes', value: true },
|
|
64
|
-
{ title: 'No', value: false }
|
|
74
|
+
{ title: 'No', value: false },
|
|
65
75
|
],
|
|
66
|
-
initial: 0
|
|
76
|
+
initial: 0,
|
|
67
77
|
});
|
|
68
78
|
if (!response.createExample) {
|
|
69
79
|
console.log(chalk.gray('🚫 Skipping .env.example creation.'));
|
|
70
80
|
process.exit(0);
|
|
71
81
|
}
|
|
72
|
-
const envContent = fs
|
|
82
|
+
const envContent = fs
|
|
83
|
+
.readFileSync(envPath, 'utf-8')
|
|
84
|
+
.split('\n')
|
|
85
|
+
.map((line) => {
|
|
86
|
+
const trimmed = line.trim();
|
|
87
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
88
|
+
return trimmed;
|
|
89
|
+
const [key] = trimmed.split('=');
|
|
90
|
+
return `${key}=`;
|
|
91
|
+
})
|
|
92
|
+
.join('\n');
|
|
73
93
|
fs.writeFileSync(examplePath, envContent);
|
|
74
94
|
console.log(chalk.green('✅ .env.example file created successfully from .env.\n'));
|
|
75
95
|
}
|
|
@@ -78,40 +98,60 @@ if (!fs.existsSync(envPath) || !fs.existsSync(examplePath)) {
|
|
|
78
98
|
console.error(chalk.red('❌ Error: .env or .env.example is missing after setup.'));
|
|
79
99
|
process.exit(1);
|
|
80
100
|
}
|
|
81
|
-
// Case 5:
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (diff.extra.length > 0) {
|
|
102
|
-
console.log(chalk.yellow('\n⚠️ Extra keys in .env (not defined in .env.example):'));
|
|
103
|
-
diff.extra.forEach((key) => console.log(chalk.yellow(` - ${key}`)));
|
|
104
|
-
}
|
|
105
|
-
if (emptyKeys.length > 0) {
|
|
106
|
-
hasWarnings = true;
|
|
107
|
-
console.log(chalk.yellow('\n⚠️ The following keys in .env have no value (empty):'));
|
|
108
|
-
emptyKeys.forEach((key) => console.log(chalk.yellow(` - ${key}`)));
|
|
109
|
-
}
|
|
110
|
-
if (checkValues && diff.valueMismatches.length > 0) {
|
|
111
|
-
hasWarnings = true;
|
|
112
|
-
console.log(chalk.yellow('\n⚠️ The following keys have different values:'));
|
|
113
|
-
diff.valueMismatches.forEach(({ key, expected, actual }) => {
|
|
114
|
-
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, ' ')),
|
|
115
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
|
|
116
156
|
}
|
|
117
|
-
process.exit(
|
|
157
|
+
process.exit(exitWithError ? 1 : 0);
|
package/dist/diffEnv.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function diffEnv(current, example) {
|
|
2
|
+
const currentKeys = Object.keys(current);
|
|
3
|
+
const exampleKeys = Object.keys(example);
|
|
4
|
+
const missing = exampleKeys.filter((key) => !currentKeys.includes(key));
|
|
5
|
+
const extra = currentKeys.filter((key) => !exampleKeys.includes(key));
|
|
6
|
+
return { missing, extra };
|
|
7
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { parseEnvFile } from './lib/parseEnv.js';
|
|
2
|
-
export { diffEnv,
|
|
2
|
+
export { diffEnv, DiffResult } from './lib/diffEnv.js';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type GitignoreCheckOptions = {
|
|
2
|
+
/** Project root directory (default: process.cwd()) */
|
|
3
|
+
cwd?: string;
|
|
4
|
+
/** Name of the env file (default: ".env") */
|
|
5
|
+
envFile?: string;
|
|
6
|
+
/** Custom logger (default: console.log) */
|
|
7
|
+
log?: (msg: string) => void;
|
|
8
|
+
};
|
|
9
|
+
/** Are we in a git repo? (checks for .git directory in cwd) */
|
|
10
|
+
export declare function isGitRepo(cwd?: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Returns:
|
|
13
|
+
* - true → .env-matching patterns are found in .gitignore
|
|
14
|
+
* - false → .env-matching patterns are NOT found (or a negation exists)
|
|
15
|
+
* - null → no .gitignore exists
|
|
16
|
+
*/
|
|
17
|
+
export declare function isEnvIgnoredByGit(options?: GitignoreCheckOptions): boolean | null;
|
|
18
|
+
/**
|
|
19
|
+
* Logs a friendly warning if .env is not ignored by Git.
|
|
20
|
+
* - Does not hard fail: non-blocking DX.
|
|
21
|
+
* - Skips if not in a git repo or if .env does not exist.
|
|
22
|
+
*/
|
|
23
|
+
export declare function warnIfEnvNotIgnored(options?: GitignoreCheckOptions): void;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
/** Are we in a git repo? (checks for .git directory in cwd) */
|
|
5
|
+
export function isGitRepo(cwd = process.cwd()) {
|
|
6
|
+
return fs.existsSync(path.resolve(cwd, '.git'));
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Returns:
|
|
10
|
+
* - true → .env-matching patterns are found in .gitignore
|
|
11
|
+
* - false → .env-matching patterns are NOT found (or a negation exists)
|
|
12
|
+
* - null → no .gitignore exists
|
|
13
|
+
*/
|
|
14
|
+
export function isEnvIgnoredByGit(options = {}) {
|
|
15
|
+
const { cwd = process.cwd(), envFile = '.env' } = options;
|
|
16
|
+
const gitignorePath = path.resolve(cwd, '.gitignore');
|
|
17
|
+
if (!fs.existsSync(gitignorePath))
|
|
18
|
+
return null;
|
|
19
|
+
const raw = fs.readFileSync(gitignorePath, 'utf8');
|
|
20
|
+
const lines = raw
|
|
21
|
+
.split(/\r?\n/)
|
|
22
|
+
.map((l) => l.trim())
|
|
23
|
+
.filter((l) => l && !l.startsWith('#'));
|
|
24
|
+
// If there is a negation (!pattern) that matches our candidates, we consider it as "not ignored"
|
|
25
|
+
if (lines.some((l) => l.startsWith('!') && matchesCandidate(l.slice(1), envFile))) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const candidates = getCandidatePatterns(envFile);
|
|
29
|
+
return lines.some((line) => candidates.has(line));
|
|
30
|
+
}
|
|
31
|
+
/** Our simple candidate patterns that typically cover env files in the root and subfolders */
|
|
32
|
+
function getCandidatePatterns(envFile = '.env') {
|
|
33
|
+
const base = envFile; // ".env"
|
|
34
|
+
const star = `${base}*`; // ".env*"
|
|
35
|
+
const dotStar = `${base}.*`; // ".env.*"
|
|
36
|
+
return new Set([
|
|
37
|
+
base,
|
|
38
|
+
`/${base}`,
|
|
39
|
+
`**/${base}`,
|
|
40
|
+
star,
|
|
41
|
+
`/${star}`,
|
|
42
|
+
`**/${star}`,
|
|
43
|
+
dotStar,
|
|
44
|
+
`/${dotStar}`,
|
|
45
|
+
`**/${dotStar}`,
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
// Matches only against our own candidate patterns (intentionally simple)
|
|
49
|
+
function matchesCandidate(pattern, envFile) {
|
|
50
|
+
return getCandidatePatterns(envFile).has(pattern);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Logs a friendly warning if .env is not ignored by Git.
|
|
54
|
+
* - Does not hard fail: non-blocking DX.
|
|
55
|
+
* - Skips if not in a git repo or if .env does not exist.
|
|
56
|
+
*/
|
|
57
|
+
export function warnIfEnvNotIgnored(options = {}) {
|
|
58
|
+
const { cwd = process.cwd(), envFile = '.env', log = console.log } = options;
|
|
59
|
+
const envPath = path.resolve(cwd, envFile);
|
|
60
|
+
if (!fs.existsSync(envPath))
|
|
61
|
+
return; // No .env file → nothing to warn about
|
|
62
|
+
if (!isGitRepo(cwd))
|
|
63
|
+
return; // Not a git repo → skip
|
|
64
|
+
const gitignorePath = path.resolve(cwd, '.gitignore');
|
|
65
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
66
|
+
log(chalk.yellow(`⚠️ No .gitignore found – your ${envFile} may be committed.\n Add:\n ${envFile}\n ${envFile}.*\n`));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const ignored = isEnvIgnoredByGit({ cwd, envFile });
|
|
70
|
+
if (ignored === false || ignored === null) {
|
|
71
|
+
log(chalk.yellow(`⚠️ ${envFile} is not ignored by Git (.gitignore).\n Consider adding:\n ${envFile}\n ${envFile}.*\n`));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type DiffResult = {
|
|
2
|
+
missing: string[];
|
|
3
|
+
extra: string[];
|
|
4
|
+
valueMismatches: {
|
|
5
|
+
key: string;
|
|
6
|
+
expected: string;
|
|
7
|
+
actual: string;
|
|
8
|
+
}[];
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Compares two .env files and returns their differences.
|
|
12
|
+
*
|
|
13
|
+
* @param current - An object representing the current `.env` file (key-value pairs).
|
|
14
|
+
* @param example - An object representing the `.env.example` file (key-value pairs).
|
|
15
|
+
* @param checkValues - If true, compare values when the example has a non-empty value.
|
|
16
|
+
* @returns A `DiffResult` object containing missing, extra, and mismatched keys.
|
|
17
|
+
*/
|
|
18
|
+
export declare function diffEnv(current: Record<string, string>, example: Record<string, string>, checkValues?: boolean): DiffResult;
|
package/dist/lib/diffEnv.js
CHANGED
|
@@ -16,7 +16,7 @@ export function diffEnv(current, example, checkValues = false) {
|
|
|
16
16
|
valueMismatches = exampleKeys
|
|
17
17
|
.filter((key) => {
|
|
18
18
|
return (currentKeys.includes(key) &&
|
|
19
|
-
example[key].trim() !==
|
|
19
|
+
example[key].trim() !== '' &&
|
|
20
20
|
current[key] !== example[key]);
|
|
21
21
|
})
|
|
22
22
|
.map((key) => ({
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a `.env` file and returns an object with key-value pairs.
|
|
3
|
+
*
|
|
4
|
+
* @param path - The file path to the `.env` file.
|
|
5
|
+
* @returns A record object representing parsed environment variables.
|
|
6
|
+
*
|
|
7
|
+
* Lines that are empty or start with `#` (comments) are ignored.
|
|
8
|
+
* Multi-line or quoted values are not supported.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseEnvFile(path: string): Record<string, string>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseEnvFile(path: string): Record<string, string>;
|
package/dist/parseEnv.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
export function parseEnvFile(path) {
|
|
3
|
+
const content = fs.readFileSync(path, 'utf-8');
|
|
4
|
+
const lines = content.split('\n');
|
|
5
|
+
const result = {};
|
|
6
|
+
for (const line of lines) {
|
|
7
|
+
const trimmed = line.trim();
|
|
8
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
9
|
+
continue;
|
|
10
|
+
const [key, ...rest] = trimmed.split('=');
|
|
11
|
+
if (!key)
|
|
12
|
+
continue;
|
|
13
|
+
result[key.trim()] = rest.join('=').trim();
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
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": {
|
|
@@ -9,18 +9,16 @@
|
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
11
11
|
"files": [
|
|
12
|
-
"dist/
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"dist/index.js",
|
|
16
|
-
"dist/index.d.ts",
|
|
17
|
-
"README.md"
|
|
12
|
+
"dist/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
18
15
|
],
|
|
19
16
|
"scripts": {
|
|
20
17
|
"build": "tsc",
|
|
21
18
|
"dev": "vitest --watch",
|
|
22
19
|
"test": "vitest",
|
|
23
20
|
"lint": "eslint ./src --ext .ts",
|
|
21
|
+
"format": "prettier --write \"src/**/*.ts\" \"src/*.ts\"",
|
|
24
22
|
"start": "node dist/cli.js"
|
|
25
23
|
},
|
|
26
24
|
"author": "Chrilleweb",
|
|
@@ -51,6 +49,7 @@
|
|
|
51
49
|
"@typescript-eslint/eslint-plugin": "^8.39.0",
|
|
52
50
|
"@typescript-eslint/parser": "^8.39.0",
|
|
53
51
|
"eslint": "^9.32.0",
|
|
52
|
+
"prettier": "^3.6.2",
|
|
54
53
|
"typescript": "^5.2.0",
|
|
55
54
|
"vitest": "^3.2.0"
|
|
56
55
|
}
|