配置顺序和失败形态
为什么顺序重要
Rslib 的配置编排不是把对象合并起来这么简单。很多配置项必须按固定顺序生效,否则最终产物会变错。
最典型的是 externals、entry、output filename、CSS、asset、dts 的顺序。这些系统互相依赖:
- Bundleless external 需要知道 JS 输出扩展名。
- CSS handler 需要知道 CSS Modules auto 规则和 JS 扩展名。
- dts 插件需要知道 dts 扩展名。
- asset preserve 需要知道 bundle 和 format。
- 用户 externals 必须早于 autoExternal。
- bundleless external 必须晚于 package external。
顺序错了,构建可能仍成功,但产物不可用。
当前 merge 顺序
composeLibRsbuildConfig 最终大致按以下顺序 merge:
- bundle 检查。
- format config。
- moduleIds。
- shims。
- syntax。
- externalHelpers。
- output filename。
- target。
- externals warn。
- user externals。
- auto external。
- target externals。
- bundleless external。
- entry。
- CSS。
- asset。
- entry chunk。
- minify。
- dts。
- banner/footer。
- decorators。
- print file size。
- exe。
这不是绝对不能调整,但任何调整都要说明行为影响。
失败形态一:用户 externals 被 autoExternal 覆盖
用户可能写:
如果 autoExternal 不排除用户 external key,可能同时生成两套 external 规则。结果取决于顺序,产物可能引用 react,也可能引用 react-custom,行为不稳定。
当前 composeAutoExternalConfig 会在用户 externals 是 object 时排除这些 key。
失败形态二:bundleless 把 npm 包改成相对路径
错误产物:
这通常意味着 bundleless external 在 package external 之前执行,或 outBase 判断不严。
正确产物应保持:
或者按用户 externals 生成目标格式的 external 引用。
失败形态三:entry 被用户配置覆盖
Bundleless entry 是 glob 扫描后动态生成的 Rspack entry。如果最终 merge 时又把用户原始 source.entry 合进去,可能覆盖掉派生 entry。
所以 composeCreateRsbuildConfig 会在派生后重置:
这看起来奇怪,但必要。它表示“这个字段已经被 Rslib 消费并转成底层配置,不应该再次作为用户原始字段参与 merge”。
失败形态四:externals 被普通 merge 打乱
Rspack externals 是数组,顺序有语义。普通深 merge 不知道哪些 external 应该排前面。Rslib 手动组合 externals,然后删除用户 config 中的 output.externals,避免最终 merge 再插入一次。
如果不这样做,bundleless external 可能不再是最后兜底。
失败形态五:dts 扩展名和 JS 扩展名不一致
例如 JS 输出:
但 dts 输出:
在某些 package exports 和 NodeNext 解析场景下,这可能不是用户想要的。dts.autoExtension 打开时,Rslib 会把 dtsExtension 传给 plugin-dts,让它输出 .d.mts 或 .d.cts。
这里依赖 composeOutputFilenameConfig 先计算 extension,再 composeDtsConfig 接收它。
失败形态六:CSS Modules 指向错误扩展名
CSS Modules 在 bundleless 中需要输出 JS mapping。如果 JS extension 是 .mjs,CSS Modules import 应指向 .mjs。如果 CSS handler 不知道最终 JS extension,就可能输出:
而实际 mapping 文件是:
因此 cssExternalHandler 需要 jsExtension。
失败形态七:exe 拿到多入口或 split chunk
Exe 生成需要单入口 JS main。如果 splitChunks 或 runtimeChunk 仍开启,SEA main 可能不是完整单文件入口,或者需要异步 chunk。composeExeConfig 会关闭:
- runtimeChunk。
- splitChunks。
- asyncChunks。
它必须在已经知道 format、bundle、target、entry 后运行。
审查顺序改动的方法
看到 composeLibRsbuildConfig merge 顺序变化时,按下面清单审查:
- 是否影响 externals 顺序。
- 是否影响 bundleless external 最后执行。
- 是否影响 entry 派生和用户 entry 重置。
- 是否影响 output extension 传给 CSS、dts、redirect。
- 是否影响 target external 和 Node builtins。
- 是否影响 exe 对 splitChunks 的覆盖。
- 是否影响用户
tools.rspack最终覆盖能力。
如果 PR 只说“重构顺序更清晰”,但没有解释行为等价性,应该要求补充测试或说明。
最小验证策略
顺序类改动建议用 inspect 和产物一起验证:
rslib inspect看最终 Rspack config。- 查看 dist 文件名。
- 查看 JS 产物内部 import。
- 查看 CSS import 和 asset url。
- 查看 dts import。
- 对多 format 同时构建做验证。
只看测试是否通过不够,尤其是快照覆盖不完整时。