dotenv-diff 1.1.4 → 1.2.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/dist/cli.js CHANGED
@@ -1,10 +1,18 @@
1
1
  #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point for the `dotenv-diff` tool.
4
+ *
5
+ * - Compares `.env` and `.env.example` files.
6
+ * - Optionally checks for value mismatches.
7
+ * - Warns about missing keys, extra keys, empty values, and mismatches.
8
+ */
2
9
  import { Command } from 'commander';
3
- import path from "path";
4
- import fs from "fs";
5
- import chalk from "chalk";
6
- import { parseEnvFile } from "./lib/parseEnv.js";
7
- import { diffEnv } from "./lib/diffEnv.js";
10
+ import path from 'path';
11
+ import fs from 'fs';
12
+ import chalk from 'chalk';
13
+ import prompts from 'prompts';
14
+ import { parseEnvFile } from './lib/parseEnv.js';
15
+ import { diffEnv } from './lib/diffEnv.js';
8
16
  const program = new Command();
9
17
  program
10
18
  .name('dotenv-diff')
@@ -13,51 +21,97 @@ program
13
21
  .parse(process.argv);
14
22
  const options = program.opts();
15
23
  const checkValues = options.checkValues ?? false;
16
- // Her kører din eksisterende diff-logik videre:
17
- const envPath = path.resolve(process.cwd(), ".env");
18
- const examplePath = path.resolve(process.cwd(), ".env.example");
19
- if (!fs.existsSync(envPath)) {
20
- console.error(chalk.red("❌ Error: .env file is missing in the project root."));
21
- process.exit(1);
24
+ const cwd = process.cwd();
25
+ const envPath = path.resolve(cwd, '.env');
26
+ const examplePath = path.resolve(cwd, '.env.example');
27
+ const envExists = fs.existsSync(envPath);
28
+ const exampleExists = fs.existsSync(examplePath);
29
+ // Case 1: Neither file exists
30
+ if (!envExists && !exampleExists) {
31
+ console.log(chalk.yellow('⚠️ No .env or .env.example file found. Skipping comparison.'));
32
+ process.exit(0);
33
+ }
34
+ // Case 2: .env is missing but .env.example exists
35
+ if (!envExists && exampleExists) {
36
+ console.log(chalk.yellow('📄 .env file not found.'));
37
+ const response = await prompts({
38
+ type: 'select',
39
+ name: 'createEnv',
40
+ message: '❓ Do you want to create a new .env file from .env.example?',
41
+ choices: [
42
+ { title: 'Yes', value: true },
43
+ { title: 'No', value: false }
44
+ ],
45
+ initial: 0
46
+ });
47
+ if (!response.createEnv) {
48
+ console.log(chalk.gray('🚫 Skipping .env creation.'));
49
+ process.exit(0);
50
+ }
51
+ const exampleContent = fs.readFileSync(examplePath, 'utf-8');
52
+ fs.writeFileSync(envPath, exampleContent);
53
+ console.log(chalk.green('✅ .env file created successfully from .env.example.\n'));
54
+ }
55
+ // Case 3: .env exists, but .env.example is missing
56
+ if (envExists && !exampleExists) {
57
+ console.log(chalk.yellow('📄 .env.example file not found.'));
58
+ const response = await prompts({
59
+ type: 'select',
60
+ name: 'createExample',
61
+ message: '❓ Do you want to create a new .env.example file from .env?',
62
+ choices: [
63
+ { title: 'Yes', value: true },
64
+ { title: 'No', value: false }
65
+ ],
66
+ initial: 0
67
+ });
68
+ if (!response.createExample) {
69
+ console.log(chalk.gray('🚫 Skipping .env.example creation.'));
70
+ process.exit(0);
71
+ }
72
+ const envContent = fs.readFileSync(envPath, 'utf-8');
73
+ fs.writeFileSync(examplePath, envContent);
74
+ console.log(chalk.green('✅ .env.example file created successfully from .env.\n'));
22
75
  }
23
- if (!fs.existsSync(examplePath)) {
24
- console.error(chalk.red("❌ Error: .env.example file is missing in the project root."));
76
+ // Case 4: Run comparison
77
+ if (!fs.existsSync(envPath) || !fs.existsSync(examplePath)) {
78
+ console.error(chalk.red('❌ Error: .env or .env.example is missing after setup.'));
25
79
  process.exit(1);
26
80
  }
27
- console.log(chalk.bold("🔍 Comparing .env and .env.example..."));
81
+ // Case 5: Both files exist, proceed with comparison
82
+ console.log(chalk.bold('🔍 Comparing .env and .env.example...\n'));
28
83
  const current = parseEnvFile(envPath);
29
84
  const example = parseEnvFile(examplePath);
30
85
  const diff = diffEnv(current, example, checkValues);
31
- // Find tomme variabler i .env
32
86
  const emptyKeys = Object.entries(current)
33
- .filter(([key, value]) => value.trim() === "")
87
+ .filter(([value]) => value.trim() === '')
34
88
  .map(([key]) => key);
35
89
  let hasWarnings = false;
36
90
  if (diff.missing.length === 0 &&
37
91
  diff.extra.length === 0 &&
38
92
  emptyKeys.length === 0 &&
39
93
  diff.valueMismatches.length === 0) {
40
- console.log(chalk.green("✅ All keys match! Your .env file is valid."));
94
+ console.log(chalk.green('✅ All keys match! Your .env file is valid.'));
41
95
  process.exit(0);
42
96
  }
43
97
  if (diff.missing.length > 0) {
44
- console.log(chalk.red("\n❌ Missing keys in .env:"));
98
+ console.log(chalk.red('\n❌ Missing keys in .env:'));
45
99
  diff.missing.forEach((key) => console.log(chalk.red(` - ${key}`)));
46
100
  }
47
101
  if (diff.extra.length > 0) {
48
- console.log(chalk.yellow("\n⚠️ Extra keys in .env (not defined in .env.example):"));
102
+ console.log(chalk.yellow('\n⚠️ Extra keys in .env (not defined in .env.example):'));
49
103
  diff.extra.forEach((key) => console.log(chalk.yellow(` - ${key}`)));
50
104
  }
51
105
  if (emptyKeys.length > 0) {
52
106
  hasWarnings = true;
53
- console.log(chalk.yellow("\n⚠️ The following keys in .env have no value (empty):"));
107
+ console.log(chalk.yellow('\n⚠️ The following keys in .env have no value (empty):'));
54
108
  emptyKeys.forEach((key) => console.log(chalk.yellow(` - ${key}`)));
55
109
  }
56
110
  if (checkValues && diff.valueMismatches.length > 0) {
57
111
  hasWarnings = true;
58
- console.log(chalk.yellow("\n⚠️ The following keys have different values:"));
112
+ console.log(chalk.yellow('\n⚠️ The following keys have different values:'));
59
113
  diff.valueMismatches.forEach(({ key, expected, actual }) => {
60
114
  console.log(chalk.yellow(` - ${key}: expected "${expected}", but got "${actual}"`));
61
115
  });
62
116
  }
63
- process.exit(diff.missing.length > 0 ? 1 : hasWarnings ? 0 : 0);
117
+ process.exit(diff.missing.length > 0 ? 1 : 0);
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Compares two .env files and returns their differences.
3
+ *
4
+ * @param current - An object representing the current `.env` file (key-value pairs).
5
+ * @param example - An object representing the `.env.example` file (key-value pairs).
6
+ * @param checkValues - If true, compare values when the example has a non-empty value.
7
+ * @returns A `DiffResult` object containing missing, extra, and mismatched keys.
8
+ */
1
9
  export function diffEnv(current, example, checkValues = false) {
2
10
  const currentKeys = Object.keys(current);
3
11
  const exampleKeys = Object.keys(example);
@@ -1,4 +1,13 @@
1
1
  import fs from 'fs';
2
+ /**
3
+ * Parses a `.env` file and returns an object with key-value pairs.
4
+ *
5
+ * @param path - The file path to the `.env` file.
6
+ * @returns A record object representing parsed environment variables.
7
+ *
8
+ * Lines that are empty or start with `#` (comments) are ignored.
9
+ * Multi-line or quoted values are not supported.
10
+ */
2
11
  export function parseEnvFile(path) {
3
12
  const content = fs.readFileSync(path, 'utf-8');
4
13
  const lines = content.split('\n');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dotenv-diff",
3
- "version": "1.1.4",
3
+ "version": "1.2.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": {
@@ -20,6 +20,7 @@
20
20
  "build": "tsc",
21
21
  "dev": "vitest --watch",
22
22
  "test": "vitest",
23
+ "lint": "eslint ./src --ext .ts",
23
24
  "start": "node dist/cli.js"
24
25
  },
25
26
  "author": "Chrilleweb",
@@ -42,10 +43,14 @@
42
43
  },
43
44
  "dependencies": {
44
45
  "chalk": "^5.4.1",
45
- "commander": "^14.0.0"
46
+ "commander": "^14.0.0",
47
+ "prompts": "^2.4.2"
46
48
  },
47
49
  "devDependencies": {
48
50
  "@types/node": "^20.0.0",
51
+ "@typescript-eslint/eslint-plugin": "^8.39.0",
52
+ "@typescript-eslint/parser": "^8.39.0",
53
+ "eslint": "^9.32.0",
49
54
  "typescript": "^5.2.0",
50
55
  "vitest": "^3.2.0"
51
56
  }