Monorepo 内部私有包设计:让内部能力保持灵活,又不污染公共依赖体系
介绍 monorepo 中`private:true`内部私有包,依托 workspace:* 实现源码内部复用、不对外发布。划分工程脚本、测试辅助等六类包,区分开发态与运行时风险,明确禁止公开包依赖私有包,配套命名、质量分级与完整生命周期管理。
内部私有包用于承载只服务于当前 Monorepo 的能力,例如工程脚本、Mock、测试工具、内部适配层和实验性实现。
1. 什么是内部私有包
内部私有包的核心特征是:
{
"private": true
}它不会参与公开 npm 发布,主要通过 Workspace 在仓库内部直接共享源码。
原方案明确将内部私有包定义为“仅仓库内部共享、不发布到任何 npm 源”的包,并强调它与公开共享包是互补关系。fileciteturn1file3L163-L179
可以简单理解为:
公开共享包:稳定、标准、可复用
内部私有包:灵活、快速、内部化2. 为什么需要私有包
并不是所有代码都值得进入公共共享体系。
例如:
- CI 脚本;
- 测试 Mock;
- 临时迁移脚本;
- 实验性技术;
- 内部 BFF 适配;
- 特定仓库资产;
- 多端内部桥接层。
如果这些能力都直接复制到多个 App 中,会产生重复维护;如果强行做成公开共享包,又会增加 API 稳定性、版本管理和发布成本。
私有包提供了一个中间层:源码级复用,但不承担公共发布契约。
3. 六类常见私有包
| 类型 | 示例 | 常见依赖位置 |
|---|---|---|
| 工程工具 | internal-scripts、internal-cli | devDependencies |
| 测试辅助 | internal-mock、test-utils | devDependencies |
| 业务中间层 | internal-adapter、domain-bridge | dependencies |
| 环境配置 | env-config、feature-flags | dependencies |
| 实验与临时 | experimental-*、temp-* | 按场景 |
| 内部资产 | internal-icons、internal-theme | dependencies |
原方案采用相同的六类划分,并特别区分开发态私有包和运行时私有包。fileciteturn1file3L151-L162
4. 开发态私有包:最常见的用法
绝大多数私有包应该属于开发态能力:
{
"devDependencies": {
"@org/internal-mock": "workspace:*",
"@org/test-utils": "workspace:*"
}
}典型内容:
- Mock 服务;
- Fixtures;
- Testing Library 封装;
- CI 脚本;
- 构建工具;
- 代码生成器。
因为这些包位于 devDependencies,不会成为发布包的运行时依赖。
原方案将开发态私有包作为标准用法,并指出其不会影响上层公开包的发布。fileciteturn1file3L222-L230
5. 运行时私有包:谨慎使用
运行时私有包可能被放入:
{
"dependencies": {
"@org/internal-adapter": "workspace:*"
}
}此时必须特别注意:如果上层包要发布到 npm,而运行时又依赖一个不发布的私有包,下游安装后就无法解析这个依赖。
原方案明确指出运行时私有包用于业务桥接、内部适配和聚合层时需要谨慎;如果发布包依赖它,就需要在构建阶段内联相关代码,或者重新评估包边界。fileciteturn1file3L231-L259
6. 私有包的标准 package.json
一个简单的内部工具可以非常轻量:
{
"name": "@org/internal-mock",
"version": "0.0.0",
"private": true,
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts"
}对于只在仓库内使用的源码包,不一定需要完整的公开发布字段。
原方案也采用 private: true + workspace:* 的最小配置,并允许直接引用源码。fileciteturn0file4L151-L169
7. 为什么 workspace:* 适合私有包
私有包的核心目标之一是降低版本管理成本。
{
"devDependencies": {
"@org/internal-mock": "workspace:*"
}
}这样开发时可以直接关联 Workspace 源码:
Application
↓
@org/internal-mock
↓
packages/internal-mock/src修改代码后,使用方可以立即获得变化,不需要执行“构建 → 发布 → 升级版本”的完整流程。
原方案将源码级共享、无版本成本作为私有包的重要价值。fileciteturn1file3L165-L179
8. 工程工具类
这是最常见的私有包类型。
例如:
packages/internal-scripts/
├── src/
│ ├── bump-deps.ts
│ ├── gen-api.ts
│ ├── archive-pkg.ts
│ └── check-deps.ts
├── bin/
└── package.json可以承载:
- 批量依赖升级;
- 包元数据检查;
- API 类型生成;
- 废弃包归档;
- 发布前检查;
- 自研 CLI。
原方案给出了类似的内部脚本目录结构和用途示例。fileciteturn1file3L43-L67
9. 测试辅助类
测试私有包可以集中管理:
- Mock 数据;
- Fixtures;
- 测试工具;
- 自定义断言;
- Testing Library 封装;
- 通用测试环境初始化。
例如:
@org/internal-mock
@org/test-utils
@org/fixtures
@org/test-helpers这些包应该默认只进入 devDependencies,避免测试代码进入生产产物。原方案也明确要求测试辅助类统一用于开发和测试阶段。fileciteturn1file3L68-L86
10. 业务中间层
有些能力确实需要多个应用共享,但又不适合作为稳定的业务域包,例如:
- 多端适配层;
- 跨域桥接;
- 内部页面片段;
- 内部 BFF 聚合;
- 过渡期兼容层。
这类能力可以暂时放入私有包,以避免将临时实现直接固化到公共业务 API。
原方案将 internal-adapter、domain-bridge、内部页面片段和内部 BFF 作为典型场景。fileciteturn1file3L87-L114
11. 环境与配置类
可以建立:
@org/env-config
@org/feature-flags
@org/deploy-config
@org/runtime-env用于:
- 环境地址;
- 功能开关;
- 部署参数;
- 运行时环境变量。
但是敏感信息必须和普通配置分离:
Repository
└── 非敏感配置
Secret Manager / CI Secret
└── 密钥、密码、Token原方案明确要求 API Key、数据库密码等敏感信息不要直接进入仓库。fileciteturn1file3L115-L127
12. 实验与临时包
实验包是私有包体系中非常有价值的一类:
@org/experimental-new-store
@org/experimental-chart
@org/temp-migration它允许团队先验证技术方案,再决定是否沉淀为正式共享包。
但必须记录:
- 用途;
- 负责人;
- 创建时间;
- 预计生命周期;
- 转正条件;
- 删除条件。
原方案建议使用 experimental- 或 temp- 前缀,并要求 README 标注生命周期和负责人。fileciteturn1file3L128-L138
13. 私有包与公开包的边界
最重要的一条规则是:
Private Package
↓
Public Package不应该出现反方向依赖:
Public Package
↓
Private Package ❌原因很简单:公开包的用户无法安装一个不存在于发布源中的私有依赖。
原方案将“私有包可以依赖公开共享包,公开共享包禁止依赖私有包”作为核心边界。fileciteturn1file5L240-L250
14. 命名规范
建议从包名就能看出其生命周期属性:
@org/internal-xxx
@org/experimental-xxx
@org/temp-xxx业务中间层也可以显式使用 internal- 或 domain-bridge 等命名。
原方案要求通过命名前缀避免私有包和正式共享包混淆。fileciteturn1file5L229-L244
15. 质量分级
私有包不需要全部采用和核心基础包相同的质量标准,可以根据生命周期和重要程度分级:
| 等级 | 典型包 | 最低要求 |
|---|---|---|
| 核心 | 工程配置、核心适配 | 测试、评审、负责人 |
| 普通 | Mock、业务中间层 | README、核心逻辑测试 |
| 临时 | 实验、迁移脚本 | 生命周期、负责人、清理计划 |
原方案也采用核心、普通、临时三级质量管控。fileciteturn1file5L245-L250
16. 生命周期管理
私有包最容易出现的问题不是创建困难,而是只创建、不删除。
推荐生命周期:
Create
↓
Internal Validation
↓
Stable / Keep Private
↓
Promote to Public Package
or
Archive创建时:
- 明确用途;
- 指定负责人;
- 记录生命周期。
定期巡检:
- 删除过期实验;
- 删除完成迁移的临时包;
- 合并重复能力;
- 评估是否应该转为正式共享包。
原方案建议按季度巡检,并在能力稳定且需要跨仓库复用时升级为公开共享包。fileciteturn1file5L251-L255
17. 标准目录结构
简单私有包:
packages/internal-mock/
├── src/
│ ├── index.ts
│ ├── user.mock.ts
│ └── order.mock.ts
├── package.json
└── README.md复杂工具:
packages/internal-scripts/
├── src/
│ ├── index.ts
│ ├── commands/
│ └── utils/
├── bin/
├── package.json
├── tsconfig.json
└── README.md原方案同样提供了简单 Mock 包和多模块内部脚本包两种结构。fileciteturn0file4L170-L211
18. 私有包什么时候应该“转正”
可以使用以下判断标准:
转为正式共享包
当它:
- 被多个独立仓库需要;
- API 已经稳定;
- 生命周期长期存在;
- 有明确维护负责人;
- 需要独立版本管理。
继续保持私有
当它:
- 只服务当前仓库;
- 仍在快速变化;
- 与内部 CI 或环境强绑定;
- 不希望承担公共兼容性成本。
直接删除或归档
当它:
- 只是一次迁移脚本;
- 实验已经结束;
- 被新能力完全替代;
- 已经没有任何使用方。
19. 总结
私有包并不是“低质量代码区”,而是一种不同生命周期的工程资产。
公开包解决长期稳定复用,私有包解决内部快速复用。
真正重要的是让两者之间存在明确的晋升和退出机制,而不是让临时能力无限期留在仓库中。
Monorepo 共享包维护体系:从包边界到版本发布的工程化实践
构建 Monorepo 共享包完整维护体系,划分五类包,要求依赖为有向无环图。统一包结构与 API 最小暴露原则,介绍 Fixed/Independent 版本模式,基于 Changesets 做版本管理,搭配全套工具链,设置 CI 门禁,规范包从创建到归档的全生命周期。
Monorepo UI 组件包设计:从设计系统到可维护组件库
该文档阐述 Monorepo UI 组件包设计,分为设计基础、原子、复合组件、UI 工具四层。遵循零业务耦合、主题化等原则,依托 Design Tokens 构建视觉体系,规范表单、浮层、图标实现,配套构建、Storybook 文档、测试,厘清通用 UI 与业务组件边界。