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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This project follows [Keep a Changelog](https://keepachangelog.com/) and [Semant

### Added

-
- Added warnings count to scan usage stats.

### Changed

Expand Down
21 changes: 18 additions & 3 deletions src/commands/scanUsage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ export async function scanUsage(
const endTime = performance.now();
scanResult.stats.duration = (endTime - startTime) / 1000; // Convert to seconds

// Recalculate stats after filtering
calculateStats(scanResult);

// If user explicitly passed --example flag, but the file doesn't exist:
if (printMissingExample(opts)) {
return { exitWithError: true };
Expand Down Expand Up @@ -128,6 +125,9 @@ export async function scanUsage(
}
}

// Recalculate stats after filtering
calculateStats(scanResult);

// JSON output
if (opts.json) {
const jsonOutput = createJsonOutput(
Expand Down Expand Up @@ -237,10 +237,25 @@ function calculateStats(scanResult: ScanResult): ScanResult {
scanResult.used.map((u: EnvUsage) => u.variable),
).size;

const warningsCount =
(scanResult.frameworkWarnings?.length ?? 0) +
(scanResult.exampleWarnings?.length ?? 0) +
(scanResult.t3EnvWarnings?.length ?? 0) +
(scanResult.logged?.length ?? 0) +
(scanResult.uppercaseWarnings?.length ?? 0) +
(scanResult.expireWarnings?.length ?? 0) +
(scanResult.inconsistentNamingWarnings?.length ?? 0) +
(scanResult.secrets?.length ?? 0) +
(scanResult.missing.length ?? 0) +
(scanResult.unused.length ?? 0) +
(scanResult.duplicates?.env?.length ?? 0) +
(scanResult.duplicates?.example?.length ?? 0);

scanResult.stats = {
filesScanned: scanResult.stats.filesScanned,
totalUsages: scanResult.used.length,
uniqueVariables,
warningsCount: warningsCount,
duration: scanResult.stats.duration,
};

Expand Down
2 changes: 2 additions & 0 deletions src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export interface ScanResult {
filesScanned: number;
totalUsages: number;
uniqueVariables: number;
warningsCount: number;
duration: number;
};
secrets: SecretFinding[];
Expand Down Expand Up @@ -188,6 +189,7 @@ export interface ScanJsonEntry {
filesScanned: number;
totalUsages: number;
uniqueVariables: number;
warningsCount: number;
duration: number;
};
missing: Array<{
Expand Down
1 change: 1 addition & 0 deletions src/services/codeBaseScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export async function scanCodebase(opts: ScanOptions): Promise<ScanResult> {
filesScanned,
totalUsages: filteredUsages.length,
uniqueVariables: uniqueVariables.length,
warningsCount: 0,
duration: 0,
},
duplicates: {
Expand Down
4 changes: 4 additions & 0 deletions src/ui/scan/printStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ interface ScanStats {
filesScanned: number;
totalUsages: number;
uniqueVariables: number;
warningsCount: number;
duration: number;
}

Expand All @@ -25,6 +26,9 @@ export function printStats(
console.log(
chalk.magenta.dim(` Unique variables: ${stats.uniqueVariables}`),
);
console.log(
chalk.magenta.dim(` Warnings: ${stats.warningsCount}`),
);
console.log(
chalk.magenta.dim(` Scan duration: ${stats.duration.toFixed(2)}s`),
);
Expand Down
68 changes: 68 additions & 0 deletions test/e2e/cli.warningsCount.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
import fs from 'fs';
import path from 'path';
import { makeTmpDir, rmrf } from '../utils/fs-helpers.js';
import { buildOnce, runCli, cleanupBuild } from '../utils/cli-helpers.js';

const tmpDirs: string[] = [];

beforeAll(() => {
buildOnce();
});

afterAll(() => {
cleanupBuild();
});

afterEach(() => {
while (tmpDirs.length) {
const dir = tmpDirs.pop();
if (dir) rmrf(dir);
}
});

function tmpDir() {
const dir = makeTmpDir();
tmpDirs.push(dir);
return dir;
}

describe('cli warnings count', () => {
it('should show correct warnings count in scan statistics', () => {
const cwd = tmpDir();

// Code using two env vars
fs.writeFileSync(
path.join(cwd, 'index.js'),
`
const apiKey = process.env.API_KEY;
const apiUrl = process.env.API_URL;
`,
);

// .env.example contains:
// - lowercase key (uppercase warning)
// - unused key (unused warning)
fs.writeFileSync(
path.join(cwd, '.env.example'),
`
api_key=test
UNUSED_KEY=value
`,
);

const res = runCli(cwd, ['--scan-usage']);

/**
* Expected warnings:
* - 2x missing (API_KEY, API_URL)
* - 1x uppercase warning (api_key)
* - 1x unused warning (UNUSED_KEY)
*
* Total: 4
*/
expect(res.status).toBe(1);
expect(res.stdout).toContain('📊 Scan Statistics:');
expect(res.stdout).toContain('Warnings: 5');
});
});
Loading