tsdown 入门教程:告别 tsup,用下一代打包工具构建你的 TypeScript 库

12 阅读7分钟

你有没有遇到过这种情况:明明只是打包一个简单的工具库,却要花大量时间配置 tsup、rollup 或 webpack,还要手动处理 .d.ts 声明文件、多格式输出、Tree Shaking……

这篇文章带你认识 tsdown —— 一个由 Rolldown 官方出品、比 tsup 快数倍的新一代 TypeScript 库打包工具。读完后你将能够:

✅ 理解 tsdown 的定位和核心优势
✅ 从零搭建一个完整的 TypeScript 库项目
✅ 掌握入口配置、多格式输出、声明文件生成等核心功能
✅ 用 Watch 模式提升开发体验


一、tsdown 是什么?为什么值得关注?

如果你写过 npm 包,大概率用过 tsup 或者 rollup。它们都能做到将 TypeScript 源码打包成可发布的 JS 文件,但有一个共同的痛点:随着项目变大,构建速度越来越慢

tsdown 的出现,就是为了解决这个问题。

用一句话理解 tsdown

tsdown = tsup 的使用体验 + Rolldown(Rust 写的打包核心)的性能

就好比你以前骑自行车上班(tsup),现在换了一辆电动摩托(tsdown):路还是那条路,操作方式差不多,但速度快了好几倍。

tsdown 的核心优势

传统打包工具链:
TS 源码 → tsc 编译 → rollup 打包 → 输出 JS
           ↓ 慢          ↓ JS 实现,慢

tsdown 的链路:
TS 源码 → Oxc(Rust 实现)→ Rolldown(Rust 实现)→ 输出 JS
           ↓ 极快                  ↓ 极快

四大核心特性:

特性说明
🚀 极速构建底层由 Rust 实现的 Oxc + Rolldown 驱动,远超 esbuild
♻️ 生态兼容兼容 Rollup 插件、Unplugin 插件、部分 Vite 插件
🛠️ 零配置开箱即用,专为库作者设计,默认值合理
🔄 平滑迁移兼容 tsup 的主要配置项,老项目迁移成本极低

值得一提的是:tsdown 是 Rolldown 的官方项目,Vite、Vitest、Nuxt、腾讯、Binance 等知名项目都在使用它。未来它还将成为 Rolldown Vite 的 Library Mode 基础。这意味着它不是玩具,而是有长期维护保障的生产级工具。


二、环境准备

在开始之前,确认你的开发环境满足以下要求:

# 推荐环境
Node.js >= 22.18.0   # 强烈推荐,低于此版本将在下个小版本移除支持
pnpm >= 9.0.0        # 本文使用 pnpm,也可替换为 npm/yarn/bun

# 检查 Node 版本
node -v

# 检查 pnpm 版本(未安装则执行下一行)
pnpm -v
npm install -g pnpm

注意 tsdown 要求 Node.js >= 20.19.0,推荐 >= 22.18.0。低于 20.19.0 将无法运行。 :::


三、10 分钟跑通第一个库项目

3.1 方式一:使用官方脚手架(推荐新手)

tsdown 提供了 create-tsdown 脚手架,一行命令生成完整项目:

pnpm create tsdown@latest

运行后,CLI 会交互式地询问你:

  • 项目名称
  • 是否使用 TypeScript(选 Yes)
  • 目标平台(Node.js / Browser / 两者都要)

生成的项目结构如下:

my-library/
├── src/
│   └── index.ts        # 你的库代码入口
├── tsdown.config.ts    # tsdown 配置文件
├── tsconfig.json       # TypeScript 配置
├── package.json
└── README.md

3.2 方式二:手动初始化(适合已有项目)

如果你想在现有项目中引入 tsdown,按以下步骤操作:

第一步:安装 tsdown

# 安装 tsdown 为开发依赖
pnpm add -D tsdown

# 如果不使用 isolatedDeclarations,还需要安装 TypeScript
pnpm add -D typescript

第二步:创建源码文件

mkdir src

创建 src/hello.ts

// 导出一个简单的问候函数
export function hello(name: string): string {
  return `Hello, ${name}! Welcome to tsdown.`
}

创建 src/index.ts(库的入口文件):

// 统一从这里导出所有公开 API
export { hello } from './hello.ts'

第三步:创建配置文件

在项目根目录创建 tsdown.config.ts

import { defineConfig } from 'tsdown'

export default defineConfig({
  entry: ['./src/index.ts'], // 指定入口文件
})

第四步:添加 npm scripts

package.json 中添加构建命令:

{
  "name": "my-library",
  "type": "module",
  "main": "./dist/index.mjs",
  "scripts": {
    "build": "tsdown",
    "dev": "tsdown --watch"
  },
  "devDependencies": {
    "tsdown": "^0.21.0"
  }
}

第五步:运行构建

pnpm build

此时运行效果:终端显示构建成功信息,dist/ 目录下生成 index.mjs 文件。

# 验证输出是否正确
node dist/index.mjs
# 输出: Hello, World! Welcome to tsdown.

🎉 恭喜!你已经成功用 tsdown 打包了第一个 TypeScript 库!


四、核心配置详解

4.1 入口文件(Entry)

入口支持多种写法,满足不同场景:

import { defineConfig } from 'tsdown'

export default defineConfig({
  // ✅ 写法一:单入口(字符串)
  entry: './src/index.ts',

  // ✅ 写法二:多入口(数组)
  // entry: ['./src/index.ts', './src/cli.ts'],

  // ✅ 写法三:带别名的多入口(对象)
  // entry: {
  //   index: './src/index.ts',   // 输出为 dist/index.mjs
  //   utils: './src/utils.ts',   // 输出为 dist/utils.mjs
  // },
})

4.2 输出格式(Format)

tsdown 默认输出 ESM 格式,但你可以根据需求配置多格式输出:

import { defineConfig } from 'tsdown'

export default defineConfig({
  entry: './src/index.ts',

  // 同时输出 ESM 和 CJS,兼容不同环境
  format: ['esm', 'cjs'],

  // 高级:不同格式使用不同编译目标
  // format: {
  //   esm: { target: ['es2020'] },
  //   cjs: { target: ['node18'] },
  // },
})

配置后的输出文件:

dist/
├── index.mjs    # ESM 格式,供现代 JS 环境使用
└── index.cjs    # CJS 格式,供 Node.js require() 使用

同时,在 package.json 中正确声明两种格式的入口:

{
  "main": "./dist/index.cjs",
  "module": "./dist/index.mjs",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  }
}

4.3 声明文件(.d.ts)

TypeScript 库必须提供 .d.ts 文件,让使用者能获得类型提示。tsdown 让这件事变得非常简单:

方式一:在 package.json 中声明 types 字段(自动启用)

{
  "types": "./dist/index.d.ts"
}

只要 package.json 里有 typestypings 字段,tsdown 会自动启用 .d.ts 生成,什么配置都不需要写

方式二:在配置文件中显式开启

import { defineConfig } from 'tsdown'

export default defineConfig({
  entry: './src/index.ts',
  dts: true, // 开启声明文件生成
})

:::tip 性能小技巧 在 tsconfig.json 中启用 isolatedDeclarations: true,tsdown 会使用 Rust 实现的 oxc-transform 来生成 .d.ts 文件,速度极快!

{
  "compilerOptions": {
    "isolatedDeclarations": true
  }
}

:::

配置完成后,dist/ 输出如下:

dist/
├── index.mjs      # JS 文件
├── index.cjs      # CJS 格式
└── index.d.ts     # TypeScript 类型声明

4.4 Watch 模式(开发神器)

开发阶段频繁修改代码时,Watch 模式会监听文件变化并自动重新构建:

# 命令行方式
tsdown --watch
# 或简写
tsdown -w

也可以在配置中默认开启(通常写在 dev script 里而非配置文件):

// 一般不推荐写在 config 里,用 npm script 控制更灵活
export default defineConfig({
  entry: './src/index.ts',
  // watch: true,  // 不推荐写死在配置里
})

五、完整实战:打包一个真实工具库

下面我们做一个实际的例子:打包一个字符串工具库 string-utils,包含驼峰转换、截断、模板填充等功能,同时输出 ESM + CJS + .d.ts

项目结构:

string-utils/
├── src/
│   ├── index.ts        # 统一导出入口
│   ├── camelCase.ts    # 驼峰转换
│   └── truncate.ts     # 字符串截断
├── tsconfig.json
├── tsdown.config.ts
└── package.json

src/camelCase.ts

/**
 * 将 kebab-case 或 snake_case 字符串转为 camelCase
 * @example toCamelCase('hello-world') → 'helloWorld'
 */
export function toCamelCase(str: string): string {
  return str
    // 将连字符或下划线后的字母替换为大写
    .replace(/[-_](.)/g, (_, char: string) => char.toUpperCase())
}

src/truncate.ts

/**
 * 超出指定长度时截断字符串并追加省略号
 * @example truncate('Hello World', 5) → 'Hello...'
 */
export function truncate(str: string, maxLength: number, suffix = '...'): string {
  // 未超出长度,直接返回原字符串
  if (str.length <= maxLength) return str

  // 截断到最大长度,追加省略标记
  return str.slice(0, maxLength) + suffix
}

src/index.ts

// 统一从这里导出所有公开函数
export { toCamelCase } from './camelCase.ts'
export { truncate } from './truncate.ts'

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "isolatedDeclarations": true  // 启用极速 dts 生成
  },
  "include": ["src"]
}

tsdown.config.ts

import { defineConfig } from 'tsdown'

export default defineConfig({
  entry: './src/index.ts',       // 入口文件
  format: ['esm', 'cjs'],        // 同时输出两种格式
  dts: true,                     // 生成 TypeScript 声明文件
  clean: true,                   // 每次构建前清空 dist 目录
  sourcemap: true,               // 生成 source map,便于调试
})

package.json

{
  "name": "string-utils",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/index.cjs",
  "module": "./dist/index.mjs",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    }
  },
  "scripts": {
    "build": "tsdown",
    "dev": "tsdown --watch"
  },
  "devDependencies": {
    "tsdown": "^0.21.0",
    "typescript": "^5.5.0"
  }
}

运行构建:

pnpm build

此时运行效果 —— dist/ 目录下生成如下文件:

dist/
├── index.mjs       # ESM 格式
├── index.cjs       # CJS 格式
├── index.d.ts      # 类型声明
└── index.mjs.map   # source map

验证输出是否正确:

# 创建一个临时测试文件
node -e "
  import('./dist/index.mjs').then(({ toCamelCase, truncate }) => {
    console.log(toCamelCase('hello-world'))     // 输出: helloWorld
    console.log(truncate('Hello World', 5))     // 输出: Hello...
  })
"

六、常见问题(FAQ)

问:tsdown 和 tsup 的主要区别是什么?

💡 答: 两者定位相同(库打包工具),使用体验也很接近,主要区别在底层引擎:tsup 基于 esbuild(Go 实现),tsdown 基于 Rolldown(Rust 实现)+ Oxc,速度更快。此外 tsdown 是 Rolldown 官方出品,与未来的 Rolldown Vite 生态深度绑定。如果你从 tsup 迁移,大部分配置可以直接照搬,改动极小。

问:配置文件支持哪些格式?

💡 答: tsdown 支持 tsdown.config.ts.js.mjs.cjs.json 等,也可以直接在 package.jsontsdown 字段中写配置。推荐使用 .ts 格式,有完整的类型提示。

问:声明文件生成报错,怎么处理?

💡 答: tsdown 内部使用 rolldown-plugin-dts 来生成 .d.ts 文件。如果遇到报错,首先检查是否安装了 typescriptpnpm add -D typescript),然后确认 tsconfig.json 配置是否正确。若确认是插件 bug,去 rolldown-plugin-dts 提 issue。

问:如何输出 IIFE 格式供浏览器 <script> 标签直接使用?

💡 答:

export default defineConfig({
  entry: './src/index.ts',
  format: 'iife',
  globalName: 'StringUtils', // 挂载到 window.StringUtils
})

构建后用 <script src="dist/index.global.js"></script> 引入即可。


七、总结与进阶路线

本文核心收获:

  • tsdown = tsup 的体验 + Rolldown(Rust)的性能,适合所有 TypeScript 库项目
  • 核心三步:安装 → 配置 tsdown.config.tspnpm build
  • 多格式输出:format: ['esm', 'cjs']
  • 声明文件:package.jsontypes 字段,或配置 dts: true
  • isolatedDeclarations: true 可显著提升 .d.ts 生成速度