ADR-0014: ESLint Configuration Strategy
Date: 2026-07-29 Status: Accepted
Context
The monorepo needs consistent code quality enforcement across frontend (Vue3 + Vite), backend (NestJS + SWC), and shared packages. ESLint v9+ mandates flat config format. The project had already migrated to flat config and extracted a shared config package (@walnut/eslint-config) without a formal architectural decision.
Three questions need explicit decisions:
- Whether to maintain
@walnut/eslint-configas a shared config package - Whether to adopt faster linting tools (oxlint, biome) alongside ESLint
- How to handle Prettier integration
Decision 1: Maintain @walnut/eslint-config as a Shared Config Package
Chosen: Keep @walnut/eslint-config as a workspace package with 3 presets.
Rationale:
- Single source of truth — upgrade an ESLint plugin once, all consumers pick it up
- 3 presets match the 3 runtime environments:
base.mjs— TypeScript,no-namespace: off,no-console: offvue.mjs— extends base + UnoCSS + Vue + TypeScript + pnpm catalog enforcementnest.mjs— extends base + TypeScript project mode + NestJS-specific rules (decorator ordering, restrict frontend imports@walnut/client/@walnut/axios)
- ESLint plugins live in the config package's
dependencies— consumers only need toextendsthe preset eslintitself is apeerDependency— avoids multiple ESLint versions coexisting
Consumer pattern:
js
// apps/admin/eslint.config.mjs
import vueConfig from "@walnut/eslint-config/vue";
export default [...vueConfig];
// apps/server/eslint.config.mjs
import nestConfig from "@walnut/eslint-config/nest";
export default [...nestConfig];Alternatives considered:
- Root-level monolithic config — rejected; would need project-specific globs and conditional rules, becoming unmaintainable as packages grow
- Per-package independent configs — rejected; duplicated rules, version drift in ESLint plugins
Decision 2: No oxlint/biome — Continue ESLint Only
Chosen: Do not introduce oxlint or biome at this time.
Rationale:
- oxlint: does not support Vue SFC (
.vuefiles) — this is a hard blocker forapps/admin - biome: does not support Vue SFC; does not support custom rules (we have NestJS decorator-ordering rules)
- ESLint performance is adequate for this project's scale (~4 packages + 2 apps)
- Re-evaluate when oxlint or biome adds Vue SFC support
Decision 3: Prettier at Root Level
Chosen: Single .prettierrc at monorepo root, no per-package Prettier configs.
Rationale:
- Formatting should be uniform across the entire monorepo
eslint-config-prettierdisables ESLint rules that conflict with Prettier- Root
package.jsonhasformat/format:checkscripts for manual use lint-stagedruns Prettier + ESLint on staged files in pre-commit hook
Consequences
- ESLint configuration changes require updating
@walnut/eslint-configonly — all consumers pick up changes on nextpnpm install - No oxlint/biome adoption path until Vue SFC support lands (track oxc#vue and biome#vue)
- Prettier + ESLint coexistence managed by
eslint-config-prettierplaced last in config arrays (flat config rule: later configs override earlier ones)
Related
packages/eslint-config/— source code for the config packagedocs/reference/02-eslint-configuration.md— industry standard practices for ESLint in monoreposdocs/adr/0012-toolchain-divergence.mdD7 — ESLint type-aware rules relaxed for workspace imports