将 TypeScript 库项目从 tsup 迁移到 tsdown。提供完整的选项映射、配置转换规则、默认值差异以及不支持的选项替代方案,使 AI 代理能够智能地执行迁移。
从 tsup 迁移到 tsdown
供 AI 代理将 tsup 项目迁移到 tsdown(基于 Rolldown 的库打包器)的知识库。
目标版本:两阶段迁移
tsdown v0.23 移除了所有先前已弃用的 tsup 兼容选项——bundle、outExtension、publicDir、removeNodeProtocol、injectStyle 和 skipNodeModulesBundle 不再被识别。它们会导致 TypeScript 类型检查失败,并且在运行时被静默忽略,因此在 v0.23+ 上遗漏映射会产生错误输出而没有任何错误提示。因此,直接迁移到 v0.23+ 是不安全的。tsdown v0.22.14 是最后一个仍然接受这些选项并带有弃用警告的版本,使其成为安全的迁移检查点。请按两个阶段进行迁移:
- 阶段 1——在
tsdown@0.22.14上迁移:安装tsdown@0.22.14,根据下面的表格迁移配置,运行构建,并通过将每个标记的 tsup 选项映射到其真正的 tsdown 等效项来解决每个弃用警告。警告是完整性检查——直到构建产生零警告,迁移才算完成。 - 阶段 2——升级到最新的 tsdown(
^0.23.0或更新版本):仅在 0.22.14 上构建无警告之后。由于配置不再使用任何已移除的兼容选项,v0.23+ 的静默忽略行为不再构成风险。
运行时要求
tsdown 需要 Node.js ^22.18.0 || ^24.11.0 || >=26.0.0 才能运行(仅构建时)——即 Node.js 22.18+、24.11+ 或 26+。奇数版本和已结束支持的版本(例如 Node.js 23、25)不受支持。打包后的输出仍然可以通过 target 选项针对较低的 Node.js 版本,因此以前使用 tsup 支持 Node.js 18 / 20 的库在迁移后可以继续支持。
支持 Node.js 18 / 20 时的推荐工作流程:
- 在 CI 中使用 Node.js 22+ 构建,设置明确的
target,例如'node18'或'node20'。 - 在需要支持的较低 Node.js 版本上测试构建输出(或打包的 tarball)。
何时使用
- 将项目从 tsup 迁移到 tsdown
- 了解 tsup 和 tsdown 选项之间的差异
- 审查或修复迁移后的配置问题
- 为用户提供 tsup→tsdown 兼容性建议
迁移概述
按照以下步骤迁移 tsup 项目:
- 重命名配置文件:
tsup.config.*→tsdown.config.* - 更新导入:
'tsup'→'tsdown' - 应用选项映射:根据下面的表格重命名/转换选项
- 保留 tsup 默认值:显式设置不同的选项(format、clean、dts、target)
- 更新 package.json:依赖项(阶段 1:
tsdown@0.22.14)、脚本、根配置字段 - 移除不支持的选项:在有替代方案的地方替换
- 在 0.22.14 上测试构建:运行
tsdown并解决每个弃用警告——需要零警告 - 升级到最新的 tsdown(
^0.23.0或更新版本)并再次验证构建
配置文件迁移
文件重命名
| tsup | tsdown |
|---|---|
tsup.config.ts |
tsdown.config.ts |
tsup.config.cts |
tsdown.config.cts |
tsup.config.mts |
tsdown.config.mts |
tsup.config.js |
tsdown.config.js |
tsup.config.cjs |
tsdown.config.cjs |
tsup.config.mjs |
tsdown.config.mjs |
tsup.config.json |
tsdown.config.json |
导入和标识符更改
// 之前
import { defineConfig } from 'tsup'
// 之后
import { defineConfig } from 'tsdown'
替换所有标识符:tsup → tsdown,TSUP → TSDOWN。
选项映射
属性重命名
| tsup | tsdown | 备注 |
|---|---|---|
entryPoints |
entry |
在 tsup 中本身也已弃用 |
cjsInterop |
cjsDefault |
CJS 默认导出处理 |
esbuildPlugins |
plugins |
现在使用 Rolldown/Unplugin 插件 |
outExtension |
outExtensions |
自定义输出扩展名 |
publicDir |
copy |
将静态文件复制到输出 |
bundle: true |
(移除) | 打包是默认行为 |
bundle: false |
unbundle: true |
保留文件结构 |
removeNodeProtocol: true |
nodeProtocol: 'strip' |
去除 node: 前缀 |
injectStyle: true |
css: { inject: true } |
CSS 注入 |
injectStyle: false |
(移除) | 默认行为 |
skipNodeModulesBundle |
deps: { neverBundle: true } |
外部化所有依赖项 |
tsdown v0.23+ 不再识别任何旧名称——始终使用新名称。兼容选项(outExtension、skipNodeModulesBundle、publicDir、bundle、removeNodeProtocol、injectStyle)在 v0.22.14 之前(含)接受并带有弃用警告,并在 v0.23 中完全移除;在 v0.23+ 上,遗留选项会被静默忽略,而不是报错,因此构建会在没有警告的情况下出错。这就是为什么阶段 1 在 v0.22.14 上运行,每个遗留选项都会被标记。
已弃用但仍接受
external 和 noExternal 是 tsdown v0.23 仍然接受的唯一 tsup 选项名称。它们会发出弃用警告,将在未来版本中移除,并且不能与其替代品组合使用(将 external 与 deps.neverBundle 混合,或将 noExternal 与 deps.alwaysBundle 混合,会抛出错误)。始终使用替代品。
| tsup(已弃用) | tsdown(首选) | 备注 |
|---|---|---|
external: [...] |
deps: { neverBundle: [...] } |
移至 deps 命名空间 |
noExternal: [...] |
deps: { alwaysBundle: [...] } |
移至 deps 命名空间 |
输出文件名差异
对于 IIFE 构建,tsdown 生成类似 [name].iife.js 的名称,而 tsup 通常生成 [name].global.js。outExtensions 可以自定义扩展名或后缀,但不会移除内置的 .iife 或 .umd 段。使用 outputOptions.entryFileNames: '[name].global.js' 保留旧的 IIFE 文件名。
依赖项命名空间移动
依赖项配置移至 deps 命名空间下。如果同时存在 external 和 noExternal,则合并为单个 deps 对象:
// 之前(tsup)
export default defineConfig({
external: ['react'],
noExternal: ['lodash-es'],
})
// 之后(tsdown)
export default defineConfig({
deps: {
neverBundle: ['react'],
alwaysBundle: ['lodash-es'],
},
})
tsdown 还添加了 deps.onlyBundle(允许打包的包白名单)——tsup 没有等效项。
tsdown v0.23+ 中的依赖项处理默认值:
dependencies、peerDependencies和optionalDependencies默认全部外部化(tsup 仅外部化dependencies和peerDependencies,因此迁移后optionalDependencies可能会从打包变为外部化)。deps.resolveDepSubpath(解析外部化包中没有exports字段的子路径导入)默认禁用。
插件导入转换
// 之前(tsup - esbuild 插件)
import plugin from 'unplugin-example/esbuild'
// 之后(tsdown - Rolldown 插件)
import plugin from 'unplugin-example/rolldown'
所有 unplugin-*/esbuild 导入都应改为 unplugin-*/rolldown。
有关每个转换的完整前后示例,请参阅 guide-option-mappings.md。
默认值差异
tsdown 更改了 tsup 的几个默认值。迁移时,显式设置这些选项以保留 tsup 行为,然后让用户决定采用哪些新默认值。
| 选项 | tsup 默认值 | tsdown 默认值 | 迁移操作 |
|---|---|---|---|
format |
'cjs' |
'esm' |
设置 format: 'cjs' 以保留 |
clean |
false |
true |
设置 clean: false 以保留 |
dts |
false |
如果 package.json 中有 types/typings 则自动启用 |
设置 dts: false 以保留 |
target |
(无) | 自动从 package.json 中的 engines.node 读取 |
设置 target: false 以保留 |
迁移后,建议用户审查这些——tsdown 的默认值通常更好:
- ESM 是现代标准
- 清理输出防止陈旧文件
- 从 package.json 自动生成 DTS 减少配置
- 从 engines.node 自动生成 target 确保一致性
不支持的选项
这些 tsup 选项在 tsdown 中没有直接等效项。移除它们并告知用户。
| tsup 选项 | 状态 | 替代方案 |
|---|---|---|
splitting |
始终启用 | 移除——tsdown 中无法禁用代码分割 |
metafile |
不可用 | 建议使用 devtools: true 进行 Vite DevTools 包分析 |
swc |
不支持 | 移除——tsdown 使用 oxc 进行转换(内置) |
experimentalDts |
不支持 | 改用 dts 选项 |
legacyOutput |
不支持 | 移除——无替代方案 |
plugins(tsup 实验性) |
不兼容 | 手动迁移到 Rolldown 插件;tsup 的插件 API 与 Rolldown 的不同 |
package.json 迁移
脚本
将所有脚本命令中的 tsup 和 tsup-node 替换为 tsdown:
// 之前
{
"scripts": {
"build": "tsup src/index.ts",
"dev": "tsup --watch"
}
}
// 之后
{
"scripts": {
"build": "tsdown src/index.ts",
"dev": "tsdown --watch"
}
}
依赖项
| 位置 | 操作 |
|---|---|
dependencies.tsup |
重命名为 dependencies.tsdown |
devDependencies.tsup |
重命名为 devDependencies.tsdown |
optionalDependencies.tsup |
重命名为 optionalDependencies.tsdown |
peerDependencies.tsup |
重命名为 peerDependencies.tsdown |
peerDependenciesMeta.tsup |
重命名为 peerDependenciesMeta.tsdown |
根配置字段
如果 package.json 有根级别的 tsup 字段(内联配置),重命名为 tsdown:
// 之前
{ "tsup": { "entry": ["src/index.ts"] } }
// 之后
{ "tsdown": { "entry": ["src/index.ts"] } }
有关详细的 package.json 示例,请参阅 guide-package-json.md。
tsdown 新特性
迁移后,向用户建议这些 tsdown 独有的特性:
| 特性 | 配置 | 描述 |
|---|---|---|
| Node 协议 | nodeProtocol: true | 'strip' |
在内置导入上添加或去除 node: 前缀 |
| 工作区 | workspace: 'packages/*' |
在 monorepo 中构建多个包 |
| 包导出 | exports: true |
自动生成 package.json 中的 exports 字段 |
| 包验证 | publint: true、attw: true |
检查包并验证类型正确性 |
| 可执行文件 | exe: true |
打包为 Node.js 独立可执行文件(SEA) |
| DevTools | devtools: true |
Vite DevTools 集成用于包分析 |
| 钩子 | hooks: { 'build:done': ... } |
生命周期钩子:build:prepare、build:before、build:done |
| CSS 模块 | css: { modules: { ... } } |
为 .module.css 文件提供作用域类名 |
| 通配符导入 | globImport: true |
支持 import.meta.glob(Vite 风格) |
有关详细比较,请参阅 guide-differences-detailed.md。
参考
| 主题 | 描述 | 参考 |
|---|---|---|
| 选项映射 | 每个选项转换的完整前后对比 | guide-option-mappings |
| 详细差异 | 架构、特性、兼容性比较 | guide-differences-detailed |
| package.json | 依赖项、脚本和配置字段迁移 | guide-package-json |
迁移检查清单
执行迁移时使用此检查清单:
- [ ] 重命名 tsup.config.* → tsdown.config.*
- [ ] 将导入从 'tsup' 更新为 'tsdown'
- [ ] 将 tsup/TSUP 标识符替换为 tsdown/TSDOWN
- [ ] 应用属性重命名(cjsInterop→cjsDefault、esbuildPlugins→plugins、outExtension→outExtensions、publicDir→copy、bundle→unbundle、removeNodeProtocol→nodeProtocol、injectStyle→css.inject)
- [ ] 将 external/noExternal 移至 deps 命名空间,将 skipNodeModulesBundle 替换为 `deps.neverBundle: true`
- [ ] 将 unplugin 导入从 /esbuild 更新为 /rolldown
- [ ] 设置显式默认值以保留 tsup 行为(format、clean、dts、target)
- [ ] 移除不支持的选项(splitting、metafile、swc 等)
- [ ] 更新 package.json 脚本(tsup→tsdown)
- [ ] 更新 package.json 依赖项(阶段 1:tsdown@0.22.14)
- [ ] 如果存在,重命名根级别的 tsup 配置字段
- [ ] 在 0.22.14 上运行 tsdown 并解决每个弃用警告(需要零警告)
- [ ] 升级到最新的 tsdown(^0.23.0 或更新版本)并再次验证构建
- [ ] 向用户建议新的 tsdown 特性






