pnpmのcatalog機能を試してみた

pnpmのcatalog機能を試してみた

2026.03.04

どうも!オペ部の西村祐二です!

モノレポで複数パッケージの依存バージョンを管理していると、パッケージごとにバージョンがバラバラになったり、更新時にmerge conflictが発生したりと、地味に面倒な場面があります。

pnpmにはこの問題を解決するcatalog機能があります。pnpm v9.5で導入された機能で、pnpm-workspace.yamlに依存バージョンを一元定義し、各package.jsonからcatalog:プロトコルで参照できる仕組みです。

さらに、2026年2月7日リリースのpnpm 10.29ではpnpm dlxでもcatalog:プロトコルのサポートが追加されました。加えてcatalogMode: strictのバグ修正も含まれており、catalog機能がより実用的になっています。

今回はこのcatalog機能を、基本的な使い方からstrictモードまで試してみました。

catalogとは

catalog:workspace全体で依存バージョンを一元管理できる仕組みです。

従来のモノレポでは、各パッケージのpackage.jsonにバージョンを直接書いていました。

packages/app/package.json
{ "dependencies": { "react": "^18.3.1" } }
packages/lib/package.json
{ "dependencies": { "react": "^18.3.1" } }

catalog機能を使うと、pnpm-workspace.yamlにバージョンを一箇所で定義し、各package.jsonからはcatalog:で参照します。

pnpm-workspace.yaml
catalog:
  react: ^18.3.1
packages/app/package.json
{ "dependencies": { "react": "catalog:" } }
packages/lib/package.json
{ "dependencies": { "react": "catalog:" } }

バージョンを変更したいときはpnpm-workspace.yamlを1箇所変えるだけで済みます。

default catalogとnamed catalog

catalogにはdefault catalognamed catalogの2種類があります。

pnpm-workspace.yaml
# default catalog(catalogフィールド)
catalog:
  react: ^18.3.1
  typescript: ^5.7.0

# named catalog(catalogsフィールド)
catalogs:
  react17:
    react: ^17.0.2
    react-dom: ^17.0.2
packages/app/package.json
{
  "dependencies": {
    "react": "catalog:",
    "typescript": "catalog:"
  }
}
packages/legacy/package.json
{
  "dependencies": {
    "react": "catalog:react17",
    "react-dom": "catalog:react17"
  }
}

named catalogは、段階的なマイグレーションでバージョンの異なるパッケージグループが混在する場合に便利です。

catalogMode

pnpm-workspace.yamlcatalogModeを設定することで、pnpm add実行時のcatalog利用ポリシーを制御できます(v10.12.1で追加)。

モード pnpm add時の動作
manual(デフォルト) catalogに自動追加しない。手動で管理
prefer catalogに互換バージョンがあればcatalogを優先。なければ通常の依存として追加
strict catalogのバージョンのみ許可。異なるバージョンを追加するとエラー。未定義のパッケージは自動的にcatalogに追加

試してみる

環境

  • pnpm 10.29以上
  • Node.js 18+
  • macOS

1. 基本的なcatalog設定

まず検証用のworkspaceを作成します。

$ mkdir pnpm-catalog-test
$ cd pnpm-catalog-test
$ pnpm init

{
  "name": "pnpm-catalog-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "packageManager": "pnpm@10.30.0"
}

pnpm-workspace.yamlを作成して、catalogを定義します。

pnpm-workspace.yaml
packages:
  - "packages/*"

catalog:
  shx: ^0.3.4
  cowsay: ^1.5.0

パッケージを作成して、catalog:プロトコルで依存を参照してみます。

$ mkdir -p packages/app
$ cd packages/app
$ pnpm init
packages/app/package.json
{
  "dependencies": {
    "shx": "catalog:",
    "cowsay": "catalog:"
  }
}
$ pnpm install  # 成功

pnpm-lock.yamlを確認すると、catalogで定義したバージョンに解決されていることが分かります。

pnpm-lock.yaml(抜粋)
catalogs:
  default:
    shx:
      specifier: ^0.3.4
      version: 0.3.4
    cowsay:
      specifier: ^1.5.0
      version: 1.6.0

2. pnpm dlxでのcatalog対応(pnpm 10.29

pnpm 10.29pnpm dlxへのcatalog:プロトコルサポートが追加されましたが、10.30.3時点ではpnpm-workspace.yamlからカタログ定義を読み込むコードパスがスキップされるバグがあり、実際には動作しませんでした(#10594)。修正PRはマージ待ちの状態です。

修正後は以下のように使えるようになる予定です。

pnpm dlx shx@catalog: echo hello

3. named catalogを使う

named catalogを使って、用途別にバージョンを管理してみます。default catalogに加えて、cliという名前のnamed catalogを定義します。

pnpm-workspace.yaml
packages:
  - "packages/*"

catalog:
  shx: ^0.3.4

catalogs:
  cli:
    cowsay: ^1.5.0
packages/app/package.json
{
  "dependencies": {
    "shx": "catalog:",
    "cowsay": "catalog:cli"
  }
}
pnpm install  # 成功

shxはdefault catalog、cowsayはnamed catalog cli からバージョンが解決されます。用途やカテゴリごとにcatalogを分けて管理できるので、段階的なマイグレーションなどで新旧バージョンを共存させたい場合に便利です。

4. catalogMode: strictの検証

strictモードを設定します。catalogModepnpm add実行時の挙動を制御する設定です。

pnpm-workspace.yamlを以下に変更し、packages/app/package.jsondependenciesは削除してクリーンな状態にします。

pnpm-workspace.yaml
packages:
  - "packages/*"

catalogMode: strict

catalog:
  shx: ^0.3.4
  cowsay: ^1.5.0

catalogに定義済みのパッケージをadd → 自動的にcatalog:で追加

pnpm add shx --dir packages/app

strictモードでは、catalogに定義済みのパッケージをpnpm addすると、package.jsonに自動的に"shx": "catalog:"と書き込まれます。

catalogと異なるバージョンを指定してadd → エラー

catalogのshxを一時的にexactバージョンに変更して検証します。

pnpm-workspace.yaml(変更箇所のみ)
catalog:
  shx: 0.3.4  # ^0.3.4 → 0.3.4 に変更
  cowsay: ^1.5.0
pnpm add shx@0.3.3 --dir packages/app
# ERR_PNPM_CATALOG_VERSION_MISMATCH  Wanted dependency outside the version range defined in catalog

catalogに定義済みのバージョンと異なるバージョンを指定すると、ERR_PNPM_CATALOG_VERSION_MISMATCHでエラーになります。

catalogに未定義のパッケージをadd → 自動的にcatalogに追加

pnpm add left-pad --dir packages/app

strictモードでは、catalogに未定義のパッケージをpnpm addするとエラーにはならず、自動的にpnpm-workspace.yamlのcatalogに追加されます。package.jsonには"left-pad": "catalog:"と書き込まれ、catalogには解決されたバージョンが追記されます。

試してみた感想

実際に使ってみて、以下のような点が良いと感じました。

  • バージョン管理が一箇所に集約されるのが分かりやすい
    • pnpm-workspace.yamlを見れば、workspace全体でどのバージョンを使っているか一目で把握できます。各package.jsonを見に行く必要がなくなります
  • pnpm dlx対応が進めば、CIスクリプトのバージョン管理も楽になりそう
    • 現時点ではバグで動作しませんが、修正されればGitHub ActionsやMakefileでpnpm dlx turbo@catalog:のように書けるようになり、バージョン更新時にCI設定ファイルを修正する必要がなくなります
  • strictモードでpnpm add時にcatalog利用を強制できる
    • pnpm addすると自動的にcatalog:で書き込まれるので、チーム開発でバージョン直書きが混入するのを防げます

一方で、気になった点としては:

  1. catalogMode: strictpnpm add時のみ適用される
    strictモードはpnpm addの挙動を制御する設定で、pnpm install時にはcatalog利用の検証は行われません。つまり、package.jsonにバージョンを直書きした状態でpnpm installしても、エラーにはならずそのままインストールされます。pnpm install時にもcatalog利用を強制する機能は現時点では未実装です
  2. strictモードでもcatalog未定義パッケージはエラーにならない
    strictモードでpnpm addすると、catalogに未定義のパッケージも自動的にcatalogに追加されます。「catalogに定義済みのパッケージのみ追加を許可する」というゲート機能ではない点に注意が必要です。一方で、catalogに定義済みのバージョンと異なるバージョンを指定した場合はエラーになります
  3. レンジ指定のcatalogエントリでバージョン競合時にクラッシュする
    catalogが^0.3.4のようなレンジで定義されている場合、異なるバージョンでpnpm addするとsemver.eq()の内部エラーでクラッシュします(10.30.3時点)。exactバージョンで定義していれば正しいエラーメッセージが表示されます
  4. パッケージ数が少ないリポジトリではメリットが薄い
    2〜3パッケージ程度のモノレポだと、catalogの管理コスト自体がオーバーヘッドになる可能性があります。ある程度の規模感があるモノレポで真価を発揮する機能だと感じました

まとめ

pnpmのcatalog機能は、モノレポでの依存バージョンをpnpm-workspace.yamlに一元管理できる仕組みです。pnpm 10.29ではpnpm dlxへのcatalog:プロトコルサポートも追加されましたが、現時点ではバグにより動作しないため修正を待つ状況です(#10594)。

catalogMode: strictを設定すれば、pnpm add時に自動的にcatalog:で依存を追加してくれるので、チーム開発でのバージョン管理の統制に役立ちます。ただしpnpm install時の検証は行われない点や、レンジ指定のcatalogエントリでのバージョン競合時にクラッシュするバグがある点は把握しておく必要があります。

モノレポでの依存管理に課題を感じている方は、ぜひ試してみてください。

誰かの参考になれば幸いです。


参考リンク:

この記事をシェアする

FacebookHatena blogX

関連記事