文章目录
- 能打包什么?
- 安装
- 用法
- 自定义配置文件
- 条件配置
- 在 package.json 中配置
- 多入口打包
- 生成类型声明文件
- sourcemap
- 生成格式
- 自定义输出文件
- 代码分割
- 产物目标环境
- 支持 es5
- 编译的环境变量
- 对开发命令行工具友好
- 监听模式 watch
- 提供成功构建的钩子 onSuccess
- 压缩产物 minify
- 自定义 loader
- Tree shaking
- 类型检查
- 支持 CSS (实验性功能)
- 元数据文件 metafile
- 自定义 esbuild 插件和配置
- 注入 cjs 和 esm shims (垫片)
- 复制文件到输出目录
- 在项目中使用 tsup
- 常见问题
- error: No matching export in "xxx.ts" for import "xxx"
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}
来修复此问题。