chore(lint): adopt biome through the ultracite preset

Biome replaces the eslint + prettier pair outright: one binary, one pass, and it is fast
enough to run on every save and every commit without anyone noticing.

Ultracite is the preset — zero-config, strict, and mostly auto-fixable — so
biome.jsonc only carries what this repo overrides on top of it.
This commit is contained in:
Nayan
2026-07-25 11:40:00 +05:30
parent 2405ca3212
commit a884e72f66
4 changed files with 231 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
# Ultracite Code Standards
This project uses **Ultracite**, a zero-config preset that enforces strict code quality standards through automated formatting and linting.
## Quick Reference
- **Format code**: `pnpm dlx ultracite fix`
- **Check for issues**: `pnpm dlx ultracite check`
- **Diagnose setup**: `pnpm dlx ultracite doctor`
Biome (the underlying engine) provides robust linting and formatting. Most issues are automatically fixable.
---
## Core Principles
Write code that is **accessible, performant, type-safe, and maintainable**. Focus on clarity and explicit intent over brevity.
### Type Safety & Explicitness
- Use explicit types for function parameters and return values when they enhance clarity
- Prefer `unknown` over `any` when the type is genuinely unknown
- Use const assertions (`as const`) for immutable values and literal types
- Leverage TypeScript's type narrowing instead of type assertions
- Use meaningful variable names instead of magic numbers - extract constants with descriptive names
### Modern JavaScript/TypeScript
- Use arrow functions for callbacks and short functions
- Prefer `for...of` loops over `.forEach()` and indexed `for` loops
- Use optional chaining (`?.`) and nullish coalescing (`??`) for safer property access
- Prefer template literals over string concatenation
- Use destructuring for object and array assignments
- Use `const` by default, `let` only when reassignment is needed, never `var`
### Async & Promises
- Always `await` promises in async functions - don't forget to use the return value
- Use `async/await` syntax instead of promise chains for better readability
- Handle errors appropriately in async code with try-catch blocks
- Don't use async functions as Promise executors
### React & JSX
- Use function components over class components
- Call hooks at the top level only, never conditionally
- Specify all dependencies in hook dependency arrays correctly
- Use the `key` prop for elements in iterables (prefer unique IDs over array indices)
- Nest children between opening and closing tags instead of passing as props
- Don't define components inside other components
- Use semantic HTML and ARIA attributes for accessibility:
- Provide meaningful alt text for images
- Use proper heading hierarchy
- Add labels for form inputs
- Include keyboard event handlers alongside mouse events
- Use semantic elements (`<button>`, `<nav>`, etc.) instead of divs with roles
### Error Handling & Debugging
- Remove `console.log`, `debugger`, and `alert` statements from production code
- Throw `Error` objects with descriptive messages, not strings or other values
- Use `try-catch` blocks meaningfully - don't catch errors just to rethrow them
- Prefer early returns over nested conditionals for error cases
### Code Organization
- Keep functions focused and under reasonable cognitive complexity limits
- Extract complex conditions into well-named boolean variables
- Use early returns to reduce nesting
- Prefer simple conditionals over nested ternary operators
- Group related code together and separate concerns
### Security
- Add `rel="noopener"` when using `target="_blank"` on links
- Avoid `dangerouslySetInnerHTML` unless absolutely necessary
- Don't use `eval()` or assign directly to `document.cookie`
- Validate and sanitize user input
### Performance
- Avoid spread syntax in accumulators within loops
- Use top-level regex literals instead of creating them in loops
- Prefer specific imports over namespace imports
- Avoid barrel files (index files that re-export everything)
- Use proper image components (e.g., Next.js `<Image>`) over `<img>` tags
### Framework-Specific Guidance
**Next.js:**
- Use Next.js `<Image>` component for images
- Use `next/head` or App Router metadata API for head elements
- Use Server Components for async data fetching instead of async Client Components
**React 19+:**
- Use ref as a prop instead of `React.forwardRef`
**Solid/Svelte/Vue/Qwik:**
- Use `class` and `for` attributes (not `className` or `htmlFor`)
---
## Testing
- Write assertions inside `it()` or `test()` blocks
- Avoid done callbacks in async tests - use async/await instead
- Don't use `.only` or `.skip` in committed code
- Keep test suites reasonably flat - avoid excessive `describe` nesting
## When Biome Can't Help
Biome's linter will catch most issues automatically. Focus your attention on:
1. **Business logic correctness** - Biome can't validate your algorithms
2. **Meaningful naming** - Use descriptive names for functions, variables, and types
3. **Architecture decisions** - Component structure, data flow, and API design
4. **Edge cases** - Handle boundary conditions and error states
5. **User experience** - Accessibility, performance, and usability considerations
6. **Documentation** - Add comments for complex logic, but prefer self-documenting code
---
Most formatting and common issues are automatically fixed by Biome. Run `pnpm dlx ultracite fix` before committing to ensure compliance.
+15
View File
@@ -0,0 +1,15 @@
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"command": "pnpm run fix --skip=correctness/noUnusedImports",
"type": "command"
}
],
"matcher": "Write|Edit"
}
]
}
}
+3
View File
@@ -0,0 +1,3 @@
{
"recommendations": ["biomejs.biome"]
}
+90
View File
@@ -0,0 +1,90 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"extends": [
"ultracite/biome/core",
"ultracite/biome/vitest",
"ultracite/biome/react",
"ultracite/biome/next",
"ultracite/biome/tanstack"
],
"files": {
// An imported UI icon set, vendored verbatim. These are build inputs for
// scripts/gen.mjs, not authored source: formatting them churns 507 files
// and reorders SVG attributes for no benefit, and `noSvgWithoutTitle` does
// not apply to a corpus that only ever reaches the DOM through the
// generator.
"includes": [
"!packages/editor-icons/assets",
// Static assets apps/web serves from /public — the wallpapers, the dock
// glyphs, the favicon. Hand-drawn SVGs, never parsed as JSX: they reach
// the page through an <img src> or a CSS url(), so `noSvgWithoutTitle` is
// checking a file that has no accessible name to give. The call site
// supplies the alt text instead.
"!**/apps/*/public"
]
},
"linter": {
"rules": {
"suspicious": {
// Unreliable here, in two ways that between them cover all 54 hits it
// reported, and every one of its "fixes" deletes a live guard:
//
// - It reads `a?.b` as non-nullish even when `a` is nullable, so it
// calls the fallback in `options.width ?? preset?.width ?? 1440`
// (`presetById` returns `DevicePreset | null`) unreachable. It is
// reached whenever the id matches no preset.
// - It does not model class fields mutated from another method, so
// `private replaying = false` reads as permanently false at its
// `if (this.replaying)` guard — the flag that stops undo/redo from
// re-recording its own replays. Same for `awaiting` and `editing`.
//
// Revisit when the rule leaves nursery; the genuine hits it also found
// were redundant `?? ""` on non-optional strings, which cost nothing.
"noUnnecessaryConditions": "off"
}
}
},
"overrides": [
{
// Each package's `src/index.ts` *is* its published surface — it is what
// the `exports` map in package.json points at. `dnd/manager.ts` is the
// same idea internally: one wrapper that owns the dnd-kit dependency so
// nothing else imports it directly. `noBarrelFile` is aimed at re-export
// hubs that defeat tree-shaking inside an app, not at a declared API.
//
// The `**/` prefix is now redundant — every package lives at
// packages/*/ under this one workspace root — but it is harmless and
// matches the same set, so it stays rather than churning the glob.
"includes": [
"**/packages/*/src/index.ts",
"packages/overlay/src/dnd/manager.ts"
],
"linter": { "rules": { "performance": { "noBarrelFile": "off" } } }
},
{
// `ultracite/biome/next` is extended at the root for the packages that do
// target Next. apps/web does not: it is a TanStack Start app, where
// `<img>` is the image element and the `<head>` in the root route's shell
// component is the document head it is supposed to render. Both rules
// exist to push you toward `next/image` and the metadata API, neither of
// which exists here — following them would mean importing from a
// framework this app does not depend on.
"includes": ["apps/web/**"],
"linter": {
"rules": {
"performance": { "noImgElement": "off" },
"style": { "noHeadElement": "off" }
}
}
},
{
// Generated by the TanStack Router CLI on every dev run and every build.
// It ships its own @ts-nocheck and lint-disable banner and is rewritten
// from src/routes, so formatting it is churn that the next `tsr generate`
// discards.
"includes": ["**/src/routeTree.gen.ts"],
"formatter": { "enabled": false },
"linter": { "enabled": false }
}
]
}