最近我把一个有接近 10 年历史的移动端老项目,从 roadhog / Webpack 3 升级到了 Rspack。这个项目不算新:React 16、dva、antd-mobile v2/v5 混用、Less、CSS Modules、SVG sprite、EJS HTML 模板、CDN publicPath、旧式 alias 和移动端兼容策略都在里面。
这类升级最怕的不是“配置不会写”,而是旧构建链路里有大量隐式行为。直接让 AI 生成一个 rspack.config.js 很容易跑通一个 demo,却破坏真实项目里的样式、图标、HTML 模板或部署路径。所以这次我没有从“写代码”开始,而是先让另一个 AI 扫描项目并生成 PDR。
我给它的提示词很简单:
给我提供一份该项目构建工具升级到rspack 的pdr,提供给AI使用,并将 pdr 写入到 docs 目录中
这个 PDR 后来放在 docs/rspack-upgrade-pdr.md。它的价值不是告诉 AI 每一行代码怎么写,而是先把迁移边界讲清楚:旧构建里有哪些行为必须对齐,哪些依赖必须移除,哪些能力要用 Rspack 原生实现,哪些事情不应该顺手重构。
这个 PDR 也不是一次性生成后就不动了。迁移过程中我会不断让 AI 更新 PDR,把新的判断补进去。尤其有两条原则被反复强调:
- 必须升级到 Rspack 最新版本,可以配套升级相关工具链。
- 尽量利用 Rspack 去对齐 Babel 和 Webpack 配置;Rspack 无法直接对齐的地方,就在项目里自己写代码、loader 或插件来补齐。
这两条很重要。第一条避免了“为了少改而停在旧版本”的短期方案;第二条避免了“为了兼容旧行为继续套旧 Webpack/Babel 链路”的迁移陷阱。对一个 10 年历史的老项目来说,升级不是把旧配置搬家,而是用新工具重新表达旧行为。
拿到 PDR 后,我在 Codex 里开启了 /goal 模式,把这份文档直接变成持续执行目标:
/goal 完成 [rspack-upgrade-pdr.md](docs/rspack-upgrade-pdr.md) 这个目标
这个动作很关键。普通问答更像一次请求一次响应,而 /goal 模式会让 Codex 围绕一个明确目标持续推进:读文档、拆任务、改代码、验证、处理新暴露的问题,直到目标真正收敛。
为什么先写 PDR
构建升级是典型的“上下文密集型”任务。老项目里真正重要的信息通常散落在不同位置:
.webpackrc.js里有入口、Less theme、Babel 插件和 roadhog 行为。webpack.config.js里有 publicPath、DefinePlugin、HTML 模板注入、externals、SVG sprite、PostCSS 和压缩逻辑。webpack.ide.js这类文件可能只是为了 IDE alias,但删错了会影响开发体验。- 业务代码里还依赖
components、assets、utils这类非相对路径。
PDR 的作用,是把这些隐式知识变成执行约束。比如这次 PDR 明确要求:
- 不能保留 roadhog / Webpack 3 双构建链路。
- Rspack 原生优先,不为了兼容旧配置而继续套 Webpack 插件。
- 旧行为必须对齐,包括 HTML 模板、Less、CSS Modules、SVG symbol、externals、alias、CDN 路径。
- TypeScript 支持要打开,但不能强迫老 JS 业务代码一起迁移。
- 包管理器切到 pnpm,Node 版本要求跟随最新 Rspack。
这些约束非常适合交给 Codex。PDR 负责定义“什么算完成”,Codex 负责在真实仓库里把目标落地。
Codex 做的不只是生成配置
这次迁移里,Codex 的价值主要体现在三件事上。
第一,它会先读项目,而不是直接套模板。它从旧 roadhog / Webpack 配置里提取行为,再映射到新的 rspack.config.js:入口仍然是 src/index.js,输出仍然是 dist,production 和 test 的 CDN publicPath 保持不变,HTML 仍然复用 src/index.ejs,DefinePlugin 注入的 __DEV__、__TEST__、__PROD__ 等变量也继续保留。
第二,它会在构建中处理真实问题,而不是只给建议。迁移过程中遇到的问题并不少:
antd-mobile-v2的 Less 使用了即将废弃的 inline JavaScript backtick 表达式。- Rspack 下 Less 规则拆分后,部分全局样式和
_base.less没有按旧行为生效。 - CSS Modules 的
localIdentName需要和旧项目输出对齐。 CustomIcon里的 SVG symbol 在 Rspack 下没有按原 SVG viewBox 正确缩放。dayjs默认英文,需要和原来的中文语境对齐。- IDEA 无法识别 alias,需要把 Rspack alias 同步到
tsconfig.json。 - 新版 Node 对 CommonJS
require('@rspack/core')加载 ESM 给出 ExperimentalWarning。
这些都不是 PDR 能完全预判的细节。Codex 的处理方式是每次只缩小一个问题:读代码、定位配置、做最小修改、跑构建验证,再继续下一个问题。
第三,它能把“不要这么做”的约束执行到代码里。比如 Less 的 warning 不能靠屏蔽日志解决,于是 Codex 针对 antd-mobile-v2 的 Less 包做预处理,并补了 Less 自定义函数来替代 inline JavaScript;SVG 问题也没有改业务图标,而是补了项目内 rspack-svg-symbol-loader,让 symbol 保留 viewBox、移除会干扰缩放的宽高属性。
迁移后的结构
最终核心脚本切到了 Rspack:
{
"start": "rspack dev",
"build": "rspack build",
"build:test": "ENV=test rspack build",
"typecheck": "tsc --noEmit"
}
rspack.config.js 里主要保留了这些能力:
builtin:swc-loader处理 JS / JSX / TS / TSX。transformImport对齐原来babel-plugin-import的按需导入行为。CssExtractRspackPlugin抽离 CSS。HtmlRspackPlugin配合一个模板参数适配器继续渲染旧 EJS。- Rspack asset modules 处理图片和普通 SVG。
- 项目内 SVG symbol loader 对齐旧
svg-sprite-loader的业务表现。 - Less、PostCSS、pxtorem、CSS Modules 按旧项目规则拆分。
tsconfig.json维护和 Rspack 一致的 alias,供 TypeScript 和 IDEA 使用。
在兼容策略上,项目也开始放弃过老 WebView,不再继续为 iOS 8 / Android 4 这类环境兜底。入口里的全量 core-js/stable 和 regenerator-runtime/runtime 可以被移除,SWC target 和 autoprefixer 目标也随之上调。这个决策不是“为了新而新”,而是先明确产品兼容边界,再让构建产物跟着边界收敛。
Codex 适合做这种升级的原因
这次体验下来,我觉得 Codex 最适合的不是一次性写一个大补丁,而是处理这种需要持续判断的工程任务。
它能把 PDR 变成执行清单。每个条目不是一句“已迁移”,而是落到真实文件和真实命令上:配置怎么写、依赖怎么换、脚本怎么跑、产物怎么检查。
它能在仓库里保持上下文。比如修 Less 时需要同时理解 antd-mobile-v2、less-loader、CSS Modules 和 _base.less;修 CustomIcon 时需要同时看 SVG loader、React 组件和 antd-mobile v2 的 icon class;修 alias 时需要同时看 Rspack resolve.alias 和 tsconfig.json。
它也能把验证作为工作的一部分。每次调整后都跑最小必要命令,比如 pnpm typecheck、pnpm build、pnpm build:test,并根据输出继续修。构建升级里最有价值的不是“AI 觉得没问题”,而是每一步都有工具输出支撑。
更重要的是,Codex 能接受“不能这么修”的工程约束。不能直接屏蔽 warning,不能为了跑通引入 Webpack 旧插件,不能为了迁移重构业务代码,不能随手改部署路径。这些约束在普通聊天式 AI 里很容易被忽略,但对一个在仓库里执行任务的编码 Agent 来说,它们必须变成实际行为。
一个更有效的 AI 升级流程
这次迁移给我的最大启发是:复杂工程任务可以拆成两类 AI 工作。
第一类是规划型 AI:扫描项目,生成 PDR,提炼风险、边界、验收标准。它不急着写代码,而是先把“完成”的定义讲清楚。
第二类是执行型 AI:像 Codex 这样进入仓库,最好用 /goal 模式把 PDR 设成持续目标,然后按 PDR 逐项落地,遇到真实错误就调试,最后用构建和类型检查验证。
这比直接丢一句“帮我升级到 Rspack”更稳定。前者让 AI 有明确的任务合同,后者让 AI 有真实的工程反馈。PDR 负责减少方向性错误,Codex 负责减少落地过程中的手工摩擦。
最后得到的不是一份看起来正确的配置,而是一条可维护的新构建链路:Rspack 接管开发和生产构建,旧 roadhog / Webpack 3 行为被显式迁移,历史样式和图标问题被逐项处理,IDE 和类型检查也跟着对齐。
这才是我认为 AI 编程工具真正有价值的地方:不是替代工程判断,而是把工程判断执行得更完整、更快,并且每一步都能被验证。
这篇文章我也是让AI生成的 😄