shared-monorepo-pnpm-workspaces について
JavaScript と TypeScript のモノレポを pnpm ワークスペースで管理するためのスキルです。パッケージのリンク、依存バージョンの集約、対象を絞ったコマンド、共有設定、公開、CI の手順を扱います。
主な機能
- 01pnpm-workspace.yaml による設定
- 02workspace:* による内部パッケージのリンク
- 03catalog: による依存バージョン管理
- 04--filter による対象指定コマンド
- 05公開と CI の設定ガイド
活用シーン
- 01pnpm モノレポのセットアップ
- 02ワークスペース間での依存バージョン共有
- 03変更されたパッケージだけでのコマンド実行
- 04単一リポジトリからの関連パッケージ公開
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agents-inc/ skills --skill shared-monorepo-pnpm-workspacesインストール後、Claude Code で/shared-monorepo-pnpm-workspacesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
metadata.yamlreference.mdexamples/ci.mdcore.mdpackages.mdpublishing.mdscripts.md詳細
モノレポ管理のための pnpm ワークスペース
クイックガイド: モノレポ管理のための pnpm 10.x ワークスペースについて説明します。
pnpm-workspace.yamlでワークスペースパッケージを定義します。内部パッケージのリンクにはworkspace:*プロトコルを使用します。依存関係のバージョン同期にはcatalog:プロトコルを使用します。対象を絞ったコマンドには--filterを使用します。パッケージ間でtsconfigとツール設定を共有します。バージョン管理と公開には Changesets を使用します。デフォルトでは依存関係が厳密に分離されます。
<critical_requirements>
重要:このスキルを使用する前に
すべてのコードは CLAUDE.md のプロジェクト規約に従う必要があります(kebab-case、名前付きエクスポート、インポート順序、
import type、名前付き定数)
(すべての内部パッケージ依存関係に workspace:* プロトコルを使用する必要があります。バージョンをハードコードしないでください)
(CI では --frozen-lockfile を使用する必要があります。CI 環境では pnpm がデフォルトで有効にします)
(リポジトリのルートにある pnpm-workspace.yaml でワークスペースパッケージを定義する必要があります)
(pnpm 固有の設定は .npmrc ではなく、pnpm-workspace.yaml に記述する必要があります(pnpm v10 での変更))
(3 つ以上のパッケージ間で依存関係のバージョンを共有する場合は、catalog: プロトコルを使用する必要があります)
(変更された特定のパッケージのみを対象とする場合は、pnpm -r ではなく --filter を使用する必要があります)
</critical_requirements>
自動検出: pnpm-workspace.yaml、pnpm workspaces、workspace protocol、workspace:*、catalog:、pnpm filter、pnpm recursive、pnpm monorepo、pnpm-lock.yaml、.npmrc pnpm、pnpm catalogs、pnpm publish
使用する場面:
- pnpm ワークスペースでモノレポをセットアップする場合
- ワークスペースパッケージの検出用に
pnpm-workspace.yamlを設定する場合 workspace:*プロトコルで内部パッケージをリンクする場合catalog:プロトコルで依存関係のバージョンを同期する場合--filterまたは-rでワークスペース全体のスクリプトを実行する場合- pnpm ワークスペースからパッケージを公開する場合
- pnpm キャッシュを使って CI/CD パイプラインをセットアップする場合
- ワークスペースパッケージ間で TypeScript、ESLint、または Prettier の設定を共有する場合
- npm/yarn ワークスペースから pnpm に移行する場合
使用しない場面:
- 共有コードのない単一パッケージのプロジェクト
- パッケージマネージャーとして Bun または Yarn を使用するプロジェクト
- npm との互換性のみが必要なプロジェクト(例:npm workspaces)
- タスクのオーケストレーションロジック(pnpm と併用する専用のタスクランナーを使用してください)
取り上げる主なパターン:
pnpm-workspace.yamlのセットアップとワークスペースパッケージの検出- ワークスペースプロトコル(
workspace:*、workspace:^、workspace:~) - 依存関係のバージョン同期に使うカタログ
- コマンドのフィルタリング(
--filter、-F、glob パターン、依存関係セレクター) - ワークスペース全体でのスクリプト実行(
-r、--parallel、--workspace-concurrency) pnpm-workspace.yamlの設定(v10:設定の保存先が.npmrcから移動)publishConfigと changesets を使った公開- GitHub Actions、キャッシュ、
--frozen-lockfileを使った CI/CD - TypeScript 設定を共有するパターン
- モノレポのディレクトリ構成規約
詳細なリソース:
- 基本セットアップ -- pnpm-workspace.yaml、.npmrc、ディレクトリ構成、設定
- 共有パッケージ -- ワークスペースプロトコル、カタログ、TypeScript/ESLint 設定
- スクリプトとフィルタリング -- --filter、再帰的な実行、依存関係の管理
- 公開とバージョン管理 -- changesets、publishConfig、Docker
- CI/CD パイプライン -- GitHub Actions、自動リリース
- コマンド早見表 -- コンパクトな参照表
## 哲学
pnpm ワークスペースは、コンテンツアドレス可能なストレージによって、厳格かつ効率的なモノレポ管理を実現します。npm/yarn とは異なり、pnpm はフラットではない node_modules を作成し、パッケージがアクセスできるのは宣言済みの依存関係のみです。この厳格さによって、依存関係の宣言漏れを早期に検出できます。ワークスペースプロトコル(workspace:*)により、内部パッケージは常にローカルにリンクされ、カタログ(catalog:)によりバージョン管理が一元化されて、バージョンの不一致を防げます。
基本原則:
- デフォルトで厳格 — パッケージは宣言されていない依存関係にアクセスできません
- ディスク効率に優れる — コンテンツアドレス可能なストアにより、プロジェクト間で同一ファイルを共有します
- セキュリティを最優先 — v10 ではサプライチェーン攻撃を防ぐため、ライフサイクルスクリプトがデフォルトでブロックされます
- 単一のロックファイル — すべてのパッケージを対象とする
pnpm-lock.yamlがワークスペースのルートに1つだけあります
pnpm ワークスペースを使う場合:
- 複数のアプリと共有パッケージを含むモノレポ
- 厳格な依存関係の分離が必要なプロジェクト
- ディスク効率の高い依存関係ストレージを求めるチーム
- 1つのリポジトリから複数の関連する npm パッケージを公開する場合
使わない場合:
- 単一パッケージのプロジェクト(ワークスペースの利点がありません)
- Yarn PnP または Berry の機能を深く活用しているプロジェクト
- npm しか利用できない環境(CI/CD の一部や、制限のある企業環境など)
基本パターン
パターン1:ワークスペース設定(pnpm-workspace.yaml)
リポジトリのルートにある pnpm-workspace.yaml ファイルで、ワークスペースパッケージを含むディレクトリを定義します。pnpm ワークスペースには必ずこのファイルが必要です。
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
pnpm v10 では、設定が .npmrc からこのファイルに移動しました。
# pnpm-workspace.yaml (v10+)
packages:
- "apps/*"
- "packages/*"
linkWorkspacePackages: true
saveWorkspaceProtocol: rolling
disallowWorkspaceCycles: true
適している理由: ワークスペースの定義と設定を一元管理できます
ワークスペースの完全な設定、ディレクトリ構造、およびすべての設定項目については、examples/core.md を参照してください。
パターン2:ワークスペースプロトコル(workspace:*)
ワークスペースプロトコルにより、内部パッケージは常にローカルのワークスペースバージョンに解決され、レジストリから取得されることはありません。
| プロトコル | 開発中 | pnpm publish 後 |
|---|---|---|
workspace:* | ローカルパッケージにリンク | 正確なバージョンに置換される(例:1.5.0) |
workspace:^ | ローカルパッケージにリンク | キャレット範囲に置換される(例:^1.5.0) |
workspace:~ | ローカルパッケージにリンク | チルダ範囲に置換される(例:~1.5.0) |
{
"dependencies": {
"@repo/ui": "workspace:*",
"@repo/types": "workspace:*"
}
}
適している理由: ローカルリンクが保証され、pnpm が外部からの解決を拒否します。また、公開時のバージョン変換により、正しいセマンティックバージョニングが確保されます
{
"dependencies": {
"@repo/ui": "^1.0.0"
}
}
適していない理由: バージョンをハードコードすると、ローカルワークスペースではなく npm レジストリからインストールされる可能性があり、パッケージ間でバージョンの不一致が発生します
プロトコルの種類、エイリアス設定、内部パッケージのセットアップについては、examples/packages.md を参照してください。
パターン3:バージョン同期に使うカタログ
カタログでは、pnpm-workspace.yaml に依存関係のバージョンを一度定義し、catalog: を使ってすべてのパッケージから参照します。
# pnpm-workspace.yaml
catalog:
react: ^19.0.0
react-dom: ^19.0.0
typescript: ^5.7.0
{
"dependencies": {
"react": "catalog:",
"react-dom": "catalog:"
}
}
適している理由: バージョンの情報源を一元化でき、1行更新するだけですべてのパッケージに反映され、マージの競合を防げます
名前付きカタログは、バージョン移行に対応しています。
catalogs:
react18:
react: ^18.3.1
react19:
react: ^19.0.0
名前付きカタログ、厳格な適用、完全な例については、examples/packages.md を参照してください。
パターン4:フィルタリングコマンド
--filter(または -F)を使うと、ワークスペース全体ではなく、特定のパッケージにコマンドの実行対象を限定できます。
# Exact package
pnpm --filter @repo/web-app build
# Package and ALL its dependencies
pnpm --filter "web-app..." build
# Changed packages since main
pnpm --filter "...[origin/main]" test
# Exclude a package
pnpm --filter "!@repo/docs" build
適している理由: 対象を絞った実行によって CI の時間を節約でき、依存関係を考慮したフィルタリングにより、正しいビルド順序が確保されます
# BAD: Running everything when only one package changed
pnpm -r build
適していない理由: すべてのパッケージを再ビルドするため CI の時間が無駄になり、変更の検出もできません
すべてのフィルターの種類、CI に最適化されたパターン、グラフの探索については、examples/scripts.md を参照してください。
### パターン 5:ワークスペース全体でのスクリプト実行
# Topological order (respects dependency graph) -- use for build
pnpm -r build
# Parallel (ignores dependency graph) -- use for test, lint
pnpm -r --parallel test
# Controlled concurrency
pnpm -r --workspace-concurrency 4 build
# BAD: Building in parallel when packages depend on each other
pnpm -r --parallel build
問題点: 依存関係のビルドが完了する前にパッケージがビルドされ、モジュールが見つからないエラーが発生する可能性があります。
依存関係の管理と依存関係の追加については、examples/scripts.md を参照してください。
パターン 6:TypeScript 設定の共有
設定パッケージを使用して、すべてのワークスペースパッケージ間で TypeScript コンパイラーオプションを共有します。
{
"name": "@repo/config-typescript",
"private": true,
"exports": {
"./base": "./tsconfig.base.json",
"./react": "./tsconfig.react.json",
"./node": "./tsconfig.node.json"
}
}
利用側の設定:
{
"extends": "@repo/config-typescript/react",
"include": ["src/**/*.ts", "src/**/*.tsx"]
}
利点: TypeScript 設定の信頼できる情報源が一元化され、変更がすべてのパッケージに反映されます。
基本、react、node の各設定と ESLint の共有については、examples/packages.md を参照してください。
パターン 7:ワークスペースからの公開
publishConfig を使用して公開対象を制御し、バージョン管理には changesets を使用します。
{
"name": "@repo/ui",
"main": "./src/index.ts",
"publishConfig": {
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"access": "public"
}
}
利点: 開発中はソース(HMR が高速)、公開時はビルド済みの出力を使用します。
pnpm changeset # Create changeset
pnpm changeset version # Bump versions + changelogs
pnpm publish -r # Publish to npm
changesets の設定、Docker によるデプロイ、移行については、examples/publishing.md を参照してください。
パターン 8:GitHub Actions による CI/CD
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
- run: pnpm install
- run: pnpm --filter "...[origin/main]" build
- run: pnpm --filter "...[origin/main]" test
利点: pnpm/action-setup@v4 がインストールを処理し、cache: "pnpm" がストアをキャッシュします。CI では --frozen-lockfile が自動的に適用され、変更に基づくフィルタリングによって影響を受けるパッケージのみをビルドします。
changesets を使った自動リリースを含むワークフロー全体については、examples/ci.md を参照してください。
パフォーマンスの最適化
インストールのパフォーマンス:
- pnpm はコンテンツアドレス可能ストレージを使用し、同一のファイルはディスク上に 1 つだけ保存されます。
- ハードリンクを利用するため、ウォームインストールは npm/yarn より最大 2 倍高速です。
--frozen-lockfile(CI でのデフォルト)を指定すると、解決処理が省略され、インストールが最速になります。
ワークスペースのパフォーマンス:
--filterを使用して、影響を受けるパッケージに対してのみコマンドを実行します。- パッケージ間に依存関係のないタスク(test、lint)には
--parallelを使用します。 - リソースが限られた CI で並列実行を制御するには、
--workspace-concurrencyを使用します。 - 変更に基づくフィルタリング(
--filter "...[origin/main]")により、変更されていないパッケージをスキップします。
ディスク使用量の削減:
- グローバルストアにより、プロジェクト間で重複が排除されます(
pnpm store path)。 - 一般的に、npm と比べてディスク使用量を 50~70%削減できます。
CI キャッシュ:
- uses: actions/setup-node@v4
with:
node-version: 22
cache: "pnpm"
これにより、実行間で pnpm のコンテンツアドレス可能ストアがキャッシュされ、2 回目以降のインストールがほぼ瞬時に完了します。
<decision_framework>
選択の指針
カタログと直接のバージョン指定の使い分け
Does 3+ packages use this dependency?
YES -> Use catalog: protocol
NO -> Direct version is fine
Will this dependency version be updated frequently?
YES -> Use catalog: (one-line update)
NO -> Direct version is acceptable
workspace:*、workspace:^、workspace:~ の使い分け
Are you publishing packages to npm?
NO -> Use workspace:* (exact local linking, version irrelevant)
YES -> Do consumers need flexible version ranges?
YES -> workspace:^ (caret range on publish)
NO -> workspace:* (exact version on publish)
--filter と -r の使い分け
Running a command in CI?
YES -> Use --filter "...[origin/main]" (only affected packages)
NO -> Running locally?
ALL packages -> pnpm -r <cmd>
ONE package -> pnpm --filter <name> <cmd>
Is the command order-dependent (build)?
YES -> Use -r (topological order) or --filter with ...
NO -> Use --parallel for speed (lint, test)
pnpm、npm、Yarn Workspaces の比較
Need strict dependency isolation?
YES -> pnpm (non-flat node_modules by default)
NO -> Any works
Need disk efficiency?
YES -> pnpm (content-addressable store)
NO -> Any works
Need zero-config PnP (no node_modules)?
YES -> Yarn Berry with PnP
NO -> pnpm or npm
</decision_framework>
<red_flags>
危険信号
優先度の高い問題:
- 内部パッケージに
workspace:*ではなくバージョンをハードコードしている(ローカルでのリンクが壊れ、レジストリからインストールされます) .npmrcに設定を記述しているが、本来はpnpm-workspace.yamlに記述すべき(pnpm v10 では暗黙に無視されます)- リポジトリのルートに
pnpm-workspace.yamlがない(pnpm がワークスペースパッケージを認識しません) - CI で
--frozen-lockfileを付けずにpnpm installを実行している(ロックファイルが気付かないうちに変更される可能性があります) - パッケージ間に依存関係があるビルドコマンドで
--parallelを使用している(競合状態が発生します)
優先度が中程度の問題:
- 3 つ以上のパッケージで同じ依存関係のバージョンを共有しているのに、
catalog:を使用していない(バージョンの不整合が発生します) - CI で
--filter "...[origin/main]"の代わりにpnpm -r buildを実行している(時間を無駄にします) - 内部パッケージに
private: trueが設定されていない(誤って npm に公開されるリスクがあります) allowBuilds(または旧式のonlyBuiltDependencies)の許可リストを設定していない(v10 ではすべてのインストールスクリプトがブロックされます)
よくある間違い:
- パッケージマネージャーを混在させている(pnpm ワークスペース内で npm install を実行するとロックファイルが壊れます)
- 不足している依存関係を適切に宣言する代わりに、手早い修正として
shamefullyHoist: trueを使用している - GitHub Actions のチェックアウトで
fetch-depth: 0を忘れている(git に基づく変更検出が機能しなくなります) - ローカルと CI で異なる pnpm のバージョンを実行している(ロックファイル形式に互換性がなくなります)
落とし穴とエッジケース:
pnpm publishではworkspace:*が実際のバージョンに置き換えられます。これは想定された動作であり、バグではありませんcatalog:エントリーは依存関係名と完全に一致する必要があります。誤字があってもエラーにならず、そのまま処理されます--filter "...[origin/main]"には git の履歴が必要です。fetch-depth: 0、または最低でもfetch-depth: 2を使用してください- pnpm v10 では、デフォルトですべてのライフサイクルスクリプトがブロックされます。
postinstallが必要なパッケージ(esbuild、sharpなど)を許可リストに追加するにはpnpm approve-buildsを使用するか、pnpm-workspace.yamlでallowBuildsを設定してください pnpm deployにはinjectWorkspacePackages: trueが必要です(またはpnpm deploy --legacyを使用して回避してください)- ワークスペース依存関係の循環により、スクリプトの実行順序が予測できなくなります。
disallowWorkspaceCycles: trueを使用してください saveWorkspaceProtocol: rolling(デフォルト)では、pnpm addによってworkspace:付きで自動保存されます。これは正しい動作です
</red_flags>
<critical_reminders>
重要な注意事項
すべてのコードは CLAUDE.md のプロジェクト規約(kebab-case、名前付きエクスポート、インポート順序、
import type、名前付き定数)に従う必要があります
(すべての内部パッケージ依存関係に workspace:* プロトコルを必ず使用してください。バージョンをハードコードしてはいけません)
(CI では必ず --frozen-lockfile を使用してください。pnpm は CI 環境でこれをデフォルトで有効にします)
(リポジトリのルートにある pnpm-workspace.yaml でワークスペースパッケージを必ず定義してください)
(pnpm 固有の設定は必ず pnpm-workspace.yaml に記述してください。.npmrc には記述しないでください(pnpm v10 で変更されました))
(3 つ以上のパッケージで依存関係のバージョンを共有する場合は、必ず catalog: プロトコルを使用してください)
(変更されたパッケージだけを対象とする場合は、pnpm -r の代わりに --filter を必ず使用してください)
これらのルールに従わないと、依存関係の解決が壊れ、バージョンの不整合、CI キャッシュの取りこぼし、セキュリティ上の脆弱性が発生します。
</critical_reminders>