Next.js 仓库贡献指南:本地开发、构建、测试与发布的全流程实践
本文以 Next.js 官方仓库的贡献指南 contributing.md 为主体,系统梳理向 Next.js 提交代码的完整流程:从选取合适的 issue、配置签名提交,到克隆仓库、pnpm dev 本地开发、pnpm build 构建、pnpm test-* 运行测试,再到 pack-next 在真实应用中验证改动、补充错误链接与 Lint 规范,以及 stable / canary 两条发布通道的工作方式。读完后,你可以独立完成一个 Next.js PR 从环境搭建到提交合并前自查的全过程。
一、贡献的入口与原则
contributing.md 开篇给出两条基本原则:
- 提交 PR 前,先检索已有的 PR 和 issue,确认没有已打开或已关闭的相关条目;
- 从 issue 追踪器入手,
good first issue(适合新手)和Documentation(文档)标签是很好的切入点,但任何打开状态的 issue 都可以参与。
指南明确:处理 issue 无需事先获得许可;但对新特性(feature),必须先在 Discussions 的 ideas 类别中发起提案并被接受后才能动手实现。
1.1 为什么新特性要先走讨论
贡献新特性 一文档解释了讨论机制的目标:
- 验证需求有效性:社区可以投票,高票需求更可能被考虑;
- 理解后续影响:任何加入 Next.js 的特性都会长期存在并且必须被维护,新特性要覆盖多种用例、考虑对生态的影响;
- 理解现状的历史原因:某个特性缺失、或现有实现是某种形态,往往有历史理由。Next.js 对不破坏已有特性(不 breaking)有强策略,新特性必须以支持渐进式采用的方式引入。
Next.js 团队自身也使用 RFC(Request For Comment)流程,可以作为高质量特性提案的参考范例。
1.2 琐碎改动(Trivial changes)会被拒绝
每一个 PR 都需要维护者审查。指南直言不讳地指出:审查者的注意力是该项目最稀缺的资源,因此像拼写修正、纯格式化、代码风格调整这类琐碎 PR 很可能被直接关闭。相对地,docs/ 目录下的文档类小改动更受欢迎,可以优先从文档贡献入手。
1.3 提交必须签名(Signed commits)
该仓库在受保护分支上要求经过 GitHub 验证的提交签名。贡献前需要:
- 配置 Git,使用 GitHub 已验证的 GPG、SSH 或 S/MIME 密钥对提交签名;
- 未签名的提交会被仓库规则拒绝,必须重签后才能合并;
- 若 PR 中混入未签名提交,需要重新签名并 force-push 分支,并确认密钥已添加到 GitHub 账户、提交显示为
Verified; - 特别注意:commit message 里的
Signed-off-by行不能替代上述签名要求。
二、本地开发环境搭建
开发流程细节见 developing.md。几个关键前提(以仓库实际配置为准):
- 开发分支是
canary,所有 PR 都应面向canary分支提交,canary分支的变更会定期发布到 npm 的@canary标签; - Node.js 版本由 .node-version 固定为
v20,建议使用 fnm 或 nvm 按该文件选择版本,与 CI 保持一致; - 包管理器由 package.json 的
packageManager字段声明为pnpm@10.33.0。pnpm 默认遵循该字段,可保证本地与 CI 行为一致。
2.1 JavaScript 依赖
corepack enable pnpm
# 或
npm install -g pnpm@latest
可选安装 fnm/nvm(对齐 CI 的 Node 版本)以及 GitHub CLI(后续 gh pr create 会自动 fork 并创建远程分支)。
2.2 Rust 依赖(仅当要改 Rust 代码时)
# Linux:安装 C 编译器
sudo apt install build-essential
# macOS:安装 Xcode 命令行工具
xcode-select --install
通过 rustup 安装 Rust 与 Cargo。如果只改 JS/TS,可以跳过这一步。
2.3 克隆、开发、提交的标准工作流
# 1. blobless clone 加速克隆(https 或 ssh 方式均可,加 --single-branch)
git clone https://github.com/vercel/next.js.git --filter=blob:none --single-branch
# 2. 基于默认分支 canary 创建开发分支
git switch --create MY_BRANCH_NAME
# 3. 安装依赖
pnpm install
# 4. 用 Turborepo 启动开发并监听 JS 代码变更(也可改用 next build 按需构建)
pnpm dev
# 5. 改了 Rust(如 Turbopack)时重建 napi 绑定
pnpm swc-build-native # 调试构建
pnpm swc-build-native --release # release 模式(基准测试用)
pnpm build-all # 同时构建 JS 与 Rust 变更
# 6. 新终端中编译类型声明文件(类型过期后需重复执行)
pnpm types
# 7. 提交
git add .
git commit -m "DESCRIBE_YOUR_CHANGES_HERE"
# 8. 创建 PR
gh pr create
从源码脚本看,这些命令背后的实现分别是:swc-build-native 对应 build-native 脚本(经由 tsx 执行),sweep 对应 sweep 脚本,pack-next / unpack-next / patch-next 分别对应 pack-next.ts、unpack-next.ts 与 patch-next.ts。
2.4 两个容易踩坑的细节
NODE_ENV 与 __NEXT_DEV_SERVER 的区别:next dev 和 next build --debug-prerender 产出的 bundle 都是 NODE_ENV=development。源码中判断"开发 bundle 中保留、生产 bundle 中剔除"的代码应使用 process.env.NODE_ENV !== 'production'(构建期检查);而"只在 dev server 运行时执行、在 next build --debug-prerender 或 next start 时不执行"的代码应使用 process.env.__NEXT_DEV_SERVER。
清理磁盘:Rust 构建产物增长很快,可用 pnpm sweep 清理旧产物,同时会清理 pnpm store、cargo 等缓存并执行 git gc。
三、构建体系:pnpm build 到底做了什么
构建文档见 building.md。一条 pnpm build 会构建 Next.js 本体、全部类型定义和各个包。从 package.json 的脚本定义看,它实际执行 turbo run build --remote-cache-timeout 60 --summarize true,借助 Turborepo 并行调度。构建过程由三个主要任务组成:
- 用 SWC 编译 TypeScript 源码:默认安装并使用最新 canary 的
next-swc二进制来编译 TS 源码,产物落在packages/next/dist/...,包含编译后的 JS 文件和 source map; - 用 Webpack 打包:基于上一步的编译产物进行打包,配置位于 next-runtime.webpack-config.js;
- 生成类型定义:使用 TypeScript 的
tsc编译器,可单独执行pnpm types。实际使用 tsconfig.build.json,它继承基础 tsconfig.json 但排除了测试文件等不需要的类型定义。
指南还提示:Next.js 使用 taskr 来并行化构建任务,任务定义位于 taskfile.js,每个任务名对应其中要执行的函数名,例如 taskr release 会执行 release() 函数。
3.1 处理 Turbopack / WASM / 其他 Rust 代码
- 正在开发 Rust 代码、或想测试尚未发布为 canary 的最新 Rust 代码时,安装 Rust 后运行
pnpm swc-build-native; - 本地测试 wasm 构建:安装 wasm-pack 后,运行
pnpm --filter=@next/swc build-wasm --target <wasm_target>构建,再用node ./scripts/setup-wasm.mjs拷贝进node_modules,最后用NODE_OPTIONS='--no-addons'启动 next 强制走 wasm 二进制; - 需要清理项目时使用
pnpm clean(对应lerna clean+ 各包 clean 任务 + 删除dist)。
四、测试体系:隔离、分层与调试
测试文档见 testing.md。运行测试前必须先构建项目(pnpm build)。官方推荐使用"目录模式"指定测试范围,例如:
# 生产模式(next build + next start)跑 app-dir/app 测试套件
pnpm test-start test/e2e/app-dir/app/
# 开发模式(next dev)
pnpm test-dev test/e2e/app-dir/app/
# 调试单个测试时换成 testonly-start,可见浏览器窗口(test-start 是 headless 后台运行)
pnpm testonly-start test/e2e/app-dir/app/
从 package.json 脚本定义可印证各命令的差异:test-start / test-dev / test-dev-turbo 都经由 run-jest.sh 启动,只是 --mode(start/dev)与 --bundler(webpack/turbo)参数不同;testonly-* 系列不带 --headless,因此能弹出浏览器窗口方便调试。
4.1 e2e 测试的隔离机制
test/e2e、test/production、test/development 下的测试与仓库完全隔离运行:运行时会在系统临时目录(如 /tmp)中创建一个本地版本的 Next.js,并链接到一个隔离的应用副本;随后在随机端口启动服务器执行测试,结束后销毁服务器并删除临时目录。这套逻辑由 nextTestSetup 自动处理。
4.2 测试类型与编写规范
- 用
pnpm new-test从对应测试类型模板创建新测试(turbo gen test); - 测试分层:
e2e(跑next dev、next start及 Vercel 部署)、development(只跑next dev)、production(只跑next start)、integration(历史位置,尽量不再新增,因为不隔离)、unit(极快、无需浏览器或 next 进程、只测某个具体工具函数); - e2e / development / production 测试都应使用
nextTestSetup,它会创建隔离的 Next.js 安装,防止测试意外依赖 monorepo 内部状态; - 新测试套件一律用 TypeScript(
.ts,unit 测试可用.tsx); - 最佳实践:条件断言可能耗时等待时,用浏览器
waitForElement或next-test-utils的check工具等待;应用修复时,先确认"没有该修复时测试会失败",以确保回归捕获能力; - 若已有与待测功能紧密相关的测试套件(如 hash 导航应并入现有导航测试),把新检查加进既有套件而不是新开。
4.3 调试用的环境变量
| 变量 | 作用 |
|---|---|
NEXT_TEST_SKIP_CLEANUP=1 |
保留测试临时目录,可进入其中运行 pnpm debug 调试完整搭好的测试项目 |
NEXT_SKIP_ISOLATE=1 |
跳过隔离安装,直接在 Next.js 仓库内运行(部分测试不兼容,但可加速本地调试) |
NEXT_TEST_MODE |
手动指定 e2e 测试模式(不使用 test-dev / test-start 时) |
NEXT_TEST_DEPLOY_URL |
配合 pnpm test-deploy 跳过 Vercel 部署步骤,直接对已有部署 URL 跑断言 |
NEXT_TEST_PREFER_OFFLINE=1 |
测试安装依赖时给包管理器加 --prefer-offline,适合弱网环境 |
其他调试手段:
- CI 失败时会尝试捕获 Playwright trace,下载解压后可用
pnpm playwright show-trace ./path/to/trace查看; - 给 next 挂 Chrome 调试器:在
nextTestSetup中传startArgs: ['--inspect']; - 调试测试进程本身:给运行 jest 的 node 进程加
--inspect,例如IS_TURBOPACK_TEST=1 TURBOPACK_DEV=1 NEXT_TEST_MODE=dev node --inspect node_modules/jest/bin/jest.js ...; - 测试性能剖析:加
NEXT_TEST_TRACE=1启用 profiling。
4.4 用 Turbopack 跑测试 / 双打包器对比
# 用 -turbo 系列脚本走 Turbopack
pnpm test-dev-turbo test/e2e/app-dir/app/
# 一次测试同时跑 Turbopack 与 Webpack:用 Jest 的 --projects 指定两份 jest 配置
pnpm test-dev test/e2e/app-dir/app/ --projects jest.config.*
这与仓库根目录同时存在 jest.config.js 和 jest.config.turbopack.js 的结构相对应。
4.5 Deploy 测试
Deploy 测试验证 Next.js 部署到 Vercel 后的行为,属于 e2e 套件的一部分,针对真实 Vercel 部署运行。它们自动在 canary 上触发;PR 中则只在修改或新建了对应测试文件时触发,也可以通过 Actions UI 手动指向自己的分支触发(需要自定义 tarball)。
本地对指定 commit 跑 deploy 测试:
NEXT_TEST_VERSION=https://vercel-packages.vercel.app/next/commits/<commitSha>/next pnpm test-deploy <path-to-test>
例如对 commit abc123 测试 test/e2e/app-dir/actions/:
NEXT_TEST_VERSION=https://vercel-packages.vercel.app/next/commits/abc123/next pnpm test-deploy test/e2e/app-dir/actions/
该命令会下载指定 commit 预构建的 Next.js tarball 并对它跑 deploy 测试。已有部署 URL 时可用 NEXT_TEST_DEPLOY_URL 跳过部署步骤。
五、在自己的应用中验证本地 Next.js
这是贡献过程中最实用的部分,文档分散在 developing.md 与 developing-using-local-app.md。
由于 Turbopack 不支持指向工作区目录之外的符号链接,pnpm link 和 file: 直接导入往往不够用。官方推荐的替代方案是:把本地 Next.js 打成 tarball,再通过 pnpm overrides 注入到测试应用。
# 生成 tarball 并注入测试项目
pnpm pack-next --tar && pnpm unpack-next path/to/project
# 跳过 monorepo 内部的 pnpm i 和 pnpm build(例如正在跑 pnpm dev 时)
pnpm pack-next --no-js-build --tar && pnpm unpack-next path/to/project
# 在项目目录内生成带可部署相对 file: 引用的 tarball
pnpm pack-next --project path/to/project --deployable-tar
# 不走 tarball 直接打补丁(前提:已添加 pack-next 生成的 overrides)
pnpm patch-next path/to/project
pnpm patch-next --no-js-build path/to/project
pnpm pack-next 还接受 -- 分隔符向 napi CLI 透传参数(如 pnpm pack-next --project ~/my-project/ -- --release),--project 会自动定位并修改父级 workspace 的 package.json,对 npm 和 pnpm 有效,bun 与 yarn 存在已知问题。在 Linux 上,为避免超过 pnpm 已知有问题的 2 GiB 上限,生成的是 stripped 的 @next/swc 二进制;可用 --compress objcopy-zstd 覆盖(更慢但保留 debuginfo)。tarball 写入仓库根目录的 tarballs 目录。
5.1 在 monorepo 内部开发
- 无依赖的小应用:直接在 monorepo 里建一个目录(如
dev-app),运行pnpm next ./dev-app即可,甚至不需要package.json; - 有依赖的既有应用:把应用挪进 monorepo,然后运行
pnpm next-with-deps ./app-path-in-monorepo; - 也可以让
pnpm dev常驻另一个终端,边改 Next.js 边测试(部分改动需要重新执行pnpm next-with-deps或pnpm next)。
5.2 作为本地依赖引入
-
在 monorepo 中后台运行
pnpm dev; -
在应用根目录执行(把
next、react、react-dom全部指向 monorepo 版本):pnpm add ./path/to/next.js/{packages/next,node_modules/{react,react-dom}} -
正常启动应用。此时 Next.js 来自本地编译产物而非 npm registry。
排障:若 pnpm dev 时报 Failed to load SWC binary,在应用 package.json 中加入对应平台的 SWC 二进制依赖(canary 版本)再试:
{
"optionalDependencies": {
"@next/swc-linux-x64-gnu": "canary",
"@next/swc-win32-x64-msvc": "canary",
"@next/swc-darwin-x64": "canary",
"@next/swc-darwin-arm64": "canary"
}
}
六、文档与错误链接
-
文档贡献:Next.js 的文档位于仓库
docs/目录(如 docs/01-app、docs/02-pages),文档贡献说明 指向官方 Docs Contribution Guide。琐碎的文字勘误类改动在这一目录是被明确欢迎的贡献入口; -
添加错误链接:adding-error-links.md 说明了 Next.js 的"短消息 + 详细文档"机制——控制台里的警告/错误保持简短,把完整说明和解决步骤放到文档。新增警告或错误时应附带这种链接,步骤为:
- 运行
pnpm new-error,自动创建错误文档并更新 manifest; - 命令结束时会给出生成的错误 URL,把它加到对应的错误抛出处。
- 运行
仓库 errors/ 目录中可以看到大量这类错误文档(如 swc-disabled.mdx、invalid-redirect-gssp.mdx 等),与该机制一一对应。
七、代码风格:Lint 与格式化
规范见 linting.md。仓库使用 ESLint、Prettier 与 alex(检查包容性用语)对代码与文档统一检查:
pnpm lint # 全量检查
pnpm lint-fix # 自动修复 ESLint 与 Prettier 问题(部分规则需手工修改)
从 package.json 的 lint 脚本可以看出它的覆盖面:类型检查(test-types、lint-typescript)、Prettier 校验、ESLint、ast-grep 规则、语言检查(alex)以及 check-unused-turbo-tasks。相关配置:
- ESLint 规则:eslint.config.mjs(另有 eslint.cli.config.mjs 用于 CLI);
- Prettier 格式:.prettierrc.json;
- alex 用语检查:.alexrc。
指南建议在 VS Code 中安装对应插件实现保存即检查/格式化,并对 alex 的告警按提示修改措辞。
八、Issue 分诊(Triaging)
面向维护者的分诊流程见 triaging.md,理解它对贡献者同样重要,因为它决定了你的 issue/PR 会怎样被处理:
- Issue 会被打上一级标签之一:
bug(Next.js 本身的缺陷)、documentation(文档问题)、examples(examples/ 目录中示例的问题); - 自动化分诊:bug report 若缺少有效复现,会被自动关闭并附上引导评论和
invalid link标签;填写模板中"Which area(s) are affected?"会自动加对应标签; - 人工分诊标签(各附引导评论):
please add a complete reproduction:复现不足,2 天无响应自动关闭;please verify canary:未用next@canary验证,canary 相当于公开 beta,14 天无响应自动关闭;please simplify reproduction:复现过于复杂,14 天无响应自动关闭;good first issue:标记为新手友好;resolved:假定新版已修复,14 天内可按引导请求重开;
- 已验证的 issue 会获得
linear: next/linear: turbopack/linear: docs标签并进入维护者跟踪,确认过的 issue 不会因久未活动而过期关闭; - 关闭后的处理:未锁定 issue 被维护者关闭时会附评论,说明 14 天内如何请求重开(需包含
Reopen:前缀与理由);所有已关闭的 PR/Issue 在 2 周无活动后会被锁定,重开窗口结束后应开新 issue 并引用旧 issue。
九、发布通道:stable 与 canary
发布流程见 release-channels-publishing.md:
- stable:
npm install next装到的就是它,遵循语义化版本,按固定节奏发布。维护者用pnpm publish-stable发布,命令会询问发布major、minor还是patch; - canary:需显式
npm install next@canary安装。基于canary分支提前发布,承载所有等待进入 stable 的变更,用于在真实应用中试跑最新特性与修复。维护者用pnpm publish-canary发布,版本号自动在上一版本上递增。
从源码结构看,canary 既是开发分支又是 npm 标签,这也解释了前文"所有 PR 面向 canary"与测试体系中 canary 版本验证要求(please verify canary)之间的关联。
十、PR 描述与 Turbopack 追踪
-
PR 描述:pull-request-descriptions.md 要求使用仓库默认 PR 模板,写清 PR 目的并链接相关 issue;
-
Turbopack 追踪:tracing.md 介绍
turbo-tasks的 tracing 能力,可在 Next.js 中用NEXT_TURBOPACK_TRACING环境变量开启:1或overview:基础用户级追踪(官方发布的 Next.js 只内置这一档,即 info 级);next:在其上叠加 Next.js 自有 crate 的debug/trace日志;turbopack:再叠加 Turbopack 自有 crate 的debug/trace日志;turbo-tasks:进一步追踪每一个 Turbo-Engine 函数执行;- 也支持
tracing_subscriber::filter::EnvFilter的任意 directives 语法。
开启后 Next.js 会写出二进制的
.next-profiles/trace-turbopack.bin文件,可用 turbo-trace-viewer 可视化(trace server 连接 57475 端口的 WebSocket)。启动方式:cargo run --bin turbo-trace-server --release -- /path/to/your/trace-turbopack.bin # 或 pnpm next internal trace .next-profiles/trace-turbopack.bin追踪查看器提供聚合/单个 span、按发生顺序/按数值排序、bottom-up(self 值)等视图,以及 Duration、分配/释放/常驻内存等度量模式。注意:更详细的追踪档位需要自建 Next.js(见第二章的
pnpm dev/pnpm swc-build-native流程);trace server 务必用--release构建,性能差距可达 10 倍。
小结:一张快速参考表
| 场景 | 命令 |
|---|---|
| 克隆仓库 | git clone --filter=blob:none --single-branch |
| 安装依赖 | pnpm install |
| 监听式开发 | pnpm dev |
| 构建全部(JS + 类型) | pnpm build / 单独类型 pnpm types |
| 构建 Rust 绑定 | pnpm swc-build-native [--release] / pnpm build-all |
| 跑测试(prod / dev / 调试 / turbo) | pnpm test-start / pnpm test-dev / pnpm testonly-start / pnpm test-dev-turbo(后接测试目录) |
| 新建测试 / 新建错误文档 | pnpm new-test / pnpm new-error |
| 本地验证到真实应用 | pnpm pack-next --tar && pnpm unpack-next <proj> 或 pnpm patch-next <proj> |
| 在 monorepo 内跑应用 | pnpm next ./dev-app / pnpm next-with-deps ./app |
| Lint 与自动修复 | pnpm lint / pnpm lint-fix |
| 清理磁盘 | pnpm sweep / pnpm clean |
整体来看,Next.js 的贡献体系可以概括为:canary 分支 + pnpm/Turborepo 工作流 + 隔离式 e2e 测试 + tarball 注入验证 + 签名提交 的组合。遵循 contributing.md 及其子文档(repository/、core/、turbopack/、docs/)给出的边界——先讨论后实现、避免琐碎 PR、提交必须签名——就能与 CI 和维护者的审查流程顺畅对齐。
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