- 什么是 tsup
- 为什么需要 tsup
- 安装
- 配置方式
- 一、核心配置选项
- 1.1 entry(入口文件)
- 1.2 format(输出格式)
- 1.3 dts(类型声明)
- 1.4 outDir(输出目录)
- 1.5 clean(清理输出目录)
- 1.6 sourcemap(Source Map)
- 1.7 target(编译目标)
- 1.8 minify(代码压缩)
- 1.9 watch(监听模式)
- 1.10 splitting(代码分割)
- 1.11 external(外部依赖)
- 1.12 noExternal(不外部化)
- 1.13 globalName(全局变量名)
- 1.14 platform(平台)
- 1.15 shims(垫片)
- 1.16 bundle(是否打包)
- 1.17 treeshake(Tree Shaking)
- 1.18 env(环境变量)
- 1.19 inject(注入代码)
- 1.20 banner 和 footer
- 二、完整推荐配置
- 三、常用命令
- 四、常见问题和最佳实践
- 五、与其他工具对比
- 六、实际案例
- 七、总结
- 参考资源
什么是 tsup #
tsup 是一个基于 esbuild 的零配置 TypeScript 打包工具,专门用于构建 TypeScript 库。它的名字来自 "TypeScript UP"。
# 安装 tsup
npm install -D tsup
# 零配置构建
tsup src/index.ts
# 生成 CJS、ESM 和类型声明
tsup src/index.ts --format cjs,esm --dts核心特性 #
- ⚡ 极速构建:基于 esbuild,比 Webpack/Rollup 快 10-100 倍
- 🎯 零配置:开箱即用,无需复杂配置
- 📦 多格式输出:支持 CJS、ESM、IIFE 等格式
- 🔷 类型声明:自动生成
.d.ts文件 - 🎨 代码分割:支持多入口和代码分割
- 🔥 HMR 支持:开发模式下支持热更新
- 📝 Source Maps:支持生成 source map
为什么需要 tsup #
传统库打包的问题 #
没有 tsup 之前,构建 TypeScript 库需要复杂的配置:
# ❌ 使用 tsc(TypeScript 编译器)
{
"compilerOptions": {
"declaration": true,
"outDir": "dist"
}
}
# 问题:
# - 不支持打包(需要额外工具)
# - 不支持多格式输出
# - 构建速度慢
# - 需要额外处理 CSS、JSON 等
# ❌ 使用 Rollup
# 需要安装和配置多个插件:
npm install -D rollup @rollup/plugin-typescript @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup-plugin-dts
# 需要复杂的配置文件
// rollup.config.js
export default [
{
input: 'src/index.ts',
output: [
{ file: 'dist/index.js', format: 'cjs' },
{ file: 'dist/index.mjs', format: 'esm' }
],
plugins: [
typescript(),
resolve(),
commonjs()
]
},
{
// 单独配置类型声明
input: 'src/index.ts',
output: { file: 'dist/index.d.ts', format: 'es' },
plugins: [dts()]
}
]
# 问题:
# - 配置复杂
# - 需要维护多个插件
# - 构建速度中等使用 tsup 后 #
# ✅ 使用 tsup:一行命令搞定
tsup src/index.ts --format cjs,esm --dts
# 生成:
# dist/index.js (CJS 格式)
# dist/index.mjs (ESM 格式)
# dist/index.d.ts (类型声明)
# 耗时:< 1 秒(esbuild 极速)效果:
- ✅ 零配置,开箱即用
- ✅ 自动生成类型声明
- ✅ 支持多种格式
- ✅ 构建速度极快
- ✅ 无需额外插件
安装 #
基础安装 #
# 使用 npm
npm install -D tsup
# 使用 yarn
yarn add -D tsup
# 使用 pnpm(推荐)
pnpm add -D tsup依赖说明 #
tsup 只需要安装自己,不需要额外依赖:
{
"devDependencies": {
"tsup": "^8.0.0",
"typescript": "^5.3.0" // TypeScript(必需)
}
}配置方式 #
方式一:命令行参数(推荐入门) #
# 基础构建
tsup src/index.ts
# 指定格式
tsup src/index.ts --format cjs,esm
# 生成类型声明
tsup src/index.ts --dts
# 完整配置
tsup src/index.ts --format cjs,esm --dts --clean --sourcemap方式二:配置文件(推荐项目) #
支持的配置文件格式:
# JavaScript
tsup.config.ts # TypeScript(推荐)
tsup.config.js # JavaScript
tsup.config.cjs # CommonJS
tsup.config.mjs # ES Module
# JSON
tsup.config.json
# package.json
{
"tsup": {
// 配置项
}
}基础配置示例:
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
sourcemap: true
});一、核心配置选项 #
1.1 entry(入口文件) #
作用:指定构建的入口文件。
// 单入口
export default defineConfig({
entry: ['src/index.ts']
});
// 多入口
export default defineConfig({
entry: ['src/index.ts', 'src/cli.ts']
});
// 使用 glob 模式
export default defineConfig({
entry: ['src/*.ts']
});
// 命名入口(自定义输出文件名)
export default defineConfig({
entry: {
index: 'src/index.ts',
cli: 'src/cli.ts'
}
});影响对比:
# 单入口
entry: ['src/index.ts']
# 输出:
# dist/index.js
# dist/index.mjs
# dist/index.d.ts
# 多入口
entry: ['src/index.ts', 'src/cli.ts']
# 输出:
# dist/index.js
# dist/index.mjs
# dist/index.d.ts
# dist/cli.js
# dist/cli.mjs
# dist/cli.d.ts
# 命名入口
entry: {
'my-lib': 'src/index.ts',
'my-cli': 'src/cli.ts'
}
# 输出:
# dist/my-lib.js
# dist/my-lib.mjs
# dist/my-cli.js
# dist/my-cli.mjs1.2 format(输出格式) #
作用:指定输出的模块格式。
export default defineConfig({
format: ['cjs', 'esm', 'iife']
});可选值:
| 格式 | 说明 | 文件扩展名 | 适用场景 |
|---|---|---|---|
cjs | CommonJS | .js | Node.js、require() |
esm | ES Module | .mjs 或 .js | 现代 Node.js、import |
iife | 立即执行函数 | .global.js | 浏览器 <script> 标签 |
影响对比:
// 源代码
export function greet(name: string) {
return `Hello ${name}`;
}
// format: ['cjs']
// dist/index.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.greet = greet;
function greet(name) {
return `Hello ${name}`;
}
// format: ['esm']
// dist/index.mjs
export function greet(name) {
return `Hello ${name}`;
}
// format: ['iife']
// dist/index.global.js
var MyLib = (function () {
'use strict';
function greet(name) {
return `Hello ${name}`;
}
return { greet };
})();推荐配置:
// 库开发(推荐同时支持 CJS 和 ESM)
export default defineConfig({
format: ['cjs', 'esm']
});
// 浏览器使用
export default defineConfig({
format: ['esm', 'iife']
});1.3 dts(类型声明) #
作用:生成 TypeScript 类型声明文件(.d.ts)。
// 简单配置
export default defineConfig({
dts: true // 自动生成 .d.ts
});
// 详细配置
export default defineConfig({
dts: {
entry: 'src/index.ts', // 入口文件
resolve: true, // 解析外部类型
compilerOptions: { // TypeScript 编译选项
strict: true
}
}
});
// 只生成类型声明
export default defineConfig({
dts: {
only: true // 不打包 JS,只生成 .d.ts
}
});影响对比:
// 源代码:src/index.ts
export interface User {
name: string;
age: number;
}
export function getUser(): User {
return { name: 'Tom', age: 25 };
}
// ❌ dts: false(或不配置)
tsup src/index.ts
# 输出:
# dist/index.js
# dist/index.mjs
# (没有类型声明)
// ✅ dts: true
tsup src/index.ts --dts
# 输出:
# dist/index.js
# dist/index.mjs
# dist/index.d.ts ← 类型声明
// dist/index.d.ts 内容
export interface User {
name: string;
age: number;
}
export declare function getUser(): User;这是 tsup 相比 Vite 的重要优势:
// ✅ tsup:内置类型声明生成
export default defineConfig({
dts: true // 一行配置搞定
});
// ❌ Vite:需要额外插件
import dts from 'vite-plugin-dts';
export default defineConfig({
plugins: [
dts({
insertTypesEntry: true,
rollupTypes: true
})
]
});1.4 outDir(输出目录) #
作用:指定输出目录。
export default defineConfig({
outDir: 'dist' // 默认值
});
// 自定义输出目录
export default defineConfig({
outDir: 'lib'
});影响对比:
# outDir: 'dist'(默认)
dist/
├── index.js
├── index.mjs
└── index.d.ts
# outDir: 'lib'
lib/
├── index.js
├── index.mjs
└── index.d.ts1.5 clean(清理输出目录) #
作用:构建前清理输出目录。
export default defineConfig({
clean: true // 推荐开启
});影响对比:
# ❌ clean: false(默认)
# 第一次构建
tsup src/index.ts
# dist/index.js
# 修改 entry 为 src/main.ts
tsup src/main.ts
# dist/index.js ← 旧文件还在
# dist/main.js ← 新文件
# ✅ clean: true
# 第一次构建
tsup src/index.ts --clean
# dist/index.js
# 修改 entry 为 src/main.ts
tsup src/main.ts --clean
# dist/main.js ← 只有新文件1.6 sourcemap(Source Map) #
作用:生成 source map,方便调试。
// 生成 source map
export default defineConfig({
sourcemap: true
});
// 生成内联 source map
export default defineConfig({
sourcemap: 'inline'
});
// 只在开发环境生成
export default defineConfig({
sourcemap: process.env.NODE_ENV === 'development'
});影响对比:
# sourcemap: false(默认)
dist/
├── index.js
└── index.mjs
# sourcemap: true
dist/
├── index.js
├── index.js.map ← source map
├── index.mjs
└── index.mjs.map ← source map
# sourcemap: 'inline'
dist/
├── index.js ← 包含内联 source map
└── index.mjs ← 包含内联 source map1.7 target(编译目标) #
作用:指定编译目标 ECMAScript 版本。
export default defineConfig({
target: 'es2020' // 默认:node16
});
// 多个目标
export default defineConfig({
target: ['es2020', 'node16']
});可选值:
'es3' | 'es5' | 'es2015' | 'es2016' | 'es2017' | 'es2018' | 'es2019' | 'es2020' | 'es2021' | 'es2022' | 'esnext' | 'node10' | 'node12' | 'node14' | 'node16' | 'node18' | 'node20'影响对比:
// 源代码
const greet = (name: string) => `Hello ${name}`;
// target: 'es5'
var greet = function (name) { return "Hello " + name; };
// target: 'es2020'
const greet = (name) => `Hello ${name}`;推荐配置:
// 库开发(兼容性)
export default defineConfig({
target: 'es2018' // 兼容 Node.js 12+
});
// 现代项目
export default defineConfig({
target: 'es2020'
});
// Node.js 项目
export default defineConfig({
target: 'node16'
});1.8 minify(代码压缩) #
作用:压缩输出代码。
// 不压缩(默认)
export default defineConfig({
minify: false
});
// 压缩
export default defineConfig({
minify: true
});
// 使用 Terser 压缩(更好的压缩率)
export default defineConfig({
minify: 'terser'
});影响对比:
// 源代码
export function add(a: number, b: number) {
return a + b;
}
// minify: false
export function add(a, b) {
return a + b;
}
// minify: true(使用 esbuild 压缩)
export function add(n,r){return n+r}
// minify: 'terser'(更激进的压缩)
export function add(n,r){return n+r}文件大小对比:
# minify: false
dist/index.js 12 KB
# minify: true
dist/index.js 8 KB (减少 33%)
# minify: 'terser'
dist/index.js 7 KB (减少 41%)1.9 watch(监听模式) #
作用:监听文件变化,自动重新构建。
export default defineConfig({
watch: true // 开发模式
});
// 或在命令行
// tsup src/index.ts --watch影响对比:
# 不使用 watch
tsup src/index.ts
# ✓ Build success
# (构建完成后退出)
# 使用 watch
tsup src/index.ts --watch
# ✓ Build success
# Watching for changes...
# (持续监听,文件改变时自动重新构建)
# 修改 src/index.ts
# ⚡ Rebuilding...
# ✓ Build success1.10 splitting(代码分割) #
作用:启用代码分割。
export default defineConfig({
splitting: true // 默认:false
});影响对比:
// 源代码
// src/index.ts
export { Button } from './components/Button';
export { Input } from './components/Input';
// src/components/Button.ts
import { shared } from './shared';
export const Button = () => { /* ... */ };
// src/components/Input.ts
import { shared } from './shared';
export const Input = () => { /* ... */ };
// ❌ splitting: false
dist/
└── index.js (包含所有代码,shared 代码重复)
// ✅ splitting: true
dist/
├── index.js (入口)
├── chunk-HASH.js (shared 代码)
├── Button.js
└── Input.js
// 优势:
// - shared 代码只打包一次
// - 按需加载
// - 减小包体积1.11 external(外部依赖) #
作用:排除不需要打包的依赖。
// 排除所有 dependencies
export default defineConfig({
external: [/.*/]
});
// 排除特定包
export default defineConfig({
external: ['react', 'react-dom']
});
// 排除所有 node_modules
export default defineConfig({
external: [/node_modules/]
});影响对比:
// package.json
{
"dependencies": {
"lodash": "^4.17.21"
}
}
// 源代码
import { debounce } from 'lodash';
export const myDebounce = debounce(() => {}, 300);
// ❌ 不配置 external
tsup src/index.ts
# dist/index.js (包含完整的 lodash 代码,体积大)
// ✅ external: ['lodash']
export default defineConfig({
external: ['lodash']
});
tsup src/index.ts
# dist/index.js (不包含 lodash,只有 require('lodash'))
// dist/index.js
const lodash = require('lodash');
exports.myDebounce = lodash.debounce(() => {}, 300);推荐配置:
// 库开发(排除所有 dependencies)
import { defineConfig } from 'tsup';
import pkg from './package.json';
export default defineConfig({
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {})
]
});
// 或者简单配置
export default defineConfig({
external: [/.*/] // 排除所有外部依赖
});1.12 noExternal(不外部化) #
作用:强制打包某些依赖。
export default defineConfig({
external: [/.*/], // 排除所有依赖
noExternal: ['lodash'] // 但打包 lodash
});1.13 globalName(全局变量名) #
作用:为 IIFE 格式指定全局变量名。
export default defineConfig({
format: ['iife'],
globalName: 'MyLib'
});影响对比:
// 源代码
export function greet() {
return 'Hello';
}
// 不配置 globalName
// dist/index.global.js
var MyModule = (function() {
// ...
})();
// globalName: 'MyLib'
// dist/index.global.js
var MyLib = (function() {
'use strict';
function greet() {
return 'Hello';
}
return { greet };
})();
// 使用
<script src="dist/index.global.js"></script>
<script>
console.log(MyLib.greet()); // 'Hello'
</script>1.14 platform(平台) #
作用:指定目标平台。
export default defineConfig({
platform: 'node' // 或 'browser', 'neutral'
});影响对比:
// platform: 'node'
// - 不打包 Node.js 内置模块(fs, path 等)
// - 优化 Node.js 环境
// platform: 'browser'
// - 打包所有依赖
// - 优化浏览器环境
// platform: 'neutral'
// - 不假定任何平台
// - 需要手动配置1.15 shims(垫片) #
作用:为某些功能添加垫片。
export default defineConfig({
shims: true // 自动添加必要的垫片
});支持的垫片:
// __dirname、__filename 垫片
// import.meta.url 垫片1.16 bundle(是否打包) #
作用:控制是否打包所有依赖。
// 打包所有依赖(默认)
export default defineConfig({
bundle: true
});
// 不打包,保留 import 语句
export default defineConfig({
bundle: false
});影响对比:
// 源代码
import { add } from './utils';
export const result = add(1, 2);
// bundle: true(默认)
// dist/index.js
function add(a, b) { return a + b; }
const result = add(1, 2);
exports.result = result;
// bundle: false
// dist/index.js
import { add } from './utils';
export const result = add(1, 2);
// (保留 import,不打包 utils)1.17 treeshake(Tree Shaking) #
作用:移除未使用的代码。
export default defineConfig({
treeshake: true // 默认开启
});
// 详细配置
export default defineConfig({
treeshake: {
preset: 'smallest' // 'smallest', 'safest', 'recommended'
}
});1.18 env(环境变量) #
作用:注入环境变量。
export default defineConfig({
env: {
NODE_ENV: 'production',
API_URL: 'https://api.example.com'
}
});在代码中使用:
// 源代码
console.log(process.env.NODE_ENV);
console.log(process.env.API_URL);
// 编译后
console.log('production');
console.log('https://api.example.com');1.19 inject(注入代码) #
作用:自动注入模块。
export default defineConfig({
inject: ['./react-shim.js'] // 自动注入 React
});1.20 banner 和 footer #
作用:在输出文件的开头/结尾添加内容。
export default defineConfig({
banner: {
js: '/* My Library v1.0.0 */',
css: '/* Styles */'
},
footer: {
js: '/* Copyright 2024 */'
}
});二、完整推荐配置 #
2.1 基础库配置 #
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
sourcemap: true,
target: 'es2018',
external: [/.*/] // 排除所有依赖
});对应的 package.json:
{
"name": "my-lib",
"version": "1.0.0",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"files": ["dist"],
"scripts": {
"build": "tsup",
"dev": "tsup --watch"
}
}2.2 React 组件库配置 #
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
sourcemap: true,
target: 'es2020',
external: ['react', 'react-dom'], // 排除 React
esbuildOptions(options) {
options.jsx = 'automatic'; // 使用新的 JSX 转换
}
});2.3 CLI 工具配置 #
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/cli.ts'],
format: ['esm'],
dts: true,
clean: true,
shims: true, // 添加 Node.js 垫片
platform: 'node',
target: 'node16',
banner: {
js: '#!/usr/bin/env node' // 添加 shebang
}
});对应的 package.json:
{
"name": "my-cli",
"bin": {
"my-cli": "./dist/cli.js"
},
"scripts": {
"build": "tsup"
}
}2.4 多入口配置 #
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: {
index: 'src/index.ts',
cli: 'src/cli.ts',
utils: 'src/utils/index.ts'
},
format: ['cjs', 'esm'],
dts: true,
clean: true,
splitting: true, // 代码分割
treeshake: true
});对应的 package.json:
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./cli": {
"types": "./dist/cli.d.ts",
"import": "./dist/cli.mjs",
"require": "./dist/cli.js"
},
"./utils": {
"types": "./dist/utils.d.ts",
"import": "./dist/utils.mjs",
"require": "./dist/utils.js"
}
}
}2.5 生产环境配置 #
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm', 'iife'],
dts: true,
clean: true,
sourcemap: false, // 生产环境不生成 source map
minify: true, // 压缩代码
target: 'es2018',
globalName: 'MyLib',
treeshake: {
preset: 'smallest' // 最激进的 tree shaking
}
});2.6 Monorepo 包配置 #
// packages/shared/tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
sourcemap: true,
external: [/.*/]
});
// packages/ui/tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
sourcemap: true,
external: ['@my-monorepo/shared'] // 排除同 monorepo 的包
});2.7 CSS 处理配置 #
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
injectStyle: true, // 将 CSS 注入到 JS 中
// 或者
// injectStyle: false, // 生成独立的 CSS 文件
});三、常用命令 #
3.1 基础命令 #
# 基础构建
tsup src/index.ts
# 指定多个入口
tsup src/index.ts src/cli.ts
# 使用 glob
tsup src/*.ts
# 指定输出目录
tsup src/index.ts --out-dir lib3.2 格式相关 #
# 指定输出格式
tsup src/index.ts --format cjs
tsup src/index.ts --format esm
tsup src/index.ts --format cjs,esm
tsup src/index.ts --format cjs,esm,iife
# 生成类型声明
tsup src/index.ts --dts
tsup src/index.ts --dts-only # 只生成类型3.3 开发相关 #
# 监听模式
tsup src/index.ts --watch
# 清理输出目录
tsup src/index.ts --clean
# 生成 source map
tsup src/index.ts --sourcemap
# 详细输出
tsup src/index.ts --verbose3.4 优化相关 #
# 压缩代码
tsup src/index.ts --minify
# 代码分割
tsup src/index.ts --splitting
# Tree shaking
tsup src/index.ts --treeshake
# 指定目标
tsup src/index.ts --target es2020
tsup src/index.ts --target node163.5 完整示例 #
# 完整构建命令
tsup src/index.ts \
--format cjs,esm \
--dts \
--clean \
--sourcemap \
--minify \
--target es2020
# package.json 脚本
{
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"build:prod": "tsup --minify --sourcemap false"
}
}四、常见问题和最佳实践 #
4.1 类型声明生成失败 #
问题:生成的 .d.ts 文件不完整或有错误。
解决方案:
// 方案 1:使用 dts 详细配置
export default defineConfig({
dts: {
resolve: true, // 解析外部类型
compilerOptions: {
strict: true
}
}
});
// 方案 2:检查 tsconfig.json
{
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": false
}
}
// 方案 3:排除问题文件
export default defineConfig({
dts: {
entry: 'src/index.ts',
exclude: ['**/*.test.ts']
}
});4.2 外部依赖打包问题 #
问题:dependencies 被打包进产物,导致体积过大。
解决方案:
// ❌ 错误:没有排除依赖
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm']
});
// ✅ 正确:排除所有依赖
import { defineConfig } from 'tsup';
import pkg from './package.json';
export default defineConfig({
external: [
...Object.keys(pkg.dependencies || {}),
...Object.keys(pkg.peerDependencies || {})
]
});
// 或者简单配置
export default defineConfig({
external: [/.*/] // 排除所有
});4.3 package.json 配置不匹配 #
问题:打包后的产物路径与 package.json 不一致。
解决方案:
// 确保 package.json 配置正确
{
"main": "./dist/index.js", // CJS 入口
"module": "./dist/index.mjs", // ESM 入口
"types": "./dist/index.d.ts", // 类型入口
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"files": ["dist"] // 发布时包含 dist 目录
}对应的 tsup 配置:
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
outDir: 'dist'
});4.4 CSS 处理 #
问题:不知道如何处理 CSS 文件。
解决方案:
// 方案 1:注入 CSS 到 JS
export default defineConfig({
injectStyle: true
});
// 使用:import './style.css' → CSS 自动注入
// 方案 2:生成独立 CSS 文件
export default defineConfig({
injectStyle: false
});
// 生成:dist/index.css
// 方案 3:使用 PostCSS
export default defineConfig({
injectStyle: true,
esbuildOptions(options) {
options.loader = {
'.css': 'css'
};
}
});4.5 环境变量替换 #
问题:需要在构建时替换环境变量。
解决方案:
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
env: {
NODE_ENV: process.env.NODE_ENV || 'development',
API_URL: process.env.API_URL || 'https://api.example.com'
}
});
// 或使用 define
export default defineConfig({
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'import.meta.env.VITE_API_URL': JSON.stringify(process.env.VITE_API_URL)
}
});4.6 Monorepo 配置 #
问题:在 Monorepo 中如何配置 tsup。
解决方案:
my-monorepo/
├── packages/
│ ├── shared/
│ │ ├── src/index.ts
│ │ ├── tsup.config.ts
│ │ └── package.json
│ └── ui/
│ ├── src/index.ts
│ ├── tsup.config.ts
│ └── package.json
└── apps/
└── web/shared 配置:
// packages/shared/tsup.config.ts
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
external: [/.*/] // 排除所有依赖
});ui 配置:
// packages/ui/tsup.config.ts
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
external: ['@my-monorepo/shared'] // 排除同 monorepo 的包
});在 Turborepo 中使用:
// turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}4.7 最佳实践 #
1. 使用配置文件 #
// ✅ 推荐:tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true
});
// ❌ 不推荐:长命令行
// tsup src/index.ts --format cjs,esm --dts --clean2. 排除外部依赖 #
// ✅ 库开发必须排除依赖
export default defineConfig({
external: [/.*/]
});
// ❌ 不排除会导致:
// - 打包体积大
// - 依赖重复安装
// - 版本冲突3. 正确配置 package.json #
{
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"files": ["dist"]
}4. 开发工作流 #
{
"scripts": {
"dev": "tsup --watch",
"build": "tsup",
"build:prod": "tsup --minify",
"prepublishOnly": "npm run build"
}
}5. 版本管理 #
// 使用 package.json 的版本
import pkg from './package.json';
export default defineConfig({
banner: {
js: `/* ${pkg.name} v${pkg.version} */`
}
});五、与其他工具对比 #
tsup vs tsc(TypeScript 编译器) #
| 特性 | tsup | tsc |
|---|---|---|
| 速度 | ⚡ 极快(esbuild) | ⚠️ 慢 |
| 打包 | ✅ 内置 | ❌ 不支持 |
| 多格式 | ✅ CJS/ESM/IIFE | ❌ 只有 target |
| 类型声明 | ✅ 自动生成 | ✅ 支持 |
| 配置 | ⭐⭐ 简单 | ⭐⭐⭐ 复杂 |
| 适用场景 | 库开发 | 类型检查 |
tsup vs Rollup #
| 特性 | tsup | Rollup |
|---|---|---|
| 速度 | ⚡ 极快 | ⚠️ 中等 |
| 配置 | ⭐⭐ 简单 | ⭐⭐⭐⭐ 复杂 |
| 插件 | ⚠️ 有限 | ✅ 丰富 |
| 类型声明 | ✅ 内置 | ⚠️ 需要插件 |
| Tree Shaking | ✅ 优秀 | ✅ 优秀 |
| 学习曲线 | ⭐ 低 | ⭐⭐⭐ 高 |
tsup vs Vite(Library Mode) #
| 特性 | tsup | Vite |
|---|---|---|
| 速度 | ⚡ 极快 | ⚡ 快 |
| 配置 | ⭐⭐ 简单 | ⭐⭐⭐ 中等 |
| 类型声明 | ✅ 内置(简单) | ⚠️ 需插件 |
| 适用场景 | 纯 TS 库 | 应用+库 |
| 功能 | 🎯 专注打包 | 🌟 全能 |
推荐选择 #
纯 TypeScript 库 → tsup ⭐⭐⭐⭐⭐
- 零配置
- 速度快
- 类型声明自动生成
带 CSS 的组件库 → Vite ⭐⭐⭐⭐
- CSS 处理更好
- 插件生态丰富
复杂库项目 → Rollup ⭐⭐⭐
- 完全控制
- 丰富的插件
只需要编译 → tsc ⭐⭐
- 类型检查
- 不需要打包六、实际案例 #
案例 1:工具函数库 #
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
sourcemap: true,
target: 'es2018',
external: [/.*/]
});// package.json
{
"name": "@my/utils",
"version": "1.0.0",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
},
"scripts": {
"build": "tsup",
"dev": "tsup --watch"
}
}案例 2:React Hooks 库 #
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
sourcemap: true,
external: ['react'],
esbuildOptions(options) {
options.jsx = 'automatic';
}
});案例 3:CLI 工具 #
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/cli.ts'],
format: ['esm'],
dts: false,
clean: true,
shims: true,
platform: 'node',
target: 'node16',
banner: {
js: '#!/usr/bin/env node'
}
});// package.json
{
"name": "my-cli",
"version": "1.0.0",
"bin": {
"my-cli": "./dist/cli.js"
},
"scripts": {
"build": "tsup",
"prepublishOnly": "npm run build"
}
}七、总结 #
核心优势 #
- 零配置:开箱即用,无需复杂配置
- 极速构建:基于 esbuild,速度极快
- 类型声明:自动生成,无需额外插件
- 多格式输出:支持 CJS、ESM、IIFE
- 简单易用:学习成本低,上手快
最小配置 #
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true
});推荐配置 #
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true,
clean: true,
sourcemap: true,
target: 'es2018',
external: [/.*/],
minify: process.env.NODE_ENV === 'production'
});何时使用 tsup #
✅ 适合:
- 纯 TypeScript 工具库
- React Hooks 库
- Node.js 工具
- CLI 工具
- 小型库项目
❌ 不适合:
- 需要复杂打包配置
- 大量自定义插件
- 带复杂 CSS 的组件库(推荐 Vite)
学习建议 #
- 从命令行开始:
tsup src/index.ts --format cjs,esm --dts - 逐步添加配置:创建
tsup.config.ts - 配置 package.json:正确导出模块
- 集成到工作流:配置 npm scripts
- 发布到 npm:测试和发布
参考资源 #
🎉 使用 tsup,让 TypeScript 库打包变得简单快速!