声明文件接入点
Core 为什么只做接入
声明文件生成的具体实现位于 packages/plugin-dts。Core 不直接调用 TypeScript Compiler API,也不直接运行 API Extractor。Core 的职责是把 lib.dts 放到正确的 environment 中,并把 JS 构建侧已经确定的上下文传给插件。
这种分工有两个好处:
- dts 后端可以独立演进,不让 core 直接承担 TypeScript 编译器复杂度。
- core 可以专注保证 JS 输出和 dts 输出的一致性。
composeDtsConfig
Core 中 dts 接入的中心是 composeDtsConfig。它读取:
libConfig.dtsformatdtsExtensionautoExternalbanner.dtsfooter.dtsredirect.dts
然后动态导入 rsbuild-plugin-dts 并返回 Rsbuild plugin 配置。
当 dts 是 false 或 undefined,不挂载插件。当 dts === true,core 会把它解释成 { bundle: false },即默认生成非打包声明文件。
为什么需要 dtsExtension
JS format 会影响声明文件扩展名。典型情况:
- ESM 产物可能需要
.d.mts。 - CJS 产物可能需要
.d.cts。 - 普通情况使用
.d.ts。
Core 在 composeOutputFilenameConfig 中计算 dtsExtension,再传给 pluginDts。如果用户没有开启 dts.autoExtension,插件仍使用 .d.ts。
这保证 dts 文件扩展名与 JS 产物模块语义一致。维护扩展名逻辑时,必须同时看 JS output 和 dts output。
Auto external 与 dts bundle
Core 会把 autoExternal 的 resolved 默认值传给 dts 插件。原因是 dts bundle 需要知道哪些依赖应被打进声明文件,哪些依赖应该保持 external。
JS 和 dts 的 external 策略如果不一致,会出现两类问题:
- JS 产物 external 了依赖,但 dts 把依赖类型打进去,导致类型体积和边界不符合预期。
- JS 产物 bundle 了依赖,但 dts 仍引用外部类型,导致消费者缺类型依赖。
因此 dts 插件中的 bundledPackages 计算要和 core 的 autoExternal 语义保持一致。
Banner 和 footer
Core 把 banner.dts 和 footer.dts 传给 dts 插件,而 JS 和 CSS banner/footer 由 composeBannerFooterConfig 处理。
这意味着 banner/footer 是按产物类型分流的:
维护时不要假设 banner 一个配置能通过同一机制覆盖所有产物。
Redirect 与 dts
Bundleless 下,JS import 会被 rewrite;dts import 也需要相应 rewrite。Core 把 redirect.dts 传给插件,插件在处理声明文件时执行路径重写。
默认 redirect.dts.extension 是 false,和 JS 默认不同。这是因为 TypeScript 声明文件的扩展名引用语义和 JS runtime import 不完全一样。修改这个默认值要非常谨慎。
dts 与 entry
dts 插件通过 environment config 的 source.entry 计算 dts entry。Core 中 entry 的处理顺序会影响 dts:
- Bundle 模式 entry 是显式文件或 Rsbuild 默认 entry。
- Bundleless 模式 entry 是 glob 展开后的动态 Rspack entry。
- dts bundle 时,插件需要把 source entry 映射到临时 declaration entry。
如果 entry 被用户配置在最终 merge 阶段覆盖,就可能让 dts entry 和 JS entry 不一致。因此 core 在派生 entry 后会重置用户 config 中的 source.entry,避免后续 merge 打乱结果。
多 environment 的 dts
每个 lib item 都会变成一个 environment。若多个 environment 都开启 dts,就会各自挂载 dts 插件。维护时需要考虑:
- 多个 format 是否写到同一个 dts distPath。
- 多个 environment 是否同时清理同一 dts 输出目录。
- dts autoExtension 是否区分
.d.mts和.d.cts。 - watch 模式下子进程是否会泄漏。
通常建议测试多 format 同时开启 dts 的情况,尤其是 ESM + CJS。
dts.build
dts.build 对应 TypeScript project references 的 build 模式。Core 只传递配置,具体校验在 plugin-dts 中完成。维护 core 时要知道这个模式对输出路径有更严格要求:tsconfig 中的 declarationDir 或 outDir 必须与 Rslib dts 输出路径一致。
这类错误应在插件中给出清晰提示,因为用户需要修改 tsconfig,而不是 Rspack 配置。
dts.isolated
dts.isolated 由 plugin-dts 通过 Rspack 内置 RslibPlugin 接入。Core 的责任是确保对应 environment 中确实有内置 RslibPlugin。当前 ESM/CJS/UMD/IIFE 会通过 format config 挂载 RslibPlugin,MF 例外。
如果未来改变 format 与 RslibPlugin 的关系,必须重新审视 isolated dts 是否还能工作。
dts 改动的测试面
Core 层 dts 改动至少应考虑:
dts: true默认 bundleless。dts.bundle: true。dts.autoExtension与 ESM/CJS。redirect.dts。banner.dts和footer.dts。- 多 lib 多 format。
bundle: false下 JS 和 dts 路径一致性。- 用户
output.externals与 autoExternal。
不要只跑 plugin-dts 单元测试。Core 接入问题通常只有在 Rslib integration case 中才能暴露。