Skip to content

0007. Linting is oxlint, not ESLint

  • Status: accepted
  • Date: 2026-07-18

Formatting belongs to Prettier, and this repository needs a linter beside it. Two constraints framed the choice: the linter is a current-generation tool, not the default one, and it integrates with the Nx target model so that lint results cache and scope to affected projects like every other target. Biome satisfies both constraints and was still rejected. Its value is the formatter and the linter together, so with the formatter disabled next to Prettier it delivers half of what it is for while owning a second configuration file in full.

Linting is oxlint, driven by a single root .oxlintrc.json. Prettier owns formatting and import order, and the two tools do not overlap.

  • The categories correctness and suspicious are errors.
  • Each project’s lint script passes the root config path explicitly, so the working directory of the invocation never changes the result.
  • Nx infers the lint target from those scripts and caches it against the root config and the oxlint binary.
  • pnpm verify, the single quality gate, runs the lint lane alongside the others.

Rules oxlint does not carry move to a Prettier plugin wherever the concern is textual. Import grouping and ordering are enforced by the sort-imports plugin, so a wrong import order fails the format check and never goes unchecked.

@nx/enforce-module-boundaries is not available, because it is an ESLint rule. Structure enforces the boundary instead: pnpm’s isolated node_modules plus TypeScript project references make an undeclared cross-package import fail to resolve, at install time and at typecheck time. Type-aware rules are the accepted gap. no-floating-promises and no-misused-promises are active in neither strict tsc nor base oxlint, so a dropped promise is not caught by a rule. Compensation is behavioural, and the runtime contract requires it anyway: supervisor and pool code fails loud on unhandledRejection and never swallows it. oxlint’s type-aware mode is the eventual answer.

Blank-line discipline is convention, not enforced. There is no oxlint port of padding-line-between-statements, so the rules about air in the code are stated in the style docs and checked by review. But padding between test blocks is the one exception: oxlint’s Vitest and jest rule sets do cover it, and it fails the lint lane.