chore(studio): promote zero-baseline eslint ratchet rules to error (#50977)

<!-- ccr-slack-attribution -->
_Requested by **Charis Lam** · [Slack
thread](https://supabase.slack.com/archives/C0161K73J1J/p1790599888647059?thread_ts=1790599888.647059&cid=C0161K73J1J)_

## Problem

The Studio ESLint "ratchet"
(`apps/studio/scripts/ratchet-eslint-rules.ts`, baseline in
`apps/studio/.github/eslint-rule-baselines.json`, tracked rules in
`apps/studio/scripts/ratchet-rules.json`) lets certain rules stay at
`warn` severity while CI blocks the *count* of violations from
increasing. Several of those tracked rules had already reached a
baseline of 0 allowed violations, meaning there's nothing left to
ratchet — they should be enforced directly instead of tracked
indirectly.

## Solution

**Before:** `no-restricted-imports`, `jsx-a11y/aria-props`,
`jsx-a11y/aria-proptypes`, `jsx-a11y/role-supports-aria-props`,
`jsx-a11y/anchor-has-content`, `jsx-a11y/aria-role`,
`jsx-a11y/no-aria-hidden-on-focusable`, `jsx-a11y/tabindex-no-positive`,
`jsx-a11y/no-distracting-elements`, and `react-hook-form/no-use-watch`
were all tracked in the ratchet baseline with a count of 0, and (apart
from `no-restricted-imports`, see below) configured as ESLint `warn` in
`apps/studio/eslint.config.cjs`.

**After:** each of those rules is removed from
`apps/studio/.github/eslint-rule-baselines.json` (both the `rules` count
and the now-empty `ruleFiles` entry) and from
`apps/studio/scripts/ratchet-rules.json`. Their severity in
`apps/studio/eslint.config.cjs` is bumped from `warn` to `error` so
they're enforced directly by lint going forward instead of being tracked
via the ratchet. `no-restricted-imports` was a special case: a later
config block in `apps/studio/eslint.config.cjs` already overrides the
shared `warn` default with `error` (confirmed via `eslint
--print-config`), so only the ratchet bookkeeping needed removing for
that rule — no severity change was needed.

Promoting `jsx-a11y/role-supports-aria-props` to `error` surfaced one
real violation that the ratchet's non-test-file filter had been hiding:
a mock `<button>` in `LocalDropdown.test.tsx` set `aria-checked`, which
that role doesn't support. Removed the unused `aria-checked` attribute
from the mock (it wasn't asserted on by any test).

Every other rule still tracked by the ratchet (e.g.
`@typescript-eslint/no-explicit-any`, `react-hooks/exhaustive-deps`,
`no-restricted-exports`, …) has a baseline above 0 and was left
untouched.

### How verified

- `pnpm --filter studio run lint:ratchet` → `Stable: No regressions for
selected rules.`
- `pnpm --filter studio run lint` → `0 errors, 2430 warnings` (no new
errors from the severity bumps)
- `npx vitest run components/interfaces/LocalDropdown.test.tsx` → 3/3
passing after the mock fix
- `npx prettier --check` on all touched files → clean
- `npx tsc --noEmit` shows one pre-existing, unrelated error in
`packages/ui-patterns` (reproduced identically on `master` before this
change)

## Review instructions

1. Confirm `apps/studio/.github/eslint-rule-baselines.json` and
`apps/studio/scripts/ratchet-rules.json` no longer list the 10 rules
named above.
2. Confirm those same rules (except `no-restricted-imports`, already
`error`) are now `'error'` in `apps/studio/eslint.config.cjs`.
3. Run `pnpm --filter studio run lint:ratchet` and `pnpm --filter studio
run lint` locally to confirm both pass.

## Checklist

Check all before review:

- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [ ] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01LZThbcWV5U1r5cvUDKPVQP

---
_Generated by [Claude
Code](https://claude.ai/code/session_01LZThbcWV5U1r5cvUDKPVQP)_

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
claude[bot]andClaude authored and GitHub committed 2026-09-28 14:08:30 +00:00
1 parent 8e20712475
commit f0e0865acc
4 files changed
+13 -43

No files matched your search

+2 -22
View File
@@ -4,24 +4,14 @@
"import/no-anonymous-default-export": 57,
"@tanstack/query/exhaustive-deps": 87,
"@typescript-eslint/no-explicit-any": 799,
"no-restricted-imports": 0,
"no-restricted-exports": 176,
"react/no-unstable-nested-components": 34,
"jsx-a11y/aria-props": 0,
"jsx-a11y/aria-proptypes": 0,
"jsx-a11y/role-supports-aria-props": 0,
"jsx-a11y/anchor-has-content": 0,
"jsx-a11y/control-has-associated-label": 178,
"jsx-a11y/label-has-associated-control": 33,
"jsx-a11y/aria-role": 0,
"jsx-a11y/no-redundant-roles": 2,
"jsx-a11y/no-aria-hidden-on-focusable": 0,
"jsx-a11y/tabindex-no-positive": 0,
"jsx-a11y/anchor-is-valid": 6,
"jsx-a11y/heading-has-content": 2,
"jsx-a11y/no-distracting-elements": 0,
"valtio/state-snapshot-rule": 1,
"react-hook-form/no-use-watch": 0
"valtio/state-snapshot-rule": 1
},
"ruleFiles": {
"react-hooks/exhaustive-deps": {
@@ -629,7 +619,6 @@
"types/next.ts": 2,
"types/ui.ts": 2
},
"no-restricted-imports": {},
"no-restricted-exports": {
"__mocks__/hooks/analytics/useFillTimeseriesSorted.ts": 1,
"__mocks__/hooks/analytics/useLogsQuery.ts": 1,
@@ -832,10 +821,6 @@
"components/ui/Charts/ComposedChart.utils.tsx": 3,
"components/ui/ErrorBoundary/ErrorBoundary.tsx": 1
},
"jsx-a11y/aria-props": {},
"jsx-a11y/aria-proptypes": {},
"jsx-a11y/role-supports-aria-props": {},
"jsx-a11y/anchor-has-content": {},
"jsx-a11y/control-has-associated-label": {
"components/grid/components/editor/TimeEditor.tsx": 1,
"components/interfaces/Account/Preferences/AnalyticsSettings.tsx": 1,
@@ -999,13 +984,10 @@
"components/ui/Charts/ComposedChart.tsx": 1,
"components/ui/Charts/ComposedChart.utils.tsx": 2
},
"jsx-a11y/aria-role": {},
"jsx-a11y/no-redundant-roles": {
"components/interfaces/Organization/BillingSettings/Subscription/EnterpriseCard.tsx": 1,
"components/interfaces/Organization/BillingSettings/Subscription/PlanCard.tsx": 1
},
"jsx-a11y/no-aria-hidden-on-focusable": {},
"jsx-a11y/tabindex-no-positive": {},
"jsx-a11y/anchor-is-valid": {
"components/interfaces/BranchManagement/Overview.tsx": 1,
"components/interfaces/Organization/ProjectClaim/confirm.tsx": 1,
@@ -1016,10 +998,8 @@
"jsx-a11y/heading-has-content": {
"components/layouts/Scaffold.tsx": 2
},
"jsx-a11y/no-distracting-elements": {},
"valtio/state-snapshot-rule": {
"state/storage-explorer.tsx": 1
},
"react-hook-form/no-use-watch": {}
}
}
}
@@ -137,7 +137,7 @@ vi.mock('ui', async () => {
checked?: boolean
onCheckedChange?: (checked: boolean) => void
}) => (
<button tabIndex={0} aria-checked={checked} onClick={() => onCheckedChange?.(!checked)}>
<button tabIndex={0} onClick={() => onCheckedChange?.(!checked)}>
{children}
</button>
),
+9 -9
View File
@@ -73,10 +73,10 @@ module.exports = defineConfig([
'barrel-files/avoid-re-export-all': 'error',
'jsx-a11y/alt-text': 'warn',
'jsx-a11y/role-has-required-aria-props': 'error',
'jsx-a11y/aria-props': 'warn',
'jsx-a11y/aria-proptypes': 'warn',
'jsx-a11y/role-supports-aria-props': 'warn',
'jsx-a11y/anchor-has-content': 'warn',
'jsx-a11y/aria-props': 'error',
'jsx-a11y/aria-proptypes': 'error',
'jsx-a11y/role-supports-aria-props': 'error',
'jsx-a11y/anchor-has-content': 'error',
'jsx-a11y/control-has-associated-label': [
'warn',
{ controlComponents: ['Button', 'Switch'] },
@@ -85,19 +85,19 @@ module.exports = defineConfig([
'warn',
{ labelComponents: ['Label'], controlComponents: ['Input', 'Switch'] },
],
'jsx-a11y/aria-role': 'warn',
'jsx-a11y/aria-role': 'error',
'jsx-a11y/no-redundant-roles': 'warn',
'jsx-a11y/no-aria-hidden-on-focusable': 'warn',
'jsx-a11y/tabindex-no-positive': 'warn',
'jsx-a11y/no-aria-hidden-on-focusable': 'error',
'jsx-a11y/tabindex-no-positive': 'error',
'jsx-a11y/anchor-is-valid': 'warn',
'jsx-a11y/heading-has-content': 'warn',
'jsx-a11y/no-distracting-elements': 'warn',
'jsx-a11y/no-distracting-elements': 'error',
'valtio/state-snapshot-rule': 'warn',
'valtio/avoid-this-in-proxy': 'error',
'react-hook-form/destructuring-formstate': 'error',
'react-hook-form/no-access-control': 'error',
'react-hook-form/no-nested-object-setvalue': 'error',
'react-hook-form/no-use-watch': 'warn',
'react-hook-form/no-use-watch': 'error',
},
},
// Analytics SQL wire boundary: every call to a SQL-bearing analytics
+1 -11
View File
@@ -3,22 +3,12 @@
"import/no-anonymous-default-export",
"@tanstack/query/exhaustive-deps",
"@typescript-eslint/no-explicit-any",
"no-restricted-imports",
"no-restricted-exports",
"react/no-unstable-nested-components",
"jsx-a11y/aria-props",
"jsx-a11y/aria-proptypes",
"jsx-a11y/role-supports-aria-props",
"jsx-a11y/anchor-has-content",
"jsx-a11y/control-has-associated-label",
"jsx-a11y/label-has-associated-control",
"jsx-a11y/aria-role",
"jsx-a11y/no-redundant-roles",
"jsx-a11y/no-aria-hidden-on-focusable",
"jsx-a11y/tabindex-no-positive",
"jsx-a11y/anchor-is-valid",
"jsx-a11y/heading-has-content",
"jsx-a11y/no-distracting-elements",
"valtio/state-snapshot-rule",
"react-hook-form/no-use-watch"
"valtio/state-snapshot-rule"
]