声明文件系统

系统定位

声明文件生成由 packages/plugin-dts 提供,包名是 rsbuild-plugin-dts@rslib/corecomposeDtsConfig 中动态导入 pluginDts 并挂载到对应 environment。

这个系统解决的问题是:JavaScript 构建由 Rspack 处理,但 TypeScript 声明文件需要遵循 tsconfig、project references、路径别名、bundleless 路径重写和多格式扩展名。Rslib 将声明文件作为 Rsbuild 插件生命周期中的并行产物处理,而不是依赖 Rspack JS emit 自动完成。

核心文件

文件职责
packages/plugin-dts/src/index.tsRsbuild 插件入口,接入生命周期,选择 dts 后端
packages/plugin-dts/src/backend.ts校验和选择 isolatedtsctsgo 后端
packages/plugin-dts/src/dts.ts子进程侧主逻辑,准备上下文、生成声明、按需 bundle
packages/plugin-dts/src/tsc.ts使用 TypeScript Compiler API emit dts
packages/plugin-dts/src/tsgo.ts使用 tsgo emit dts
packages/plugin-dts/src/isolated.ts接入 Rspack isolated dts emit 后再做后处理
packages/plugin-dts/src/apiExtractor.ts使用 API Extractor 打包声明文件
packages/plugin-dts/src/utils.tstsconfig 加载、路径重写、清理、banner/footer、dts 后处理

从 Rslib 到 Plugin

packages/core/src/config.ts 中的 composeDtsConfiglib.dts 转换为插件配置:

Rslib 配置Plugin 配置
dts.bundlebundle
dts.distPathdistPath
dts.buildbuild
dts.abortOnErrorabortOnError
dts.autoExtensiondtsExtension
dts.aliasalias
dts.isolatedisolated
dts.tsgotsgo
banner.dtsbanner
footer.dtsfooter
redirect.dtsredirect
autoExternalautoExternal

dts === true 时,Rslib 默认将其转换为 { bundle: false }。也就是说默认生成 bundleless declarations,而不是打包后的单个 .d.ts

插件生命周期

后端选择

resolveDtsGenerationBackend 的规则很直接:

条件后端
isolated: trueisolated
tsgo: truetsgo
其他tsc

isolated: true 有额外约束:

  • 不能同时启用 tsgo
  • 不能同时启用 build
  • 不能设置 abortOnError: false

这些约束来自 isolated dts 的实现方式:声明诊断由 Rspack 编译错误承载,插件只负责配置和后处理,因此不适合允许错误后继续。

上下文准备

prepareDtsContext 是所有后端共享的准备步骤:

  1. 合并 dts.alias 和 tsconfig compilerOptions.paths
  2. 根据 tsconfig 推导 rootDir。若 tsconfig 设置 rootDir 则使用它;若 composite 为 true,默认使用 tsconfig 所在目录;否则取所有非声明输入文件的最长公共路径。
  3. 计算 dtsEmitPath
  4. 如果 build: true,要求 tsconfig 中 declarationDiroutDir 与 dts 输出路径一致。
  5. 如果 bundle: true,使用 .rslib 临时声明目录,否则直接输出到目标 dts 目录。
  6. 如果 bundle,需要把 source entry 映射到临时声明入口,供 API Extractor 使用。

TSC 后端

packages/plugin-dts/src/tsc.ts 使用 TypeScript Compiler API,覆盖几种模式:

模式API适用场景
普通非 watch 且非 compositets.createProgram常规项目
非 watch 且 compositets.createIncrementalProgram增量项目
build: truets.createSolutionBuilderproject references
watch 且非 buildts.createWatchProgramwatch 模式
watch 且 buildts.createSolutionBuilderWithWatchproject references watch

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。它仍共享 prepareDtsContextprocessDtsFiles 的后处理路径,但 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。流程为:

  1. createIsolatedDtsContext 准备 dts 上下文。
  2. applyIsolatedDtsOptions 在 bundler config 中找到 Rspack RslibPlugin 实例。
  3. emitDts.rootDiremitDts.declarationDir 写入插件参数。
  4. Rspack 编译时产出 .d.ts
  5. processIsolatedDts.d.ts 重写为需要的扩展名,并执行统一后处理。
  6. 如果启用 bundle,再调用 API Extractor。

如果找不到内置 RslibPlugin,isolated 后端会报错。这通常意味着 format 或插件组合不满足 Rslib 内置插件的前提。

Bundle 声明文件

bundleDtsIfNeededbundle: 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 覆盖计算结果。

清理策略

pluginDtsonBeforeEnvironmentCompile 中执行清理:

清理对象条件
dts 输出文件output.cleanDistPath !== false
.rslib 临时目录bundle: true
tsbuildinfotsc 或 tsgo 后端,并且 composite、incremental 或 build: true

这保证每次构建不会混入旧声明文件,但也意味着修改清理逻辑时必须关注 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 参数结构是否仍可用。