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(2026-09-23 起全部为
.ts,见 ADR 0019):base.ts— TypeScript,no-namespace: off,no-console: off(currently zero consumers — no shared package has a local ESLint config; they all fall back to the rootvuepreset via upward resolution. 2026-11 起最后一个本地配置packages/platform-web/ui/eslint.config.ts随该包删除,base的消费者面回到零)vue.ts— extends base + UnoCSS + Vue + TypeScript + pnpm catalog enforcementnest.ts— extends base + TypeScript project mode + NestJS-specific rules (decorator ordering via the localnest-local-rules.tsplugin, restrict frontend imports@walnut/client/@walnut/http—— 这两条是当前实际生效的禁令;ADR 0017 曾提议再补@walnut/ui/@walnut/i18n/@walnut/security,一直未实现且前者的包已于 2026-11 删除)
- ESLint plugins live in the config package's
dependencies— consumers only need toextendsthe preset eslintitself is apeerDependency— avoids multiple ESLint versions coexisting- ESLint loads
eslint.config.tsthroughjiti(ESLint's official TS config loader), which is why the root gained ajitidevDependency. The package's owntypes:checkis a realtsc --noEmit(it used to be a no-opecho)
Consumer pattern:
// apps/admin/eslint.config.ts
import vueConfig from "@walnut/eslint-config/vue";
export default vueConfig();
// apps/server/eslint.config.ts
import nestConfig from "@walnut/eslint-config/nest";
export default nestConfig();Decision 2: No oxlint/biome — Continue ESLint Only
Chosen: Do not introduce oxlint or biome at this time.
Rationale:
- oxlint: official Vue SFC support is still at the RFC stage; third-party plugins (
oxlint-vueand friends) exist but are not mature — for a.vuesurface the size ofapps/admin, the risk outweighs the gain - biome: likewise no official Vue SFC support; and no custom-rule support (we have a NestJS decorator-ordering rule of our own)
- ESLint performance is adequate at this project's scale
- Re-evaluate when oxlint or biome ships official Vue SFC support
Last revised: 2026-09-23 —— 措辞修正。上面两条原先写的是「不支持 Vue SFC」, 那是个已经过期的绝对判断:事实是官方框架支持仍在 RFC 阶段,第三方插件可用但不成熟。 结论(暂不作为主 linter)不变,理由换成了经得起复查的那条。
Decision 3: Prettier Deprecated — Formatting via ESLint
Chosen: Prettier is no longer used. Formatting is handled by ESLint's stylistic rules (from the @antfu presets). There is no .prettierrc, no eslint-config-prettier, and no format / format:check scripts; lint-staged runs ESLint only on staged files in the pre-commit hook.
Rationale:
- One tool covers both linting and formatting — no config drift between a formatter and ESLint rules
- The
@antfustylistic rules make a separate Prettier setup redundant
Alternatives considered
Decision 1: 把 @walnut/eslint-config 作为共享配置包维护
- 根级单体配置 —— 否决;需要按项目写 glob 与条件规则,随包增多会变得无法维护。
- 各包各自独立配置 —— 否决;规则重复,ESLint 插件版本会漂移。
Consequences
- ESLint configuration changes require updating
@walnut/eslint-configonly — all consumers pick up changes on nextpnpm install - No oxlint/biome adoption path until official Vue SFC support lands (track oxc#vue and biome#vue)
- No Prettier — formatting is enforced purely via ESLint stylistic rules; re-introducing Prettier would require
eslint-config-prettierto be placed last in config arrays (flat config rule: later configs override earlier ones)
Related
packages/tooling/eslint-config/— source code for the config package(预设为base.ts/vue.ts/nest.ts,另有本地规则插件nest-local-rules.ts)- ADR 0012 Decision 7 — ESLint type-aware rules relaxed for workspace imports
- ADR 0019 — 全仓
.mjs→.ts(含本包的 4 个文件:3 个预设 +nest-local-rules),以及jiti的引入 apps/docs/src/zh-CN/content/industry-research/02-eslint-configuration.md— industry standard practices for ESLint in monoreposmonorepo/eslint.md— operator-facing ESLint guide