项目文件夹

文件
2026-07-13 21:35:40 +08:00

150 行
4.0 KiB
Markdown

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
---
name: library-development
description: 使用 tsdown 构建和发布 TypeScript 库。适用于创建 npm 包、配置库打包或设置 package.json 导出字段时。
---
# 库开发
| 方面 | 选择 |
|--------|--------|
| 打包工具 | tsdown |
| 输出格式 | 仅纯 ESM(无 CJS) |
| 类型声明 | 通过 tsdown 生成 |
| 导出 | 通过 tsdown 自动生成 |
## tsdown 配置
使用 tsdown 并启用以下选项:
```ts
// tsdown.config.ts
import { defineConfig } from 'tsdown'
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
exports: true,
})
```
| 选项 | 值 | 用途 |
|--------|-------|---------|
| `format` | `['esm']` | 纯 ESM,无 CommonJS |
| `dts` | `true` | 生成 `.d.ts` 文件 |
| `exports` | `true` | 自动更新 `package.json` 中的 `exports` 字段 |
### 多个入口点
```ts
export default defineConfig({
entry: [
'src/index.ts',
'src/utils.ts',
],
format: ['esm'],
dts: true,
exports: true,
})
```
当运行 `tsdown` 时,`exports: true` 选项会自动生成 `package.json` 中的 `exports` 字段。
---
## API 稳定性
对于已发布的库,锁定公开 API 表面,使意外的破坏性变更在代码审查中以差异形式显现。
| 工具 | 用途 |
|------|---------|
| [`tsnapi`](https://github.com/antfu/tsnapi) | 通过 Vitest 将运行时导出与类型声明快照到已提交的 `.snapshot.js` / `.snapshot.d.ts` 文件中 |
| [`tsdown-stale-guard`](https://github.com/antfu-collective/tsdown-stale-guard) | 记录构建输入/输出哈希值,以便针对过时构建运行测试时快速失败 |
将两者安装为开发依赖。将 `tsdown-stale-guard` 作为 tsdown 插件接入,使每次构建都记录其哈希值:
```ts
// tsdown.config.ts
import { defineConfig } from 'tsdown'
import { StaleGuardRecorder } from 'tsdown-stale-guard'
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
exports: true,
plugins: [
StaleGuardRecorder(),
],
})
```
从 Vitest 测试中快照公开 API——优先使用 Vitest 辅助函数而非 tsdown/Rolldown 插件,以便构建配置专注于构建。每个文件通过一个文件级别的 `beforeAll(guardStaleBuild)` 来确保构建是最新的,从而使已提交的 `dist/` 永远不会与源码脱节:
```ts
// test/api.test.ts(单个包)
import { beforeAll } from 'vitest'
import { snapshotApiPerEntry } from 'tsnapi/vitest'
import { guardStaleBuild } from 'tsdown-stale-guard'
beforeAll(async () => {
await guardStaleBuild()
})
await snapshotApiPerEntry(new URL('..', import.meta.url).pathname)
```
对于 monorepo,将 `describePackagesApiSnapshots()` 包裹在 `describe` 中,使每个包的测试套件共享一个过时构建检测门:
```ts
// test/api.test.tsmonorepo,从根目录运行)
import { beforeAll, describe } from 'vitest'
import { describePackagesApiSnapshots } from 'tsnapi/vitest'
import { guardStaleBuild } from 'tsdown-stale-guard'
describe('packages api', async () => {
beforeAll(async () => {
await guardStaleBuild()
})
await describePackagesApiSnapshots()
})
```
### 更新快照
在有意的 API 变更之后,一次性重建并更新两个快照:
```bash
nr build # 重新生成 dist/ 和过时构建哈希值
nr test -u # 更新 .snapshot.js / .snapshot.d.ts 文件
```
---
## package.json
纯 ESM 库的必填字段:
```json
{
"type": "module",
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"files": ["dist"],
"scripts": {
"build": "tsdown",
"prepack": "pnpm build",
"test": "vitest",
"release": "bumpp -r"
}
}
```
`exports: true` 时,`exports` 字段由 tsdown 管理。
### prepack 脚本
对于每个公开包,在 `scripts` 中添加 `"prepack": "pnpm build"`。这确保在发布前(例如运行 `npm publish``pnpm publish` 时)自动构建该包。这可以防止意外发布过时或缺失的构建产物。