Nx 工作区 Vite 7 升级到 Vite 8 完整迁移指南:Rolldown 替换 Rollup 的配置改造与排障实战
Nx 工作区 Vite 7 升级到 Vite 8 完整迁移指南Rolldown 替换 Rollup 的配置改造与排障实战【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx本文基于 Nx 仓库中面向 LLM/Agent 执行的 Vite 8 迁移指令ai-instructions-for-vite-8.md编写并对照仓库内实际迁移代码与测试进行源码级印证。本文面向正在 Nx 单仓monorepo中从 Vite 7 升级到 Vite 8 的开发者读完你将掌握rollupOptions到rolldownOptions的自动与手工改造、vitejs/plugin-reactv6Oxc 取代 Babel的适配、Angular Vitest 的oxc-project/runtime依赖补齐、moduleResolution类型解析修复以及一整套可落地的迁移后验证与回退固定 Vite 7策略。迁移背景Vite 8 用 Rolldown 取代 RollupVite 8 最核心的底层变化是把生产构建器从 Rollup 换成 Rolldown。Rolldown 用 Rust 重写了打包核心API 层面尽力兼容 Rollup但并非逐字节等价同一个输入下Rolldown 生成的 chunk 数量、module 划分与 Rollup 存在差异少数选项的语义也发生了变化例如output.manualChunks在 Rolldown 中只接受函数形式对象形式的 glob 映射不再合法。同时 Vite 8 更新了一批插件 API其中对多数 React 项目影响最大的是vitejs/plugin-reactv6 弃用 Babel。在 Nx 仓库中这一升级被登记为 Nx 23.0.0 的迁移。查看 packages/vite/migrations.json 可以看到23.0.0版本条目的包更新规则要求vite 7.0.0 8.0.0时触发将vite升级到^8.0.0同时把vitejs/plugin-react升级到^6.0.0与remix-run/dev标记为incompatibleWith即该场景下需人工确认兼容性。配套的迁移产物包括两个迁移rename-rollup-options-to-rolldown-options自动执行的重命名 codemod和create-ai-instructions-for-vite-8把本文所述的迁移指令注入到迁移流程供 LLM/Agent 按步骤执行两者都要求vite 8.0.0才触发。仓库内nx/vite的 peerDependencies 同时声明支持^5 || ^6 || ^7 || ^8见 packages/vite/package.json说明插件层对多版本是兼容的破坏面主要集中在用户侧配置。迁移前清单动手之前先完成三件事盘点所有使用 Vite 的项目用 Nx 的可发现性命令列出带build、serve目标的项目nx show projects --with-target build nx show projects --with-target serve定位所有 Vite 配置文件全局搜索vite.config.{ts,js,mts,mjs,cts,cjs}检查project.json中是否内联了与 Vite 相关的选项。提示Nx 的nx/vite/plugin正是通过**/vite.config.{js,ts,mjs,mts,cjs,cts}这个 glob 自动推断项目的见 packages/vite/src/plugins/plugin.ts因此配置文件名必须符合该模式才会被识别。检查 Cypress 组件测试版本Cypress 15.14.0 才支持 Vite 8。nx migrate会自动把 Cypress 升到该版本如果你在package.json里显式把 Cypress 钉在了 15.14.0 以下需要在升级 Vite 之前先解除钉版。迁移步骤分类1. 把rollupOptions重命名为rolldownOptionsnx migrate提供的 codemod 会自动处理vite.config.{ts,js,mts,mjs,cts,cjs}文件中的重命名。如果你在其他地方比如配置文件导入的 helper 模块、共享配置构建函数也声明了rollupOptions需要手工重命名。搜索模式在任意 TypeScript/JavaScript 文件中搜索rollupOptions。// ❌ BEFORE (Vite 7) export default defineConfig({ build: { rollupOptions: { external: [react], output: { manualChunks: { vendor: [react, react-dom] } }, }, }, }); // ✅ AFTER (Vite 8) export default defineConfig({ build: { rolldownOptions: { external: [react], output: { manualChunks: { vendor: [react, react-dom] } }, }, }, });codemod 的源码实现与边界想理解自动迁移能做到什么程度值得读一读 rename-rollup-options-to-rolldown-options.ts它使用phenomnomnominal/tsquery的 AST 选择器PropertyAssignment :matches(Identifier[namerollupOptions], StringLiteral[valuerollupOptions])匹配属性键因此既能命中裸键形式rollupOptions:也能命中 JSON 风格带引号的rollupOptions:形式对带引号的键会保留原有引号风格。只处理匹配**/vite.*config*.{js,ts,mjs,mts,cjs,cts}的文件且对不含rollupOptions的文件直接跳过多次运行是幂等的已有rolldownOptions不会重复改写。这些行为都有对应的单元测试覆盖见 rename-rollup-options-to-rolldown-options.spec.ts。重命名同时覆盖顶层build和嵌套的environments.env.build两种位置对应environmentsAPI 下的 client/ssr 等多环境构建示例见 rename-rollup-options-to-rolldown-options.md。Action Items验证 codemod 覆盖了每个配置文件在 vite 配置内rg rollupOptions应返回零命中手工重命名 helper 模块或共享配置构建器中的rollupOptions更新解析build.rollupOptions的 CI 脚本例如自定义的 bundle 体积断言注意Vite 8 仍把rollupOptions当作已废弃别名接受会复制值到rolldownOptions并打印弃用警告但在同一层级同时混用两者可能引发优先级歧义——rolldownOptions优先。2.vitejs/plugin-reactv6Oxc 取代 BabelVite 8 要求vitejs/plugin-react^6该版本用 Oxc 替代 Babel 完成 JSX 转换插件的babel选项已被移除。// ❌ BEFORE (Vite 7, plugin-react v4) import react from vitejs/plugin-react; export default defineConfig({ plugins: [ react({ babel: { plugins: [babel-plugin-styled-components], }, }), ], }); // ✅ AFTER (Vite 8, plugin-react v6) import react from vitejs/plugin-react; export default defineConfig({ plugins: [react()], });Action Items移除react()调用中的babel选项如果你依赖某个 Babel 插件如 styled-components、emotion、relay寻找 Oxc 兼容的替代品或改用vitejs/plugin-react-swc同样不依赖 Babel。任意 Babel 插件并没有现成的即插即用替代方案如果无法放弃 Babel 插件暂时留在 Vite 7 plugin-react v4见下文「项目级 Vite 7 固定」运行pnpm install或你所用包管理器等价命令让新版本 plugin-react 完成解析从仓库线索看Nx 的 Vite 配置生成器在生成 React 项目配置时会根据场景在vitejs/plugin-react与vitejs/plugin-react-swc之间做选择见 packages/vite/src/generators/configuration/configuration.ts这印证了 SWC 路径是官方认可的无 Babel 替代方向。3. Angular Vitestvitest-analog 路径补充oxc-project/runtime如果 Angular 项目的test目标用的是nx/vitest:test即由analogjs/vite-plugin-angular接线的 vitest-analog 配置需要在工作区根devDependencies中显式声明oxc-project/runtime。为什么会缺这个依赖analogjs/vite-plugin-angular注册的angularVitestPlugin仅在测试模式下激活的transform钩子会匹配包含async的angular/*fesm2022模块以及任意angular/cdk文件并调用vite.transformWithOxc(code, id, { target: es2016, … })。这个故意降级是为了配合 Zone.jsfakeAsync等工具依赖对 promise 调度的 monkey-patch无法拦截原生async/await所以插件把它们降级成 Zone.js 可以拦截的形式。在target: es2016下oxc 把辅助函数作为外部oxc-project/runtime/helpers/*导入发出oxc 默认HelperMode Runtime。而上游链路analogjs、angular/core、vite、rolldown都没有以消费方工作区可解析的方式声明oxc-project/runtime因此vite:import-analysis在缺少该依赖时无法解析这些导入。不需要此依赖的情况test目标使用angular/build:unit-test或nx/angular:unit-testvitest-angular 路径的 Angular 项目——该路径不加载analogjs/vite-plugin-angular设置了optimizeDeps.noDiscovery: true并使用内存中的测试提供器因此产生 helper 导入的降级转换根本不会运行。搜索模式test.executor为nx/vitest:test且vite.config.*中引入analogjs/vite-plugin-angular的项目。rg nx/vitest:test --type json rg analogjs/vite-plugin-angular --type ts --type jsAction Items对每个受影响的工作区把oxc-project/runtime加到根devDependenciesNx 的 Angular 生成器在 vitest-analog 路径上会自动添加请检查早于该行为的老工作区运行pnpm install或等价命令运行项目测试确认 helper 能解析背景知识nx/vite:test执行器与nx/vite包中的 vitest 能力已在 Nx 23 移除vitest 支持统一由nx/vitest提供Nx 23 的自动迁移会兜底完成nx/vite:test→nx/vitest:test的切换详见 ensure-vitest-package-migration.md。4.moduleResolution: node下的类型解析Vite 8 的类型只通过条件exports提供删除了 Vite 7 时代顶层types字段TypeScript 在moduleResolution: node下无法解析典型症状是defineConfig、UserConfig或插件返回类型报错。Action Items更新受影响的tsconfig*.jsonmoduleResolution: bundler推荐或node16/nodenext如果无法修改moduleResolution用 vite 导入处的显式as any收窄影响范围Nx 生成的配置在个别位置已这样做修改后运行tsc --noEmit确认类型干净解析5. 打包校验脚本重新定基线Rolldown 与 Rollup 对同一输入产生的 chunk 数、module 数与 Rollup 不同。自定义的构建校验例如“bundle 恰好有 N 个 chunk”需要重新定基线。Action Items找出断言 chunk/module 数量或名称的脚本重新构建并更新期望值今后优先断言体积预算size budget而非精确计数6. 项目级 Vite 7 固定存在自定义 Babel 插件时如果某个项目依赖没有 Oxc 等价物的 Babel 插件把该项目钉在 Vite 7// package.json (workspace root) { devDependencies: { vite: ^7.1.0, vitejs/plugin-react: ^4.3.0, }, }如果只有部分项目需要留在 7、其余升到 8使用包管理器的 overrides 机制pnpm根package.json的pnpm.overridesnpm/yarnoverrides/resolutionsAction Items记录哪些项目钉在 Vite 7 及原因跟进 Oxc 插件等价物以便日后解除钉版迁移后验证逐项目跑测试nx run-many -t test -p PROJECT_NAME构建所有受影响项目nx affected -t build验证开发服务器nx serve PROJECT_NAME打开应用确认源码改动后 HMR 依然生效。验证 CI 流水线nx prepush对照迁移清单复核所有rollupOptions引用已重命名为rolldownOptionsvitejs/plugin-react已升级到 v6或钉在 v4 且记录原因react()调用中不再残留babel: { ... }选项或对应项目已钉在 Vite 7Angular vitest-analog 项目已在根devDependencies声明oxc-project/runtimeCypress 已由nx migrate升级 15.14.0 以支持 Vite 8所有受影响项目tsc --noEmit通过构建、测试、开发服务器命令全部成功补充说明Nx 的 Vite build 执行器会通过动态加载loadViteDynamicImport引入 Vite 的build/resolveConfig/mergeConfig等 API并用mergeConfig把 Nx 侧解析出的root、configFile、build.outDir与项目自身配置合并后交给 Vite 构建见 packages/vite/src/executors/build/build.impl.ts。这也意味着迁移是否成功最终以nx build的真实输出为准而不是只看配置语法正确。常见问题与解决IssueCannot find name rollupOptions或构建选项被忽略Solution重命名为rolldownOptions。Vite 8 仍把rollupOptions作为废弃别名接受会把值复制到rolldownOptions并打印弃用警告但同一层级混用两者可能产生优先级意外rolldownOptions优先。IssueBabel 插件不再生效例如 styled-components 的 className 丢失Solutionvitejs/plugin-react6移除了 Babel。寻找 Oxc 兼容替代、改用vitejs/plugin-react-swc或钉在 Vite 7 plugin-react v4。IssueAngular Vitest 报Failed to resolve import oxc-project/runtime/helpers/...Solution把oxc-project/runtime加到根devDependencies并重新安装。这只影响test目标使用nx/vitest:testvitest-analog 配置的项目使用angular/build:unit-test/nx/angular:unit-test的项目不受影响。Issue从 vite 导入defineConfig、UserConfig或Plugin时出现类型错误Solution在 tsconfig 中设置moduleResolution: bundler需要 Node 风格解析时用nodenext。IssueCypress 组件测试在 Vite 8 下无法启动Solution确认安装的是cypress 15.14.0Vite 8 支持在该版本落地。nx migrate会自动升级 Cypress如果你在package.json中把它钉低了移除钉版并重新安装。Issue升级后 bundle 体积或 chunk 数量断言失败SolutionRolldown 的 chunk 划分方式与 Rollup 不同重新定基线期望值即可。待审查文件清单# Vite 配置文件 find . -name vite.config.* -not -path */node_modules/* # Cypress 组件测试配置 rg nx/(angular|react|next|remix)/plugins/component-testing # plugin-react 中的 Babel 插件用法 rg vitejs/plugin-react.*babel|babel:\s*\{ --type ts --type js # 使用 Vitest 的 Angular 项目 rg angular/build -l package.json护栏迁移红线不要通过删除断言或把断言替换为expect(true).toBe(true)来强行让测试通过未找到等价方案就擅自剥离react()插件选项迁移后把 Cypress 回滚到 15.14.0 以下——旧版 Cypress 在 Vite 8 下无法启动。面向 LLM/Agent 的执行纪律按迁移指令执行时这本身是 Nx 注入给 LLM 的执行文档见 ai-instructions-for-vite-8.md系统化推进完成一个类别再进入下一个类别每步改后即测每个步骤完成后构建并测试受影响的项目向用户汇报说明哪些类别适用、哪些被跳过使用待办工具跟踪让迁移进度可见遇到无 Oxc 等价物的 Babel 插件依赖时停下询问固定 Vite 7 属于工作区层面的决策不应擅自替用户拍板。【免费下载链接】nxThe Monorepo Platform that amplifies both developers and AI agents. Nx optimizes your builds, scales your CI, and fixes failed PRs automatically. Ship in half the time.项目地址: https://gitcode.com/GitHub_Trending/nx/nx创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考