首页
/ airi 构建工具链实践:从 tsup 迁移到 tsdown 的完整指南

airi 构建工具链实践:从 tsup 迁移到 tsdown 的完整指南

2026-09-07 17:17:24作者:傅爽业Veleda

本篇基于 airi 仓库内置的迁移指南 guide-migrate-from-tsup.md,系统讲解如何把 TypeScript 库的打包工具从 tsup 切换到基于 Rolldown 的 tsdown。读完你可以掌握 tsdown-migrate 自动迁移工具的使用方式、两者的默认值与选项差异、常见配置迁移模式,并能参考 airi 这个大型 pnpm + Turbo monorepo 中 30 个包的实际 tsdown 配置,完成一次可落地、可验证的构建工具切换。

概览:为什么可以迁移

迁移指南的开篇给出了核心理由:tsdown 构建在 Rolldown(Rust 实现)之上,而 tsup 基于 esbuild,tsdown 在保持兼容性的同时提供更快的打包速度和更强的能力。这份指南位于仓库的 Agent 技能参考目录 .agents/skills/tsdown/references/guide-migrate-from-tsup.md,是 tsdown 技能文档体系 SKILL.md 中的迁移章节,与 CLI 参考入门指南 等文档互为补充。

airi 仓库本身正是 tsdown 的用户:根目录 package.jsontsdown 作为 catalog 依赖("tsdown": "catalog:"),并以 turbo run build 驱动各包构建;仓库中 packagespluginsserver/packagesservicesintegrations 等目录下共有 30 个包的 build 脚本直接写为 tsdown。换言之,airi 的库层构建链路已经整体跑在 tsdown 上,本文的迁移路径与仓库现状完全一致。

自动迁移:tsdown-migrate 工具

指南推荐的第一选择是运行自动迁移工具,它会自动改写配置文件。

单包迁移

npx tsdown-migrate

Monorepo 批量迁移

# 使用 glob 模式
npx tsdown-migrate packages/*

# 多个目录
npx tsdown-migrate packages/foo packages/bar

迁移选项

选项 说明
[...dirs] 要迁移的目录,支持 glob 模式
--dry-run / -d 预览变更而不实际修改文件

重要前提:运行迁移前务必先提交(Commit)你的改动,这样出问题时才能用版本控制快速回退。

关键差异:默认值与选项重命名

迁移后行为变化主要来自默认值调整,这是最容易产生“隐性差异”的地方。

默认值变化

选项 tsup tsdown
format ['cjs'] ['esm']
clean false true
dts false 若 package.json 中存在 types/typings 字段则自动启用
target 需手动指定 自动从 package.json 的 engines.node 读取

四个变化中影响最大的通常是 formatclean:迁移后产物默认 ESM 优先,且输出目录会被默认清理,若你的 CI 或发布流程依赖旧的 CJS 单格式输出,需要在配置中显式补上 format: ['cjs']

选项重命名

tsup tsdown
outExtension outExtensions

输出文件名差异

对于 IIFE 构建,tsdown 产出 [name].iife.js,而 tsup 常见产出是 [name].global.jsoutExtensions 只能自定义扩展名或后缀,无法去掉 .iife.umd 段;如果必须保留旧的 IIFE 文件名,指南给出的做法是通过输出选项覆盖入口文件名:

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['iife'],
  outputOptions: {
    entryFileNames: '[name].global.js',
  },
})

tsdown 的新特性

Node 协议控制

tsup 没有对应能力,tsdown 新增 nodeProtocol 选项,用于统一 Node.js 内置模块的 node: 前缀:

export default defineConfig({
  nodeProtocol: true,      // 添加 node: 前缀(fs → node:fs)
  nodeProtocol: 'strip',   // 移除 node: 前缀(node:fs → fs)
  nodeProtocol: false,     // 保持原样(默认)
})

更好的 Workspace 支持

在 monorepo 中可以直接用 glob 让一次构建覆盖所有包,无需逐个包配置:

export default defineConfig({
  workspace: 'packages/*',  // 构建所有匹配的包
})

这一点对 airi 这类把 packages/**plugins/**integrations/** 等全部纳入 workspaces 的仓库尤为契合(见根 package.jsonworkspaces 字段)。

迁移检查清单

指南给出的六步清单,建议逐条执行:

  1. 备份代码 —— 提交所有改动;
  2. 运行迁移工具 —— npx tsdown-migrate
  3. 审查变更 —— 检查被修改的配置文件;
  4. 更新 scripts —— 把 package.json 中的 tsup 改成 tsdown
  5. 测试构建 —— 运行 pnpm build 验证(airi 根目录即通过 turbo run build 触发各包构建);
  6. 调整配置 —— 根据实际产物微调。

常见迁移模式

基础库

迁移前(tsup):

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['cjs', 'esm'],
  dts: true,
})

迁移后(tsdown):

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],  // ESM 现在是默认项
  dts: true,
  clean: true,  // 现在默认开启
})

带自定义 Target

迁移前(tsup):

export default defineConfig({
  entry: ['src/index.ts'],
  target: 'es2020',
})

迁移后(tsdown):

export default defineConfig({
  entry: ['src/index.ts'],
  // target 会自动从 package.json 的 engines.node 读取
  // 也可以显式覆盖:
  target: 'es2020',
})

CLI 构建脚本

迁移前(package.json):

{
  "scripts": {
    "build": "tsup",
    "dev": "tsup --watch"
  }
}

迁移后(package.json):

{
  "scripts": {
    "build": "tsdown",
    "dev": "tsdown --watch"
  }
}

这正是 airi 仓库中 30 个包当前采用的形式,例如 packages/i18n/package.jsonpackages/audio/package.jsonbuild 脚本均为 tsdown

airi 仓库中的真实配置对照

结合仓库源码可以看到迁移后的配置实际长什么样,它们与上文差异表一一对应:

  • packages/core-agent/tsdown.config.ts:多入口 + dts: true,未显式写 format——按新默认值即为 ESM 输出:

    export default defineConfig({
      entry: [
        'src/index.ts',
        'src/agents/spark-notify/index.ts',
      ],
      dts: true,
    })
    
  • packages/plugin-sdk/tsdown.config.ts:显式锁定 format: 'esm',并配置了 4 个入口,对应指南中“多入口点”支持项。

  • packages/electron-eventa/tsdown.config.ts:同样是 ESM 单格式 + dts: true 的库包写法。

  • packages/audio/tsdown.config.ts:展示了对象形式入口、unbundle: true(保留目录结构)与 external 白名单的组合用法:

    export default defineConfig({
      entry: {
        'index': 'src/index.ts',
        'audio-context/index': 'src/audio-context/index.ts',
        'audio-context/processor.worklet': 'src/audio-context/processor.worklet.ts',
        'encoding/index': 'src/encoding/index.ts',
      },
      unbundle: true,
      external: [
        '@alexanderolsen/libsamplerate-js/dist/libsamplerate.worklet.js?worker&url',
        './processor.worklet?worker&url',
      ],
    })
    

    注意该配置没有 dts: true——是否生成声明文件在 airi 各包间是按需开启的,这与指南中“dts 默认依据 package.json 是否有 types/typings 字段自动启用”的规则一致。

这些配置与 CLI 参考 中的 --dts--unbundle--format 等开关可以互相印证:CLI 标志与配置项一一对应,且 CLI 优先级更高。

功能兼容性

已支持的 tsup 特性

指南确认以下 tsup 能力在 tsdown 中均可用:

  • 多入口(Multiple entry points)
  • 多格式输出(ESM、CJS、IIFE、UMD)
  • TypeScript 声明文件
  • Source maps
  • 压缩(Minification)
  • Watch 模式
  • External 依赖
  • Tree shaking
  • Shims
  • 插件(Rollup 兼容)

暂缺特性

部分 tsup 特性尚未提供。指南建议到 tsdown 项目的 Issue 跟踪状态并提需求(此处不贴外部链接,可查 guide-migrate-from-tsup.md 原文获取入口)。

故障排查

迁移后构建失败

  1. 检查 Node.js 版本 —— 运行 tsdown 本身要求 Node.js 22.18.0+(仅构建时要求);产物仍可通过 target 指向更低的 Node 版本。若要支持 Node.js 18 / 20,推荐在 CI 中用 Node.js 22+ 构建,再把产物(或打包后的 tarball)放到低版本 Node 上验证。
  2. 安装 TypeScript —— 生成 DTS 时必需。
  3. 复查配置变更 —— 确认 format 与各选项正确。
  4. 检查依赖 —— 确认所有依赖已安装。

产物不同

  • 格式顺序 —— tsdown 默认 ESM 优先;
  • Clean 行为 —— tsdown 默认清理 outDir;
  • Target —— tsdown 会从 package.json 自动检测。

性能问题

tsdown 理应比 tsup 更快,如果不符合预期,指南给出三个方向:

  1. 为更快的 DTS 生成启用 isolatedDeclarations
  2. 检查是否有大型依赖被意外打包;
  3. 按需使用 skipNodeModulesBundle

资源与致谢

本指南是 tsdown 文档的一部分,完整的选项与 CLI 参考可在仓库内查阅:入门指南CLI 参考技能索引。tsdown 大量借鉴了 tsup 并吸收了其部分代码库,指南最后向 tsup 作者及社区致谢。

登录后查看全文
热门项目推荐
相关项目推荐