mcpField
M
mono-repo-typescript

mono-repo-typescript

github.com/beecode-rsDevelopment047 installsUpdated 2w ago
Repository

About mono-repo-typescript

A skill for designing TypeScript monorepos managed with pnpm workspaces. It covers package boundaries, shared configuration, build orchestration and Docker Compose development setups for projects spanning services, apps and libraries.

Key features

  1. 01Organizes packages by application and role
  2. 02Configures pnpm workspaces and catalogs
  3. 03Guides TypeScript project references and aliases
  4. 04Coordinates builds and package dependencies
  5. 05Covers Docker Compose development setup

Use cases

  1. 01Starting a TypeScript project with multiple packages
  2. 02Structuring shared libraries and services
  3. 03Standardizing package configuration and builds
  4. 04Setting up a local multi-service environment

Install

Copy the command and run it in a terminal.

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

After installing, type/mono-repo-typescriptin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

LICENSE.txt
references/
backend-packages.md
frontend-packages.md
infrastructure.md
scripts/
build-esm-fix.js
clean.js

Details

From the project README

TypeScript Monorepo Architecture

Purpose

Guide the structure and orchestration of TypeScript monorepos with multiple packages including microservices, web apps, mobile apps, and shared libraries. Focus on high-level architecture, build orchestration, and development environment.

Scope boundaries:

  • This skill: Monorepo structure, package organization, Docker Compose, build orchestration
  • writing-clean-ts skill: Individual package code patterns (services, repositories, controllers)
  • test-typescript skill: Testing patterns and coverage

When to Use

  • Setting up a new monorepo with pnpm workspaces
  • Structuring packages folder (microservices, web, mobile, libs)
  • Configuring shared libraries (common, node-common, react-common)
  • Setting up ES modules with path aliases
  • Centralizing configuration (TypeScript, ESLint, Vitest)
  • Orchestrating Docker Compose for development
  • Managing inter-package dependencies
  • Starting ANY new TypeScript project that might grow to multiple packages

When NOT to Use

  • Single-package projects → Use writing-clean-ts skill instead
  • Yarn or npm workspaces → This skill is pnpm-specific
  • Non-TypeScript monorepos → Patterns here assume TypeScript throughout
  • Existing monorepo with different conventions → Adapt patterns as needed

Quick Decision Guide

Choosing the right package type:

You need...Create...Dependencies
Shared business models, utilitiespackages/common/None (pure)
Database, logging, messaging (Node.js)packages/node-common/common
Shared React components, hookspackages/react-common/common
Backend API/microservicepackages/node-{domain}/common, node-common
Web application (Vite)packages/react-{name}/common, react-common
Mobile app (Expo)packages/mobile-app/common, react-common
Marketing/public sitepackages/website/common, react-common

Dependency flow:

common → node-common → node-{service}
common → react-common → react-{app} / mobile-app

Monorepo Structure Overview

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

Root Orchestration

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"
    }
  }
}

Note: The pnpm.overrides section shows how to link packages from an external monorepo. This is an example for a specific project setup - remove if not needed.

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:

Note: Use catalog: in package.json to reference these versions. This ensures consistency across all packages.

tsconfig.json (Project References)
{
  "compilerOptions": {
    "composite": true
  },
  "references": [
    { "path": "packages/common" },
    { "path": "packages/node-common" },
    { "path": "packages/react-common" },
    { "path": "packages/node-core" },
    { "path": "packages/react-admin" }
  ],
  "files": []
}

Package Types Overview

PackagePurposeDependencies
commonShared business models, utilitiesNone (pure)
node-commonNode.js infrastructure (DB, logging, MQ)common
react-commonShared React components, hookscommon
node-*Backend microservicescommon, node-common
react-*Frontend web appscommon, react-common
mobile-appReact Native/Expo appcommon, react-common

For detailed package configurations, see:

ES Modules & Path Aliases

The #src/* Pattern

Each package uses path aliases for clean imports:

tsconfig.json:

{
  "compilerOptions": {
    "paths": {
      "#src": ["./src"],
      "#src/*": ["./src/*"]
    }
  }
}

package.json:

{
  "imports": {
    "#src/*": "./dist/*.js"
  }
}

Usage in code:

import { someUtil } from '#src/util/some-util.js'
import { UserModel } from '#src/business/model/user/user-model.js'

Why this matters: The #src/* alias works both at compile-time (TypeScript paths) and runtime (Node.js subpath imports), so built code works without path rewriting issues.

Build Process
# 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

Inter-Package Dependencies

Dependency Hierarchy
                    @app/common (core models, utilities)
                          │
          ┌───────────────┼───────────────┐
          │               │               │
          ▼               ▼               ▼
      node-common    node-core      react-common
          │               │               │
          │               │               │
          └───────────────┴───────────────┘
                          │
                          ▼
                    Frontend Apps
              (react-admin, react-core, mobile-app)
Workspace Dependencies

In package.json:

{
  "dependencies": {
    "@app/common": "workspace:*",
    "@app/node-common": "workspace:*",
    "@app/react-common": "workspace:*",
    "@app/config": "workspace:*"
  }
}
Catalog Dependencies

In package.json:

{
  "dependencies": {
    "zod": "catalog:",
    "express": "catalog:",
    "typeorm": "catalog:"
  },
  "devDependencies": {
    "typescript": "catalog:",
    "vitest": "catalog:"
  }
}

Docker Compose Development

For full Docker setup, see Infrastructure Reference

Quick Start
# 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`)"
Common Commands
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

Process

1. Analyze Requirements
  • Identify package types needed (services, apps, libs)
  • Determine shared code boundaries
  • Plan dependency hierarchy
2. Structure Packages
  • Create packages in ./packages/ folder
  • Set up common libs first (common, node-common, react-common)
  • Configure package.json with proper exports and workspace deps
  • Create tsconfig.json extending base configs
3. Configure Root
  • Set up pnpm-workspace.yaml with catalog
  • Configure root package.json scripts
  • Create tsconfig.json with project references
  • Set up centralized config in config/
4. Set Up Docker
  • Create compose.yml with all services
  • Configure Traefik routing
  • Set up health checks
  • Configure volume mounts for development
5. Verify Setup
pnpm install      # Install dependencies
pnpm build        # Build all packages
pnpm test         # Run tests
docker compose up -d  # Start development environment

Adding a New Package

Use this checklist when creating a new package:

Backend Package
  1. Create directory: packages/node-{name}/
  2. Create package.json with:
    • name: @app/node-{name}
    • type: module
    • exports and imports for #src/*
    • Dependencies: @app/common, @app/node-common as workspace:*
    • Build scripts
  3. Create tsconfig.json extending ../../config/typescript/tsconfig.base.json
  4. Create tsconfig.build.json for production builds
  5. Create src/ with app/server-app.ts entry point
  6. Add to root tsconfig.json references
  7. Add to compose.yml with Traefik labels
  8. Run pnpm install
Frontend Package
  1. Create directory: packages/react-{name}/
  2. Create package.json with:
    • name: @app/react-{name}
    • type: module
    • Dependencies: @app/common, @app/react-common as workspace:*
    • Vite config for dev server
  3. Create tsconfig.json extending ../../config/typescript/tsconfig.react.base.json
  4. Create vite.config.ts with vite-tsconfig-paths plugin
  5. Create src/ with main.tsx entry point
  6. Add to root tsconfig.json references
  7. Add to compose.yml if needed
  8. Run pnpm install

Best Practices

Package Naming
  • Use @app/ prefix for internal packages
  • Backend: node-{domain} (e.g., node-core, node-auth)
  • Frontend: react-{purpose} (e.g., react-admin, react-core)
  • Shared: common, node-common, react-common
Version Management
  • Use catalog: for shared dependency versions
  • Use workspace:* for internal packages
  • Keep versions consistent across packages
Build Order
  1. Build common libs first
  2. Build platform-specific libs second
  3. Build apps/services last
  4. Use TypeScript project references for correct order
Development Workflow
  • Use Docker Compose for full stack development
  • Use volume mounts for hot reload
  • Use Traefik for routing between services
  • Use health checks for service dependencies

Common Pitfalls

  • Circular dependencies: Never let common depend on platform-specific libs
  • Mixed module systems: All packages should use ES modules ("type": "module")
  • Missing exports: Always define exports field in package.json
  • Incorrect paths: Ensure tsconfig paths match package.json imports
  • Catalog drift: Keep pnpm-workspace.yaml catalog versions consistent
  • Missing .js extensions: ES modules require .js in import paths

Troubleshooting

ERR_PNPM_NO_MATCHING_VERSION

Cause: Package references a catalog: dependency that doesn't exist in pnpm-workspace.yaml

Fix: Add the missing package to the catalog, or use explicit version:

# pnpm-workspace.yaml
catalog:
  your-missing-package: ^1.0.0
TypeScript project reference errors

Cause: A package is referenced in root tsconfig.json but doesn't exist or has errors

Fix:

  1. Ensure package exists in packages/ folder
  2. Run pnpm install to link workspace packages
  3. Build dependencies first: pnpm --filter @app/common build
Cannot find module '#src/...'

Cause: Path alias not configured correctly or package not built

Fix:

  1. Check tsconfig.json has paths configured
  2. Check package.json has imports configured
  3. Run pnpm build in the package
  4. Ensure you're using .js extension in imports
ES module errors in built code

Cause: TypeScript emits imports without .js extensions

Fix: Run tsc-alias after build, or use the bundled fix script:

node scripts/build-esm-fix.js packages/your-package
Docker volume permission errors

Cause: Container runs as different user than host

Fix:

  1. Add USER node to Dockerfile after install
  2. Or run: docker compose exec node-core chown -R node:node /app
Tests fail with "Cannot find module"

Cause: Vitest config doesn't resolve path aliases

Fix: Use vite-tsconfig-paths in vitest config:

import tsconfigPaths from 'vite-tsconfig-paths'
export default {
  test: { ... },
  plugins: [tsconfigPaths()]
}
Workspace package not found

Cause: workspace:* dependency not installed

Fix:

  1. Run pnpm install at root
  2. Check package name matches exactly in package.json
  3. Verify package is in packages/ folder

Bundled Scripts

This skill includes helper scripts in scripts/:

  • clean.js - Removes dist/ folder
  • build-esm-fix.js - Adds .js extensions to ES module imports

Copy these to resource/script/npm/ in your project and reference in package.json:

{
  "scripts": {
    "build": "node ../../resource/script/npm/clean.js . && tsc -p ./tsconfig.build.json && tsc-alias -p ./tsconfig.build.json"
  }
}

References

External Resources

Comments

Markdown supported