diff --git a/extension/eslint.config.js b/extension/eslint.config.js index 38f5e86..088cf09 100644 --- a/extension/eslint.config.js +++ b/extension/eslint.config.js @@ -208,7 +208,6 @@ export default tseslint.config( "unicorn/no-unreadable-new-expression": "warn", "unicorn/require-array-sort-compare": "warn", "unicorn/prefer-uint8array-base64": "warn", - "unicorn/no-declarations-before-early-exit": "warn", // no-global-object-property-assignment stays at its recommended `error` // for production code; it's disabled only for tests (which legitimately // assign globals to set up mocks) in the test-files block below. diff --git a/extension/scripts/load-default-overrides.ts b/extension/scripts/load-default-overrides.ts index 4c0b076..3873d03 100644 --- a/extension/scripts/load-default-overrides.ts +++ b/extension/scripts/load-default-overrides.ts @@ -171,10 +171,6 @@ function flattenIssues( ): z.core.$ZodIssue[] { const out: z.core.$ZodIssue[] = []; for (const issue of issues) { - const prefixedIssue = - prefix.length === 0 - ? issue - : { ...issue, path: [...prefix, ...issue.path] }; if ( issue.code === "invalid_union" && "errors" in issue && @@ -199,6 +195,10 @@ function flattenIssues( continue; } } + const prefixedIssue = + prefix.length === 0 + ? issue + : { ...issue, path: [...prefix, ...issue.path] }; out.push(prefixedIssue); } return out; diff --git a/extension/src/lib/checkout-checkbox-defense-source.ts b/extension/src/lib/checkout-checkbox-defense-source.ts index 4109185..7108f40 100644 --- a/extension/src/lib/checkout-checkbox-defense-source.ts +++ b/extension/src/lib/checkout-checkbox-defense-source.ts @@ -46,15 +46,6 @@ export function installCheckoutCheckboxDefense(this: Window): void { } defenseWindow[FLAG] = true; - // Mirror of CHECKOUT_CHECKBOX_CLEARED_ATTR from lib/dom-markers.ts. - // Hard-coded because this function runs in the page world with no - // module imports; the isolated-world rule and the markers registry - // share the same literal, asserted by a unit test. The lint rule that - // bans inline `data-abs-*` literals exists to keep the registry the - // single source of truth — this is the one principled exception. - // eslint-disable-next-line no-restricted-syntax - const CLEARED_ATTR = "data-abs-cleared"; - // Mirror of the URLPattern set in lib/checkout-url.ts as a single // anchored regex. `/cart`, `/cart/`, `/cart/sub` match; `/cartx`, // `/products/cart-bag`, `/orders` do not. Asserted against the rule's @@ -87,6 +78,16 @@ export function installCheckoutCheckboxDefense(this: Window): void { // disable the defense rather than block the page. return; } + + // Mirror of CHECKOUT_CHECKBOX_CLEARED_ATTR from lib/dom-markers.ts. + // Hard-coded because this function runs in the page world with no + // module imports; the isolated-world rule and the markers registry + // share the same literal, asserted by a unit test. The lint rule that + // bans inline `data-abs-*` literals exists to keep the registry the + // single source of truth — this is the one principled exception. + // eslint-disable-next-line no-restricted-syntax + const CLEARED_ATTR = "data-abs-cleared"; + const nativeSetter = descriptor.set; const nativeGetter = descriptor.get; diff --git a/extension/src/lib/yielding-text-walk.ts b/extension/src/lib/yielding-text-walk.ts index 5bef8e5..0045088 100644 --- a/extension/src/lib/yielding-text-walk.ts +++ b/extension/src/lib/yielding-text-walk.ts @@ -108,10 +108,6 @@ export function walkTextNodesChunked( shouldSkipParent ? { minLength, shouldSkipParent } : { minLength }, ); - if (signal?.aborted) { - return; - } - let index = 0; function runFinalize(): void { @@ -197,10 +193,6 @@ export function walkTextNodeGroupsChunked( shouldSkipParent ? { minLength, shouldSkipParent } : { minLength }, ); - if (signal?.aborted) { - return; - } - let index = 0; function runFinalize(): void { diff --git a/extension/src/popup/Popup.tsx b/extension/src/popup/Popup.tsx index 7b8d3bd..a77875a 100644 --- a/extension/src/popup/Popup.tsx +++ b/extension/src/popup/Popup.tsx @@ -31,6 +31,9 @@ export function Popup() { }); }, []); + // Hooks must run unconditionally before any early return, so this cannot + // move below the loading guard (would violate react-hooks/rules-of-hooks). + // eslint-disable-next-line unicorn/no-declarations-before-early-exit -- React hook ordering const trace = useTabDebugTrace(debugTraceEnabled ? activeTabId : null); if (enforcementEnabled === null || debugTraceEnabled === null) { diff --git a/extension/src/rules/__tests__/hidden-fee-annotate.property.test.ts b/extension/src/rules/__tests__/hidden-fee-annotate.property.test.ts index f137155..3cd61b9 100644 --- a/extension/src/rules/__tests__/hidden-fee-annotate.property.test.ts +++ b/extension/src/rules/__tests__/hidden-fee-annotate.property.test.ts @@ -73,13 +73,13 @@ describe("matchFeePhrase precision (property)", () => { fc.constantFrom(...PHRASES), fc.stringMatching(/^[A-Za-z][A-Za-z ]{0,30}$/), (phrase, suffix) => { - const text = `${phrase} ${suffix}`; // Guard the rare case where fast-check picks a suffix that // happens to start with another phrase from the set — the // resulting concatenation could still legitimately match. if (PHRASES.has(`${phrase} ${suffix}`.toLowerCase())) { return; } + const text = `${phrase} ${suffix}`; expect(matchFeePhrase(text)).toBeNull(); }, ), diff --git a/extension/src/rules/hidden-text-strip.ts b/extension/src/rules/hidden-text-strip.ts index c84e11f..80771d1 100644 --- a/extension/src/rules/hidden-text-strip.ts +++ b/extension/src/rules/hidden-text-strip.ts @@ -257,10 +257,10 @@ function hasStructuralSrOnlyPattern(style: CSSStyleDeclaration): boolean { return false; } const width = parsePixelLength(style.width); - const height = parsePixelLength(style.height); if (width === null || width > SR_ONLY_MAX_SIZE_PX) { return false; } + const height = parsePixelLength(style.height); if (height === null || height > SR_ONLY_MAX_SIZE_PX) { return false; }