声明文件系统
系统定位
声明文件生成由 packages/plugin-dts 提供,包名是 rsbuild-plugin-dts。@rslib/core 在 composeDtsConfig 中动态导入 pluginDts 并挂载到对应 environment。
这个系统解决的问题是:JavaScript 构建由 Rspack 处理,但 TypeScript 声明文件需要遵循 tsconfig、project references、路径别名、bundleless 路径重写和多格式扩展名。Rslib 将声明文件作为 Rsbuild 插件生命周期中的并行产物处理,而不是依赖 Rspack JS emit 自动完成。
核心文件
从 Rslib 到 Plugin
packages/core/src/config.ts 中的 composeDtsConfig 将 lib.dts 转换为插件配置:
当 dts === true 时,Rslib 默认将其转换为 { bundle: false }。也就是说默认生成 bundleless declarations,而不是打包后的单个 .d.ts。
插件生命周期
后端选择
resolveDtsGenerationBackend 的规则很直接:
isolated: true 有额外约束:
- 不能同时启用
tsgo。 - 不能同时启用
build。 - 不能设置
abortOnError: false。
这些约束来自 isolated dts 的实现方式:声明诊断由 Rspack 编译错误承载,插件只负责配置和后处理,因此不适合允许错误后继续。
上下文准备
prepareDtsContext 是所有后端共享的准备步骤:
- 合并
dts.alias和 tsconfigcompilerOptions.paths。 - 根据 tsconfig 推导
rootDir。若 tsconfig 设置rootDir则使用它;若composite为 true,默认使用 tsconfig 所在目录;否则取所有非声明输入文件的最长公共路径。 - 计算
dtsEmitPath。 - 如果
build: true,要求 tsconfig 中declarationDir或outDir与 dts 输出路径一致。 - 如果
bundle: true,使用.rslib临时声明目录,否则直接输出到目标 dts 目录。 - 如果 bundle,需要把 source entry 映射到临时声明入口,供 API Extractor 使用。
TSC 后端
packages/plugin-dts/src/tsc.ts 使用 TypeScript Compiler API,覆盖几种模式:
TSC 后端会覆写 host 的 writeFile:
- 根据
dtsExtension重命名.d.ts、.d.mts、.d.cts。 - 更新 declaration map 中的扩展名。
- emit 后调用
processDtsFiles做 redirect、paths alias、banner/footer 等后处理。 - 如果有 diagnostics,先处理文件再报错,保证输出目录状态可用于调试。
TSGo 后端
tsgo 后端用于实验性 TypeScript native preview。它仍共享 prepareDtsContext 和 processDtsFiles 的后处理路径,但 emit 阶段由 packages/plugin-dts/src/tsgo.ts 执行。
维护时要注意:generateDts 中 tsgo 和 tsc 的 bundle 时机不同。tsgo 路径会在 emit 完且无错误后立即 bundleDtsIfNeeded,而 tsc 非 watch 路径在 emit 完成后统一 bundle。
Isolated 后端
packages/plugin-dts/src/isolated.ts 依赖 Rspack 内置 RslibPlugin 的 isolated declaration emit。流程为:
createIsolatedDtsContext准备 dts 上下文。applyIsolatedDtsOptions在 bundler config 中找到 RspackRslibPlugin实例。- 将
emitDts.rootDir和emitDts.declarationDir写入插件参数。 - Rspack 编译时产出
.d.ts。 processIsolatedDts将.d.ts重写为需要的扩展名,并执行统一后处理。- 如果启用 bundle,再调用 API Extractor。
如果找不到内置 RslibPlugin,isolated 后端会报错。这通常意味着 format 或插件组合不满足 Rslib 内置插件的前提。
Bundle 声明文件
bundleDtsIfNeeded 在 bundle: true 时调用 apiExtractor.ts。API Extractor 的输入是已经生成到临时目录的 declaration entry,输出是目标 distPath 下的单个 entry dts 文件。
calcBundledPackages 用“非 external 的依赖需要被打进 dts bundle”的思路计算 bundledPackages:
- 默认 external dependencies、peerDependencies、optionalDependencies,不 external devDependencies。
- 用户
output.externals中的依赖不 bundle。 @types/react默认排除。- 用户可用
dts.bundle.bundledPackages覆盖计算结果。
清理策略
pluginDts 在 onBeforeEnvironmentCompile 中执行清理:
这保证每次构建不会混入旧声明文件,但也意味着修改清理逻辑时必须关注 watch 和多 environment 并行构建。
错误处理
插件默认 abortOnError = true。在 onAfterBuild 第二阶段:
- 如果 dts 子任务失败且
abortOnError为 true,抛出错误使构建失败。 - 如果
abortOnError为 false,记录错误和警告,但不终止构建。 - isolated 后端不允许
abortOnError: false。
子进程模式下,dts.ts 会监听 process.on("message") 接收 DtsGenOptions,成功发送 success,失败发送 error 并退出。父进程在 close build 或 close dev server 时会 kill 剩余子进程。
维护检查点
- 修改后端选择时必须更新
backend.ts的约束和对应测试。 - 修改路径重写时应检查 bundleless JS redirect 和 dts redirect 是否一致。
- 修改 bundle 行为时应检查 API Extractor 多 entry、bundledPackages 和临时目录清理。
- 修改 watch 行为时要确认子进程不泄漏,且 tsc watch 的 diagnostics 和 post-process 顺序仍正确。
- 修改 isolated 行为时要同时检查 Rspack
RslibPlugin参数结构是否仍可用。