mirror of
https://github.com/coder/coder.git
synced 2026-10-16 14:48:56 +08:00
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._
122 lines
3.7 KiB
JSON
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"
|
|
}
|