十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Next.js 本地 React 同步流程详解:从 build-for-next.sh 到 pnpm sync-react 的集成验证

Next.js 本地 React 同步流程详解:从 build-for-next.sh 到 pnpm sync-react 的集成验证 Next.js 本地 React 同步流程详解从 build-for-next.sh 到 pnpm sync-react 的集成验证【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js本篇技术指南基于 Next.js 仓库内置的 Agent 技能文档 .agents/skills/react-sync/SKILL.md 展开介绍 Next.js 维护者验证本地 React 改动的完整工作流先在 React 仓库构建出 Next.js 消费的 bundle 变体build/oss-stable与build/oss-experimental再通过pnpm sync-react将其同步进本地 Next.js checkout最后运行聚焦测试完成集成验证。读完后你可以独立执行该流程理解sync-react脚本内部的版本改写逻辑、双通道stable/experimental机制以及 Next.js 将 React 打包为 vendored 副本的底层原因。一、适用场景什么时候需要这条流程该技能文档的定位是内部internal: true工作流触发条件在文档 frontmatter 中写得很明确当你在React 仓库中修改了代码且这些改动需要在 Next.js 中验证或者被要求执行buildForNext、pnpm sync-react、将本地 React checkout 与 Next.js 保持同步时使用本流程。它与常规升级 npm 上已发布的 React canary 版本是两条不同的路径。sync-react脚本本身支持多种来源npm canary、vp:///commit-sha、本地 checkout而本技能文档聚焦的正是本地 checkout 场景React 源码尚未发布到 npm必须先本地构建再作为file://依赖注入 Next.js。前提条件以当前仓库为准Next.js 仓库要求 Node.js20.9.0见 package.json 的engines字段包管理器锁定为pnpm10.33.0package.json 的packageManager字段经 Corepack 生效React 仓库必须已按脚本要求完成构建这是文档第 24 行注释的硬性要求见 scripts/sync-react.js。二、第一步构建 Next.js 消费的 bundle 变体文档给出的第一步操作是工作目录必须在 React 仓库内然后以绝对路径调用 Next.js 仓库中的构建脚本cd react-repo bash next-repo/.agents/skills/react-sync/scripts/build-for-next.sh之所以要求传绝对路径 cwd 在 React 仓库是因为脚本内部调用的是 React 仓库自身的node ./scripts/rollup/build.js而脚本文件本身存放在 Next.js 仓库中两处分离。执行完成后React 仓库根目录下会生成两个关键目录build/oss-stable稳定通道RELEASE_CHANNELstable的构建产物build/oss-experimental实验通道RELEASE_CHANNELexperimental的构建产物。脚本内部做了什么查看 .agents/skills/react-sync/scripts/build-for-next.sh 可以确认完整细节。脚本以set -euo pipefail严格模式运行定义了 21 个 bundle 入口bundles( react/index react/jsx react-jsx-runtime.react-server react-jsx-dev-runtime.react-server react/compiler-runtime react.react-server react-dom/index react-dom/client react-dom/profiling react-dom/server react-dom.react-server react-dom-server.browser react-dom-server.bun react-dom-server.edge react-dom-server.node react-dom-server-legacy.browser react-dom-server-legacy.node react-is scheduler react-server-dom-webpack/ react-server-dom-turbopack/ )这个清单值得注意两点其一它覆盖了 App Router 所需的RSC 双端入口*.react-server变体与react-server-dom-webpack/、react-server-dom-turbopack/两个 Flight 打包层这与 Next.js 对 React 的 vendoring 需求一一对应其二构建产物按 8 种 target type 输出typeBUN_DEV,BUN_PROD,NODE_DEV,NODE_PROD,NODE_PROFILING,ESM_DEV,ESM_PROD,NODE_ES2015随后脚本分两次调用 React 的 rollup 构建RELEASE_CHANNELstable node ./scripts/rollup/build.js ${bundles[]} --type$type mv ./build/node_modules ./build/oss-stable RELEASE_CHANNELexperimental node ./scripts/rollup/build.js ${bundles[]} --type$type --unsafe-partial mv ./build/node_modules ./build/oss-experimental稳定通道正常构建后把 rollup 默认输出目录build/node_modules重命名为build/oss-stable实验通道额外带--unsafe-partial参数用于构建未完成的 partial 变更重命名为build/oss-experimental。为什么是这两个目录名这不是随意的约定。在 scripts/sync-react.js 中当--version传入file://路径时脚本会精确地把稳定与实验通道指向这两个子目录if (version ! null version.startsWith(file://)) { experimentalNewVersionStr new URL(build/oss-experimental/, version).href newVersionStr new URL(build/oss-stable/, version).href }因此第一步的产物结构必须与第二步的解析逻辑严格对齐目录名不能改动。三、第二步将构建产物同步进 Next.js文档给出的同步命令带有PATH前缀必须在 Next.js 仓库根目录执行cd next-repo PATH$(dirname $(command -v corepack)):$PATH \ pnpm sync-react --version react-reposync-react是仓库根 package.json 中注册的 npm script实际执行node ./scripts/sync-react.js。传入的react-repo是本地 React checkout 的绝对路径。PATH 前缀的作用一个具体的兼容性陷阱文档花了一整段解释为什么要加这个PATH前缀这是本技能文档中最有实战价值的细节。原因是特定 Agent 环境Codex存在PATH优先级缺陷Codex 会向PATH中注入一个自带的pnpm可执行文件且它排在用户 Corepack shim 之前命令行查找发生在 Corepack 读取仓库packageManager字段之前因此裸调用pnpm可能命中 Codex 自带的版本而不是仓库锁定的pnpm10.33.0命令前缀PATH$(dirname $(command -v corepack)):$PATH把激活 Corepack shim 的目录置于PATH最前面使命令查找重新经由 Corepack 解析到仓库锁定的版本。文档还特别指出只在外层命令前加corepack pnpm是不够的因为sync-react脚本内部还会嵌套调用pnpm install见下文子进程依然从PATH中解析pnpm。只有修改PATH本身才能同时覆盖外层命令与嵌套调用。sync-react.js 内部执行了什么理解脚本源码后本地同步的完整动作链是版本字符串归一化scripts/sync-react.js--version以/开头的路径会被pathToFileURL转换为file://URL并补上结尾斜杠使其被当作目录处理。双通道版本改写脚本先同步experimental通道更新react-experimental-builtin、react-dom-experimental-builtin、scheduler-experimental-builtin、react-server-dom-webpack-experimental、react-server-dom-turbopack-experimental等 devDependencies再同步稳定通道。稳定通道的改写范围更大除了react-builtin、react-dom-builtin、scheduler-builtin、react-is-builtin外还会更新根package.json中pnpm.overrides下的react、react-dom、scheduler、react-isscripts/sync-react.js。当前仓库 package.json 的 overrides 中可以看到这种npm:reactcanary形态的锁定结果。本地file://场景下getSchedulerVersion不做 npm registry 查询scheduler 直接复用同一本地路径scripts/sync-react.jsnpm 版本场景下则从 registry 拉取react-dommanifest 中的scheduler依赖版本。Pages Router 的 peer 版本联动Next.js 同时服务 App Router使用内置 React与 Pages Router使用用户自装的 React因此脚本会更新三处引用const nextjsReactPeerVersion ...的文件——run-tests.js、packages/create-next-app/templates/index.ts 与 test/lib/next-modes/base.ts当前值均为19.2.8并同步更新packages/next/package.json、packages/third-parties/package.json的peerDependencies范围形如^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || active见 scripts/sync-react.js。安装依赖并重建 vendored 文件pnpm install --no-frozen-lockfile因为版本刚被改写lockfile 必然变化随后在packages/next目录内执行pnpm ncc-compiled即taskr ncc见 packages/next/package.json重新编译 vendored React 产物scripts/sync-react.js。若只想改package.json不自动安装可用pnpm run sync-react --no-install随后手动执行pnpm install与pnpm ncc-compiled脚本头部注释scripts/sync-react.js。补充说明脚本还支持--create-pull、--commit、--actor等选项scripts/sync-react.js用于 CI 场景自动创建带签名 commit 的 Pull Request本地开发验证不需要这些选项。四、为什么同步后要重建React 的 vendoring 机制pnpm ncc-compiled这一步不是形式流程而是由 Next.js 的架构决定的App Router 不通过node_modules解析 React而是在pnpm build阶段把 React 复制进packages/next/src/compiled/。这一过程由 packages/next/taskfile.js 中的copy_vendor_react()任务完成它分 stable/experimental 两次执行分别产出compiled/react/与compiled/react-experimental/两条通道。从 taskfile 源码可以看到两个关键动作packages/next/taskfile.js包名改写把 vendored 包的name追加-builtin/-experimental-builtin后缀避免 React 内部 Haste module map 与node_modules中的同名包冲突require 别名重写源码中require(react)、require(react-dom)、require(scheduler)被静态替换为require(next/dist/compiled/react...)等指向编译产物的路径。运行时 webpack 配置再通过makeAppAliases({ experimental })将别名指向对应通道。而 Pages Router 仍然从node_modules正常解析 React。这一机制也正是sync-react同时维护两条 devDependency 通道与两组pnpm.overrides的原因App Router 吃 vendored 副本由*-builtin包驱动Pages Router 的 peer 声明则指向 npm 上的版本范围。五、第三步检查同步结果并运行聚焦测试文档第三步的要求是先检查同步结果确认两个 checkout 中的无关改动都被保留本地同步会触及package.json、lockfile、peer 声明文件与dist/compiled产物diff 面较大按需重建 Next.jsvendored React 变化通常意味着需要重新构建运行与改动行为匹配的聚焦测试命令而不是全量测试。结合仓库实际可用的测试入口常见的聚焦方式包括# 单元测试不涉及浏览器启动最快 pnpm test-unit # dev/start 模式的 e2e 测试按 bundler 区分 pnpm test-dev-webpack pnpm test-start-webpack pnpm test-dev-turbo # 指定测试文件run-jest.sh 透传 jest 参数 pnpm test-dev-webpack -- --testPathPattern app/server-components另有一个测试侧的版本控制点测试基础设施中通过NEXT_TEST_REACT_VERSION环境变量可覆盖被测 React 版本run-tests.js、test/lib/next-modes/base.ts排查vendored 版本 vs 用户安装版本差异时有用。六、延伸与 react-vendoring 技能的边界SKILL.md 末尾将 react-vendoring 列为关联技能两者分工明确react-sync负责把新 React 放进 Next.jsreact-vendoring负责放进去之后vendored 副本的类型边界与 React Server 层约束。后者规定了若干同步后改动时容易踩坑的边界例如所有对react-server-dom-webpack/*Flight server/static API的导入必须经由entry-base.ts其他文件需通过ComponentMod参数访问新增 Node-only API如renderToPipeableStream需要在packages/next/types/$$compiled.internal.d.ts补类型声明Turbopack 会在运行时把react-server-dom-webpack/*静默重映射为react-server-dom-turbopack/*调试时堆栈中出现的 turbopack 变体属于正常现象。七、流程速查步骤工作目录命令产物1. 构建 React 变体React checkoutbash next-repo/.agents/skills/react-sync/scripts/build-for-next.shbuild/oss-stable、build/oss-experimental2. 同步进 Next.jsNext.js checkoutPATH$(dirname $(command -v corepack)):$PATH pnpm sync-react --version react-repo双通道 devDependencies、pnpm.overrides、lockfile、vendored 副本3. 验证Next.js checkout检查 diff → 按需重建 → 聚焦测试如pnpm test-dev-webpack集成验证结论整体来看这条流程是 Next.js 与 React 两个仓库协同开发的关键基础设施build-for-next.sh定义了Next.js 需要哪些 bundle的消费契约sync-react把本地 React 构建以file://依赖形式注入双通道并联动 peer 声明而 vendored 机制保证了 App Router 始终运行在与测试环境一致的 React 副本上。理解这条链路是排查 RSC、Flight 协议与 React 升级相关问题的必要前提。【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表