关于 mono-repo-typescript
这项技能指导使用 pnpm workspaces 管理的 TypeScript 单体仓库架构,涵盖包划分、共享配置、构建编排和 Docker Compose 开发环境,适用于包含服务、应用与库的项目。
主要功能
- 01按应用类型和职责组织包
- 02配置 pnpm 工作区与版本目录
- 03指导 TypeScript 项目引用和路径别名
- 04协调构建顺序与包依赖
- 05涵盖 Docker Compose 开发环境
适用场景
- 01启动包含多个包的 TypeScript 项目
- 02规划共享库与服务的结构
- 03统一包配置与构建流程
- 04搭建本地多服务开发环境
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ beecode-rs/ claude-skills --skill mono-repo-typescript安装后在 Claude Code 中输入/mono-repo-typescript确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSE.txtreferences/backend-packages.mdfrontend-packages.mdinfrastructure.mdscripts/build-esm-fix.jsclean.js详细介绍
TypeScript 单体仓库架构
目的
指导包含多个软件包的 TypeScript 单体仓库的结构设计与编排,涵盖微服务、Web 应用、移动应用和共享库。重点关注高层架构、构建编排和开发环境。
范围边界:
- 本技能: 单体仓库结构、软件包组织、Docker Compose、构建编排
- writing-clean-ts 技能: 单个软件包的代码模式(服务、仓库、控制器)
- test-typescript 技能: 测试模式和覆盖率
适用场景
- 使用 pnpm 工作区搭建新的单体仓库
- 规划 packages 文件夹(微服务、Web、移动应用、库)
- 配置共享库(common、node-common、react-common)
- 使用路径别名设置 ES 模块
- 集中管理配置(TypeScript、ESLint、Vitest)
- 为开发环境编排 Docker Compose
- 管理软件包间依赖
- 启动任何可能扩展为包含多个软件包的 TypeScript 项目
不适用场景
- 单软件包项目 → 请改用
writing-clean-ts技能 - Yarn 或 npm 工作区 → 本技能专用于 pnpm
- 非 TypeScript 单体仓库 → 此处的模式假设所有代码都使用 TypeScript
- 采用其他约定的现有单体仓库 → 根据需要调整模式
快速决策指南
选择合适的软件包类型:
| 你的需求…… | 创建…… | 依赖项 |
|---|---|---|
| 共享业务模型、工具函数 | packages/common/ | 无(纯共享) |
| 数据库、日志记录、消息传递(Node.js) | packages/node-common/ | common |
| 共享 React 组件、钩子 | packages/react-common/ | common |
| 后端 API/微服务 | packages/node-{domain}/ | common、node-common |
| Web 应用(Vite) | packages/react-{name}/ | common、react-common |
| 移动应用(Expo) | packages/mobile-app/ | common、react-common |
| 营销/公共网站 | packages/website/ | common、react-common |
依赖流向:
common → node-common → node-{service}
common → react-common → react-{app} / mobile-app
单体仓库结构概览
project-root/
├── packages/ # All packages live here
│ ├── common/ # Shared business models, utilities
│ ├── node-common/ # Node.js-specific utilities
│ ├── react-common/ # React shared components
│ ├── config/ # Shared ESLint, Prettier, TypeScript configs
│ ├── node-core/ # Backend microservice
│ ├── node-auth/ # Auth microservice
│ ├── react-admin/ # Admin web app
│ ├── react-core/ # Main web app
│ ├── mobile-app/ # Mobile app (React Native/Expo)
│ └── website/ # Marketing site (Next.js)
├── config/ # Root-level shared configs
│ ├── typescript/ # TypeScript base configs
│ ├── vitest/ # Vitest base configs
│ └── eslint/ # ESLint base configs
├── resource/
│ ├── script/ # Build and utility scripts
│ ├── docker/ # Dockerfiles
│ └── secret/ # Encrypted secrets
├── package.json # Root orchestration
├── pnpm-workspace.yaml # Workspace and catalog config
├── tsconfig.json # TypeScript project references
├── compose.yml # Docker Compose for development
└── .env.example # Environment template
根目录编排
package.json
{
"name": "project-name",
"type": "module",
"private": true,
"engines": {
"node": ">=22 <23",
"pnpm": ">=10 <11"
},
"scripts": {
"build": "tsc -b && pnpm -r build",
"clean": "pnpm -r clean",
"lint": "prettier . && eslint . && pnpm -r lint",
"test": "pnpm -r test",
"watch": "tsc -b -w",
"ws-build": "pnpm --filter",
"ws-test": "pnpm --filter"
},
"devDependencies": {
"@types/node": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:",
"eslint": "catalog:",
"prettier": "catalog:"
},
"pnpm": {
"overrides": {
"@internal/msh-*": "link:../external-msh-monorepo/packages/$1"
}
}
}
注意:
pnpm.overrides部分展示了如何链接外部单体仓库中的软件包。这是针对特定项目设置的示例,如不需要,请移除。
pnpm-workspace.yaml
packages:
- packages/*
catalog:
typescript: ^5.7
vitest: ^3.0
eslint: ^9.22
prettier: ^3.5
"@types/node": ^22
zod: ^3.24
express: ^5.0
react: ^19
"@mui/material": ^7
catalogProtocols:
# Example for external monorepo linking:
# msh-internal: link:
注意: 在 package.json 中使用
catalog:引用这些版本,以确保所有软件包保持一致。
tsconfig.json(项目引用)
{
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "packages/common" },
{ "path": "packages/node-common" },
{ "path": "packages/react-common" },
{ "path": "packages/node-core" },
{ "path": "packages/react-admin" }
],
"files": []
}
软件包类型概览
| 软件包 | 用途 | 依赖项 |
|---|---|---|
common | 共享业务模型、工具函数 | 无(纯共享) |
node-common | Node.js 基础设施(数据库、日志记录、MQ) | common |
react-common | 共享 React 组件、钩子 | common |
node-* | 后端微服务 | common、node-common |
react-* | 前端 Web 应用 | common、react-common |
mobile-app | React Native/Expo 应用 | common、react-common |
有关软件包配置的详细信息,请参阅:
ES 模块与路径别名
#src/* 模式
每个软件包都使用路径别名,让导入更简洁:
tsconfig.json:
{
"compilerOptions": {
"paths": {
"#src": ["./src"],
"#src/*": ["./src/*"]
}
}
}
package.json:
{
"imports": {
"#src/*": "./dist/*.js"
}
}
代码中的用法:
import { someUtil } from '#src/util/some-util.js'
import { UserModel } from '#src/business/model/user/user-model.js'
重要原因: #src/* 别名在编译时(TypeScript 路径)和运行时(Node.js 子路径导入)都能生效,因此构建后的代码无需重写路径也能正常运行。
构建流程
# 1. Clean dist
node ../../resource/script/npm/clean.js .
# 2. Compile TypeScript
tsc -p ./tsconfig.build.json
# 3. Resolve aliases (tsc-alias)
tsc-alias -p ./tsconfig.build.json
软件包间依赖
依赖层级
@app/common (core models, utilities)
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
node-common node-core react-common
│ │ │
│ │ │
└───────────────┴───────────────┘
│
▼
Frontend Apps
(react-admin, react-core, mobile-app)
工作区依赖
在 package.json 中:
{
"dependencies": {
"@app/common": "workspace:*",
"@app/node-common": "workspace:*",
"@app/react-common": "workspace:*",
"@app/config": "workspace:*"
}
}
Catalog 依赖
在 package.json 中:
{
"dependencies": {
"zod": "catalog:",
"express": "catalog:",
"typeorm": "catalog:"
},
"devDependencies": {
"typescript": "catalog:",
"vitest": "catalog:"
}
}
Docker Compose 开发
完整的 Docker 设置请参阅基础设施参考
快速开始
# compose.yml - minimal example
services:
node-core:
build:
context: .
dockerfile: resource/docker/Dockerfile.node-base
command: pnpm run serve
working_dir: /app/packages/node-core
volumes:
- ./:/app:ro
depends_on:
pg: { condition: service_healthy }
labels:
- "traefik.http.routers.node-core.rule=PathPrefix(`/api/core`)"
常用命令
docker compose up -d # Start all services
docker compose logs -f node-core # View logs
docker compose up -d --build node-core # Rebuild
docker compose down # Stop all
流程
1. 分析需求
- 确定所需的包类型(服务、应用、库)
- 确定共享代码的边界
- 规划依赖层级
2. 组织包结构
- 在
./packages/文件夹中创建包 - 先设置通用库(common、node-common、react-common)
- 配置 package.json,设置正确的导出和工作区依赖
- 创建继承基础配置的 tsconfig.json
3. 配置根目录
- 设置带有 catalog 的 pnpm-workspace.yaml
- 配置根目录 package.json 脚本
- 创建包含项目引用的 tsconfig.json
- 在 config/ 中设置集中式配置
4. 设置 Docker
- 创建包含所有服务的 compose.yml
- 配置 Traefik 路由
- 设置健康检查
- 配置用于开发的卷挂载
5. 验证设置
pnpm install # Install dependencies
pnpm build # Build all packages
pnpm test # Run tests
docker compose up -d # Start development environment
添加新包
创建新包时,请使用此检查清单:
后端包
- 创建目录:
packages/node-{name}/ - 创建
package.json,包含:name:@app/node-{name}type:module- 为
#src/*配置exports和imports - 依赖:将
@app/common、@app/node-common设为workspace:* - 构建脚本
- 创建继承
../../config/typescript/tsconfig.base.json的tsconfig.json - 为生产构建创建
tsconfig.build.json - 创建
src/,并将app/server-app.ts设为入口点 - 将其添加到根目录
tsconfig.json的 references 中 - 将其添加到带有 Traefik 标签的
compose.yml中 - 运行
pnpm install
前端包
- 创建目录:
packages/react-{name}/ - 创建
package.json,包含:name:@app/react-{name}type:module- 依赖:将
@app/common、@app/react-common设为workspace:* - 用于开发服务器的 Vite 配置
- 创建继承
../../config/typescript/tsconfig.react.base.json的tsconfig.json - 创建带有
vite-tsconfig-paths插件的vite.config.ts - 创建
src/,并将main.tsx设为入口点 - 将其添加到根目录
tsconfig.json的 references 中 - 如有需要,将其添加到
compose.yml中 - 运行
pnpm install
最佳实践
包命名
- 内部包使用
@app/前缀 - 后端:
node-{domain}(例如,node-core、node-auth) - 前端:
react-{purpose}(例如,react-admin、react-core) - 共享包:
common、node-common、react-common
版本管理
- 使用
catalog:统一管理共享依赖版本 - 内部包使用
workspace:* - 保持各包的版本一致
构建顺序
- 先构建通用库
- 再构建特定于平台的库
- 最后构建应用和服务
- 使用 TypeScript 项目引用确保顺序正确
开发工作流
- 使用 Docker Compose 进行全栈开发
- 使用卷挂载实现热重载
- 使用 Traefik 在服务间路由
- 使用健康检查管理服务依赖
常见问题
- 循环依赖: 不要让通用库依赖特定于平台的库
- 模块系统混用: 所有包都应使用 ES 模块(
"type": "module") - 缺少导出: 始终在 package.json 中定义
exports字段 - 路径错误: 确保 tsconfig 路径与 package.json 中的 imports 一致
- Catalog 版本不一致: 确保 pnpm-workspace.yaml 中的 catalog 版本保持一致
- 缺少 .js 扩展名: ES 模块的导入路径必须包含
.js
故障排除
ERR_PNPM_NO_MATCHING_VERSION
原因: 包引用了 pnpm-workspace.yaml 中不存在的 catalog: 依赖项
解决方法: 将缺少的包添加到 catalog,或使用显式版本:
# pnpm-workspace.yaml
catalog:
your-missing-package: ^1.0.0
TypeScript 项目引用错误
原因: 根目录 tsconfig.json 引用了不存在或存在错误的包
解决方法:
- 确保包存在于
packages/文件夹中 - 运行
pnpm install以链接工作区包 - 先构建依赖项:
pnpm --filter @app/common build
Cannot find module '#src/...'
原因: 路径别名配置不正确,或程序包尚未构建
修复方法:
- 检查
tsconfig.json是否配置了paths - 检查
package.json是否配置了imports - 在程序包中运行
pnpm build - 确保导入时使用
.js扩展名
构建后的代码中出现 ES 模块错误
原因: TypeScript 输出的导入语句没有 .js 扩展名
修复方法: 构建后运行 tsc-alias,或使用打包的修复脚本:
node scripts/build-esm-fix.js packages/your-package
Docker 卷权限错误
原因: 容器以与主机不同的用户身份运行
修复方法:
- 在安装后将
USER node添加到 Dockerfile - 或运行:
docker compose exec node-core chown -R node:node /app
测试因 "Cannot find module" 失败
原因: Vitest 配置无法解析路径别名
修复方法: 在 Vitest 配置中使用 vite-tsconfig-paths:
import tsconfigPaths from 'vite-tsconfig-paths'
export default {
test: { ... },
plugins: [tsconfigPaths()]
}
找不到工作区程序包
原因: 未安装 workspace:* 依赖项
修复方法:
- 在根目录运行
pnpm install - 检查
package.json中的程序包名称是否完全一致 - 确认程序包位于
packages/文件夹中
打包脚本
此技能在 scripts/ 中包含辅助脚本:
clean.js- 删除dist/文件夹build-esm-fix.js- 为 ES 模块导入添加.js扩展名
将这些脚本复制到项目中的 resource/script/npm/,并在 package.json 中引用:
{
"scripts": {
"build": "node ../../resource/script/npm/clean.js . && tsc -p ./tsconfig.build.json && tsc-alias -p ./tsconfig.build.json"
}
}
参考资料
- 后端程序包 - common、node-common、微服务的详细配置
- 前端程序包 - react-common、Web 应用、移动端的详细配置
- 基础设施 - Docker Compose、构建脚本、集中式配置