mcpField
M
mono-repo-typescript

mono-repo-typescript

github.com/beecode-rs开发047 次安装2 周前更新
代码仓库

关于 mono-repo-typescript

这项技能指导使用 pnpm workspaces 管理的 TypeScript 单体仓库架构,涵盖包划分、共享配置、构建编排和 Docker Compose 开发环境,适用于包含服务、应用与库的项目。

主要功能

  1. 01按应用类型和职责组织包
  2. 02配置 pnpm 工作区与版本目录
  3. 03指导 TypeScript 项目引用和路径别名
  4. 04协调构建顺序与包依赖
  5. 05涵盖 Docker Compose 开发环境

适用场景

  1. 01启动包含多个包的 TypeScript 项目
  2. 02规划共享库与服务的结构
  3. 03统一包配置与构建流程
  4. 04搭建本地多服务开发环境

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/beecode-rs/claude-skills --skill mono-repo-typescript

安装后在 Claude Code 中输入/mono-repo-typescript确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

LICENSE.txt
references/
backend-packages.md
frontend-packages.md
infrastructure.md
scripts/
build-esm-fix.js
clean.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-commonNode.js 基础设施(数据库、日志记录、MQ)common
react-common共享 React 组件、钩子common
node-*后端微服务common、node-common
react-*前端 Web 应用common、react-common
mobile-appReact 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

添加新包

创建新包时,请使用此检查清单:

后端包
  1. 创建目录:packages/node-{name}/
  2. 创建 package.json,包含:
    • name:@app/node-{name}
    • type:module
    • 为 #src/* 配置 exports 和 imports
    • 依赖:将 @app/common、@app/node-common 设为 workspace:*
    • 构建脚本
  3. 创建继承 ../../config/typescript/tsconfig.base.json 的 tsconfig.json
  4. 为生产构建创建 tsconfig.build.json
  5. 创建 src/,并将 app/server-app.ts 设为入口点
  6. 将其添加到根目录 tsconfig.json 的 references 中
  7. 将其添加到带有 Traefik 标签的 compose.yml 中
  8. 运行 pnpm install
前端包
  1. 创建目录:packages/react-{name}/
  2. 创建 package.json,包含:
    • name:@app/react-{name}
    • type:module
    • 依赖:将 @app/common、@app/react-common 设为 workspace:*
    • 用于开发服务器的 Vite 配置
  3. 创建继承 ../../config/typescript/tsconfig.react.base.json 的 tsconfig.json
  4. 创建带有 vite-tsconfig-paths 插件的 vite.config.ts
  5. 创建 src/,并将 main.tsx 设为入口点
  6. 将其添加到根目录 tsconfig.json 的 references 中
  7. 如有需要,将其添加到 compose.yml 中
  8. 运行 pnpm install

最佳实践

包命名
  • 内部包使用 @app/ 前缀
  • 后端:node-{domain}(例如,node-core、node-auth)
  • 前端:react-{purpose}(例如,react-admin、react-core)
  • 共享包:common、node-common、react-common
版本管理
  • 使用 catalog: 统一管理共享依赖版本
  • 内部包使用 workspace:*
  • 保持各包的版本一致
构建顺序
  1. 先构建通用库
  2. 再构建特定于平台的库
  3. 最后构建应用和服务
  4. 使用 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 引用了不存在或存在错误的包

解决方法:

  1. 确保包存在于 packages/ 文件夹中
  2. 运行 pnpm install 以链接工作区包
  3. 先构建依赖项:pnpm --filter @app/common build
Cannot find module '#src/...'

原因: 路径别名配置不正确,或程序包尚未构建

修复方法:

  1. 检查 tsconfig.json 是否配置了 paths
  2. 检查 package.json 是否配置了 imports
  3. 在程序包中运行 pnpm build
  4. 确保导入时使用 .js 扩展名
构建后的代码中出现 ES 模块错误

原因: TypeScript 输出的导入语句没有 .js 扩展名

修复方法: 构建后运行 tsc-alias,或使用打包的修复脚本:

node scripts/build-esm-fix.js packages/your-package
Docker 卷权限错误

原因: 容器以与主机不同的用户身份运行

修复方法:

  1. 在安装后将 USER node 添加到 Dockerfile
  2. 或运行: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:* 依赖项

修复方法:

  1. 在根目录运行 pnpm install
  2. 检查 package.json 中的程序包名称是否完全一致
  3. 确认程序包位于 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"
  }
}

参考资料

外部资源

评论

支持 Markdown