mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-15 15:46:30 +03:00
Port: fail URL scans when the browser is unavailable (#711)
Upstream sha f2f9958be1e6a4ecb1fbd5ef1ae1b7d9c53e0d24 (Fix: fail URL scans when the browser is unavailable). `detect` gains an operational-failure flag. Exit 1 now means at least one requested target could not be scanned, and it takes precedence over exit 2, because findings from the targets that did scan do not turn a partial scan into a complete one. The flag is set by an unreachable path, an unreadable directory or file in a dir walk, a per-file scan that throws, a URL scan that throws, and a shared-browser setup failure. - `walk_dir_reporting` and `build_import_graph_reporting` take a read-error callback; the plain wrappers stay for callers that do not report. A file the graph could not read is skipped for the scan too. - `SharedBrowser::ensure_launched` is the eager half of `createBrowserDetector()`: the CLI brings the browser up before the loop so a launch failure prints one `Error:` line and every URL target is skipped, instead of the lazy launch reporting once per URL. - The static engine and the text path spell a permission failure the way Node does (`EACCES: permission denied, open '<path>'`), which is what `Error: cannot scan <target>: <message>` prints. - Usage text and docs/CLI-CONTRACT.md carry the exit-status block. Verified against origin/main's JS: missing target, missing target alongside a flagging file, unreadable file, unreadable file beside a readable sibling, unreadable directory, unreadable nested directory, a clean scan, and a browser-unavailable scan of one and of two URLs all agree on exit code, stdout and stderr (the browser-not-found wording is the pre-existing puppeteer-vs-discovery difference). Oracle: `detect-missing-file` and `detect-missing-file-json` re-recorded at exit 1, plus new `detect-missing-file-with-findings`, `detect-unreadable-file-json` and `detect-unreadable-file-in-dir`, each cross-checked against origin/main. `detect-help` carries the new block. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vau2X53xGTjjTCXWMVBoNY
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
bc45026706
commit
ed50cc5ea0
@@ -56,6 +56,32 @@ export default function cases() {
|
||||
{ id: 'detect-no-args', verb: 'detect', args: [] },
|
||||
{ id: 'detect-missing-file', verb: 'detect', args: ['--no-config', 'does-not-exist.html'] },
|
||||
{ id: 'detect-missing-file-json', verb: 'detect', args: ['--no-config', '--json', 'does-not-exist.html'] },
|
||||
// #711: a target that cannot be scanned forces exit 1, and that takes
|
||||
// precedence over findings from the targets that did scan.
|
||||
{
|
||||
id: 'detect-missing-file-with-findings', verb: 'detect',
|
||||
args: ['--no-config', '--json', `<REPO>/tests/fixtures/antipatterns/layout.html`, 'does-not-exist.html'],
|
||||
isolateHome: false,
|
||||
},
|
||||
{
|
||||
id: 'detect-unreadable-file-json', verb: 'detect',
|
||||
setup: (ws) => {
|
||||
const p = path.join(ws, 'locked.html');
|
||||
fs.writeFileSync(p, '<div style="border-left: 4px solid #ff0000">x</div>\n');
|
||||
fs.chmodSync(p, 0o000);
|
||||
},
|
||||
args: ['--no-config', '--json', 'locked.html'],
|
||||
},
|
||||
{
|
||||
id: 'detect-unreadable-file-in-dir', verb: 'detect',
|
||||
setup: (ws) => {
|
||||
fs.writeFileSync(path.join(ws, 'a.html'), '<div style="border-left: 4px solid #ff0000">x</div>\n');
|
||||
const p = path.join(ws, 'b.html');
|
||||
fs.writeFileSync(p, '<div style="border-left: 4px solid #ff0000">x</div>\n');
|
||||
fs.chmodSync(p, 0o000);
|
||||
},
|
||||
args: ['--no-config', '--json', '.'],
|
||||
},
|
||||
{ id: 'detect-unknown-flag', verb: 'detect', args: ['--bogus', `<REPO>/tests/fixtures/antipatterns/blinking-cursor.html`], isolateHome: false },
|
||||
{ id: 'detect-bad-viewport', verb: 'detect', args: ['--viewport', 'wide', `<REPO>/tests/fixtures/antipatterns/blinking-cursor.html`], isolateHome: false },
|
||||
{ id: 'cli-help', verb: 'cli-help', args: [] },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"stdout": "Usage: impeccable detect [options] [file-or-dir-or-url...]\n\nScan files or URLs for UI anti-patterns and design quality issues.\n\nOptions:\n --json Output results as JSON\n --quiet In text mode, only print the final findings count\n --scope <name> Only report rules in the given design domain\n (type, layout). Comma-separated.\n --viewport <WxH> Browser viewport for URL scans (default 1280x800),\n e.g. --viewport 390x844 for a mobile-width pass\n --no-config Do not apply project config, detector ignores, inline\n ignore comments, or DESIGN.md\n --no-inline-ignores Do not honor in-file impeccable-disable* ignore comments\n --no-design-system Do not load local DESIGN.md / .impeccable/design.json context\n --no-advisory Suppress advisory findings entirely (e.g. em-dash overuse)\n --help Show this help message\n\nAdvisory findings:\n Some rules are advisory: detected and listed in a separate section, but never\n counted as failures and never changing the exit code. They stay out of the\n failure count so they never block automation. --no-advisory hides them.\n\nOutput streams:\n Human-readable findings go to stderr so stdout stays available for structured\n output. Use --json for JSON on stdout, or redirect text with 2> findings.txt.\n\nProject config:\n Respects .impeccable/config.json and .impeccable/config.local.json detector\n settings: detector.ignoreRules, detector.ignoreFiles, detector.ignoreValues,\n and detector.designSystem.enabled.\n\nInline ignores:\n In-file comments waive a finding where it lives and travel with the file:\n <!-- impeccable-disable overused-font -- exported brand doc -->\n .brand { font-family: Inter } /* impeccable-disable-line overused-font */\n // impeccable-disable-next-line bounce-easing: intentional bounce\n impeccable-disable applies to the whole file; -line / -next-line are scoped.\n List one or more rule ids (comma-separated), or omit them / use * for all.\n\nDetection modes:\n HTML files Static HTML/CSS analysis (default, catches linked CSS)\n Non-HTML files Regex pattern matching (CSS, JSX, TSX, etc.)\n URLs Puppeteer full browser rendering (auto-detected;\n http(s):// and file:// URLs; accessible linked CSS included)\n\nExamples:\n impeccable detect src/\n impeccable detect index.html\n impeccable detect https://example.com\n impeccable detect --json .\n impeccable detect --no-config src/\n",
|
||||
"stdout": "Usage: impeccable detect [options] [file-or-dir-or-url...]\n\nScan files or URLs for UI anti-patterns and design quality issues.\n\nOptions:\n --json Output results as JSON\n --quiet In text mode, only print the final findings count\n --scope <name> Only report rules in the given design domain\n (type, layout). Comma-separated.\n --viewport <WxH> Browser viewport for URL scans (default 1280x800),\n e.g. --viewport 390x844 for a mobile-width pass\n --no-config Do not apply project config, detector ignores, inline\n ignore comments, or DESIGN.md\n --no-inline-ignores Do not honor in-file impeccable-disable* ignore comments\n --no-design-system Do not load local DESIGN.md / .impeccable/design.json context\n --no-advisory Suppress advisory findings entirely (e.g. em-dash overuse)\n --help Show this help message\n\nAdvisory findings:\n Some rules are advisory: detected and listed in a separate section, but never\n counted as failures and never changing the exit code. They stay out of the\n failure count so they never block automation. --no-advisory hides them.\n\nOutput streams:\n Human-readable findings go to stderr so stdout stays available for structured\n output. Use --json for JSON on stdout, or redirect text with 2> findings.txt.\n\nExit status:\n 0 Scan completed with no primary findings (advisories may still be listed)\n 1 At least one requested target could not be scanned\n 2 Scan completed with primary findings\n Operational failure takes precedence when a multi-target scan is partial.\n\nProject config:\n Respects .impeccable/config.json and .impeccable/config.local.json detector\n settings: detector.ignoreRules, detector.ignoreFiles, detector.ignoreValues,\n and detector.designSystem.enabled.\n\nInline ignores:\n In-file comments waive a finding where it lives and travel with the file:\n <!-- impeccable-disable overused-font -- exported brand doc -->\n .brand { font-family: Inter } /* impeccable-disable-line overused-font */\n // impeccable-disable-next-line bounce-easing: intentional bounce\n impeccable-disable applies to the whole file; -line / -next-line are scoped.\n List one or more rule ids (comma-separated), or omit them / use * for all.\n\nDetection modes:\n HTML files Static HTML/CSS analysis (default, catches linked CSS)\n Non-HTML files Regex pattern matching (CSS, JSX, TSX, etc.)\n URLs Puppeteer full browser rendering (auto-detected;\n http(s):// and file:// URLs; accessible linked CSS included)\n\nExamples:\n impeccable detect src/\n impeccable detect index.html\n impeccable detect https://example.com\n impeccable detect --json .\n impeccable detect --no-config src/\n",
|
||||
"stderr": "",
|
||||
"exit": 0,
|
||||
"signal": null,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"stdout": "[]\n",
|
||||
"stderr": "Warning: cannot access does-not-exist.html\n",
|
||||
"exit": 0,
|
||||
"exit": 1,
|
||||
"signal": null,
|
||||
"files": {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"stdout": "[\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n },\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n },\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n },\n {\n \"antipattern\": \"nested-cards\",\n \"name\": \"Nested cards\",\n \"description\": \"Cards inside cards create visual noise and excessive depth. Flatten the hierarchy — use spacing, typography, and dividers instead of nesting containers.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"<REPO>/tests/fixtures/antipatterns/layout.html\",\n \"line\": 0,\n \"snippet\": \"Card inside card (div)\"\n }\n]\n",
|
||||
"stderr": "Warning: cannot access does-not-exist.html\n",
|
||||
"exit": 1,
|
||||
"signal": null,
|
||||
"files": {}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"stdout": "",
|
||||
"stderr": "Warning: cannot access does-not-exist.html\n",
|
||||
"exit": 0,
|
||||
"exit": 1,
|
||||
"signal": null,
|
||||
"files": {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"stdout": "[\n {\n \"antipattern\": \"side-tab\",\n \"name\": \"Side-tab accent border\",\n \"description\": \"Thick colored border on one side of a card — the most recognizable tell of AI-generated UIs. Use a subtler accent or remove it entirely.\",\n \"severity\": \"warning\",\n \"category\": \"slop\",\n \"file\": \"<WS>/a.html\",\n \"line\": 0,\n \"snippet\": \"border-left: 4px\"\n }\n]\n",
|
||||
"stderr": "Error: cannot scan <WS>/b.html: EACCES: permission denied, open '<WS>/b.html'\n",
|
||||
"exit": 1,
|
||||
"signal": null,
|
||||
"files": {}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"stdout": "[]\n",
|
||||
"stderr": "Error: cannot scan locked.html: EACCES: permission denied, open '<WS>/locked.html'\n",
|
||||
"exit": 1,
|
||||
"signal": null,
|
||||
"files": {}
|
||||
}
|
||||
Reference in New Issue
Block a user