Skip to content

Commit e57e653

Browse files
authored
Merge pull request #67 from Chrilleweb/cmn/dev
refactor code
2 parents 4c2cbbb + 4837364 commit e57e653

3 files changed

Lines changed: 173 additions & 109 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/) and [Semant
1212
### Fixed
1313
-
1414

15-
## [2.2.7] - 2025-09-27
15+
## [2.2.7] - 2025-09-28
1616
### Added
1717
- Added warning on .env not ignored by .gitignore on default.
1818
- added `dotenv-diff-ignore` comment to ignore lines from secret detection.

src/cli/run.ts

Lines changed: 170 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import chalk from 'chalk';
66
import { normalizeOptions } from '../config/options.js';
77
import { discoverEnvFiles } from '../services/envDiscovery.js';
88
import { pairWithExample } from '../services/envPairing.js';
9-
import { ensureFilesOrPrompt } from '../commands/init.js';
9+
import { ensureFilesOrPrompt } from '../services/ensureFilesOrPrompt.js';
1010
import { compareMany } from '../commands/compare.js';
11-
import type { CompareJsonEntry } from '../config/types.js';
11+
import { type CompareJsonEntry, type Options } from '../config/types.js';
1212
import { scanUsage } from '../commands/scanUsage.js';
1313

1414
/**
@@ -17,134 +17,189 @@ import { scanUsage } from '../commands/scanUsage.js';
1717
*/
1818
export async function run(program: Command) {
1919
program.parse(process.argv);
20-
const raw = program.opts();
21-
const opts = normalizeOptions(raw);
20+
const opts = normalizeOptions(program.opts());
2221

22+
setupGlobalConfig(opts);
23+
24+
// Route to appropriate command
25+
if (opts.compare) {
26+
await runCompareMode(opts);
27+
} else {
28+
await runScanMode(opts);
29+
}
30+
}
31+
32+
/**
33+
* Setup global configuration
34+
*/
35+
function setupGlobalConfig(opts: Options) {
2336
if (opts.noColor) {
2437
chalk.level = 0; // disable colors globally
2538
}
39+
}
2640

27-
// DEFAULT: scan-usage unless --compare is set
28-
if (!opts.compare) {
29-
const envPath =
30-
opts.envFlag || (fs.existsSync('.env') ? '.env' : undefined);
41+
/**
42+
* Run scan-usage mode (default behavior)
43+
*/
44+
async function runScanMode(opts: Options) {
45+
const envPath = opts.envFlag || (fs.existsSync('.env') ? '.env' : undefined);
3146

32-
const { exitWithError } = await scanUsage({
33-
cwd: opts.cwd,
34-
include: opts.includeFiles,
35-
exclude: opts.excludeFiles,
36-
ignore: opts.ignore,
37-
ignoreRegex: opts.ignoreRegex,
38-
examplePath: opts.exampleFlag,
39-
envPath,
40-
fix: opts.fix,
41-
json: opts.json,
42-
showUnused: opts.showUnused,
43-
showStats: opts.showStats,
44-
isCiMode: opts.isCiMode,
45-
secrets: opts.secrets,
46-
strict: opts.strict ?? false,
47-
...(opts.files ? { files: opts.files } : {}),
48-
});
47+
const { exitWithError } = await scanUsage({
48+
cwd: opts.cwd,
49+
include: opts.includeFiles,
50+
exclude: opts.excludeFiles,
51+
ignore: opts.ignore,
52+
ignoreRegex: opts.ignoreRegex,
53+
examplePath: opts.exampleFlag,
54+
envPath,
55+
fix: opts.fix,
56+
json: opts.json,
57+
showUnused: opts.showUnused,
58+
showStats: opts.showStats,
59+
isCiMode: opts.isCiMode,
60+
secrets: opts.secrets,
61+
strict: opts.strict ?? false,
62+
...(opts.files ? { files: opts.files } : {}),
63+
});
4964

50-
process.exit(exitWithError ? 1 : 0);
51-
}
65+
process.exit(exitWithError ? 1 : 0);
66+
}
5267

53-
// Special-case: both flags → direct comparison of exactly those two files
68+
/**
69+
* Run compare mode
70+
*/
71+
async function runCompareMode(opts: Options) {
72+
// Handle direct file comparison (both --env and --example specified)
5473
if (opts.envFlag && opts.exampleFlag) {
55-
const envExists = fs.existsSync(opts.envFlag);
56-
const exExists = fs.existsSync(opts.exampleFlag);
57-
58-
// Handle missing files with prompting (unless in CI mode)
59-
if (!envExists || !exExists) {
60-
// Check if we should prompt for file creation
61-
if (!opts.isCiMode) {
62-
const res = await ensureFilesOrPrompt({
63-
cwd: opts.cwd,
64-
primaryEnv: opts.envFlag,
65-
primaryExample: opts.exampleFlag,
66-
alreadyWarnedMissingEnv: false,
67-
isYesMode: opts.isYesMode,
68-
isCiMode: opts.isCiMode,
69-
});
70-
71-
if (res.shouldExit) {
72-
if (opts.json) console.log(JSON.stringify([], null, 2));
73-
process.exit(res.exitCode);
74-
}
75-
} else {
76-
// In CI mode, we just show errors and exit
77-
if (!envExists) {
78-
console.error(
79-
chalk.red(
80-
`❌ Error: --env file not found: ${path.basename(opts.envFlag)}`,
81-
),
82-
);
83-
}
84-
if (!exExists) {
85-
console.error(
86-
chalk.red(
87-
`❌ Error: --example file not found: ${path.basename(opts.exampleFlag)}`,
88-
),
89-
);
90-
}
91-
process.exit(1);
92-
}
93-
}
74+
await runDirectFileComparison(opts);
75+
return;
76+
}
77+
78+
// Handle auto-discovery comparison
79+
await runAutoDiscoveryComparison(opts);
80+
}
9481

95-
const report: CompareJsonEntry[] = [];
96-
const { exitWithError } = await compareMany(
97-
[
98-
{
99-
envName: path.basename(opts.envFlag),
100-
envPath: opts.envFlag,
101-
examplePath: opts.exampleFlag,
102-
},
103-
],
82+
/**
83+
* Compare two specific files directly
84+
*/
85+
async function runDirectFileComparison(opts: Options) {
86+
const envExists = fs.existsSync(opts.envFlag!);
87+
const exExists = fs.existsSync(opts.exampleFlag!);
88+
89+
// Handle missing files
90+
if (!envExists || !exExists) {
91+
const shouldExit = await handleMissingFiles(
92+
opts,
93+
opts.envFlag!,
94+
opts.exampleFlag!,
95+
);
96+
if (shouldExit) return;
97+
}
98+
99+
// Perform comparison
100+
const report: CompareJsonEntry[] = [];
101+
const { exitWithError } = await compareMany(
102+
[
104103
{
105-
checkValues: opts.checkValues,
106-
cwd: opts.cwd,
107-
allowDuplicates: opts.allowDuplicates,
108-
json: opts.json,
109-
ignore: opts.ignore,
110-
ignoreRegex: opts.ignoreRegex,
111-
showStats: opts.showStats,
112-
collect: (e) => report.push(e),
113-
...(opts.only ? { only: opts.only } : {}),
104+
envName: path.basename(opts.envFlag!),
105+
envPath: opts.envFlag!,
106+
examplePath: opts.exampleFlag!,
114107
},
115-
);
108+
],
109+
buildCompareOptions(opts, report),
110+
);
116111

117-
if (opts.json) {
118-
console.log(JSON.stringify(report, null, 2));
119-
}
120-
process.exit(exitWithError ? 1 : 0);
121-
}
112+
outputResults(report, opts, exitWithError);
113+
}
122114

123-
// Auto-discovery flow
124-
const d = discoverEnvFiles({
115+
/**
116+
* Compare using auto-discovery
117+
*/
118+
async function runAutoDiscoveryComparison(opts: Options) {
119+
// Discover available env files
120+
const discovery = discoverEnvFiles({
125121
cwd: opts.cwd,
126122
envFlag: opts.envFlag ?? null,
127123
exampleFlag: opts.exampleFlag ?? null,
128124
});
129125

130-
// Init cases (may create files or early-exit)
131-
const res = await ensureFilesOrPrompt({
132-
cwd: d.cwd,
133-
primaryEnv: d.primaryEnv,
134-
primaryExample: d.primaryExample,
135-
alreadyWarnedMissingEnv: d.alreadyWarnedMissingEnv,
126+
// Ensure required files exist or prompt to create them
127+
const initResult = await ensureFilesOrPrompt({
128+
cwd: discovery.cwd,
129+
primaryEnv: discovery.primaryEnv,
130+
primaryExample: discovery.primaryExample,
131+
alreadyWarnedMissingEnv: discovery.alreadyWarnedMissingEnv,
136132
isYesMode: opts.isYesMode,
137133
isCiMode: opts.isCiMode,
138134
});
139-
if (res.shouldExit) {
140-
if (opts.json) console.log(JSON.stringify([], null, 2));
141-
process.exit(res.exitCode);
135+
136+
if (initResult.shouldExit) {
137+
outputResults([], opts, initResult.exitCode !== 0);
138+
return;
142139
}
143140

144141
// Compare all discovered pairs
145-
const pairs = pairWithExample(d);
142+
const pairs = pairWithExample(discovery);
146143
const report: CompareJsonEntry[] = [];
147-
const { exitWithError } = await compareMany(pairs, {
144+
const { exitWithError } = await compareMany(
145+
pairs,
146+
buildCompareOptions(opts, report),
147+
);
148+
149+
outputResults(report, opts, exitWithError);
150+
}
151+
152+
/**
153+
* Handle missing files in CI vs interactive mode
154+
*/
155+
async function handleMissingFiles(
156+
opts: Options,
157+
envFlag: string,
158+
exampleFlag: string,
159+
): Promise<boolean> {
160+
const envExists = fs.existsSync(envFlag);
161+
const exExists = fs.existsSync(exampleFlag);
162+
163+
if (opts.isCiMode) {
164+
// In CI mode, just show errors and exit
165+
if (!envExists) {
166+
console.error(
167+
chalk.red(`❌ Error: --env file not found: ${path.basename(envFlag)}`),
168+
);
169+
}
170+
if (!exExists) {
171+
console.error(
172+
chalk.red(
173+
`❌ Error: --example file not found: ${path.basename(exampleFlag)}`,
174+
),
175+
);
176+
}
177+
process.exit(1);
178+
} else {
179+
// Interactive mode - try to prompt for file creation
180+
const result = await ensureFilesOrPrompt({
181+
cwd: opts.cwd,
182+
primaryEnv: envFlag,
183+
primaryExample: exampleFlag,
184+
alreadyWarnedMissingEnv: false,
185+
isYesMode: opts.isYesMode,
186+
isCiMode: opts.isCiMode,
187+
});
188+
189+
if (result.shouldExit) {
190+
outputResults([], opts, result.exitCode !== 0);
191+
return true;
192+
}
193+
}
194+
195+
return false;
196+
}
197+
198+
/**
199+
* Build options object for compareMany function
200+
*/
201+
function buildCompareOptions(opts: Options, report: CompareJsonEntry[]) {
202+
return {
148203
checkValues: opts.checkValues,
149204
cwd: opts.cwd,
150205
allowDuplicates: opts.allowDuplicates,
@@ -153,13 +208,21 @@ export async function run(program: Command) {
153208
ignore: opts.ignore,
154209
ignoreRegex: opts.ignoreRegex,
155210
showStats: opts.showStats,
156-
collect: (e) => report.push(e),
211+
collect: (e: CompareJsonEntry) => report.push(e),
157212
...(opts.only ? { only: opts.only } : {}),
158-
});
213+
};
214+
}
159215

216+
/**
217+
* Output results and exit with appropriate code
218+
*/
219+
function outputResults(
220+
report: CompareJsonEntry[],
221+
opts: Options,
222+
exitWithError: boolean,
223+
) {
160224
if (opts.json) {
161225
console.log(JSON.stringify(report, null, 2));
162226
}
163-
164227
process.exit(exitWithError ? 1 : 0);
165228
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import fs from 'fs';
22
import path from 'path';
33
import chalk from 'chalk';
44
import { confirmYesNo } from '../ui/prompts.js';
5-
import { warnIfEnvNotIgnored } from '../services/git.js';
5+
import { warnIfEnvNotIgnored } from './git.js';
66

77
/**
88
* Ensures that the necessary .env files exist or prompts the user to create them.
9+
* This function handles only scenarios where the --compare flag is set
910
* @param args - The arguments for the function.
1011
* @returns An object indicating whether a file was created or if the process should exit.
1112
*/

0 commit comments

Comments
 (0)