0001. モノレポ構成 (pnpm workspaces + Turborepo)¶
- ステータス: Accepted(実装済み)
- 関連: 0009 API を Cloudflare Workers で構築 / 0014 フロントエンド技術選定
コンテキストと課題¶
waste-link は Web・モバイル・(後から追加された) API・共有ロジック・DB スキーマを単一プロダクトとして 開発する。これらは型 (Zod スキーマ) や定数を共有し、リリースのタイミングも近い。リポジトリを分割すると 型の共有・横断的な変更・CI が煩雑になる。1 つのリポジトリで複数パッケージを管理しつつ、 ビルド/型検査/テストを依存関係順に効率良く回せる構成が必要だった。
決定¶
pnpm workspaces によるモノレポ + Turborepo によるタスクオーケストレーション を採用する。
ワークスペースは 3 グロブで構成する:
含まれるパッケージ:
| パッケージ | name | 役割 |
|---|---|---|
apps/web |
@waste-link/web |
Web フロント (Vite + React) |
apps/api |
@waste-link/api |
API (Hono on Cloudflare Workers) |
apps/mobile |
@waste-link/mobile |
モバイル (Expo / React Native) |
packages/shared |
@workspace/shared |
共有 Zod スキーマ・定数・純関数 |
packages/supabase |
@workspace/supabase |
Supabase クライアント / 自動生成型 |
- パッケージマネージャは pnpm を固定:
package.jsonのpackageManager: "pnpm@9.15.0"を Single Source of Truth とし、Node は>=22(.nvmrc=22)。 - タスクは Turborepo 経由:
turbo.jsonでbuild/typecheck/testは^build(依存パッケージのビルド) に依存し、build出力はdist/**等をキャッシュ、devはcache: falseかつpersistent: true。ルートpackage.jsonのdev/build/typecheck/testはturbo run ...に委譲する (lintのみbiome check .を直接実行)。 - 共有パッケージはビルドレス:
@workspace/shared/@workspace/supabaseはmainが./src/index.tsのソース直参照で、TS の path alias (tsconfig.base.json) で解決する。 - node_modules は hoisted レイアウト:
.npmrcでnode-linker=hoisted。これは Expo / Metro / React Native が pnpm の厳格な isolated レイアウトを完全サポートせず@expo/metro-runtime等の解決に失敗するため、Expo 公式推奨に合わせた判断。
影響 (Consequences)¶
良い点¶
- 共有スキーマ (
@workspace/shared) を Web / API / Mobile がworkspace:*依存で直接参照でき、 型と検証ロジックが 1 箇所に集約される。 - Turborepo の
^build依存とキャッシュにより、CI (pnpm turbo run typecheck test build) が 依存順に走り、未変更パッケージはキャッシュで短縮される。 packageManagerフィールド固定で、ローカルと CI (pnpm/action-setup@v4がこの値を参照) の pnpm バージョンずれを防ぐ。
トレードオフ / 注意点¶
- hoisted node_modules は pnpm の隔離 (phantom dependency 防止) という利点を一部捨てている。 宣言していない依存にアクセスできてしまうリスクは、Expo 互換のため受け入れている。
- スコープ名が二系統 (
@waste-link/*と@workspace/*) に分かれている。apps は@waste-link/*、共有パッケージは@workspace/*。新規パッケージ追加時に取り違えやすい。 - ビルドレスな共有パッケージは TS path alias 前提のため、各アプリの
tsconfig/ バンドラ (Vite・wrangler・Metro) でエイリアス解決を揃える必要がある。
根拠 (典拠)¶
pnpm-workspace.yaml— ワークスペースグロブ (apps/*/packages/*/scripts)。turbo.json—dev(cache:false, persistent) /build(^build, outputsdist/**) /typecheck/lint/test(outputscoverage/**) /clean。package.json(ルート) —packageManager: "pnpm@9.15.0"、engines.node: ">=22"、scripts (dev/build/lint=biome check ./typecheck/test)、devDeps (@biomejs/biome1.9.4 /turbo^2.3.3 /typescript^5.7.2 /@changesets/cli)。.nvmrc—22。.npmrc—node-linker=hoisted(Expo/RN 互換のコメント付き) /auto-install-peers=true/strict-peer-dependencies=false。apps/web/package.json・apps/api/package.json・apps/mobile/package.json・packages/shared/package.json・packages/supabase/package.json— 各name。tsconfig.base.json— ES2022 /moduleResolution: bundler/strict/noUncheckedIndexedAccess/ path alias@workspace/shared・@workspace/supabase。.github/workflows/ci.yml:26-36— pnpm バージョンはpackageManagerを参照する旨のコメント。