mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 06:06:37 +03:00
Add quiet mode to detect CLI (#259)
This commit is contained in:
+11
-4
@@ -22,6 +22,10 @@ import {
|
||||
// Output formatting
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function formatFindingSummary(count) {
|
||||
return `${count} anti-pattern${count === 1 ? '' : 's'} found.`;
|
||||
}
|
||||
|
||||
function formatFindings(findings, jsonMode) {
|
||||
if (jsonMode) return JSON.stringify(findings, null, 2);
|
||||
|
||||
@@ -39,7 +43,7 @@ function formatFindings(findings, jsonMode) {
|
||||
out.push(` → ${item.description}`);
|
||||
}
|
||||
}
|
||||
out.push(`\n${findings.length} anti-pattern${findings.length === 1 ? '' : 's'} found.`);
|
||||
out.push(`\n${formatFindingSummary(findings.length)}`);
|
||||
return out.join('\n');
|
||||
}
|
||||
|
||||
@@ -86,6 +90,7 @@ Scan files or URLs for UI anti-patterns and design quality issues.
|
||||
|
||||
Options:
|
||||
--json Output results as JSON
|
||||
--quiet In text mode, only print the final findings count
|
||||
--gpt Also report GPT-specific provider tells (off by default)
|
||||
--gemini Also report Gemini-specific provider tells (off by default)
|
||||
--no-config Do not apply project config, detector ignores, or DESIGN.md
|
||||
@@ -118,6 +123,7 @@ async function detectCli() {
|
||||
});
|
||||
if (args[0] === 'detect') args = args.slice(1);
|
||||
const jsonMode = args.includes('--json');
|
||||
const quietMode = args.includes('--quiet');
|
||||
const helpMode = args.includes('--help');
|
||||
// --fast (regex-only) is deprecated: since the jsdom removal, the static
|
||||
// HTML/CSS analysis is fast and covers every rule, so the regex-only path
|
||||
@@ -169,8 +175,8 @@ async function detectCli() {
|
||||
catch { process.stderr.write(`Warning: cannot access ${target}\n`); continue; }
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// Check for framework dev server config (skip in JSON mode to avoid polluting output)
|
||||
if (!jsonMode) {
|
||||
// Check for framework dev server config (skip in JSON/quiet modes to avoid polluting output)
|
||||
if (!jsonMode && !quietMode) {
|
||||
const fwConfig = detectFrameworkConfig(resolved);
|
||||
if (fwConfig) {
|
||||
const probe = await isPortListening(fwConfig.port, fwConfig.fingerprint);
|
||||
@@ -200,7 +206,7 @@ async function detectCli() {
|
||||
const htmlCount = files.filter(f => HTML_EXTENSIONS.has(path.extname(f).toLowerCase())).length;
|
||||
|
||||
// Warn and confirm if scanning many files (static HTML/CSS processes each HTML file)
|
||||
if (files.length > 50 && process.stdin.isTTY && !jsonMode) {
|
||||
if (files.length > 50 && process.stdin.isTTY && !jsonMode && !quietMode) {
|
||||
process.stderr.write(
|
||||
`\nFound ${files.length} files (${htmlCount} HTML) in ${target}.\n` +
|
||||
`Scanning may take a while${htmlCount > 10 ? ' (static HTML/CSS processes each HTML file individually)' : ''}.\n` +
|
||||
@@ -258,6 +264,7 @@ async function detectCli() {
|
||||
|
||||
if (allFindings.length > 0) {
|
||||
if (jsonMode) process.stdout.write(formatFindings(allFindings, true) + '\n');
|
||||
else if (quietMode) process.stderr.write(formatFindingSummary(allFindings.length) + '\n');
|
||||
else process.stderr.write(formatFindings(allFindings, false) + '\n');
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
@@ -879,6 +879,7 @@ describe('CLI', () => {
|
||||
const { stdout, code } = run('--help');
|
||||
expect(code).toBe(0);
|
||||
expect(stdout).toContain('Usage:');
|
||||
expect(stdout).toContain('--quiet');
|
||||
});
|
||||
|
||||
test('detect subcommand is not treated as a scan target', () => {
|
||||
@@ -906,6 +907,32 @@ describe('CLI', () => {
|
||||
expect(parsed.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('--quiet suppresses text details and keeps the summary exit signal', () => {
|
||||
const { stdout, stderr, code } = run('--quiet', path.join(FIXTURES, 'should-flag.html'));
|
||||
expect(code).toBe(2);
|
||||
expect(stdout).toBe('');
|
||||
expect(stderr.trim()).toMatch(/^[1-9]\d* anti-patterns? found\.$/);
|
||||
expect(stderr).not.toContain('side-tab');
|
||||
expect(stderr).not.toContain('line ');
|
||||
});
|
||||
|
||||
test('--quiet stays silent on clean files', () => {
|
||||
const { stdout, stderr, code } = run('--quiet', path.join(FIXTURES, 'should-pass.html'));
|
||||
expect(code).toBe(0);
|
||||
expect(stdout).toBe('');
|
||||
expect(stderr).toBe('');
|
||||
});
|
||||
|
||||
test('--quiet does not affect JSON output', () => {
|
||||
const { stdout, stderr, code } = run('--json', '--quiet', path.join(FIXTURES, 'should-flag.html'));
|
||||
expect(code).toBe(2);
|
||||
expect(stderr).toBe('');
|
||||
const parsed = JSON.parse(stdout.trim());
|
||||
expect(parsed).toBeArray();
|
||||
expect(parsed.length).toBeGreaterThan(0);
|
||||
expect(parsed.some(f => f.antipattern === 'side-tab')).toBe(true);
|
||||
});
|
||||
|
||||
test('-json alias outputs valid JSON', () => {
|
||||
const { stdout, stderr, code } = run('-json', path.join(FIXTURES, 'should-flag.html'));
|
||||
expect(code).toBe(2);
|
||||
@@ -1745,6 +1772,15 @@ describe('CLI -- dev server suggestion', () => {
|
||||
expect(stderr).toContain('3000');
|
||||
});
|
||||
|
||||
test('--quiet suppresses framework URL scan suggestions', () => {
|
||||
const { stderr, code } = run('--quiet', path.join(FIXTURES, 'framework-next-tailwind'));
|
||||
expect(code).toBe(2);
|
||||
expect(stderr.trim()).toMatch(/^[1-9]\d* anti-patterns? found\.$/);
|
||||
expect(stderr).not.toContain('Next.js');
|
||||
expect(stderr).not.toContain('3000');
|
||||
expect(stderr).not.toContain('Start the dev server');
|
||||
});
|
||||
|
||||
test('suggests URL scan when Vite config found', () => {
|
||||
const { stderr } = run(path.join(FIXTURES, 'framework-vite'));
|
||||
expect(stderr).toContain('Vite');
|
||||
|
||||
Reference in New Issue
Block a user