Next.js 仓库代码规范体系:ESLint、Prettier 与 alex 的三层 Linting 实践
本文围绕 Next.js 仓库的官方 Linting 指南展开,系统讲解 pnpm lint / pnpm lint-fix 的完整执行链路:从脚本编排、双配置 ESLint 架构(IDE 快速模式与 CI 类型检查模式)、Prettier 格式化规则,到文档语言检查工具 alex 的配置细节,并结合 ESLint 配置、Prettier 配置 等仓库源码文件,帮助贡献者准确理解每一项规则的含义与作用范围,从而在提交 PR 前高效通过代码规范检查。
官方命令:pnpm lint 与 pnpm lint-fix
Next.js 仓库使用 ESLint、Prettier 和 alex 三类工具对全部代码和文档进行 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 数组,主要区块包括:
- 全局 ignore 规则:从 .config/eslintignore.mjs 导入,基于
globalIgnores排除node_modules、.next、dist、crates/**、测试 fixture、编译产物(packages/next/src/compiled/**)等; - 基础规则块:作用于
**/*.{js,jsx,mjs,ts,tsx,mts,mdx},用@babel/eslint-parser解析(presets: ['next/babel']、supportsTopLevelAwait: true),并打开reportUnusedDisableDirectives: 'error',即无效的eslint-disable注释本身会报错; - Jest 测试块:对
test/**与**/*.test.ts(x)文件启用plugin:jest/recommended,并针对 Next.js 自研测试运行器扩展了jest/no-standalone-expect,将retry、itSkipDeploy、itCI、itHeaded、itTurbopack等自定义测试块函数识别为合法断言容器; - TypeScript 块:对
.ts/.tsx/.mts文件启用tseslint.configs.recommended与stylistic,并按需关闭一批风格类规则(如no-explicit-any、no-var-requires),同时重写@typescript-eslint/no-unused-vars,允许_前缀的忽略变量; - packages 专属块:对
packages/**/*.ts(x)启用内部插件@next/eslint-plugin-internal的typechecked-require、jsdoc/no-types、jsdoc/no-undefined-types等规则,并对packages/**加强no-shadow与import/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.yaml、test-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-language(alex . --quiet)。
.alexrc 采用 allow 白名单机制,放行了在 Next.js 语境下属于正常技术用法的词汇,例如:
{
"allow": [
"attacks",
"color",
"dead",
"deno",
"dirty",
"execute",
"executed",
"execution",
"failed",
"failure",
"hook",
"hooks",
"invalid",
"simple",
"special",
"white",
...
]
}
可以看到,白名单同时容纳了产品名(deno、fly.io、railway、sst)、技术语义词(color、hooks、failed)以及文档中确实需要的形容词(simple、special、white)。配套还有 .alexignore,将 CODE_OF_CONDUCT.md、examples/、各 LICENSE.md、AGENTS.md 等文件排除在扫描之外——这些文件要么本身就是第三方/模板内容,要么不适合自动审查。
贡献者如果在 PR 中收到 alex 警告,按 Linting 文档的指引,需要按其提示修改对应措辞,而不是直接忽略。
ast-grep:AST 级模式检查的补充层
虽然 Linting 文档主要介绍三个工具,但从 package.json 的 lint 编排可以看到,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 可维护性的同时,把昂贵的类型检查规则限制在了必要的文件范围与执行时机内。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00