mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-11 21:57:14 +03:00
Sync generated provider output
This commit is contained in:
@@ -1675,6 +1675,69 @@ if (IS_BROWSER) {
|
||||
addBrowserFindings(groupMap, document.body, mapped);
|
||||
}
|
||||
|
||||
// Value-level suppression (issue #639). `disabledRules` above handles
|
||||
// whole rules; this applies the config's remaining ignoreValues entries,
|
||||
// which the CLI filters through isIgnoredFindingValue in
|
||||
// cli/lib/impeccable-config.mjs, so a project waiver like
|
||||
// overused-font = "geist mono" reaches the overlay and extension too.
|
||||
const _normValue = (v) => String(v || '').trim().replace(/^["']|["']$/g, '')
|
||||
.replace(/\+/g, ' ').replace(/\s+/g, ' ').toLowerCase();
|
||||
const _disabledValues = EXTENSION_MODE
|
||||
? (Array.isArray(window.__IMPECCABLE_CONFIG__?.disabledValues) ? window.__IMPECCABLE_CONFIG__.disabledValues : [])
|
||||
.filter(e => e && typeof e === 'object' && e.rule && e.value)
|
||||
.map(e => ({ rule: String(e.rule).trim().toLowerCase(), value: _normValue(e.value) }))
|
||||
: [];
|
||||
if (_disabledValues.length > 0) {
|
||||
// The six rules whose findings carry a matchable value; keep in step
|
||||
// with extractFindingIgnoreValue in cli/lib/impeccable-config.mjs.
|
||||
// Everything else is suppressed by rule or by file scope, both already
|
||||
// resolved into disabledRules before the scan message was sent.
|
||||
const _directValueRules = new Set([
|
||||
'overused-font',
|
||||
'bounce-easing',
|
||||
'design-system-font',
|
||||
'design-system-color',
|
||||
'design-system-radius',
|
||||
'design-system-font-size',
|
||||
]);
|
||||
// The design-system checks set `ignoreValue` on their findings; the
|
||||
// detail fallbacks catch overused-font, whose value lives in its
|
||||
// sentence. Two CLI matchers are not mirrored here: the motion
|
||||
// extractor (a value-scoped bounce-easing waiver only matches when the
|
||||
// finding carries ignoreValue directly) and the design-system-color
|
||||
// color-equality fallback (a waiver written as rgb() will not match a
|
||||
// finding reported as hex; store the reported form).
|
||||
const _findingValue = (f) => {
|
||||
if (!f || !_directValueRules.has(f.type || f.id)) return '';
|
||||
const direct = f.ignoreValue || f.value;
|
||||
if (direct) return _normValue(direct);
|
||||
for (const text of [f.detail, f.snippet]) {
|
||||
if (typeof text !== 'string' || !text) continue;
|
||||
const primary = text.match(/Primary font:\s*([^()\n;]+)/i);
|
||||
if (primary) return _normValue(primary[1]);
|
||||
const google = text.match(/Google Fonts:\s*([^()\n;]+)/i);
|
||||
if (google) return _normValue(google[1]);
|
||||
const family = text.match(/font-family\s*:\s*["']?([^'",;\n]+)/i);
|
||||
if (family) return _normValue(family[1]);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
const _valueIgnored = (f) => {
|
||||
const value = _findingValue(f);
|
||||
if (!value) return false;
|
||||
const rule = f.type || f.id;
|
||||
return _disabledValues.some(e => e.rule === rule && e.value === value);
|
||||
};
|
||||
for (const [el, list] of [...groupMap.entries()]) {
|
||||
const kept = list.filter(f => !_valueIgnored(f));
|
||||
if (kept.length > 0) groupMap.set(el, kept);
|
||||
else groupMap.delete(el);
|
||||
}
|
||||
for (let i = pageLevelFindings.length - 1; i >= 0; i--) {
|
||||
if (_valueIgnored(pageLevelFindings[i])) pageLevelFindings.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
groupMap,
|
||||
allFindings: browserFindingsFromMap(groupMap),
|
||||
|
||||
@@ -8334,6 +8334,69 @@ if (IS_BROWSER) {
|
||||
addBrowserFindings(groupMap, document.body, mapped);
|
||||
}
|
||||
|
||||
// Value-level suppression (issue #639). `disabledRules` above handles
|
||||
// whole rules; this applies the config's remaining ignoreValues entries,
|
||||
// which the CLI filters through isIgnoredFindingValue in
|
||||
// cli/lib/impeccable-config.mjs, so a project waiver like
|
||||
// overused-font = "geist mono" reaches the overlay and extension too.
|
||||
const _normValue = (v) => String(v || '').trim().replace(/^["']|["']$/g, '')
|
||||
.replace(/\+/g, ' ').replace(/\s+/g, ' ').toLowerCase();
|
||||
const _disabledValues = EXTENSION_MODE
|
||||
? (Array.isArray(window.__IMPECCABLE_CONFIG__?.disabledValues) ? window.__IMPECCABLE_CONFIG__.disabledValues : [])
|
||||
.filter(e => e && typeof e === 'object' && e.rule && e.value)
|
||||
.map(e => ({ rule: String(e.rule).trim().toLowerCase(), value: _normValue(e.value) }))
|
||||
: [];
|
||||
if (_disabledValues.length > 0) {
|
||||
// The six rules whose findings carry a matchable value; keep in step
|
||||
// with extractFindingIgnoreValue in cli/lib/impeccable-config.mjs.
|
||||
// Everything else is suppressed by rule or by file scope, both already
|
||||
// resolved into disabledRules before the scan message was sent.
|
||||
const _directValueRules = new Set([
|
||||
'overused-font',
|
||||
'bounce-easing',
|
||||
'design-system-font',
|
||||
'design-system-color',
|
||||
'design-system-radius',
|
||||
'design-system-font-size',
|
||||
]);
|
||||
// The design-system checks set `ignoreValue` on their findings; the
|
||||
// detail fallbacks catch overused-font, whose value lives in its
|
||||
// sentence. Two CLI matchers are not mirrored here: the motion
|
||||
// extractor (a value-scoped bounce-easing waiver only matches when the
|
||||
// finding carries ignoreValue directly) and the design-system-color
|
||||
// color-equality fallback (a waiver written as rgb() will not match a
|
||||
// finding reported as hex; store the reported form).
|
||||
const _findingValue = (f) => {
|
||||
if (!f || !_directValueRules.has(f.type || f.id)) return '';
|
||||
const direct = f.ignoreValue || f.value;
|
||||
if (direct) return _normValue(direct);
|
||||
for (const text of [f.detail, f.snippet]) {
|
||||
if (typeof text !== 'string' || !text) continue;
|
||||
const primary = text.match(/Primary font:\s*([^()\n;]+)/i);
|
||||
if (primary) return _normValue(primary[1]);
|
||||
const google = text.match(/Google Fonts:\s*([^()\n;]+)/i);
|
||||
if (google) return _normValue(google[1]);
|
||||
const family = text.match(/font-family\s*:\s*["']?([^'",;\n]+)/i);
|
||||
if (family) return _normValue(family[1]);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
const _valueIgnored = (f) => {
|
||||
const value = _findingValue(f);
|
||||
if (!value) return false;
|
||||
const rule = f.type || f.id;
|
||||
return _disabledValues.some(e => e.rule === rule && e.value === value);
|
||||
};
|
||||
for (const [el, list] of [...groupMap.entries()]) {
|
||||
const kept = list.filter(f => !_valueIgnored(f));
|
||||
if (kept.length > 0) groupMap.set(el, kept);
|
||||
else groupMap.delete(el);
|
||||
}
|
||||
for (let i = pageLevelFindings.length - 1; i >= 0; i--) {
|
||||
if (_valueIgnored(pageLevelFindings[i])) pageLevelFindings.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
groupMap,
|
||||
allFindings: browserFindingsFromMap(groupMap),
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Browser-side resolution of project detector waivers for Impeccable live mode.
|
||||
*
|
||||
* The live server serializes `.impeccable/config.json` + `config.local.json`
|
||||
* detector ignores (plus the served-root prefixes from the inject config's
|
||||
* `files` globs) into `window.__IMPECCABLE_PROJECT_IGNORES__`. This part
|
||||
* resolves that config against the current page's URL path when a detect scan
|
||||
* starts, so the overlay suppresses the same findings the CLI and the edit
|
||||
* hook do (issue #639).
|
||||
*
|
||||
* Mirrors filterDetectionFindings in cli/lib/impeccable-config.mjs:
|
||||
* 1. `ignoreRules` suppress a rule project-wide.
|
||||
* 2. `ignoreValues` entries with `value: "*"` suppress their rule in the
|
||||
* files their globs name. The CLI never applies an unscoped wildcard
|
||||
* (isIgnoredFindingValue returns false for it), so neither does this.
|
||||
* 3. Remaining `ignoreValues` entries match on the finding's own value;
|
||||
* those are forwarded as `disabledValues` for the detector bundle to
|
||||
* apply where the findings are assembled.
|
||||
*
|
||||
* Kept separate from live-browser.js so the glob and page-scope logic can be
|
||||
* unit tested in Node (tests/live-browser-ignores.test.mjs) without the full
|
||||
* overlay UI bundle.
|
||||
*/
|
||||
(function (root) {
|
||||
'use strict';
|
||||
if (!root) return;
|
||||
|
||||
// Keep in step with normalizeIgnoreRule / normalizeIgnoreValue in
|
||||
// cli/lib/impeccable-config.mjs.
|
||||
function normalizeIgnoreRule(rule) {
|
||||
return String(rule || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function normalizeIgnoreValue(value) {
|
||||
return String(value || '')
|
||||
.trim()
|
||||
.replace(/^["']|["']$/g, '')
|
||||
.replace(/\+/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
// Glob -> RegExp. Supports `**`, `*`, `?`, and `{a,b}` alternation.
|
||||
// Keep in step with globToRegex in cli/lib/impeccable-config.mjs.
|
||||
function globToRegex(glob) {
|
||||
let re = '^';
|
||||
let i = 0;
|
||||
while (i < glob.length) {
|
||||
const c = glob[i];
|
||||
if (c === '*') {
|
||||
if (glob[i + 1] === '*') {
|
||||
re += '.*';
|
||||
i += 2;
|
||||
if (glob[i] === '/') i += 1;
|
||||
} else {
|
||||
re += '[^/]*';
|
||||
i += 1;
|
||||
}
|
||||
} else if (c === '?') {
|
||||
re += '[^/]';
|
||||
i += 1;
|
||||
} else if (c === '{') {
|
||||
const end = glob.indexOf('}', i);
|
||||
if (end === -1) { re += '\\{'; i += 1; continue; }
|
||||
const parts = glob.slice(i + 1, end).split(',').map((p) => p.replace(/[.+^$()|[\]\\]/g, '\\$&'));
|
||||
re += `(?:${parts.join('|')})`;
|
||||
i = end + 1;
|
||||
} else if (/[.+^$()|[\]\\]/.test(c)) {
|
||||
re += `\\${c}`;
|
||||
i += 1;
|
||||
} else {
|
||||
re += c;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
re += '$';
|
||||
return new RegExp(re);
|
||||
}
|
||||
|
||||
// The project-relative paths this page could be known as. Ignore globs are
|
||||
// project-relative (prototype/foo.html) and the URL is site-relative
|
||||
// (/foo.html), because a static server's root usually sits inside the
|
||||
// project; `roots` carries that prefix. The server reads it from the inject
|
||||
// config's own `files` globs, which already state where the served pages
|
||||
// are. Do not derive it from the ignore globs: a single entry scoped to
|
||||
// prototype/library/** would then lend prototype/library/ as a candidate
|
||||
// prefix to every page, and that rule would suppress site-wide.
|
||||
//
|
||||
// Each prefixed path also contributes its slash suffixes, mirroring
|
||||
// findingMatchesScopedIgnoreFile in cli/lib/impeccable-config.mjs (which
|
||||
// matches globs against every path suffix of the finding's file).
|
||||
//
|
||||
// One live session is served by one server, so a single document root must
|
||||
// sit at or above every configured page. The only prefix that can safely
|
||||
// be asserted is therefore the deepest common ancestor of the glob roots.
|
||||
// Treating each glob's own prefix as an identity goes wrong in both
|
||||
// directions: disjoint roots (src/ and public/) invent simultaneous
|
||||
// identities for one URL, so a waiver scoped to src/foo.html hides a
|
||||
// finding on a page served from public/foo.html; nested roots (prototype/
|
||||
// and prototype/library/, from globs at two depths in one tree) are not
|
||||
// alternatives at all, and demanding a waiver match under both stops
|
||||
// prototype/index.html from applying anywhere. When the globs share no
|
||||
// common root, no prefix is asserted and only the URL path itself matches.
|
||||
function pageCandidates(pathname, roots) {
|
||||
let pagePath = String(pathname || '');
|
||||
try {
|
||||
pagePath = decodeURIComponent(pagePath);
|
||||
} catch {
|
||||
// Malformed percent-escape: match on the raw path rather than throwing.
|
||||
}
|
||||
pagePath = pagePath.replace(/^\/+/, '');
|
||||
// A directory URL serves that directory's index, and the ignore globs
|
||||
// name files. Without this, /news/ never matches prototype/news/index.html.
|
||||
if (pagePath === '' || pagePath.endsWith('/')) pagePath += 'index.html';
|
||||
|
||||
const prefixes = [];
|
||||
for (const entry of Array.isArray(roots) ? roots : []) {
|
||||
if (typeof entry !== 'string') continue;
|
||||
prefixes.push(entry.split('/').filter(Boolean));
|
||||
}
|
||||
let common = prefixes.length > 0 ? prefixes[0] : [];
|
||||
for (const segments of prefixes.slice(1)) {
|
||||
let i = 0;
|
||||
while (i < common.length && i < segments.length && common[i] === segments[i]) i += 1;
|
||||
common = common.slice(0, i);
|
||||
}
|
||||
|
||||
const candidates = new Set();
|
||||
const addSuffixes = (fullPath) => {
|
||||
const parts = fullPath.split('/').filter(Boolean);
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
candidates.add(parts.slice(i).join('/'));
|
||||
}
|
||||
};
|
||||
addSuffixes(pagePath);
|
||||
if (common.length > 0) addSuffixes(common.join('/') + '/' + pagePath);
|
||||
return [...candidates];
|
||||
}
|
||||
|
||||
function matchesScope(globs, candidates) {
|
||||
return globs.some((glob) => {
|
||||
let re;
|
||||
try {
|
||||
re = globToRegex(String(glob));
|
||||
} catch {
|
||||
// Malformed glob: skip it, as matchesAnyGlob does in the CLI.
|
||||
return false;
|
||||
}
|
||||
return candidates.some((candidate) => re.test(candidate));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the serialized project ignores for one page.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {object} options.ignores window.__IMPECCABLE_PROJECT_IGNORES__,
|
||||
* in whatever state it arrived: absent, null, or hand-edited into the
|
||||
* wrong shape. Every read tolerates that and degrades to no filtering.
|
||||
* @param {string} options.pathname location.pathname of the scanned page.
|
||||
* @returns {{ disabledRules: string[], disabledValues: Array<{rule: string, value: string}> }}
|
||||
*/
|
||||
function resolveDetectIgnores({ ignores, pathname } = {}) {
|
||||
const config = ignores && typeof ignores === 'object' ? ignores : {};
|
||||
const asArray = (value) => (Array.isArray(value) ? value : []);
|
||||
const candidates = pageCandidates(pathname, config.roots);
|
||||
|
||||
const disabledRules = new Set(
|
||||
asArray(config.ignoreRules)
|
||||
.filter((rule) => typeof rule === 'string')
|
||||
.map(normalizeIgnoreRule)
|
||||
.filter(Boolean),
|
||||
);
|
||||
const disabledValues = [];
|
||||
|
||||
for (const entry of asArray(config.ignoreValues)) {
|
||||
if (!entry || typeof entry !== 'object') continue;
|
||||
const rule = normalizeIgnoreRule(entry.rule);
|
||||
const value = normalizeIgnoreValue(entry.value);
|
||||
if (!rule || !value) continue;
|
||||
const files = [
|
||||
...(typeof entry.file === 'string' && entry.file.trim() ? [entry.file.trim()] : []),
|
||||
...asArray(entry.files).filter((glob) => typeof glob === 'string' && glob.trim()),
|
||||
];
|
||||
if (value === '*') {
|
||||
// Wildcards suppress their rule only inside the files they name.
|
||||
if (files.length > 0 && matchesScope(files, candidates)) disabledRules.add(rule);
|
||||
continue;
|
||||
}
|
||||
if (files.length > 0 && !matchesScope(files, candidates)) continue;
|
||||
disabledValues.push({ rule, value });
|
||||
}
|
||||
|
||||
return { disabledRules: [...disabledRules], disabledValues };
|
||||
}
|
||||
|
||||
root.__IMPECCABLE_LIVE_IGNORES__ = {
|
||||
version: 1,
|
||||
resolveDetectIgnores,
|
||||
};
|
||||
})(typeof window !== 'undefined' ? window : globalThis);
|
||||
@@ -11143,10 +11143,24 @@ void main() {
|
||||
const scanId = String(++detectScanSeq);
|
||||
activeDetectScanId = scanId;
|
||||
pendingDetectScanId = scanId;
|
||||
// Send the project's detector waivers with the scan so the overlay
|
||||
// filters the same findings the CLI and the edit hook do (issue #639).
|
||||
// live-browser-ignores.js resolves .impeccable config for this page:
|
||||
// ignoreRules suppress outright, wildcard ignoreValues suppress their
|
||||
// rule in the files they name, and the rest match on the finding's own
|
||||
// value inside the detector. Guarded so a stale cached live.js without
|
||||
// the resolver part still scans, just unfiltered as before.
|
||||
const ignoresApi = window.__IMPECCABLE_LIVE_IGNORES__;
|
||||
const ignores = typeof ignoresApi?.resolveDetectIgnores === 'function'
|
||||
? ignoresApi.resolveDetectIgnores({
|
||||
ignores: window.__IMPECCABLE_PROJECT_IGNORES__,
|
||||
pathname: location.pathname,
|
||||
})
|
||||
: { disabledRules: [], disabledValues: [] };
|
||||
window.postMessage({
|
||||
source: 'impeccable-command',
|
||||
action: 'scan',
|
||||
config: { scanId },
|
||||
config: { scanId, disabledRules: ignores.disabledRules, disabledValues: ignores.disabledValues },
|
||||
}, '*');
|
||||
}
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -6,6 +6,7 @@ import { LIVE_CHROME_MOUNT_CONTRACT, LIVE_UI_SURFACES } from './ui-surfaces.mjs'
|
||||
export const LIVE_BROWSER_SCRIPT_PARTS = Object.freeze([
|
||||
Object.freeze({ name: 'session-state', file: 'live-browser-session.js' }),
|
||||
Object.freeze({ name: 'dom-helpers', file: 'live-browser-dom.js' }),
|
||||
Object.freeze({ name: 'project-ignores', file: 'live-browser-ignores.js' }),
|
||||
Object.freeze({ name: 'browser-ui', file: 'live-browser.js' }),
|
||||
]);
|
||||
|
||||
@@ -47,6 +48,11 @@ export function assembleLiveBrowserScript({
|
||||
// so tests can assemble with a stand-in.
|
||||
uiSurfaces = LIVE_UI_SURFACES,
|
||||
mountContract = LIVE_CHROME_MOUNT_CONTRACT,
|
||||
// Project detector waivers ({ ignoreRules, ignoreValues, roots }), read from
|
||||
// .impeccable config by live-server.mjs. live-browser-ignores.js resolves
|
||||
// them against the page when a detect scan starts, so the overlay filters
|
||||
// the same findings the CLI and the edit hook do (issue #639).
|
||||
projectIgnores = null,
|
||||
}) {
|
||||
const prelude =
|
||||
`window.__IMPECCABLE_TOKEN__ = '${token}';\n` +
|
||||
@@ -66,7 +72,8 @@ export function assembleLiveBrowserScript({
|
||||
// repo's tests, the impeccable-site Live UI lab) import the module directly,
|
||||
// which is what keeps the two from drifting.
|
||||
`window.__IMPECCABLE_LIVE_UI_SURFACES__ = ${JSON.stringify(uiSurfaces)};\n` +
|
||||
`window.__IMPECCABLE_LIVE_MOUNT_CONTRACT__ = ${JSON.stringify(mountContract)};\n`;
|
||||
`window.__IMPECCABLE_LIVE_MOUNT_CONTRACT__ = ${JSON.stringify(mountContract)};\n` +
|
||||
`window.__IMPECCABLE_PROJECT_IGNORES__ = ${JSON.stringify(projectIgnores)};\n`;
|
||||
|
||||
const body = parts.map((part) => {
|
||||
const file = part.file || path.basename(part.path || '');
|
||||
|
||||
Reference in New Issue
Block a user