tsup
Bundle your TypeScript library with no config, powered by esbuild.
tsup 号称不用配置文件,打包 ts 库。底层为 esbuild。tsup 这么宣传,说明它肯定做了很多预设封装,不需要太多配置,开箱即用。
rollup 已经是牛夫人了。
能打包什么?
node.js 支持的都能打包,.js, .json, .mjs.。加上 ts 相关的 .ts, .tsx。
安装
npm i tsup -D
# Or Yarn
yarn add tsup --dev
# Or pnpm
pnpm add tsup -D
用法
tsup [...files]
默认打包到 ./dist
。
能自动排除 node_modules 。
默认打包为 es5,CommonJS,
eg:
tsup src/index.ts src/cli.ts
生成 dist/index.js 和 dist/cli.js
自定义配置文件
可选的文件:
- tsup.config.ts
- tsup.config.js
- tsup.config.cjs
- tsup.config.json
–config 配置项可指定配置文件。
import { defineConfig } from 'tsup'
export default defineConfig({
entry: ['src/index.ts'],
splitting: false,
sourcemap: true,
clean: true,
})
条件配置
导出一个函数,tsup cli 会执行这个函数。
import { defineConfig } from 'tsup'
export default defineConfig((options) => {
return {
minify: !options.watch,
}
})
在 package.json 中配置
{
"tsup": {
"entry": ["src/index.ts"],
"splitting": false,
"sourcemap": true,
"clean": true
},
"scripts": {
"build": "tsup"
}
}
多入口打包
export default defineConfig({
// Outputs `dist/a.js` and `dist/b.js`.
entry: ['src/a.ts', 'src/b.ts'],
// Outputs `dist/foo.js` and `dist/bar.js`
entry: {
foo: 'src/a.ts',
bar: 'src/b.ts',
},
})
生成类型声明文件
tsup index.ts --dts
sourcemap
tsup index.ts --sourcemap
生成格式
支持 esm,cjs (默认) 和 iife。
tsup src/index.ts --format esm,cjs,iife
dist
├── index.mjs # esm
├── index.global.js # iife
└── index.js # cjs
如果 package.json 的 type 字段为 module。命名会变成:
dist
├── index.js # esm
├── index.global.js # iife
└── index.cjs # cjs
自定义输出文件
export default defineConfig({
outExtension({ format }) {
return {
js: `.${format}.js`,
}
},
})
代码分割
默认支持 esm ,并且默认开启。CJS 需要 --splitting 手动开启。
产物目标环境
target 选项配置构建产物的目标环境,默认是 node16。
可支持:
- chrome
- edge
- firefox
- hermes
- ie
- ios
- node
- opera
- rhino
- safari
target 也可以指定 js 版本,比如 es2020,es5。
支持 es5
esbuild 是不支持 es5 的。tsup 是怎么做到的?
tsup 会先打包成 es2020,然后通过 SWC 转成 es5。
编译的环境变量
tsup src/index.ts --env.NODE_ENV production
对开发命令行工具友好
当入口文件如 src/cli.ts 包含 hashbang(即 #!/bin/env node)时,tsup 会自动使输出文件可执行,因此您无需运行 chmod +x dist/cli.js 来手动设置可执行权限。
监听模式 watch
tsup src/index.ts --watch
可选择忽视监听的文件:
tsup src src/index.ts --watch --ignore-watch folder1 --ignore-watch folder2
提供成功构建的钩子 onSuccess
这在监听模式下非常有用。
import { defineConfig } from 'tsup'
export default defineConfig({
async onSuccess() {
// Start some long running task
// Like a server
const server = http.createServer((req, res) => {
res.end('Hello World!')
})
server.listen(3000)
return () => {
server.close()
}
},
})
压缩产物 minify
tsup src/index.ts --minify
默认是用 esbuild 压缩,也可以用 terser 代替。前提必须安装 terser。
npm install -D terser
tsup src/index.ts --minify terser
在 tsup.config.js 中,您可以传递 terserOptions,这些选项将原封不动地传递给 terser.minify。
自定义 loader
esbuild 提供这些 loader:
type Loader =
| 'js'
| 'jsx'
| 'ts'
| 'tsx'
| 'css'
| 'json'
| 'text'
| 'base64'
| 'file'
| 'dataurl'
| 'binary'
| 'copy'
| 'default'
会发现上面没有图片的 loader,但我们可以指定 loader 去处理某些后缀的图片。
import { defineConfig } from 'tsup'
export default defineConfig({
loader: {
'.jpg': 'base64',
'.webp': 'file',
},
})
Tree shaking
esbuild 默认开启了 tree shaking。但它的 tree shaking 可能有问题,所以 tsup 允许你选择 rollup 的 tree shaking 去替代 esbuild。
import { defineConfig } from 'tsup'
export default defineConfig({
treeshake: true, // 使用 rollup tree shaking
}
类型检查
esbuild 之所以快,就是因为它不会执行 ts 类型检查。你应该依靠 IDE 在完成类型检查。
如果你就想在构建时,执行类型检查,可以开启 --dts。
它会生成类型声明文件,自然也会进行类型检查。
支持 CSS (实验性功能)
元数据文件 metafile
传递 --metafile 标志,告诉 esbuild 以 JSON 格式生成一些关于构建的元数据。您可以将输出文件提供给像 bundle buddy 这样的分析工具,以可视化您的包中的模块以及每个模块占用多少空间。
生成的文件格式为:metafile-{format}.json
。eg:metafile-cjs.json
自定义 esbuild 插件和配置
import { defineConfig } from 'tsup'
export default defineConfig({
esbuildPlugins: [YourPlugin],
esbuildOptions(options, context) {
options.define.foo = '"bar"'
},
})
注入 cjs 和 esm shims (垫片)
shims 本意为垫片,类似于补丁的意思
启用此选项将在构建 esm/cjs 项目时填充一些代码以使其工作。
例如 __dirname
仅在 cjs 模块中可用,而 import.meta.url
仅在 esm 模块中可用。打完补丁后,esm 中就也能用 __dirname 了。
import { defineConfig } from 'tsup'
export default defineConfig({
shims: true,
})
打的补丁具体是什么样的?
CJS 项目会添加如下代码:
// import.meta.url:
(import.meta.url as typeof document) === "undefined"
? new URL("file:" + __filename).href
: (document.currentScript && document.currentScript.src) || new URL("main.js", document.baseURI).href;
esm 项目会添加如下代码:
// __dirname
path.dirname(fileURLToPath(import.meta.url))
vite 早期配置中就得使用这段代码。现在可以直接使用 __diranme 了。看来也是把这段代码作为了补丁,默认提供。
复制文件到输出目录
使用 --publicDir 将 ./public 文件夹内的文件复制到输出目录。
您还可以使用 --publicDir [dir]
来选择一个自定义目录。
在项目中使用 tsup
tsup 提供了 js API,可以在 js 中使用。
import { build } from 'tsup'
await build({
entry: ['src/index.ts'],
sourcemap: true,
dts: true,
})
常见问题
error: No matching export in “xxx.ts” for import “xxx”
当您在 tsconfig 中启用了emitDecoratorMetadata
时,通常会发生这种情况。
在这种模式下,我们使用 SWC 将装饰器转换为 JavaScript,因此导出的类型将被消除,这就是为什么 esbuild 无法找到相应的导出。
您可以通过将 import 语句从 import {SomeType}
更改为import {type SomeType}
或import type {SomeType}
来修复此问题。