TypeScript Monorepo 設定

Monorepo は複数のプロジェクトを同じコードリポジトリに置く開発モードです。

TypeScript はプロジェクト参照とツールチェーンを通じて Monorepo をサポートし、複数のパッケージを効率的に管理できます。


SVG 図: Monorepo 構造 背景 タイトル Monorepo プロジェクト構造 ルートディレクトリ ルートディレクトリ (root) package.json | tsconfig.json | lerna.json | turbo.json パッケージディレクトリ packages/ ディレクトリ パッケージ1 utils パッケージ2 ui-components パッケージ3 hooks パッケージ4 app 下半分: ツール 管理ツール: npm workspaces | yarn workspaces | pnpm | lerna | turbo 矢印マーカー

なぜ Monorepo が必要か

プロジェクトに複数のパッケージ(ツールライブラリ、コンポーネントライブラリ、アプリケーションなど)が含まれる場合、従来の方法では複数のコードリポジトリを保守する必要があります。

Monorepo はすべてのパッケージを同じリポジトリに置くため、コード共有がより便利になり、バージョン管理がより統一されます。

TypeScript のプロジェクト参照機能により、Monorepo プロジェクトの型チェックとビルドがより効率的になります。

概念:Monorepo(単一リポジトリ)は複数の関連プロジェクトを同じコードリポジトリに置き、コード共有と協調開発を容易にします。


pnpm Workspace

pnpm はモダンなパッケージマネージャーで、Workspace 機能をネイティブにサポートしています。

ルートディレクトリ package.json

{
    "name": "my-monorepo",
    "version": "1.0.0",
    "private": true,

    // pnpm workspace を有効化
    "packages": [
        "packages/*"
    ],

    // 開発依存関係
    "devDependencies": {
        "typescript": "^5.0.0"
    },

    // スクリプト
    "scripts": {
        "build": "pnpm -r run build",
        "clean": "pnpm -r run clean",
        "type-check": "pnpm -r run type-check"
    }
}

pnpm:pnpm の Workspace 機能は、packages ディレクトリ内のパッケージを自動的にリンクできます。


プロジェクト構造

Monorepo プロジェクトのディレクトリ構造を作成します。

ディレクトリ構造

my-monorepo/
├── packages/
│   ├── utils/# ツールパッケージ
│   │   ├── src/
│   │   │   └── index.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   ├── ui-components/# UI コンポーネントパッケージ
│   │   ├── src/
│   │   │   ├── Button.tsx
│   │   │   └── index.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   └── app/# アプリケーション
│       ├── src/
│       │   └── index.tsx
│       ├── package.json
│       └── tsconfig.json
│
├── package.json# ルートディレクトリ設定
├── tsconfig.base.json# 基本設定
└── pnpm-workspace.yaml# pnpm 設定

packages:すべてのパッケージは packages ディレクトリの下に配置され、各パッケージは独立した package.json と tsconfig.json を持ちます。


基本的なTypeScript設定

すべてのパッケージが使用する基本設定を作成します。

tsconfig.base.json

{
    // コンパイラオプション
    "compilerOptions": {
        // ターゲットバージョン
        "target": "ES2020",

        // モジュールシステム
        "module": "ESNext",

        // 厳密モード
        "strict": true,

        // ライブラリの型チェックをスキップ
        "skipLibCheck": true,

        // ES モジュールの相互運用を有効化
        "esModuleInterop": true,

        // ファイル名の大文字小文字の一致を強制
        "forceConsistentCasingInFileNames": true,

        // モジュール解決
        "moduleResolution": "bundler",

        // JSON モジュールを解析
        "resolveJsonModule": true,

        // モジュールを分離
        "isolatedModules": true,

        // 出力ファイルを生成しない
        "noEmit": true
    }
}

継承:各パッケージの tsconfig.json は基本設定を継承し、カスタマイズが必要なオプションのみを上書きします。


ツールパッケージ設定

ツールパッケージ用に TypeScript を設定します。

packages/utils/tsconfig.json

{
    // 基本設定を継承
    "extends": "../../tsconfig.base.json",

    // コンパイラオプション
    "compilerOptions": {
        // 出力ディレクトリ
        "outDir": "./dist",

        // 宣言ファイルディレクトリ
        "declarationDir": "./dist/types",

        // 宣言ファイルを生成
        "declaration": true,

        // エントリファイルの宣言を生成
        "declarationMap": true,

        // モジュールのエクスポート方法
        "module": "ESNext",

        // プロジェクト参照を有効化
        "composite": true
    },

    // 含めるファイル
    "include": ["src/**/*"],

    // 除外するファイル
    "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

composite:プロジェクト参照を有効にすると、TypeScript はこのパッケージをインクリメンタルコンパイルできます。


アプリケーション設定

メインアプリケーション用に TypeScript を設定し、他のパッケージを参照します。

packages/app/tsconfig.json

{
    // 基本設定を継承
    "extends": "../../tsconfig.base.json",

    // コンパイラオプション
    "compilerOptions": {
        // 出力ディレクトリ
        "outDir": "./dist",

        // JSX 設定
        "jsx": "react-jsx",

        // パスエイリアス
        "baseUrl": ".",
        "paths": {
            "@my-utils/*": ["../utils/src/*"],
            "@my-ui/*": ["../ui-components/src/*"]
        }
    },

    // プロジェクト参照
    "references": [
        { "path": "../utils" },
        { "path": "../ui-components" }
    ],

    // 含めるファイル
    "include": ["src/**/*"],

    // 除外するファイル
    "exclude": ["node_modules", "dist"]
}

パスエイリアス:パスエイリアスを設定すると、同じリポジトリ内の他のパッケージを直接参照できます。


パッケージ間の依存関係

package.json で同じリポジトリ内のパッケージへの依存関係を宣言します。

packages/app/package.json

{
    "name": "@my-org/app",
    "version": "1.0.0",
    "private": true,

    "dependencies": {
        // 同じリポジトリのパッケージを参照
        "@my-org/utils": "workspace:*",
        "@my-org/ui-components": "workspace:*",

        // 外部依存関係
        "react": "^18.2.0",
        "react-dom": "^18.2.0"
    },

    "devDependencies": {
        // 開発依存関係
        "@types/react": "^18.2.0",
        "typescript": "^5.0.0"
    }
}

workspace:workspace:* を使用して同じリポジトリ内の他のパッケージを指定すると、pnpm が自動的に解決します。


ビルドスクリプト

Monorepo 用に統一されたビルドスクリプトを作成します。

ルートディレクトリ package.json

{
    "scripts": {
        // すべてのパッケージをビルド
        "build": "pnpm -r run build",

        // すべてのビルド成果物をクリーンアップ
        "clean": "pnpm -r run clean",

        // 型チェック
        "type-check": "pnpm -r run type-check",

        // すべてのパッケージをテスト
        "test": "pnpm -r run test",

        // インクリメンタルビルド
        "build:watch": "pnpm -r --parallel run build:watch",

        // アプリケーションを起動
        "dev": "pnpm --filter @my-org/app run dev"
    }
}

pnpm -r:すべてのパッケージの同名スクリプトを再帰的に実行します。


注意事項

  • パッケージ命名規則:@org-name/package 形式を使用
  • 独立したバージョン:各パッケージは独立してバージョン管理できます
  • workspace プロトコル:workspace:* を使用して同じリポジトリのパッケージを参照
  • ビルド順序:依存されるパッケージを先にビルドする必要があります

ベストプラクティス:Monorepo は複数の関連プロジェクトの管理に適しており、コードの再利用と開発効率を大幅に向上できます。


まとめ

Monorepo はモダンなフロントエンドプロジェクト管理の推奨モードです。

  • pnpm Workspace:Monorepo をネイティブサポート
  • プロジェクト参照:インクリメンタルコンパイルを実現
  • パスエイリアス:同じリポジトリのパッケージを簡単に参照
  • 一元管理:設定と依存関係を共有

推奨:プロジェクトに複数の関連パッケージが含まれる場合は、Monorepo 方式を優先的に検討してください。

その他の拡張