首页
/ Next.js 仓库代码规范体系:ESLint、Prettier 与 alex 的三层 Linting 实践

Next.js 仓库代码规范体系:ESLint、Prettier 与 alex 的三层 Linting 实践

2026-09-06 18:57:58作者:彭桢灵Jeremy

本文围绕 Next.js 仓库的官方 Linting 指南展开,系统讲解 pnpm lint / pnpm lint-fix 的完整执行链路:从脚本编排、双配置 ESLint 架构(IDE 快速模式与 CI 类型检查模式)、Prettier 格式化规则,到文档语言检查工具 alex 的配置细节,并结合 ESLint 配置Prettier 配置 等仓库源码文件,帮助贡献者准确理解每一项规则的含义与作用范围,从而在提交 PR 前高效通过代码规范检查。

官方命令:pnpm lint 与 pnpm lint-fix

Next.js 仓库使用 ESLintPrettieralex 三类工具对全部代码和文档进行 lint 与格式化,这一说明来自仓库的 Linting 贡献指南 contributing/repository/linting.md

对全仓库执行 lint 检查:

pnpm lint

如果出现错误,可以运行 ESLint 和 Prettier 的自动修复:

pnpm lint-fix

需要注意的是:并非所有规则都能自动修复,部分问题需要手动修改;另外,如果 alex 对文档提出语言警告,需要按其提示修改相关措辞。

pnpm lint 实际执行了哪些任务

查看根目录 package.json 中的 scripts 定义,可以还原 pnpm lint 的真实构成。它并不是单一的 ESLint 调用,而是用 npm-run-all 并行跑六项检查:

"lint": "run-p test-types lint-typescript prettier-check \"lint-eslint .\" lint-ast-grep lint-language check-unused-turbo-tasks",
"lint-fix": "pnpm prettier-fix && pnpm lint-eslint --fix .",
"lint-language": "alex . --quiet",
"prettier-check": "prettier --check .",
"prettier-fix": "prettier --write .",
"lint-eslint": "eslint --config eslint.cli.config.mjs",
"lint-ast-grep": "ast-grep scan",
"lint-typescript": "turbo run typescript",

也就是说,一次 pnpm lint 涵盖了:

子任务 对应脚本 作用
test-types tsc 根工程级 TypeScript 类型检查
lint-typescript turbo run typescript 通过 Turbo 在各 workspace 包内做类型检查
prettier-check prettier --check . 检查全仓库文件格式是否符合 Prettier 规则
lint-eslint . eslint --config eslint.cli.config.mjs 使用 CI 版配置(含类型检查规则)跑 ESLint
lint-ast-grep ast-grep scan ast-grep 规则集 做 AST 级模式检查
lint-language alex . --quiet 用 alex 扫描文档与代码中的敏感措辞
check-unused-turbo-tasks node scripts/check-unused-turbo-tasks.mjs 检查 Turbo 任务定义中是否有未使用的任务

pnpm lint-fix 的构成更简单:先 prettier --write . 重写格式,再 eslint --config eslint.cli.config.mjs --fix . 自动修复可修复的 ESLint 问题。这也解释了文档中"部分规则无法自动修复"的原因——lint-fix 只覆盖 Prettier 与 ESLint 两类可修复问题,ast-grep 模式违规和 alex 的语言警告仍需手动处理。

双配置 ESLint:IDE 快速配置与 CLI 类型检查配置

文档推荐在 VS Code 中安装 ESLint 插件以获得编辑器内实时提示,并指出启用的规则见 ESLint 配置。实际上仓库维护了两套 ESLint 配置,这一设计是理解 Next.js 规范体系的关键。

eslint.config.mjs:面向 IDE 的默认配置

eslint.config.mjs 文件开头的注释明确说明了双配置策略:

// This is the default eslint config that is used by IDEs. It does not use
// computation-heavy type-checked rules to ensure maximum responsiveness while
// writing code. In addition, there is .eslintrc.cli.json that does use
// type-checked rules in addition to the rules defined here, and it is used
// when running `pnpm lint-eslint` locally or in CI.

即:IDE 中加载的是 eslint.config.mjs(不启用计算开销大的 type-checked 规则,保证编辑响应速度);而本地执行 pnpm lint-eslint 或 CI 时,加载的是 eslint.cli.config.mjs,它在基础配置之上追加类型检查规则。

eslint.config.mjs 的整体结构是一个 ESLint Flat Config 数组,主要区块包括:

  1. 全局 ignore 规则:从 .config/eslintignore.mjs 导入,基于 globalIgnores 排除 node_modules.nextdistcrates/**、测试 fixture、编译产物(packages/next/src/compiled/**)等;
  2. 基础规则块:作用于 **/*.{js,jsx,mjs,ts,tsx,mts,mdx},用 @babel/eslint-parser 解析(presets: ['next/babel']supportsTopLevelAwait: true),并打开 reportUnusedDisableDirectives: 'error',即无效的 eslint-disable 注释本身会报错;
  3. Jest 测试块:对 test/****/*.test.ts(x) 文件启用 plugin:jest/recommended,并针对 Next.js 自研测试运行器扩展了 jest/no-standalone-expect,将 retryitSkipDeployitCIitHeadeditTurbopack 等自定义测试块函数识别为合法断言容器;
  4. TypeScript 块:对 .ts/.tsx/.mts 文件启用 tseslint.configs.recommendedstylistic,并按需关闭一批风格类规则(如 no-explicit-anyno-var-requires),同时重写 @typescript-eslint/no-unused-vars,允许 _ 前缀的忽略变量;
  5. packages 专属块:对 packages/**/*.ts(x) 启用内部插件 @next/eslint-plugin-internaltypechecked-requirejsdoc/no-typesjsdoc/no-undefined-types 等规则,并对 packages/** 加强 no-shadowimport/no-extraneous-dependencies(禁止引入 package.json 未声明的运行时依赖)。

基础规则块中的一些代表性规则(节选自 eslint.config.mjs):

  • eqeqeq: ['error', 'smart']no-fallthrough: 'error'no-eval: 'error'no-unreachable: 'error' 等大量可靠性规则全部为 error
  • react-hooks/rules-of-hooks: 'error'react-hooks/exhaustive-deps: 'error',对 React Hooks 使用强约束;
  • default-case 要求 switch 有默认分支(注释 ^no default$ 可豁免),但这一规则在类型检查覆盖的文件中会被关闭(见下文);
  • no-restricted-imports 禁止直接导入 */next-devtools/dev-overlay*,必须走 next/dist/compiled/next-devtools
  • no-restricted-syntax 禁止 substr()(提示改用 slice()/substring()),并要求 workUnitStore.type 的判断必须使用穷举 switch 而非 if/三元——这些细节规则体现了"用 lint 固化架构约束"的思路。

eslint.cli.config.mjs:CI 的类型检查增强层

eslint.cli.config.mjs 的内容非常精炼,它继承基础配置后,仅对 **/*.ts**/*.tsx 开启 parserOptions.project: true(启用类型感知),并追加规则:

rules: {
  '@typescript-eslint/switch-exhaustiveness-check': [
    'error',
    { requireDefaultForNonUnion: true },
  ],
}

该文件注释说明:类型检查规则"非常慢且消耗大量内存",因此排除了 bench/**examples/**test/**turbopack/**evals/evals/** 等非核心文件。为保持行为一致,eslint.config.mjs 中还有一个专门镜像这些 files/ignores glob 的配置块(eslint.config.mjs),在这些文件上关闭 default-case——因为 switch-exhaustiveness-check 已强制 union/enum 的穷举覆盖,再要求 default 反而是冗余的。注释特别提醒:修改这两处 glob 时必须保持同步。

Prettier 格式化配置

Linting 文档推荐安装 VS Code 的 Prettier 插件,格式配置位于 Prettier 配置。完整内容只有三行:

{
  "trailingComma": "es5",
  "singleQuote": true,
  "semi": false
}

即:ES5 兼容位置加尾逗号、统一单引号、不加分号。这解释了 Next.js 源码"无分号 + 单引号"的标志性风格。

配套的文件排除清单在 .prettierignore,其中有几个值得注意的排除项:

  • 构建产物:.next/dist/target/compiled/
  • pnpm-lock.yamltest-timings.json 等生成文件;
  • SWC/RSC 编译器测试输入文件(如 crates/next-custom-transforms/tests/fixture/... 下的 input.js)——注释说明 "prettier destroys 'use server'/'use client' directives in multi-file code examples",即格式化会破坏多文件示例中的指令 pragma;
  • packages/next-codemod/**/*.js 等转换测试 fixture。

提交前格式化由 lint-staged 配置 自动完成,husky 的 pre-commit 钩子(.husky/pre-commit)执行 pnpm lint-staged,规则为:

module.exports = {
  '*.{js,jsx,mjs,ts,tsx,mts,mdx}': [
    'prettier --with-node-modules --ignore-path .prettierignore --write',
    'eslint --config eslint.config.mjs --fix',
  ],
  '*.{json,md,css,html,yml,yaml,scss}': [
    'prettier --with-node-modules --ignore-path .prettierignore --write',
  ],
  '*.rs': ['rustfmt --edition 2024 --'],
}

注意这里提交钩子使用的是 IDE 版 eslint.config.mjs 而非 CLI 版,避免在提交时为全部 TS 文件跑重量级的类型检查;另外的 *.rs 文件走 rustfmt,覆盖仓库中的 Rust 部分。husky 还有一个 pre-push 钩子,用于拦截直接向 canary 保护分支推送的操作。

alex:面向包容性语言的文档检查

Linting 文档还推荐在 VS Code 安装 AlexJS Linter 扩展,配置位于 alex 配置。alex 用于扫描文档中可能对特定群体不友好的措辞,是 Next.js 仓库少数作用于文字内容而非代码的 lint 工具,对应 pnpm lint-languagealex . --quiet)。

.alexrc 采用 allow 白名单机制,放行了在 Next.js 语境下属于正常技术用法的词汇,例如:

{
  "allow": [
    "attacks",
    "color",
    "dead",
    "deno",
    "dirty",
    "execute",
    "executed",
    "execution",
    "failed",
    "failure",
    "hook",
    "hooks",
    "invalid",
    "simple",
    "special",
    "white",
    ...
  ]
}

可以看到,白名单同时容纳了产品名(denofly.iorailwaysst)、技术语义词(colorhooksfailed)以及文档中确实需要的形容词(simplespecialwhite)。配套还有 .alexignore,将 CODE_OF_CONDUCT.mdexamples/、各 LICENSE.mdAGENTS.md 等文件排除在扫描之外——这些文件要么本身就是第三方/模板内容,要么不适合自动审查。

贡献者如果在 PR 中收到 alex 警告,按 Linting 文档的指引,需要按其提示修改对应措辞,而不是直接忽略。

ast-grep:AST 级模式检查的补充层

虽然 Linting 文档主要介绍三个工具,但从 package.jsonlint 编排可以看到,pnpm lint 还包含 ast-grep scan。ast-grep 基于 AST 模式匹配,能表达 ESLint 规则难以覆盖的结构性约束,其规则集位于 .config/ast-grep/rules/

no-typeof-window-require.yml 为例,该规则禁止用 typeof window 条件门控 require() 调用:

severity: error
language: TypeScript
files:
  - packages/next/src/**
rule:
  pattern: require($$$ARGS)
  inside:
    stopBy: end
    any:
      - kind: ternary_expression
        has:
          field: condition
          has:
            stopBy: end
            pattern: typeof window
      - kind: if_statement
        ...

规则的 note 说明背后的架构原因:用 typeof window 包裹 require() 会把服务端分支打进行者端 bundle,正确做法是拆分为 <name>.ts<name>.browser.ts 两个文件,由 scripts/generate-browser-variant-aliases.mjs 自动完成浏览器变体别名。规则文件还注明 ast-grep 规则是单语言的,.tsx 版本由配套的 no-typeof-window-require-tsx.yml 覆盖,两份 rule 体需保持同步。目录下的 rule-tests/ 与快照文件则为每条规则提供了可回归测试的 fixture,保证规则本身的正确性。

小结:一次 pnpm lint 的完整检查矩阵

综合以上仓库证据,可以归纳出 Next.js 仓库 pnpm lint 的检查矩阵:

工具 作用对象 配置位置 可否自动修复
格式 Prettier 全仓库文本文件 .prettierrc.json.prettierignore 可(pnpm prettier-fix
代码规则(IDE) ESLint JS/TS/MDX eslint.config.mjs.config/eslintignore.mjs 部分可
代码规则(CI,类型感知) ESLint TS/TSX 核心文件 eslint.cli.config.mjs 部分可
AST 结构 ast-grep packages/next/src/** .config/ast-grep/rules/ 不可
类型 tsc / Turbo 根工程与 workspace tsconfig.json 不可
文档语言 alex 文档与代码文字 .alexrc.alexignore 不可,需按提示改措辞

对贡献者而言,日常开发流程是:编辑器内由 eslint.config.mjs + Prettier 插件提供即时反馈,提交时 husky 钩子对暂存文件跑 lint-staged(Prettier 写入 + ESLint --fix + Rust 文件 rustfmt),提交前最终用 pnpm lint 做全量验证,遇到可修复错误时先跑 pnpm lint-fix 再手动处理剩余项。这套"IDE 轻量、CI 重量、提交钩子兜底"的分层设计,在保证大型 monorepo 可维护性的同时,把昂贵的类型检查规则限制在了必要的文件范围与执行时机内。

登录后查看全文
热门项目推荐
相关项目推荐