airi 单体仓库的库构建实践:tsdown(Rolldown/Oxc)完整使用指南
本文基于 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: true、dts: { 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: true、minify: '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.json 的 exports 字段 |
| 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: true、attw: true |
发布前校验包结构 / 类型正确性 |
对应参考:option-shims、option-cjs-default、option-package-exports、option-css、option-unbundle、option-root、option-exe、option-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 的 audio 与 i18n 包都采用此模式。
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.yaml 与 package.json 的 workspaces 字段,覆盖 packages/、plugins/、integrations/、services/、apps/、server/ 等目录)。tsdown 在其中是统一的标准库构建器:
- 版本统一管理:根 package.json 的
devDependencies中以 pnpm catalog 方式声明"tsdown": "catalog:",各工作区包共享同一版本。 - 构建编排:根脚本
build:packages为turbo run build -F="./packages/*",即通过 Turbo 按依赖图并行调度每个包的build(内部就是tsdown)。postinstall钩子还会自动触发pnpm run build:packages,保证首次安装后即可开发。 - 逐包配置:仓库内共有 30 个以上
tsdown.config.ts,分布在packages/(如 audio、cap-vite、i18n、core-agent)、integrations/vscode/、plugins/、server/packages/与services/computer-use-mcp/。
几个典型配置体现的取舍:
| 包 | 关键配置 | 对应的文档概念 |
|---|---|---|
| packages/cap-vite | target: 'node18'、dts: true、sourcemap: true |
产物低版本目标(option-target) |
| packages/audio | unbundle: true、external: [...'?worker&url'] |
unbundle 模式 + 手动 external |
| packages/i18n | copy、plugins: [Yaml()]、unbundle: true |
资源复制 + Unplugin 插件 |
| packages/core-agent | 数组多入口 + dts: true |
多入口 + 声明生成 |
| packages/plugin-protocol | sourcemap: true、unused: 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 条最佳实践,完整保留:
- TypeScript 库务必生成类型声明:
{ dts: true } - 外置依赖,避免打包不必要的代码:
{ deps: { neverBundle: [/^react/, /^@myorg\//] } } - 启用 tree shaking 以获得最优包体积:
{ treeshake: true } - 生产构建启用压缩:
{ minify: true } - 添加 shims 改善 ESM/CJS 兼容:
{ shims: true }(补充__dirname、__filename等) - 自动生成 package.json exports:
{ exports: true } - 开发期使用监听模式:
tsdown --watch - 多文件工具库保留目录结构:
{ unbundle: true } - CI 中发布前校验包:
{ publint: 'ci-only', attw: 'ci-only' }
小结
tsdown 的设计可以概括为三层:面向 npm 库的一等公民选项(format / dts / deps / exports)、面向平台差异的 target + platform 双轴(构建时 Node 22.18+ 与运行时低版本解耦)、以及面向复杂场景的逃生通道(plugins、inputOptions、hooks、workspace)。在 airi 仓库中,它通过 pnpm catalog 统一版本、Turbo 统一调度,在 30 余个包上以 cap-vite、audio、i18n 等各不相同又风格一致的 tsdown.config.ts 落地,是"统一工具链 + 逐包微调"的典型单体仓库构建实践。如需逐项深入,可从 .agents/skills/tsdown/references/ 的 35 篇参考文档入手。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00