配置编排常规说明
为什么配置编排是 core 的核心
Rslib 的大部分价值都体现在配置编排里。用户写的是面向库构建的 RslibConfig,底层执行的是 Rsbuild 和 Rspack 需要的 environment config。中间这层翻译既要隐藏库构建的复杂度,也要保留足够的用户覆盖能力。
src/config.ts 不是简单把字段改名。它要同时处理以下问题:
- 多个
libitem 如何变成多个 environment。 - 不同 format 如何设置 Rspack output、parser、library、runtime。
- bundle 和 bundleless 如何使用不同的 entry 和 external 策略。
- package.json 依赖如何转成自动 external。
- Node builtins 如何在 node target 下被 external。
- CSS、asset、dts、exe 如何接入构建生命周期。
- 用户的 Rsbuild 配置如何保留优先级。
理解配置编排时,最有效的方式是从“最终要生成什么”倒推。最终生成的是一个 Record<string, EnvironmentConfig>,key 是 environment id,value 是 Rsbuild environment 配置。
主流程
这个流程对应三个公开或半公开入口:
composeCreateRsbuildConfig:把 Rslib config 变成带 format 和 id 信息的 Rsbuild config 列表。composeRsbuildEnvironments:给每个 config 分配 environment id,返回 environment map。pruneEnvironments:根据--lib或 API 参数裁剪 environment。
合并顺序
最终 config 的合并顺序是:
这个顺序不是随意的:
constantRsbuildConfig提供基础默认值。libRsbuildConfig根据 Rslib 字段覆盖或补充默认值。userConfigWithoutLibOnlyFields让用户仍能覆盖大部分 Rsbuild 和 Rspack 配置。
维护时最常见的问题是误把某个字段留在 userConfig 里。比如 source.entry 先被 composeEntryConfig 用来计算 entry、outBase、runtime chunk、dts entry,然后会被重置为空对象,避免后续 merge 时覆盖已经派生好的 entry。output.externals 也会被删除,因为 externals 的顺序需要 Rslib 手动控制,不能让 merge 机制打乱。
内置常量配置
createConstantRsbuildConfig 表达的是“Rslib 认为库构建应当默认这样”的底线:
- 关闭 html plugin。
chunkSplit.strategy = "custom"。- 开启 build cache。
- 设置 Rspack
optimization.nodeEnv = false,避免库构建中硬编码process.env.NODE_ENV。 - 设置 TypeScript extension alias,支持用户在源码里写
.js引用但解析到.ts。 - 默认 target 为 node。
- JS 和 CSS distPath 默认都输出到根目录。
这些默认值通常不应该被轻易删除。要改它们,必须确认是用户配置覆盖不足,还是默认值本身不再适合库构建。
composeLibRsbuildConfig 的结构
composeLibRsbuildConfig 可以看成一个配置装配流水线。它先读取输入状态,再调用一系列 compose 函数,最后按顺序 merge。
输入状态包括:
pkgJson:用于 autoExternal、extension、externalHelpers、MF uniqueName。compilerOptions:用于 decorators 默认值。format、bundle、autoExtension、autoExternal、redirect、shims。source.entry、source.tsconfigPath。output.target、output.externals、output.filename、output.minify。experiments.exe。
输出状态包括:
- 格式化后的 Rspack output。
- 入口配置和 bundleless outBase。
- externals 列表。
- CSS 和 asset 插件。
- dts 插件。
- exe 插件。
- 语法 target 和 browserslist。
- 输出文件名和 chunk 文件名。
Compose 函数组别
维护时不要只看某个 compose 函数本身,还要看它在 merge 列表里的位置。
Externals 的顺序语义
Externals 是配置编排中最敏感的顺序之一。当前顺序是:
- warn config。
- user externals。
- auto external。
- target externals。
- bundleless externals。
这样安排的原因是:
- warn config 必须先看到可能被 external 的 commonjs request,才能给 ESM external type 提示。
- 用户 externals 的优先级应该高于 Rslib 自动推导。
- auto external 应该在 bundleless 路径重写前生效,否则依赖可能被错误解析成文件。
- Node builtins external 是 target 规则。
- bundleless external 是最后兜底,只处理源文件内部引用的输出路径重写。
如果把 bundleless external 放早了,第三方依赖或用户 external 可能被 resolver 解析到本地文件,导致库发布后 import 指向错误。这个问题通常不会在简单 fixture 中暴露,需要通过 package.json 依赖和 node_modules 结构测试覆盖。
Entry 和 outBase 的联动
Entry 是 bundle 和 bundleless 分岔的起点。
Bundle 模式下,entry 必须是文件。因为 bundler 会从入口打包依赖图,glob 或目录入口没有明确的单 bundle 语义。Rslib 会提前校验,避免让 Rspack 报出更难理解的错误。
Bundleless 模式下,entry 通常是 glob。Rslib 会扫描所有匹配文件,过滤声明文件,然后根据 outBase 生成 entry name。默认 outBase 是所有非声明输入文件的最长公共路径。这个设计让输出保留源码目录结构。
outBase 后续还会被多个系统使用:
- bundleless external 判断请求是否来自源码范围。
- CSS 插件判断全局 CSS entry 和输出路径。
- EntryChunkPlugin 在 watch 中登记 context dependency。
- dts bundleless 输出需要和 JS 输出保持路径关系。
因此 outBase 不是一个局部变量,而是 bundleless 的全局坐标系。
文件扩展名的联动
composeOutputFilenameConfig 输出两个重要结果:jsExtension 和 dtsExtension。它们不只用于文件名:
- JS entry 输出用
jsExtension。 - chunk filename 推导也用它。
- bundleless JS redirect 会把
.ts、.tsx、无扩展名 import 改成目标 JS 扩展名。 - CSS Modules import 会指向对应 JS 模块扩展名。
- asset redirect 在某些配置下也会把资源请求改成 JS 输出扩展名。
- dts 插件用
dtsExtension决定.d.ts、.d.mts、.d.cts。
改扩展名相关逻辑时,必须用实际产物验证 import 是否存在。只看输出文件名是不够的,真正重要的是产物内部 import graph 是否能被 Node、浏览器或 bundler 解析。
User config 的保留和剥离
Rslib 允许用户传 Rsbuild 配置,但 LibConfig 中有一些字段是 Rslib 专属的,不能直接交给 Rsbuild。例如:
formatbundleautoExtensionautoExternalredirectsyntaxexternalHelpersdtsshimsumdNameoutBaseexperiments
composeCreateRsbuildConfig 在最终 merge 前会用 omit 去掉这些字段。这样既允许用户在 lib item 中写 Rsbuild 字段,又避免 Rslib 专属字段泄漏到底层。
何时新增 compose 函数
新增逻辑时,不要默认塞进已有大函数。可以用以下规则判断:
- 如果逻辑只服务某个 format,优先放到
composeFormatConfig对应分支。 - 如果逻辑产生独立 Rsbuild config,且需要在 merge 顺序中明确位置,可以新增 compose 函数。
- 如果逻辑依赖多个前序计算结果,例如
outBase、jsExtension,应在这些结果计算后调用。 - 如果逻辑只是小型工具,放到 utils 或本文件局部函数。
新增 compose 函数时,文档和测试应说明它放在 merge 列表中的原因。
审查配置编排 PR 的方法
审查这类 PR 时,建议按下面顺序看:
- 看类型变化,确认用户 API 是否变化。
- 看默认值变化,确认是否 breaking。
- 看 compose 顺序变化,确认有没有影响 externals、entry、dts、CSS。
- 看多 format 行为,确认是否只测了 esm。
- 看 bundleless 行为,确认是否只测了 bundle。
- 看 inspect 输出,确认用户能否诊断最终配置。
- 看测试 fixture,确认真实产物是否被断言,而不是只断言 config。
配置编排 bug 的特点是“看起来只是配置”,但最终会表现为运行时 import 失败、声明文件路径错误、CSS 丢失、Node builtins 被打包、或者 UMD/IIFE runtime 不符合预期。测试应尽量断言产物内容和运行结果。