正在加载,请稍候…

TypeScript Monorepo with Turborepo: Shared Code and Incremental Builds

Build a TypeScript monorepo with Turborepo. Learn task pipelines, remote caching, shared packages, workspace configuration, and optimizing build performance.

TypeScript Monorepo with Turborepo

Why Turborepo?

Problems with large monorepos:
  - Long build times (rebuilding unchanged code)
  - Complex task dependencies (build A before B)
  - No sharing build caches

Turborepo solves:
  - Incremental builds (only rebuild changed packages)
  - Remote caching (share cache across CI runs)
  - Parallel execution with dependency awareness

Setup

# Create new monorepo
npx create-turbo@latest my-monorepo
cd my-monorepo

# Or add to existing
npm install -D turbo

Structure

monorepo/
├── apps/
│   ├── web/          (Next.js)
│   ├── api/          (Express/Fastify)
│   └── mobile/       (React Native)
├── packages/
│   ├── ui/           (shared React components)
│   ├── utils/        (shared utilities)
│   ├── types/        (shared TypeScript types)
│   └── config/
│       ├── eslint/
│       └── tsconfig/
├── turbo.json
└── package.json      (root)

turbo.json Configuration

{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": [".env.local"],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],  // Build dependencies first
      "outputs": ["dist/**", ".next/**"],
      "cache": true
    },
    "test": {
      "dependsOn": ["^build"],
      "outputs": ["coverage/**"],
      "cache": true
    },
    "lint": {
      "outputs": []
    },
    "dev": {
      "cache": false,
      "persistent": true  // Long-running process
    },
    "type-check": {
      "outputs": []
    }
  }
}

Shared Package Example

// packages/ui/src/Button.tsx
import React from 'react';

export interface ButtonProps {
  variant?: 'primary' | 'secondary';
  children: React.ReactNode;
  onClick?: () => void;
  disabled?: boolean;
}

export function Button({ variant = 'primary', children, onClick, disabled }: ButtonProps) {
  return (
    <button
      className={`btn btn-${variant}`}
      onClick={onClick}
      disabled={disabled}
    >
      {children}
    </button>
  );
}
// packages/ui/package.json
{
  "name": "@myapp/ui",
  "version": "0.0.0",
  "main": "./src/index.ts",
  "types": "./src/index.ts",
  "exports": {
    ".": "./src/index.ts"
  }
}
// apps/web/package.json
{
  "dependencies": {
    "@myapp/ui": "*"
  }
}

Remote Caching

# Login to Turbo Remote Cache
npx turbo login

# Link to remote cache
npx turbo link

# Or use Vercel Remote Cache
# Or self-host with turbo-remote-cache package

# Build with remote cache
turbo build --remote-only

Task Commands

# Run all builds (parallel, with caching)
turbo build

# Run specific app's dev server
turbo dev --filter=web

# Run tests for changed packages only
turbo test --filter=[HEAD^1]

# Run in all packages matching pattern
turbo build --filter=./packages/*

# Dry run (show what would run)
turbo build --dry

# Force rebuild (ignore cache)
turbo build --force

Shared TypeScript Config

// packages/config/tsconfig/base.json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "skipLibCheck": true,
    "isolatedModules": true,
    "resolveJsonModule": true
  }
}
// apps/web/tsconfig.json
{
  "extends": "@myapp/config/tsconfig/nextjs.json",
  "include": [".", "next-env.d.ts"],
  "exclude": ["node_modules"]
}

Turborepo's incremental builds can reduce CI times by 80%+ once the remote cache is warm.