From ab15644c71d0c087cb45718b6f5ebc007991c58f Mon Sep 17 00:00:00 2001 From: Will Manning Date: Wed, 4 Nov 2026 02:00:45 -0500 Subject: [PATCH 1/9] Set ten Biome rules to error, and fix what they find Set noExplicitAny, noArrayIndexKey, noAssignInExpressions, noShadowRestrictedNames, useImportType, noParameterAssign, useExhaustiveDependencies, noForEach, noUselessFragments, and noDangerouslySetInnerHtml to error for both sites. Fix the eight findings in code, and mark the nine deliberate uses with a biome-ignore comment that gives the reason. CLAUDE.md lists the four rules that stay off, with the findings each would raise. Signed-off-by: Will Manning --- CLAUDE.md | 10 +++- biome.json | 23 ++++--- scripts/verify.ts | 6 +- src/app/(bench)/bench/layout.tsx | 1 + src/app/(bench)/bench/page.tsx | 1 + src/app/(site)/blog/[slug]/page.tsx | 1 + src/app/(site)/layout.tsx | 1 + src/app/(site)/page.tsx | 1 + .../components/Chart.empty-window.test.tsx | 7 +-- .../components/Chart.lazy-hydration.test.tsx | 18 +++--- src/bench/components/GroupPermalink.test.tsx | 2 +- src/bench/components/OpenGroupsScript.tsx | 2 + src/bench/styles/tokens.test.ts | 60 +++++++++---------- src/components/install-tabs/index.tsx | 1 + src/components/logo-banner/index.tsx | 1 + 15 files changed, 70 insertions(+), 65 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 34d3b655..e21f89a2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -111,12 +111,18 @@ To see bench as bench.vortex.dev serves it, send its host: `curl -H 'Host: bench bench uses Biome, not ESLint or Prettier. The bench override in the root `biome.json` keeps the style bench had under Prettier (single quotes, trailing commas, 100 columns, 2-space indent) for bench's files, and `bun run check:ci` checks bench along with the site. `biome.json` cannot hold comments, so the reasons for each difference from the site's rules are here: -- `correctness/useExhaustiveDependencies` is on (the site has it off). It replaces ESLint's `react-hooks/exhaustive-deps`. An effect that omits dependencies on purpose carries a `biome-ignore` comment that says why. - The site's `a11y` rules apply to bench unchanged. Seven places in existing markup break one of them on purpose, and each carries an inline `biome-ignore` comment that says why: `useAriaPropsSupportedByRole` for `aria-label` on a plain `div` (the header nav controls, the chart toolbar), `useSemanticElements` for `role="group"` on a `div` in the toolbars (a `fieldset` would change their layout), `useFocusableInteractive` for the pointer-driven range strip, and `noNoninteractiveTabindex` for the group info icon, which takes focus so its tooltip shows for keyboard users. New markup follows the rules, or carries its own `biome-ignore` with a reason. - `complexity/noImportantStyles` is off: `!important` overrides Chart.js's inline canvas size and the offscreen prebuild layout in `src/bench/styles/globals.css`. - `organizeImports` is off: Prettier never sorted bench's imports, and reordering them would rewrite 27 files and could change the order CSS loads in. -Next.js's `core-web-vitals` lint rules have no Biome equivalent; the site accepts that too. The site's config also turns off `suspicious/noExplicitAny`, so bench's old typescript-eslint `no-explicit-any` check is gone too. +Next.js's `core-web-vitals` lint rules have no Biome equivalent; the site accepts that too. Two rules replace bench's old ESLint checks for both sites: `correctness/useExhaustiveDependencies` replaces `react-hooks/exhaustive-deps`, and `suspicious/noExplicitAny` replaces typescript-eslint's `no-explicit-any`. An effect that omits dependencies on purpose carries a `biome-ignore` comment that says why. + +Both sites run Biome's recommended rules, with the rules in `biome.json` set to `error` so that `check:ci` fails on them (Biome's default for some is a warning, which does not fail it). Each deliberate exception carries a `biome-ignore` comment with its reason: the inline scripts and JSON-LD (`noDangerouslySetInnerHtml`; `jsonLdScript` escapes `<`), and the two lists whose index is the only key (`noArrayIndexKey`). Four rules stay off. The count is what each would raise on 2026-11-04: + +- `style/noNonNullAssertion` (192, 190 of them in bench, mostly tests). Turn it on file by file, as bench's code changes. +- `style/noDescendingSpecificity` (13, 12 in `src/bench/styles/globals.css`). Fixing it reorders CSS rules, which can change what wins the cascade, so it needs a visual check of each page. +- `performance/noImgElement` (5). The logos and marks are SVGs and small PNGs with fixed sizes, which `next/image` would not make smaller. +- `complexity/noImportantStyles`, for bench only (3; see above). ## Licensing diff --git a/biome.json b/biome.json index 0555bce4..7a6380f8 100644 --- a/biome.json +++ b/biome.json @@ -57,15 +57,15 @@ "rules": { "preset": "recommended", "suspicious": { - "noExplicitAny": "off", - "noArrayIndexKey": "off", - "noAssignInExpressions": "off", - "noShadowRestrictedNames": "off" + "noExplicitAny": "error", + "noArrayIndexKey": "error", + "noAssignInExpressions": "error", + "noShadowRestrictedNames": "error" }, "style": { - "useImportType": "warn", + "useImportType": "error", "noNonNullAssertion": "off", - "noParameterAssign": "off", + "noParameterAssign": "error", "noDescendingSpecificity": "off" }, "correctness": { @@ -76,7 +76,7 @@ } }, "noUnusedImports": "error", - "useExhaustiveDependencies": "off", + "useExhaustiveDependencies": "error", "useHookAtTopLevel": "error" }, "a11y": { @@ -86,14 +86,14 @@ "noSvgWithoutTitle": "error" }, "complexity": { - "noForEach": "off", - "noUselessFragments": "off" + "noForEach": "error", + "noUselessFragments": "error" }, "performance": { "noImgElement": "off" }, "security": { - "noDangerouslySetInnerHtml": "off" + "noDangerouslySetInnerHtml": "error" } } }, @@ -125,9 +125,6 @@ }, "linter": { "rules": { - "correctness": { - "useExhaustiveDependencies": "error" - }, "complexity": { "noImportantStyles": "off" } diff --git a/scripts/verify.ts b/scripts/verify.ts index 8535ff91..5b998376 100644 --- a/scripts/verify.ts +++ b/scripts/verify.ts @@ -120,8 +120,7 @@ function jsonLd(html: string): JsonLdResult { const blocks: Array> = []; const re = /]+type="application\/ld\+json"[^>]*>([\s\S]*?)<\/script>/gi; - let m: RegExpExecArray | null; - while ((m = re.exec(html)) !== null) { + for (const m of html.matchAll(re)) { const raw = m[1] ?? ""; try { blocks.push(JSON.parse(raw)); @@ -135,8 +134,7 @@ function jsonLd(html: string): JsonLdResult { function internalHrefs(html: string): string[] { const hrefs = new Set(); const re = /]+href="(\/[^"#]*)(?:#[^"]*)?"/gi; - let m: RegExpExecArray | null; - while ((m = re.exec(html)) !== null) { + for (const m of html.matchAll(re)) { const href = m[1]; if (!href) continue; if (href.startsWith("//")) continue; diff --git a/src/app/(bench)/bench/layout.tsx b/src/app/(bench)/bench/layout.tsx index 3bcbdbf5..12b5b85e 100644 --- a/src/app/(bench)/bench/layout.tsx +++ b/src/app/(bench)/bench/layout.tsx @@ -85,6 +85,7 @@ export default async function RootLayout({ children }: { children: ReactNode }) suppressHydrationWarning > + {/* biome-ignore lint/security/noDangerouslySetInnerHtml: a constant script that sets the theme before first paint. */}