chore: clear the six code smells SonarQube reported (#181)
Linting / lint (pull_request) Successful in 2m47s
SonarQube Analysis / sonarqube (pull_request) Failing after 55m42s

The list finally arrived from the reporting added earlier, and confirmed what #181 could only suspect: these are not the five eslint-plugin-sonarjs warnings that issue lists. Those were fixed under #261 and the count staying at five was a coincidence. It is six now, 25 minutes of debt, and one of them was mine.

admin.ts imported '../utils' twice — I added readId in #207 without noticing the file already imported from there. One import now.

filters.ts had a redundant `as ItemStatus[]`. TypeScript narrows an array through `.every()` with a type predicate from 5.5, and this project is on 5.9, so the assertion stopped telling the compiler anything. Removed, and the build confirms the narrowing holds without it.

adminSettings.ts was the only CRITICAL: cognitive complexity 18 against a limit of 15, almost all of it three near-identical loops differing only in how they validated. Each validation is now a small pure reader returning a refusal rather than sending one, and the handler is one loop over a table. Adding a setting type means adding a row.

That refactor is deliberately behaviour-preserving. Two things were left alone on purpose: the blanket rejection of empty text, which is wrong for the two settings whose documented default is empty and is filed as #280 rather than folded in where it would be invisible; and the absence of the `count` settings, which no caller submits and which the admin screen has no control for. I had started adding count validation and reverted it — widening behaviour under cover of a complexity fix is how a refactor stops being reviewable.

The three S6478s are render props, not components defined during render. ErrorBoundary's `fallback` is typed `(error: Error) => React.ReactNode` and called as `this.props.fallback(...)`, so React only ever sees returned elements and never a new component type — the subtree destruction the rule describes does not happen, and the rule's own message offers `allowAsProps` for this shape, which cannot be set from here. Hoisted rather than suppressed because none of them closes over anything local, so at module level each is one stable function instead of a new closure per render. That is a mild improvement, not a contortion.

Verified: 402 backend unit, 358 backend integration, 30 frontend unit, 157 e2e, both lints clean, both builds clean. The e2e run matters most here — storefront-errors.spec.ts exercises all three hoisted fallbacks, and it was run on its own first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-03 09:54:47 -05:00
co-authored by Claude Opus 5
parent e36e61880e
commit cd2676233c
5 changed files with 178 additions and 91 deletions
+28 -11
View File
@@ -119,6 +119,33 @@ const STOREFRONT_DIMENSIONS = [
priceDimension
];
// Hoisted out of the component that used to declare it inline.
//
// S6478 flags a function-returning-JSX in a prop as "defining a component
// during render". Here it is a render prop — ErrorBoundary's `fallback` is
// typed `(error: Error) => React.ReactNode` and called as
// `this.props.fallback(...)` — so React only ever sees the returned elements,
// never a new component type, and the subtree destruction the rule warns about
// does not happen. The rule's own message offers `allowAsProps` for exactly
// this shape, which cannot be set from here.
//
// Hoisting rather than suppressing because it costs nothing: these close over
// nothing local, so at module level they are one stable function instead of a
// new closure per render, which is mildly better and not a contortion. See #181.
function catalogueErrorFallback(error: Error) {
return (
<ErrorFallback
error={error}
title="The item list didn't load"
actions={
<Button type="primary" onClick={() => window.location.reload()}>
Reload
</Button>
}
/>
);
}
export default function App() {
const [authModalOpen, setAuthModalOpen] = useState(false);
const [searchParams, setSearchParams] = useSearchParams();
@@ -231,17 +258,7 @@ export default function App() {
// the existing end-to-end assertion that the fallback renders still
// holds.
resetKey={filterKey}
fallback={(error) => (
<ErrorFallback
error={error}
title="The item list didn't load"
actions={
<Button type="primary" onClick={() => window.location.reload()}>
Reload
</Button>
}
/>
)}
fallback={catalogueErrorFallback}
>
{import.meta.env.DEV && <DevThrow scope="catalogue" />}
<Catalogue