首页
/ airi 单体仓库的库构建实践:tsdown(Rolldown/Oxc)完整使用指南

airi 单体仓库的库构建实践:tsdown(Rolldown/Oxc)完整使用指南

2026-09-07 16:44:25作者:吴年前Myrtle

本文基于 airi 仓库内置的 Agent 技能文档 .agents/skills/tsdown/SKILL.md 展开,系统讲解 tsdown——一个由 Rolldown 与 Oxc 驱动的高性能 TypeScript/JavaScript 库打包器的配置体系、构建选项、依赖处理策略、CLI 用法与常见模式,并结合 airi 仓库中 30 余个包的真实 tsdown.config.ts 配置,展示这套构建工具在 pnpm + Turbo 单体仓库中的落地方式。读完后你能够掌握:如何为 npm 库生成 ESM/CJS/IIFE/UMD 多格式产物与类型声明,如何精细控制依赖打包边界,以及如何在 monorepo 中以统一配置批量构建并发布包。

tsdown 是什么,什么时候用它

tsdown 定位为 "The Elegant Library Bundler":一个由 Rolldown 和 Oxc 提供动力的库打包器,核心场景是构建发布到 npm 的 TypeScript/JavaScript 库。根据其技能文档 SKILL.md 的描述,典型使用场景包括:

  • 为 npm 构建 TypeScript/JavaScript 库;
  • 生成 TypeScript 声明文件(.d.ts);
  • 多格式打包(ESM、CJS、IIFE、UMD);
  • 通过 tree shaking 与 minification 优化包体积;
  • 以最小改动从 tsup 迁移(提供 npx tsdown-migrate 命令);
  • 构建 React、Vue、Solid 或 Svelte 组件库。

运行环境要求(重要前提)

文档对运行环境有一条关键且容易被误解的要求:tsdown 本身需要 Node.js 22.18.0 及以上版本才能运行,但这只是构建时(build-time)要求。打包产物可以通过 target 选项指向低得多的 Node.js 版本,因此用 tsdown 构建的库并不会被锁死在 Node.js 22+ 运行时

如果你的包需要同时支持 Node.js 18 / 20,文档给出的推荐工作流是:

  • 在 CI 中用 Node.js 22+ 构建(例如设置 target: 'node18'target: 'node20');
  • 在低版本 Node.js 上测试构建产物(或打包好的 tarball)——例如用矩阵任务在 Node.js 18 / 20 / 22 上分别跑发布包的测试。

这一点在 airi 仓库中可以直接得到印证:packages/cap-vite/tsdown.config.ts 中显式配置了 target: 'node18',即构建工具运行在新版 Node 上,而产物面向 Node 18 环境:

// packages/cap-vite/tsdown.config.ts
import { defineConfig } from 'tsdown'

export default defineConfig({
  entry: {
    'index': 'src/index.ts',
    'bin/run': 'src/bin/run.ts',
    'vite-plugin': 'src/vite-plugin.ts',
    'vite-wrapper-config': 'src/vite-wrapper-config.ts',
  },
  target: 'node18',       // 产物面向 Node 18,构建时不要求运行时也是 18
  outDir: 'dist',
  dts: true,
  sourcemap: true,
})

此外,guide-getting-started 提到 tsdown 对 Deno 与 Bun 提供实验性支持。

快速开始

安装并做第一次构建只需四步(命令均来自 SKILL.md 的 Quick Start 一节):

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

# 基本用法:无参数,读取默认 tsdown.config.ts
npx tsdown

# 指定配置文件
npx tsdown --config tsdown.config.ts

# 监听模式
npx tsdown --watch

# 从 tsup 迁移
npx tsdown-migrate

最小的配置文件(tsdown.config.ts)如下:

import { defineConfig } from 'tsdown'

export default defineConfig({
  entry: ['./src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  clean: true,
})

defineConfig 提供类型推导;entry 指定入口,format 指定输出格式数组,dts 开启声明文件生成,clean 在构建前清理输出目录。

核心参考文档索引

技能目录 .agents/skills/tsdown/references/ 下共有 35 个参考文件,按 guide-*option-*advanced-*recipe-*reference-* 前缀分类(见 references/README.md)。核心参考条目如下:

主题 说明 参考文件
快速上手 安装、首次构建、CLI 基础 guide-getting-started
配置文件 配置文件格式、多配置、workspace option-config-file
CLI 参考 全部 CLI 命令与选项 reference-cli
从 tsup 迁移 迁移指南与兼容性说明 guide-migrate-from-tsup
插件 Rolldown、Rollup、Unplugin 支持 advanced-plugins
Hooks 生命周期钩子,注入自定义逻辑 advanced-hooks
编程式 API 从 Node.js 脚本发起构建 advanced-programmatic
Rolldown 选项 直接向 Rolldown 透传选项 advanced-rolldown-options
CI 环境 CI 检测、'ci-only' / 'local-only' 取值 advanced-ci

原文档还建议:如需完整的选项映射迁移辅助,可安装专门的 tsdown-migrate 技能(npx skills add rolldown/tsdown --skill tsdown-migrate)。当前仓库 .agents/skills/ 目录下尚未包含该技能目录,迁移时以 npx tsdown-migrate 命令为准。

构建选项详解

SKILL.md 的 Build Options 表格覆盖了最常用的构建参数,这里完整保留并补充说明:

选项 用法 参考
入口 entry: ['src/*.ts', '!**/*.test.ts'] option-entry
输出格式 format: ['esm', 'cjs', 'iife', 'umd'] option-output-format
输出目录 outDir: 'dist'outExtensions option-output-directory
类型声明 dts: truedts: { sourcemap, compilerOptions, vue } option-dts
目标环境 target: 'es2020'target: 'esnext' option-target
平台 platform: 'node'platform: 'browser' option-platform
Tree shaking treeshake: true 及自定义选项 option-tree-shaking
压缩 minify: trueminify: 'dce-only'option-minification 中取值还包含完整 MinifyOptions 对象) option-minification
Source map sourcemap: true'inline''hidden' option-sourcemap
监听模式 watch: true 及 watch 选项 option-watch-mode
清理 clean: true 与清理模式 option-cleaning
日志级别 logLevel: 'silent'failOnWarn: false option-log-level

其中几个值得注意的细节:

  • 入口支持 glob 与排除模式'!**/*.test.ts' 这种写法在 airi 中被直接采用,例如 packages/i18n/tsdown.config.ts 使用对象形式的多入口:
// packages/i18n/tsdown.config.ts(节选)
export default defineConfig({
  entry: {
    'index': 'src/index.ts',
    'locales/index': 'src/locales/index.ts',
    'locales/en/index': 'src/locales/en/index.ts',
    'locales/zh-Hans/index': 'src/locales/zh-Hans/index.ts',
  },
  copy: [{ from: 'src/locales', to: 'dist/locales' }],
  unbundle: true,
  plugins: [Yaml()],   // unplugin-yaml/rolldown,编译 YAML 翻译文件
})

这里同时用到了 copy(把 locale 资源目录原样复制到 dist)、unbundle 与插件系统(Yaml() 来自 unplugin-yaml/rolldown),印证了 advanced-plugins 中 "支持 Rolldown / Rollup / Unplugin 插件" 的能力。

  • dts 支持对象配置dts: { sourcemap, compilerOptions, vue } 允许自定义声明文件生成的编译器选项,对 Vue 库还可走 vue-tsc 路径(见 recipe-vue)。

依赖处理:控制打包边界

库打包最关键的决策之一是"哪些模块进 bundle、哪些保持 external"。SKILL.md 给出的 deps 配置族如下:

特性 用法
永不打包 deps: { neverBundle: ['react', /^@myorg\//] }(支持正则)
总是打包 deps: { alwaysBundle: ['dep-to-bundle'] }
白名单打包 deps: { onlyBundle: ['cac', 'bumpp'] }
跳过 node_modules deps: { skipNodeModulesBundle: true }
自动 external 依赖 / peerDependencies / optionalDependencies 自动外置

详细说明见 option-dependencies

airi 中的 packages/audio/tsdown.config.ts 展示了另一种更直接的外置方式——通过 external 数组保留带 Vite 风格查询参数的虚拟模块(worker / url 资源):

// packages/audio/tsdown.config.ts
import { defineConfig } from 'tsdown'

export default defineConfig({
  entry: {
    'index': 'src/index.ts',
    'audio-context/index': 'src/audio-context/index.ts',
    'audio-context/processor.worklet': 'src/audio-context/processor.worklet.ts',
    'encoding/index': 'src/encoding/index.ts',
  },
  unbundle: true,
  external: [
    '@alexanderolsen/libsamplerate-js/dist/libsamplerate.worklet.js?worker&url',
    './processor.worklet?worker&url',
  ],
})

这个包构建音频管线,入口里有 ?worker&url 形式的虚拟导入;如果不把它们标记为 external,打包器会尝试解析并内联这些不存在的"文件路径"。这与文档中 "Auto external 自动外置 + 手动 external 兜底" 的组合策略一致。

输出增强选项

特性 用法 说明
Shims shims: true 添加 ESM/CJS 兼容垫片(如 __dirname__filename
CJS default cjsDefault: true(默认)/ false 控制 CJS 输出的 default 导出行为
Package exports exports: true 自动生成 package.jsonexports 字段
CSS 处理 [实验性] css: { ... } 完整 CSS 管线:预处理器、Lightning CSS、PostCSS、CSS Modules、代码分割;需要 @tsdown/css
CSS Modules css: { modules: { localsConvention: 'camelCase' } } .module.css 的 scoped 类名
CSS inject css: { inject: true } 在 JS 产物中保留 CSS 导入
Unbundle 模式 unbundle: true 保留源码目录结构,逐文件产出
Root 目录 root: 'src' 控制入口路径到输出路径的映射(类似 TS rootDir
可执行文件 [实验性] exe: true 打包为独立可执行文件,跨平台构建依赖 @tsdown/exe
包校验 publint: trueattw: true 发布前校验包结构 / 类型正确性

对应参考:option-shimsoption-cjs-defaultoption-package-exportsoption-cssoption-unbundleoption-rootoption-exeoption-lint

关于 exe(基于 Node.js Single Executable Applications)的几个硬性约束,reference-cli 中写得很明确:需要 Node.js >= 25.5.0,不支持 Bun/Deno;开启后默认格式变为 cjs(Node.js >= 25.7.0 除外)、dts 默认关闭、代码分割被禁用,且仅支持单入口。

框架与运行时支持

框架 指南
React JSX 转换、React Compiler:recipe-react
Vue SFC 支持、JSX:recipe-vue
Solid SolidJS JSX 转换:recipe-solid
Svelte Svelte 组件库(推荐源码分发方式):recipe-svelte
WASM 通过 rolldown-plugin-wasm 处理 WebAssembly 模块:recipe-wasm

常见配置模式(可直接复制)

以下模式全部来自 SKILL.md 的 Common Patterns 与 Configuration Features 章节,保持原文写法以便直接复用。

1. 基础库打包(ESM + CJS + 类型)

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  clean: true,
})

2. 多入口

export default defineConfig({
  entry: {
    index: 'src/index.ts',
    utils: 'src/utils.ts',
    cli: 'src/cli.ts',
  },
  format: ['esm', 'cjs'],
  dts: true,
})

airi 的 packages/core-agent/tsdown.config.ts 是数组形式的多入口简化版:entry: ['src/index.ts', 'src/agents/spark-notify/index.ts']dts: true,说明多入口 + 声明生成是该仓库库包的标准配置。

3. 浏览器库(IIFE/UMD)

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['iife'],
  globalName: 'MyLib',
  platform: 'browser',
  minify: true,
})

4. React 组件库(自动 JSX + 外置 react)

export default defineConfig({
  entry: ['src/index.tsx'],
  format: ['esm', 'cjs'],
  dts: true,
  deps: {
    neverBundle: ['react', 'react-dom'],
  },
  inputOptions: {
    jsx: { runtime: 'automatic' },
  },
})

inputOptions 即"直接向 Rolldown 透传选项"的通道(advanced-rolldown-options)。

5. 保留目录结构(unbundle)

export default defineConfig({
  entry: ['src/**/*.ts', '!**/*.test.ts'],
  unbundle: true, // 保留文件结构
  format: ['esm'],
  dts: true,
})

unbundle(bundleless)模式下每个源文件独立产出,目录结构原样保留,适合文件数量多、消费者按需 import 的工具库;airi 的 audioi18n 包都采用此模式。

6. CI 感知配置(ci-only

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  failOnWarn: 'ci-only',  // 仅在 CI 中把警告升级为失败
  publint: 'ci-only',
  attw: 'ci-only',
})

'ci-only' / 'local-only' 是 tsdown 的 CI 环境检测取值:同一份配置在本地开发时不阻断,在 CI 中严格校验。细节见 advanced-ci

7. WASM 支持

import { wasm } from 'rolldown-plugin-wasm'
import { defineConfig } from 'tsdown'

export default defineConfig({
  entry: ['src/index.ts'],
  plugins: [wasm()],
})

8. 带 CSS 与 Sass 的库

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  target: 'chrome100',
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "src/styles/variables" as *;`,
      },
    },
  },
})

实验性 CSS 管线需要 @tsdown/css 依赖,支持预处理、Lightning CSS、PostCSS、CSS Modules 与代码分割(option-css)。

9. 独立可执行文件与跨平台构建

// 单机打包为可执行文件
export default defineConfig({
  entry: ['src/cli.ts'],
  exe: true,
})

// 跨平台构建(需要 @tsdown/exe)
export default defineConfig({
  entry: ['src/cli.ts'],
  exe: {
    targets: [
      { platform: 'linux', arch: 'x64', nodeVersion: '25.7.0' },
      { platform: 'darwin', arch: 'arm64', nodeVersion: '25.7.0' },
      { platform: 'win', arch: 'x64', nodeVersion: '25.7.0' },
    ],
  },
})

10. Hooks 注入生命周期逻辑

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
  hooks: {
    'build:before': async (context) => {
      console.log('Building...')
    },
    'build:done': async (context) => {
      console.log('Build complete!')
    },
  },
})

钩子系统的完整事件列表见 advanced-hooks

11. 多配置与条件配置

导出数组即可得到多份互不干扰的构建配置:

export default defineConfig([
  {
    entry: ['src/index.ts'],
    format: ['esm', 'cjs'],
    dts: true,
  },
  {
    entry: ['src/cli.ts'],
    format: ['esm'],
    platform: 'node',
  },
])

导出函数则可获得动态配置能力(回调参数含 watch 等运行信息):

export default defineConfig((options) => {
  const isDev = options.watch
  return {
    entry: ['src/index.ts'],
    format: ['esm', 'cjs'],
    minify: !isDev,
    sourcemap: isDev,
  }
})

12. Workspace / Monorepo 模式

export default defineConfig({
  workspace: 'packages/*',
  entry: ['src/index.ts'],
  format: ['esm', 'cjs'],
  dts: true,
})

配合 CLI 的 -W / -F 可以按包过滤构建(见下文 CLI 速查)。

airi 仓库中的真实落地:pnpm catalog + Turbo 批量构建

airi 是一个规模不小的 pnpm 单体仓库(工作区定义见 pnpm-workspace.yamlpackage.jsonworkspaces 字段,覆盖 packages/plugins/integrations/services/apps/server/ 等目录)。tsdown 在其中是统一的标准库构建器:

  1. 版本统一管理:根 package.jsondevDependencies 中以 pnpm catalog 方式声明 "tsdown": "catalog:",各工作区包共享同一版本。
  2. 构建编排:根脚本 build:packagesturbo run build -F="./packages/*",即通过 Turbo 按依赖图并行调度每个包的 build(内部就是 tsdown)。postinstall 钩子还会自动触发 pnpm run build:packages,保证首次安装后即可开发。
  3. 逐包配置:仓库内共有 30 个以上 tsdown.config.ts,分布在 packages/(如 audiocap-vitei18ncore-agent)、integrations/vscode/plugins/server/packages/services/computer-use-mcp/

几个典型配置体现的取舍:

关键配置 对应的文档概念
packages/cap-vite target: 'node18'dts: truesourcemap: true 产物低版本目标(option-target
packages/audio unbundle: trueexternal: [...'?worker&url'] unbundle 模式 + 手动 external
packages/i18n copyplugins: [Yaml()]unbundle: true 资源复制 + Unplugin 插件
packages/core-agent 数组多入口 + dts: true 多入口 + 声明生成
packages/plugin-protocol sourcemap: trueunused: true source map + 未使用依赖检查(--unused

注意 packages/plugin-protocol/tsdown.config.ts 中的 unused: true:它对应 CLI 的 --unused("check for unused dependencies"),用于在构建时发现 package.json 中声明了却未实际引用的依赖,是发布前 hygiene 检查的一环。

CLI 速查与标志映射规则

标志映射规则

所有 CLI 标志都可以在配置文件中设置,且 CLI 标志优先于配置文件。映射规则(来自 reference-cli):

  • --foo 等价于 foo: true
  • --no-foo 等价于 foo: false
  • --foo.bar 等价于 foo: { bar: true }
  • 同名标志可重复出现以累积数组:--format esm --format cjs 等价于 format: ['esm', 'cjs']
  • 标志同时支持 camelCase 与 kebab-case(--outDir--out-dir 等价)。

常用命令

以下命令块继承自 SKILL.md 的 CLI Quick Reference:

# 基本命令
tsdown                          # 构建一次
tsdown --watch                  # 监听模式
tsdown --config custom.ts       # 自定义配置
npx tsdown-migrate              # 从 tsup 迁移

# 输出选项
tsdown --format esm,cjs         # 多格式
tsdown -d lib                   # 自定义输出目录(--out-dir)
tsdown --minify                 # 启用压缩
tsdown --dts                    # 生成类型声明
tsdown --exe                    # 打包为独立可执行文件
tsdown --unbundle               # bundleless 模式

# 入口选项
tsdown src/index.ts             # 单入口
tsdown src/*.ts                 # glob 模式
tsdown src/a.ts src/b.ts        # 多入口

# Workspace / Monorepo
tsdown -W                       # 开启 workspace 模式
tsdown -W -F my-package         # 过滤指定包
tsdown --filter /^pkg-/        # 正则过滤

# 开发
tsdown --watch                  # 监听模式
tsdown --sourcemap              # 生成 source map
tsdown --clean                  # 清理输出目录
tsdown --from-vite              # 复用 Vite 配置
tsdown --tsconfig tsconfig.build.json  # 自定义 tsconfig

更多高频标志

reference-cli 还列出了完整的标志全集,值得留意的一组是:

  • 编译期环境变量--env.NODE_ENV=production --env.API_URL=...(运行时通过 import.meta.env.*process.env.* 访问)、--env-file .env.production--env-prefix TSDOWN_(可重复,默认前缀 TSDOWN_);
  • 资源复制--copy public --copy assets
  • 包管理--exports(自动生成 exports 字段)、--publint--attw--unused
  • 日志--log-level error--no-report(关闭体积报告)、--debug rolldown
  • 监听增强--ignore-watch test--on-success "echo Build complete!"
  • 集成--from-vite(复用 vite.config.*,加 vitest 参数则复用 vitest.config.*);
  • 其他--config-loader unrun(配置加载器可选 auto / native / unrun)、--no-config--root src--no-fail-on-warn

典型组合用法:

# 库(ESM + CJS + 类型)
tsdown --format esm --format cjs --dts --clean

# 生产构建
tsdown --minify --clean --no-report

# 浏览器 IIFE
tsdown --format iife --platform browser --minify

# Node CLI 工具
tsdown --format esm --platform node --shims

# Monorepo 包
tsdown --clean --dts --exports --publint

最佳实践

SKILL.md 给出的 9 条最佳实践,完整保留:

  1. TypeScript 库务必生成类型声明{ dts: true }
  2. 外置依赖,避免打包不必要的代码:{ deps: { neverBundle: [/^react/, /^@myorg\//] } }
  3. 启用 tree shaking 以获得最优包体积:{ treeshake: true }
  4. 生产构建启用压缩{ minify: true }
  5. 添加 shims 改善 ESM/CJS 兼容:{ shims: true }(补充 __dirname__filename 等)
  6. 自动生成 package.json exports{ exports: true }
  7. 开发期使用监听模式tsdown --watch
  8. 多文件工具库保留目录结构{ unbundle: true }
  9. CI 中发布前校验包{ publint: 'ci-only', attw: 'ci-only' }

小结

tsdown 的设计可以概括为三层:面向 npm 库的一等公民选项(format / dts / deps / exports)、面向平台差异的 target + platform 双轴(构建时 Node 22.18+ 与运行时低版本解耦)、以及面向复杂场景的逃生通道(pluginsinputOptionshooksworkspace)。在 airi 仓库中,它通过 pnpm catalog 统一版本、Turbo 统一调度,在 30 余个包上以 cap-viteaudioi18n 等各不相同又风格一致的 tsdown.config.ts 落地,是"统一工具链 + 逐包微调"的典型单体仓库构建实践。如需逐项深入,可从 .agents/skills/tsdown/references/ 的 35 篇参考文档入手。

登录后查看全文
热门项目推荐
相关项目推荐