Files
coder/biome.jsonc
T
Jeremy Ruppel 408cc8ec02 chore: enable exhaustive-deps oxlint rule (#30569)
Enables oxlint's `exhaustive-deps` rule as an error and turns off the
overlapping Biome `useExhaustiveDependencies` rule (6/6). Stacked on
#30568.

The earlier PRs in the stack fix all 13 violations, so this PR only
changes config. It also updates an `AgentChatInput` comment that
referred to Biome's hooks lint.

<details>
<summary>Implementation plan and findings</summary>


### Findings

- With `"exhaustive-deps": "error"`, oxlint 1.80 reported 13 violations
in 11 files under `site/`.
- No `biome-ignore lint/correctness/useExhaustiveDependencies`
suppressions remained, so the work was fixing the places where oxlint is
stricter than Biome.
- oxlint treats `useEffectEvent` results as stable.
- Formik's `setFieldValue` and `setFieldTouched` are stable
(`useEventCallback`), but the `form` object from `useFormik` is not.
Method calls like `form.setFieldValue(...)` make the rule require
`form`.
- `AgentsPage` and `TemplateBuilder` use React Compiler, so "changes
every render" reports there are false positives at runtime.
`site/AGENTS.md` rules out adding `useMemo` or `useCallback` there.

### Stack

| PR | Fixes |
|---|---|
| #30564 Formik setters | `CreateTokenForm`, `CreateWorkspacePageView`
(two effects): destructure the stable setters |
| #30565 `useEffectEvent` | `TerminalPanel` ready fallback,
`useWorkspaceFileUploads` unmount abort and scope reset |
| #30566 TemplateBuilder | `ModuleSelectStep`,
`TemplateCustomizationsStep`: module-level empty arrays instead of `??
[]` |
| #30567 `useMemo` deps | `CreateWorkspacePage`: `readonly` module-level
`emptyPresets`. `DeploymentBannerView`: `formatLastAggregated` helper
replaces a memo whose only dep was a recompute trigger |
| #30568 effect deps | `NotificationsPage` (`searchParams`),
`RenameChatDialog` (`[prevChatId]`), `WorkspaceMoreActions`
(`[disabled]`) |
| This PR | Enable the rule. Biome `useExhaustiveDependencies` off |

### Decisions

- Fixes come first and the rule is turned on last, so every PR passes
lint on its own.
- `ModuleSelectStep`: dropping the manual `useMemo` made `lint:compiler`
flag `handleToggle`, so it uses the module-level constant like
`TemplateCustomizationsStep`.
- `emptyPresets` is shared across renders and instances. `presets` is
typed `readonly` through the view, so the compiler rejects mutation.
Every use only reads it, and loaded query data is already shared across
consumers.

### Tests

- Added: `CreateTokenPage` default lifetime submission,
`DeploymentBannerView` "last aggregated" countdown. Both were checked to
fail against a broken implementation.
- Existing coverage: `useWorkspaceFileUploads` (unmount, StrictMode,
scope changes), `CreateWorkspacePage` presets.
- Not covered by Vitest: `TerminalPanel`, `RenameChatDialog`,
`NotificationsPage`, `WorkspaceMoreActions` (needs a hand-built
`DashboardContext`), and the TemplateBuilder steps.

### Validation

`lint:oxlint`, `lint:check`, `lint:types`, `lint:compiler`,
`lint:circular-deps`, `knip`, Biome format, and 283 Vitest tests across
the affected files.

</details>

_Generated by Coder Agents on behalf of @jeremyruppel._
2026-10-09 16:00:17 -04:00

122 lines
3.7 KiB
JSON

{
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true,
"defaultBranch": "main"
},
"files": {
// static/*.html are Go templates with {{ }} directives that
// Biome's HTML parser does not support.
"includes": ["**", "!**/pnpm-lock.yaml", "!**/static/*.html"],
"ignoreUnknown": true,
"maxSize": 2097152
},
"linter": {
"rules": {
"a11y": {
"useKeyWithClickEvents": "off", // oxlint owns click-events-have-key-events
"useMediaCaption": "off", // oxlint owns media-has-caption
"noSvgWithoutTitle": "off",
"useButtonType": "off",
"useSemanticElements": "off",
"noStaticElementInteractions": "off"
},
"correctness": {
"noInvalidBuiltinInstantiation": "off", // oxlint owns new-for-builtins
"useExhaustiveDependencies": "off", // oxlint owns exhaustive-deps
"noUnusedImports": "warn",
"useUniqueElementIds": "off", // TODO: This is new but we want to fix it
"noNestedComponentDefinitions": "off", // TODO: Investigate, since it is used by shadcn components
"noUnusedVariables": {
"level": "warn",
"options": {
"ignoreRestSiblings": true
}
}
},
"security": {
"noDangerouslySetInnerHtml": "off" // oxlint owns no-danger
},
"style": {
"noNonNullAssertion": "off",
"noParameterAssign": "off",
"useDefaultParameterLast": "off",
"useSelfClosingElements": "off",
"useAsConstAssertion": "error",
"useEnumInitializers": "error",
"useSingleVarDeclarator": "error",
"useConsistentCurlyBraces": "off", // oxlint owns jsx-curly-brace-presence
"noUnusedTemplateLiteral": "error",
"useNumberNamespace": "error",
"noInferrableTypes": "off", // oxlint owns no-inferrable-types
"noUselessElse": "error",
"noRestrictedImports": {
"level": "error",
"options": {
"paths": {
"react": {
"message": "React 19 no longer requires forwardRef. Use ref as a prop instead.",
"importNames": ["forwardRef"]
},
"@pierre/diffs": {
"message": "Parse diffs through parseDiffString so every rendered file carries a content-derived cache key (pierrecomputer/pierre#1052).",
"importNames": [
"parsePatchFiles",
"processPatch",
"processFile",
"parseDiffFromFile"
]
},
// "#/components/Stack/Stack": "Use Tailwind flex utilities instead (e.g., <div className='flex flex-col gap-4'>).",
"lodash": "Use lodash/<name> instead."
}
}
}
},
"suspicious": {
"noFallthroughSwitchClause": "off", // oxlint owns no-fallthrough
"noConfusingVoidType": "off", // oxlint owns no-invalid-void-type
"noExplicitAny": "off", // oxlint owns no-explicit-any
"noArrayIndexKey": "off",
"noThenProperty": "off",
"noTemplateCurlyInString": "off",
"useIterableCallbackReturn": "off",
"noUnknownAtRules": "off", // Allow Tailwind directives
"noEmptyInterface": "off", // oxlint owns no-empty-interface
"noConsole": "off" // oxlint owns no-console
},
"complexity": {
"noImportantStyles": "off", // TODO: check and fix !important styles
"noUselessThisAlias": "off",
"useLiteralKeys": "off"
}
}
},
"css": {
"parser": {
// Biome 2.3+ requires opt-in for @apply and other
// Tailwind directives.
"tailwindDirectives": true
}
},
"overrides": [
{
// parseDiff.ts is the single choke point allowed to import
// the raw @pierre/diffs parsers.
"includes": [
"**/DiffViewer/parseDiff.ts",
"**/DiffViewer/parseDiff.test.ts"
],
"linter": {
"rules": {
"style": {
"noRestrictedImports": "off"
}
}
}
}
],
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json"
}