Monorepo 发布策略
多个包联动发布是 Monorepo 最容易踩坑的地方。这节讲包之间的依赖声明、发布顺序、以及发布后的验证。
包之间的依赖声明
ai-chat-ui 有三个包:core、vue、markdown。依赖方向是 core → vue → markdown。
// packages/vue/package.json
{
"dependencies": {
"@toimc/core": "workspace:*"
},
"peerDependencies": {
"vue": "^3.5.0"
}
}
json
// packages/markdown/package.json
{
"dependencies": {
"@toimc/core": "workspace:*"
},
"peerDependencies": {
"vue": "^3.5.0"
}
}
json
关键点:
- workspace:*:开发时用 pnpm workspace 协议引用本地包, Changeset 发布时自动替换成实际版本号
- peerDependencies:vue 由宿主项目提供,不打进包里
- dependencies vs peerDependencies:@toimc/core 是运行时必须的用 dependencies,vue 是宿主提供的用 peerDependencies
发布顺序
依赖方向决定了发布顺序:core 必须先于 vue 和 markdown 发布。
Changeset 自动处理发布顺序。但如果手动发布:
# 按依赖顺序发布
pnpm --filter @toimc/core publish --access public
pnpm --filter @toimc/vue publish --access public
pnpm --filter @toimc/markdown publish --access public
bash
发布失败回滚:如果 vue 发布成功但 markdown 失败,不需要回滚 vue。npm 不支持撤回已发布的版本(只能 deprecated),所以发布前一定要充分测试。
exports 子路径设计
大型组件库通常支持子路径导入,让用户按需加载:
// packages/vue/package.json
{
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
},
"./style.css": "./dist/style.css",
"./themes/*": "./dist/themes/*"
}
}
json
用户可以:
// 全量导入
import { ChatWindow, Message } from '@toimc/vue'
// 只导入样式
import '@toimc/vue/style.css'
// 导入特定主题
import '@toimc/vue/themes/purple.css'
typescript
子路径设计原则:
- . 是主入口,导出所有组件和 composables
- ./style.css 单独导出样式文件,支持 CSS Variables 主题定制
- ./themes/* 导出预设主题,按需加载
构建脚本统一管理
Monorepo 根目录统一管理所有包的构建:
// 根目录 package.json
{
"scripts": {
"build": "pnpm --filter @toimc/core build && pnpm --filter @toimc/vue build && pnpm --filter @toimc/markdown build",
"build:watch": "pnpm -r --parallel run build --watch",
"release": "pnpm build && pnpm test:run && changeset publish"
}
}
json
--filter 按顺序构建确保依赖可用,--parallel 用于 watch 模式并行加快速度。
发布后验证
发布后立即验证包是否可用:
# 创建测试项目
mkdir test-ai-chat && cd test-ai-chat
pnpm init
pnpm add @toimc/vue @toimc/core
# 验证导入
node -e "const { useChat } = require('@toimc/core'); console.log('core OK')"
node -e "import('@toimc/vue').then(m => console.log('vue OK', Object.keys(m)))"
bash
用 AI 做自动化验证:
> @toimc/core@0.2.0 和 @toimc/vue@0.2.0 刚发布到 npm。
> 请创建一个临时项目验证:
> 1. pnpm add 能正常安装
> 2. ESM 和 CJS 导入都能工作
> 3. TypeScript 类型声明正确
> 4. tree-shake 正常(只导入一个组件不会引入全部)
> 验证完后删除临时项目。
text
npm 包的 .npmrc 配置
# .npmrc(项目根目录)
shamefully-hoist=true
strict-peer-dependencies=false
# .npmrc(发布机器或 CI)
# 推荐改用 actions/setup-node 的 registry-url + NODE_AUTH_TOKEN 环境变量,而非直接写 .npmrc
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
ini
CI 中 NPM_TOKEN 通过 GitHub Secrets 注入,不写在代码里。
小结
- workspace:* 协议引用本地包,发布时自动替换为实际版本号
- 发布顺序按依赖方向:core → vue → markdown
- exports 子路径设计支持按需加载(主入口/样式/主题)
- 发布后立即验证:安装、导入、类型、tree-shake
- AI 可以自动化验证流程,检查包的可用性
npm 发布链路到此闭环。但「交付」还差两块:文档站要让公网用户看到(04 节:GitHub Pages + Actions),mock server 这类常驻服务也要有线上归宿(05 节:Vercel 与 Cloudflare)。
↑