mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-17 16:46:31 +03:00
Fix: honour .impeccable detector ignores in the live overlay (#639)
The live overlay's detect scan ran unfiltered: requestDetectScan() posted
only { scanId }, so detector.ignoreRules and detector.ignoreValues in
.impeccable/config.json reached impeccable detect and the edit hook but
never the surface a designer actually watches.
The server now serializes the project's detector waivers into the /live.js
prelude (window.__IMPECCABLE_PROJECT_IGNORES__), read per request through
hook-lib's readConfig so config.local.json wins and edits land on the next
tab reload. A new script part, live-browser-ignores.js, resolves that
config against the page URL when a scan starts: ignoreRules suppress
outright, wildcard ignoreValues suppress their rule in the files their
globs name, and the remaining entries ride along as disabledValues for the
detector to match on each finding's own value. The detector bundle applies
those where the findings are assembled, since the overlay draws its own
markers from the collected findings.
Scope resolution mirrors cli/lib/impeccable-config.mjs deliberately: the
same glob dialect (globToRegex, including {a,b} alternation), the same
path-suffix matching as findingMatchesScopedIgnoreFile, and the same
refusal to apply an unscoped wildcard entry. The served-root prefixes that
bridge project-relative globs and site-relative URLs come from the inject
config's own files globs, never from the ignore globs; deriving them from
the ignore globs lets one entry scoped to prototype/library/** lend its
prefix to every page and suppress site-wide, which looks like success
because the numbers go down.
Known gaps, recorded in the detector comment: the motion value extractor
is not mirrored, so a value-scoped bounce-easing waiver only matches when
the finding carries ignoreValue directly, and design-system-color matches
on the normalized string without the CLI's color-equality fallback.
Tests: unit tests for the resolver part (stale globals, string ignoreRules,
malformed entries, directory URLs, percent-escapes, glob metacharacters,
the roots trap), an extension-mode puppeteer test that disabledValues
suppress exactly the waived findings, and the live-browser regression pin
now asserts the new scan config shape instead of { scanId }.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Abdul Wahab
co-authored by
Claude Fable 5
parent
f86473ba7d
commit
5330fa358e
@@ -22,6 +22,7 @@ import net from 'node:net';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { parseDesignMd } from './lib/design-parser.mjs';
|
||||
import { loadContext } from './context.mjs';
|
||||
import { readConfig as readHookConfig } from './hook-lib.mjs';
|
||||
import {
|
||||
assembleLiveBrowserScript,
|
||||
assertLiveBrowserScriptParts,
|
||||
@@ -45,6 +46,7 @@ import {
|
||||
readLiveServerInfo,
|
||||
removeLiveServerInfo,
|
||||
resolveDesignSidecarPath,
|
||||
resolveLiveConfigPath,
|
||||
writeLiveServerInfo,
|
||||
} from './lib/impeccable-paths.mjs';
|
||||
import { countByPage as countPendingByPage } from './live/manual-edits-buffer.mjs';
|
||||
@@ -694,6 +696,51 @@ function isLoopbackOrigin(origin) {
|
||||
// HTTP request handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Project detector waivers for the browser overlay (issue #639). The CLI and
|
||||
// the edit hook filter findings through .impeccable/config.json; the overlay
|
||||
// scans in the browser, so the same config rides along in the /live.js
|
||||
// prelude and live-browser-ignores.js applies it per page at scan time.
|
||||
function readProjectDetectorIgnores() {
|
||||
// readConfig merges config.json with the gitignored config.local.json and
|
||||
// type-checks both, exactly as the edit hook reads the same pair.
|
||||
const config = readHookConfig(process.cwd());
|
||||
return {
|
||||
ignoreRules: Array.isArray(config.ignoreRules) ? config.ignoreRules : [],
|
||||
// Serve only what the browser matches on; createdAt/reason stay local.
|
||||
ignoreValues: (Array.isArray(config.ignoreValues) ? config.ignoreValues : []).map((entry) => ({
|
||||
rule: entry.rule,
|
||||
value: entry.value,
|
||||
...(Array.isArray(entry.files) && entry.files.length > 0 ? { files: entry.files } : {}),
|
||||
})),
|
||||
roots: readLiveServedRoots(),
|
||||
};
|
||||
}
|
||||
|
||||
// Where the served pages live inside the project. Ignore globs are
|
||||
// project-relative and the browser only knows its URL path, so it needs the
|
||||
// prefix; the inject config's own `files` globs are the authority on it.
|
||||
// Deriving it from the ignore globs instead fails silently: one entry scoped
|
||||
// to prototype/library/** would lend prototype/library/ as a candidate prefix
|
||||
// to every page, and that rule would suppress site-wide.
|
||||
function readLiveServedRoots() {
|
||||
try {
|
||||
const configPath = resolveLiveConfigPath({ cwd: process.cwd(), scriptsDir: __dirname });
|
||||
const live = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||
const files = Array.isArray(live?.files) ? live.files : [];
|
||||
return [...new Set(files
|
||||
.filter((glob) => typeof glob === 'string' && glob)
|
||||
.map((glob) => {
|
||||
const wildcardAt = glob.search(/[*?{]/);
|
||||
const head = wildcardAt === -1 ? glob : glob.slice(0, wildcardAt);
|
||||
const cut = head.lastIndexOf('/');
|
||||
return cut > -1 ? head.slice(0, cut + 1) : '';
|
||||
}))];
|
||||
} catch {
|
||||
// No readable inject config: the browser matches URL paths as-is.
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function createRequestHandler({ detectScript, liveScriptParts }) {
|
||||
return (req, res) => {
|
||||
const url = new URL(req.url, `http://localhost:${state.port}`);
|
||||
@@ -754,6 +801,9 @@ function createRequestHandler({ detectScript, liveScriptParts }) {
|
||||
commandPrefix: IMPECCABLE_COMMAND_PREFIX,
|
||||
appRoot: process.cwd(),
|
||||
parts,
|
||||
// Read per request rather than cached, so editing the config and
|
||||
// reloading the tab is enough to pick up a new waiver.
|
||||
projectIgnores: readProjectDetectorIgnores(),
|
||||
});
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'application/javascript',
|
||||
|
||||
Reference in New Issue
Block a user