Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds
Powrót do kolekcji
Przewodnik15 min czytania

Turborepo

Turborepo to wysokowydajny build system dla JavaScript/TypeScript monorepos z inteligentnym cache'owaniem, parallel execution i remote caching od Vercel.

Turborepo - Kompletny Przewodnik po Monorepo Build System

Czym jest Turborepo?

Turborepo to wysokowydajny build system stworzony specjalnie dla JavaScript i TypeScript monorepos. Przejęty przez Vercel w 2021 roku, stał się jednym z najpopularniejszych narzędzi do zarządzania dużymi bazami kodu. Główną ideą Turborepo jest przyspieszenie buildów poprzez inteligentne cache'owanie, parallel execution i incremental builds.

W tradycyjnym podejściu, budowanie monorepo oznacza uruchamianie tych samych tasków wielokrotnie, nawet gdy kod się nie zmienił. Turborepo rozwiązuje ten problem poprzez zapamiętywanie wyników poprzednich buildów i pomijanie niepotrzebnej pracy. Rezultat? Buildy, które trwały minuty, teraz kończą się w sekundach.

Dlaczego Turborepo?

Kluczowe zalety Turborepo

  1. Inteligentne cache'owanie - Nie buduje tego, co już zbudowane
  2. Parallel execution - Wykorzystuje wszystkie rdzenie CPU
  3. Remote caching - Współdziel cache między zespołem i CI
  4. Incremental builds - Buduj tylko to, co się zmieniło
  5. Pipeline definition - Definiuj zależności między taskami
  6. Zero config - Działa z npm, pnpm, yarn workspaces
  7. Vercel integration - Natywna integracja z Vercel
  8. Pruned subsets - Deployuj tylko potrzebne pakiety

Turborepo vs Inne Build Systems

CechaTurborepoNxLernaRush
CachingLokalny + RemoteLokalny + RemoteBrak (plugin)Lokalny
SetupMinimalRozbudowanyProstySkomplikowany
Learning curveŁatwaŚredniaŁatwaTrudna
Remote cacheVercel (free)Nx CloudBrakAzure
EcosystemRosnącyDużyLegacyEnterprise
Task runnerWbudowanyWbudowanynpm/yarnCustom
CenaFree + Vercel tiersFree + Cloud tiersFreeFree

Kiedy wybrać Turborepo?

  • Szybkość jest priorytetem - najszybszy czas do działającego buildu
  • Już używasz Vercel - natywna integracja
  • Prosty setup - minimalna konfiguracja
  • Masz npm/pnpm/yarn workspaces - zero migration effort
  • Potrzebujesz remote cache - współdzielenie między CI i devami

Instalacja i Konfiguracja

Nowy projekt

Code
Bash
# Stwórz nowe monorepo z Turborepo
npx create-turbo@latest my-monorepo

# Lub z konkretnym template
npx create-turbo@latest my-monorepo --example with-tailwind
npx create-turbo@latest my-monorepo --example kitchen-sink

# Z pnpm
pnpm dlx create-turbo@latest my-monorepo

Dodanie do istniejącego projektu

Code
Bash
# Zainstaluj Turborepo
npm install turbo --save-dev

# Lub globalnie
npm install turbo --global

# Z pnpm
pnpm add turbo --save-dev --workspace-root

Struktura projektu

Code
TEXT
my-monorepo/
├── apps/
│   ├── web/                    # Next.js app
│   │   ├── src/
│   │   ├── package.json
│   │   └── tsconfig.json
│   ├── docs/                   # Dokumentacja
│   │   ├── src/
│   │   └── package.json
│   └── admin/                  # Panel admin
│       └── package.json
├── packages/
│   ├── ui/                     # Shared UI components
│   │   ├── src/
│   │   ├── package.json
│   │   └── tsconfig.json
│   ├── config-eslint/          # Shared ESLint config
│   │   └── package.json
│   ├── config-typescript/      # Shared TS config
│   │   └── package.json
│   └── utils/                  # Shared utilities
│       ├── src/
│       └── package.json
├── package.json                # Root package.json
├── turbo.json                  # Turborepo config
├── pnpm-workspace.yaml         # Workspace config (pnpm)
└── .gitignore

Root package.json

Code
JSON
{
  "name": "my-monorepo",
  "private": true,
  "workspaces": ["apps/*", "packages/*"],
  "scripts": {
    "build": "turbo build",
    "dev": "turbo dev",
    "lint": "turbo lint",
    "test": "turbo test",
    "clean": "turbo clean",
    "format": "prettier --write \"**/*.{ts,tsx,md}\""
  },
  "devDependencies": {
    "turbo": "^2.0.0",
    "prettier": "^3.0.0"
  },
  "packageManager": "pnpm@8.15.0"
}

turbo.json - Główna konfiguracja

Code
JSON
{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": [".env"],
  "globalEnv": ["NODE_ENV", "CI"],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", "dist/**"],
      "env": ["DATABASE_URL", "API_KEY"]
    },
    "lint": {
      "dependsOn": ["^lint"],
      "outputs": []
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "inputs": ["src/**/*.ts", "src/**/*.tsx", "test/**/*.ts"]
    },
    "clean": {
      "cache": false
    }
  }
}

Pipeline i Tasks

Zrozumienie Pipeline

Pipeline definiuje relacje między taskami w monorepo:

Code
JSON
{
  "tasks": {
    "build": {
      // ^ oznacza "najpierw zbuduj dependencies"
      "dependsOn": ["^build"],
      // Pliki wyjściowe do cache'owania
      "outputs": ["dist/**", ".next/**"]
    },
    "test": {
      // Zależy od lokalnego build (bez ^)
      "dependsOn": ["build"],
      // Pliki wejściowe wpływające na cache
      "inputs": ["src/**", "test/**"]
    },
    "deploy": {
      // Zależy od build i test tego samego pakietu
      "dependsOn": ["build", "test"]
    }
  }
}

Typy zależności

Code
JSON
{
  "tasks": {
    "build": {
      // ^build - build dependencies first (topological)
      "dependsOn": ["^build"]
    },
    "test": {
      // build - build THIS package first (same package)
      "dependsOn": ["build"]
    },
    "deploy": {
      // Oba typy razem
      "dependsOn": ["^build", "test", "lint"]
    },
    "e2e": {
      // Zależność od konkretnego pakietu
      "dependsOn": ["web#build"]
    }
  }
}

Outputs i Caching

Code
JSON
{
  "tasks": {
    "build": {
      "outputs": [
        "dist/**",
        ".next/**",
        "!.next/cache/**",  // Exclude cache
        "build/**"
      ]
    },
    "lint": {
      // Brak outputs = task nie produkuje plików
      "outputs": []
    },
    "test": {
      "outputs": ["coverage/**"]
    }
  }
}

Inputs - kontrola cache invalidation

Code
JSON
{
  "tasks": {
    "build": {
      // Tylko te pliki wpływają na cache
      "inputs": [
        "src/**/*.ts",
        "src/**/*.tsx",
        "package.json",
        "tsconfig.json"
      ]
    },
    "lint": {
      "inputs": [
        "src/**/*.ts",
        "src/**/*.tsx",
        ".eslintrc.js",
        "package.json"
      ]
    },
    "test": {
      "inputs": [
        "$TURBO_DEFAULT$",  // Domyślne inputs
        "jest.config.js",
        "test/**"
      ]
    }
  }
}

Environment Variables

Code
JSON
{
  // Globalne env - wpływają na wszystkie taski
  "globalEnv": ["CI", "NODE_ENV"],

  // Globalne dependencies - pliki wpływające na wszystko
  "globalDependencies": [".env", "tsconfig.base.json"],

  "tasks": {
    "build": {
      // Task-specific env
      "env": ["DATABASE_URL", "API_KEY", "NEXT_PUBLIC_*"],
      "passThroughEnv": ["AWS_SECRET_KEY"]  // Nie wpływa na cache
    }
  }
}

Uruchamianie Tasków

Podstawowe komendy

Code
Bash
# Uruchom task we wszystkich pakietach
turbo build
turbo lint
turbo test

# Dev mode (bez cache, persistent)
turbo dev

# Verbose output
turbo build --verbosity=2

# Dry run - pokaż co zostanie uruchomione
turbo build --dry-run

# Pokaż graph zależności
turbo build --graph
turbo build --graph=graph.png

Filtrowanie pakietów

Code
Bash
# Tylko konkretny pakiet
turbo build --filter=web
turbo build --filter=@repo/ui

# Pakiet i jego dependencies
turbo build --filter=web...

# Pakiet i jego dependents (pakiety zależne od niego)
turbo build --filter=...@repo/ui

# Pakiety w konkretnym folderze
turbo build --filter="./apps/*"
turbo build --filter="./packages/*"

# Wykluczenie pakietów
turbo build --filter="!docs"

# Kombinacje
turbo build --filter="web..." --filter="!docs"

# Tylko zmienione od ostatniego commita
turbo build --filter="[HEAD^1]"
turbo build --filter="...[main...HEAD]"

Zaawansowane filtry

Code
Bash
# Pakiety zmienione w PR
turbo build --filter="[origin/main...HEAD]"

# Pakiety zależne od zmienionego ui
turbo build --filter="...@repo/ui[HEAD^1]"

# Wszystkie apps
turbo build --filter="./apps/**"

# Pakiety z konkretnym tagiem w package.json
turbo build --filter="@repo/*"

Opcje wykonania

Code
Bash
# Limit parallel tasks
turbo build --concurrency=4
turbo build --concurrency=50%  # 50% CPU cores

# Kontynuuj mimo błędów
turbo build --continue

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

# Output logs
turbo build --output-logs=full
turbo build --output-logs=hash-only
turbo build --output-logs=new-only
turbo build --output-logs=errors-only

Remote Caching

Konfiguracja z Vercel

Code
Bash
# Login do Vercel
npx turbo login

# Połącz projekt
npx turbo link

# Teraz cache jest współdzielony!
turbo build

Weryfikacja Remote Cache

Code
Bash
# Sprawdź status
turbo build --summarize

# Output pokaże:
# Cache: 5 cached, 2 computed
# Remote: 3 downloaded, 2 uploaded

Self-hosted Remote Cache

Code
TypeScript
// Własny serwer cache (przykład z Express)
import express from 'express'
import { createHash } from 'crypto'

const app = express()
const cache = new Map<string, Buffer>()

// GET artifact
app.get('/v8/artifacts/:hash', (req, res) => {
  const artifact = cache.get(req.params.hash)
  if (artifact) {
    res.send(artifact)
  } else {
    res.status(404).send('Not found')
  }
})

// PUT artifact
app.put('/v8/artifacts/:hash', express.raw({ limit: '50mb' }), (req, res) => {
  cache.set(req.params.hash, req.body)
  res.status(200).send('OK')
})

app.listen(3001)
Code
Bash
# Użycie własnego serwera
turbo build --api="http://localhost:3001" --token="secret"

Konfiguracja w CI/CD

.github/workflows/ci.yml
YAML
# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v2
        with:
          version: 8

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - run: pnpm install

      - run: pnpm build
        env:
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
          TURBO_TEAM: ${{ vars.TURBO_TEAM }}

Workspace Packages

Package z UI komponentami

packages/ui/package.json
JSON
// packages/ui/package.json
{
  "name": "@repo/ui",
  "version": "0.0.0",
  "private": true,
  "exports": {
    ".": "./src/index.ts",
    "./button": "./src/button.tsx",
    "./card": "./src/card.tsx",
    "./styles.css": "./styles.css"
  },
  "scripts": {
    "build": "tsup src/index.ts --format cjs,esm --dts",
    "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
    "lint": "eslint src/",
    "test": "vitest run"
  },
  "peerDependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "devDependencies": {
    "@repo/config-typescript": "workspace:*",
    "tsup": "^8.0.0",
    "typescript": "^5.0.0"
  }
}
TSpackages/ui/src/index.ts
TypeScript
// packages/ui/src/index.ts
export { Button } from './button'
export { Card } from './card'
export { Input } from './input'
export type { ButtonProps, CardProps, InputProps } from './types'

Shared Config Packages

packages/config-eslint/package.json
JSON
// packages/config-eslint/package.json
{
  "name": "@repo/config-eslint",
  "version": "0.0.0",
  "private": true,
  "main": "index.js",
  "dependencies": {
    "@typescript-eslint/eslint-plugin": "^7.0.0",
    "@typescript-eslint/parser": "^7.0.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-react": "^7.0.0",
    "eslint-plugin-react-hooks": "^4.0.0"
  }
}
JSpackages/config-eslint/index.js
JavaScript
// packages/config-eslint/index.js
module.exports = {
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'prettier'
  ],
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint', 'react'],
  rules: {
    'react/react-in-jsx-scope': 'off',
    '@typescript-eslint/no-unused-vars': 'warn'
  },
  settings: {
    react: { version: 'detect' }
  }
}

Używanie shared packages

apps/web/package.json
JSON
// apps/web/package.json
{
  "name": "web",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "lint": "eslint . --ext .ts,.tsx"
  },
  "dependencies": {
    "@repo/ui": "workspace:*",
    "@repo/utils": "workspace:*",
    "next": "^14.0.0",
    "react": "^18.0.0"
  },
  "devDependencies": {
    "@repo/config-eslint": "workspace:*",
    "@repo/config-typescript": "workspace:*"
  }
}
JSapps/web/.eslintrc.js
JavaScript
// apps/web/.eslintrc.js
module.exports = {
  extends: ['@repo/config-eslint'],
  parserOptions: {
    project: './tsconfig.json'
  }
}

TypeScript Config Sharing

packages/config-typescript/base.json
JSON
// packages/config-typescript/base.json
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true
  }
}
packages/config-typescript/nextjs.json
JSON
// packages/config-typescript/nextjs.json
{
  "$schema": "https://json.schemastore.org/tsconfig",
  "extends": "./base.json",
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "ES2022"],
    "module": "ESNext",
    "target": "ES2022",
    "jsx": "preserve",
    "plugins": [{ "name": "next" }]
  }
}
apps/web/tsconfig.json
JSON
// apps/web/tsconfig.json
{
  "extends": "@repo/config-typescript/nextjs.json",
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

Package-specific Tasks

Nadpisywanie tasków per-package

turbo.json
JSON
// turbo.json
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    // Nadpisanie dla konkretnego pakietu
    "web#build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**"],
      "env": ["NEXT_PUBLIC_API_URL"]
    },
    // Nadpisanie dla docs
    "docs#build": {
      "dependsOn": ["^build"],
      "outputs": [".docusaurus/**", "build/**"]
    }
  }
}

Lokalne turbo.json w pakiecie

apps/web/turbo.json
JSON
// apps/web/turbo.json
{
  "$schema": "https://turbo.build/schema.json",
  "extends": ["//"],  // Dziedzicz z root turbo.json
  "tasks": {
    "build": {
      // Nadpisz tylko specyficzne opcje
      "env": ["ANALYZE", "NEXT_PUBLIC_SENTRY_DSN"]
    },
    "dev": {
      "persistent": true,
      "cache": false
    }
  }
}

Generators (Codegen)

Tworzenie generatora

Code
Bash
# Stwórz folder generators
mkdir -p turbo/generators
TSturbo/generators/config.ts
TypeScript
// turbo/generators/config.ts
import type { PlopTypes } from '@turbo/gen'

export default function generator(plop: PlopTypes.NodePlopAPI): void {
  // Generator nowego pakietu
  plop.setGenerator('package', {
    description: 'Stwórz nowy package',
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'Nazwa pakietu:'
      },
      {
        type: 'list',
        name: 'type',
        message: 'Typ pakietu:',
        choices: ['lib', 'config', 'tool']
      }
    ],
    actions: [
      {
        type: 'add',
        path: 'packages/{{name}}/package.json',
        templateFile: 'templates/package.json.hbs'
      },
      {
        type: 'add',
        path: 'packages/{{name}}/src/index.ts',
        templateFile: 'templates/index.ts.hbs'
      },
      {
        type: 'add',
        path: 'packages/{{name}}/tsconfig.json',
        templateFile: 'templates/tsconfig.json.hbs'
      }
    ]
  })

  // Generator nowej app
  plop.setGenerator('app', {
    description: 'Stwórz nową aplikację',
    prompts: [
      {
        type: 'input',
        name: 'name',
        message: 'Nazwa aplikacji:'
      },
      {
        type: 'list',
        name: 'framework',
        message: 'Framework:',
        choices: ['next', 'remix', 'astro']
      }
    ],
    actions: (data) => {
      const actions: PlopTypes.ActionType[] = []

      if (data?.framework === 'next') {
        actions.push({
          type: 'addMany',
          destination: 'apps/{{name}}',
          templateFiles: 'templates/next/**/*',
          base: 'templates/next'
        })
      }

      return actions
    }
  })
}

Użycie generatora

Code
Bash
# Lista dostępnych generatorów
turbo gen

# Uruchom konkretny generator
turbo gen package
turbo gen app

# Z argumentami
turbo gen package --name=my-lib --type=lib

Templates

Code
HANDLEBARS
{{!-- turbo/generators/templates/package.json.hbs --}}
{
  "name": "@repo/{{name}}",
  "version": "0.0.0",
  "private": true,
  "main": "./src/index.ts",
  "types": "./src/index.ts",
  "scripts": {
    "build": "tsup src/index.ts --format cjs,esm --dts",
    "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
    "lint": "eslint src/",
    "test": "vitest run"
  },
  "devDependencies": {
    "@repo/config-typescript": "workspace:*",
    "tsup": "^8.0.0",
    "typescript": "^5.0.0"
  }
}

Pruned Deployments

Docker z Pruned Monorepo

Code
DOCKERFILE
# Dockerfile
FROM node:20-alpine AS base

# Pruner stage
FROM base AS pruner
RUN npm install -g turbo
WORKDIR /app
COPY . .
RUN turbo prune web --docker

# Installer stage
FROM base AS installer
WORKDIR /app

# Instaluj tylko potrzebne dependencies
COPY --from=pruner /app/out/json/ .
COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN corepack enable pnpm && pnpm install --frozen-lockfile

# Builder stage
FROM base AS builder
WORKDIR /app
COPY --from=installer /app/ .
COPY --from=pruner /app/out/full/ .

RUN corepack enable pnpm && pnpm turbo build --filter=web

# Runner stage
FROM base AS runner
WORKDIR /app

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs

COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public

CMD ["node", "apps/web/server.js"]

Pruning dla CI

Code
Bash
# Stwórz pruned subset
turbo prune web --docker

# Wynik w out/:
# out/
# ├── json/           # package.json files
# ├── full/           # Full source code
# └── pnpm-lock.yaml  # Pruned lockfile

Debugging i Troubleshooting

Cache Debugging

Code
Bash
# Pokaż dlaczego task został wykonany
turbo build --summarize

# Pokaż hash computation
turbo build --dry-run=json | jq

# Force cache miss
turbo build --force

# Wyczyść cache
turbo clean
rm -rf node_modules/.cache/turbo

Task Graph

Code
Bash
# Generuj graf zależności
turbo build --graph=graph.html
turbo build --graph=graph.png
turbo build --graph=graph.json

# Pokaż w konsoli
turbo build --graph

Verbose Logging

Code
Bash
# Poziomy verbosity
turbo build --verbosity=0  # Quiet
turbo build --verbosity=1  # Default
turbo build --verbosity=2  # Verbose

# Pokaż wszystkie logi
turbo build --output-logs=full

# Tylko errory
turbo build --output-logs=errors-only

Common Issues

Code
Bash
# Problem: Cache nie działa
# Rozwiązanie: Sprawdź inputs i outputs

# Problem: Task wykonuje się mimo braku zmian
# Sprawdź env variables
turbo build --summarize | grep -A 10 "environment"

# Problem: Circular dependency
turbo build --graph  # Wizualizuj zależności

# Problem: Slow builds
turbo build --profile=profile.json
# Analizuj w Chrome DevTools

Best Practices

Struktura pakietów

Code
TEXT
packages/
├── core/            # Biznesowa logika (bez UI)
├── ui/              # Shared komponenty React
├── hooks/           # Shared React hooks
├── utils/           # Helpers, formatters
├── types/           # Shared TypeScript types
├── config-*/        # Shared configs (eslint, ts, prettier)
└── api-client/      # Generated API client

Naming Conventions

Code
JSON
// Używaj scope dla pakietów
{
  "name": "@repo/ui",        // ✅
  "name": "@mycompany/ui",   // ✅
  "name": "ui",              // ❌ Konflikt z npm packages
}

Dependency Management

Code
JSON
// Root package.json - shared devDependencies
{
  "devDependencies": {
    "turbo": "^2.0.0",
    "typescript": "^5.0.0",
    "prettier": "^3.0.0"
  }
}

// Package - tylko specyficzne deps
{
  "dependencies": {
    "@repo/ui": "workspace:*"  // Internal dependency
  },
  "devDependencies": {
    "@repo/config-eslint": "workspace:*"
  }
}

Task Organization

Code
JSON
{
  "tasks": {
    // Build pipeline
    "build": { "dependsOn": ["^build"] },
    "build:production": { "dependsOn": ["^build", "lint", "test"] },

    // Dev
    "dev": { "cache": false, "persistent": true },

    // Quality
    "lint": { "outputs": [] },
    "lint:fix": { "outputs": [], "cache": false },
    "typecheck": { "outputs": [] },
    "test": { "dependsOn": ["build"] },
    "test:watch": { "cache": false, "persistent": true },

    // Maintenance
    "clean": { "cache": false }
  }
}

Integracje

Vercel Deployment

vercel.json
JSON
// vercel.json
{
  "buildCommand": "pnpm turbo build --filter=web",
  "installCommand": "pnpm install",
  "framework": "nextjs",
  "outputDirectory": "apps/web/.next"
}

GitHub Actions

.github/workflows/ci.yml
YAML
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ vars.TURBO_TEAM }}

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2  # Potrzebne dla --filter=[HEAD^1]

      - uses: pnpm/action-setup@v2
        with:
          version: 8

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install

      - name: Build
        run: pnpm turbo build

      - name: Test
        run: pnpm turbo test

      - name: Lint
        run: pnpm turbo lint

Changesets (versioning)

Code
Bash
npm install @changesets/cli -D
npx changeset init
.changeset/config.json
JSON
// .changeset/config.json
{
  "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "fixed": [],
  "linked": [],
  "access": "restricted",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": ["@repo/config-*"]
}

FAQ - Najczęściej Zadawane Pytania

Czy Turborepo wymaga Vercel?

Nie! Turborepo jest open-source i działa bez Vercel. Remote caching można hostować samodzielnie lub używać Vercel jako wygodnej opcji.

Jak migrować z Lerna/Nx?

Turborepo jest addytywny - możesz dodać turbo.json do istniejącego monorepo bez usuwania innych narzędzi. Stopniowo przenoś taski do Turborepo.

Czy mogę używać npm zamiast pnpm?

Tak! Turborepo działa z npm workspaces, pnpm workspaces i yarn workspaces. pnpm jest rekomendowany ze względu na wydajność.

Jak debugować problemy z cache?

Użyj turbo build --summarize żeby zobaczyć co wpływa na cache hash. Sprawdź env variables, inputs i outputs w turbo.json.

Czy Turborepo wspiera monorepo z różnymi językami?

Turborepo jest zoptymalizowany dla JavaScript/TypeScript, ale może orchestrować dowolne taski. Dla polyglot monorepo rozważ Nx lub Bazel.

Jaka jest różnica między dependsOn: ["^build"] a dependsOn: ["build"]?

  • ^build - najpierw zbuduj dependencies (pakiety od których zależymy)
  • build - najpierw zbuduj w tym samym pakiecie

Jak zoptymalizować CI build time?

  1. Włącz remote caching
  2. Użyj --filter dla changed packages
  3. Parallel jobs w CI
  4. Optymalizuj inputs/outputs

Podsumowanie

Turborepo to potężne narzędzie do zarządzania monorepo, które oferuje:

  • Inteligentne caching - lokalne i remote
  • Parallel execution - maksymalne wykorzystanie CPU
  • Minimal config - działa out-of-the-box
  • Vercel integration - natywna integracja
  • Incremental adoption - łatwa migracja

Idealny dla zespołów szukających prostego, ale potężnego rozwiązania do monorepo bez zbędnej złożoności.