【初心者クイズ】せっかくバージョンアップしたのにどうして？ pnpm の不思議な挙動【回答あり】

![タイトルバナー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3869171/b5bafd98-6454-4b37-8991-a7a2ddee083c.png)

Node.js 初学者、天沙士郎です。TypeScript 学習中です。

最近学習のため開発環境を mise-en-place と pnpm で構築したんです。pnpm は活動が活発なようで新バージョンのリリースがありバージョンアップしたんです。そこでなかなかに不思議な挙動に出会いました。

みなさん、なにが起こっているかわかるでしょうか？ (回答あり)

## 実行環境

- MacBook Air Retina, 13-inch, 2018 Intel
- MacOS Sonoma 14.7.5

## ツールについて

https://mise.jdx.dev/

https://pnpm.io/ja/

僕は mise で Python や Node.js などの各種実行環境を構築しています。mise は実行環境のバージョン制御、仮想環境準備、direnv を一手に引き受けてくれるの、便利ですもんね。
pnpm は Node.js のアプリやモジュールを使うのに、効率的かつ高速に作業ができるのがいいですよね。

## 準備

### mise で node と pnpm を導入

では早速やっていきましょう。まずはベースとなる環境構築から。

```zsh:ツールインストール
brew install mise # mise 未インストールなら実行

mkdir project1 #  プロジェクトフォルダを作り node と pnpm をインストール
cd project1
mise use node@latest
mise use pnpm@latest
pnpm init
```

これで mise を使って node と pnpm を使う準備ができました。ほんとにとっても簡単ですね。

### 確認 : バージョンと実行ファイルの場所

ここで一旦確認。

```zsh: mise でツール一覧
mise list

Tool    Version  Source                                  Requested 
node    23.11.0  ~/dev/learning/node/project1/mise.toml  latest
pnpm    10.8.0   ~/dev/learning/node/project1/mise.toml  latest
python  3.13.2  
ruff    0.9.10  
uv      0.6.13  
```

- node と pnpm がリストにある →  インストールできてる
- Source にパスがある →  設定もちゃんとできてる

ということでインストールと設定はできました。
あと node と pnpm の場所とバージョンを確認しておきましょう。

```zsh: node のバージョンと場所
which node
/Users/shiro/.local/share/mise/installs/node/23.11.0/bin/node

node -v
v23.11.0
```

```zsh: pnpm のバージョンと場所
which pnpm
/Users/shiro/.local/share/mise/installs/pnpm/10.8.0/pnpm

pnpm -v
10.8.0
```

はい、OK です。問題ないですね。

## pnpm のバージョンアップ。<font color="red">あれぇ？</font>

さて pnpm がバージョンアップしたので導入しましょう。mise で導入したので mise でバージョンアップします。コマンド一発！やっぱり便利。

```zsh
mise upgrade

# 以下はコマンド実行結果
mise pnpm@10.9.0 ✓ installed
mise uninstall pnpm@10.8.0 ✓ remove ~/Library/Caches/mise/pnpm/10.8.0
```

インストールできたようなので確認です。

```zsh: mise でツール一覧
mise list
Tool    Version  Source                                  Requested 
node    23.11.0  ~/dev/learning/node/project1/mise.toml  latest
pnpm    10.9.0   ~/dev/learning/node/project1/mise.toml  latest
python  3.13.2  
ruff    0.9.10  
uv      0.6.13  
```

ちゃんと `10.9.0` とバージョンが上がっています。では場所とバージョンを確認です。

```zsh: 場所とバージョンの確認
which pnpm
/Users/shiro/.local/share/mise/installs/pnpm/10.9.0/pnpm

pnpm -v
10.8.0
```

<font color="red">**は、はい？**</font>
実行媒体の場所は mise 管理下のフォルダでバージョンが `10.9.0` になってます。これはいい。
<font color="red">**でもバージョンは `pnpm -v` の結果が `10.8.0` ？**</font> なにこれ？😱

<font color="red">**さて、なぜでしょう？**</font>

## ヒント

pnpm は実行時にとあるファイルをみて…

## 答え合わせ

サクッといきます。答えは

- pnpm は実行時にプロジェクトフォルダの `package.json` をみて実行するバージョンを決定する。
- 実行している pnpm のバージョンと異なるバージョンが指定されていた場合、設定ファイルに記載されたバージョンを実行する。

です。

実は[準備の pnpm インストール](#mise-で-node-と-pnpm-を導入)で `pnpm init` したときにできる設定ファイル、 `package.json` は次のようになっています。

```json: package.json : pnpm init 時に作成
{
  "name": "project1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "packageManager": "pnpm@10.8.0"
}
```

最後の行に `packageManager` という項目に `pnpm@10.8.0` とバージョン番号が指定されているんですね。これで pnpm は「あ、 10.8.0 で実行しなきゃ」となってバージョン番号が 10.8.0 になったというわけです。

### 解決方法

`package.json` の `packageManager` という項目に `pnpm@10.9.0` と書いてバージョンを確認します。

```zsh: package.json 設定変更後のバージョン確認
pnpm -v
10.9.0
```

わかってしまえばなんてことない話でした。

## 謎を深堀り

これだけだとなんなので、少し調べたことをまとめておきます。なにかのお役に立てれば幸い。

### プロジェクトの pnpm をバージョンアップするには？

これは簡単で[解決方法](#解決方法)の通り `package.json` の `packageManager` に利用するバージョンを指定すれば OK。

ここで最新バージョンを指定したらちゃんと mise 管理下の pnpm のバージョンが帰ってきました。

ただ本来この指定はプロジェクトで使用するパッケージのバージョンを固定化して、開発などでパッケージマネージャのバージョン違いでの不具合を起こさないようにするためのもの。すでに導入済みのプロジェクトでバージョンアップするかどうかはよーくよく検討する必要があります。

なおこの指定は Corepack というパッケージマネージャーマネージャー(ややこしい)で、npm、 yarn、 pnpm など Node.js のパッケージマネージャを管理するツールで定義されたものらしいです。Corepack は実験的なプロジェクトで、今は Node.js に含まれるようですが[将来独立する可能性](https://nodejs.org/docs/latest-v23.x/api/corepack.html#corepack)もあるようです。

### 設定ファイルでバージョン指定しただけでなんで動くの？

設定ファイルの `package.json` で違うバージョンを指定しても、実際にそのバージョンの実行媒体があるわけでもないのになんで動くんだろう？って不思議じゃないですか。

調べてみたら pnpm はなんと自前で必要なバージョンをダウンロードして管理・実行してるんですね。設定ファイルでインストールされていないバージョンが指定されたとき `pnpm -v` を実行すると、確かにちょっと間があってから結果が返ってくる。実はこの「間」、必要なバージョンの pnpm をダウンロードしている時間でした。

ちなみにインターネット接続がない状態で実行するとプロンプトが帰ってこなくて `⌃C` で止めたらこんなエラーが出ます。

```zsh:pnpm -vがダウンロードしている間に止めたときのエラーメッセージ
pnpm -v
^C ERROR  Command was killed with SIGINT (User interruption with CTRL-C):
 /Users/shiro/.local/share/mise/installs/pnpm/10.9.0/pnpm add @pnpm/macos-x64@10.6.0 
   --loglevel=error --allow-build=@pnpm/exe --config.node-linker=hoisted
   --config.bin=/Users/shiro/Library/pnpm/.tools/@pnpm+macos-x64/10.6.0_tmp_53171/bin
For help, run: pnpm help
```

`pnpm add` コマンドが実行されててパッケージを追加しようとしてるのが見て取れます。

### じゃあ実行媒体はどこに？

私の環境では `/Users/shiro/Library/pnpm/.tools` フォルダにバージョン違いの実行媒体が保存されています。
上記エラーメッセージ中のコマンドのオプション `--config.bin` で指定されているところですね。グローバルストアと呼ばれるパッケージを一元管理しているフォルダの近くです。

あと不要なバージョンの実行媒体のアンインストールについては [pnpm 本家のアンインストールのページ](https://pnpm.io/ja/uninstall)とか色々探したんだけどどこにも見つけられませんでした。なので試してみたんだけど、このフォルダの pnpm のバージョン番号の名前のフォルダを削除したらアンインストールできました。どなたか公式の情報をご存知であれば教えて下さい🙏

### mise で pnpm をアップグレードしたけど大丈夫？

大丈夫です。あ、いえ、結果的に大丈夫でした。

以下の2点が変わらなければ問題ないと思います。

- mise でアップグレードした pnpm が抱えているバージョン違いの実行媒体の保管場所がアップグレード前の pnpm と同じ
- pnpm が設定ファイル `package.json` で指定されたバージョンを実行する仕組み

ツールのかなり根幹部分になると思うのでしばらく変わることはないんじゃないかと予想してます。

### あれ？じゃあ mise って…

ここではたと気づきます。pnpm はご覧のように `package.json` で指定されたバージョンで実行する機能を持っています。さらに Node.js の実行バージョンも pnpm で管理できる。じゃあ mise で Node.js 環境を管理する意味ってあるんでしょうか？

もし Node.js と pnpm しか使わないよ、というのであれば pnpm をグローバルにインストールして使うので十分だと思います。node も pnpm もモジュールさえも一元管理できる pnpm ですから、とても効率的。しかもシンプルですよね。

でも初学者の僕は mise を使ってみようと考えています。理由は2つ。

1. python や rust など他の環境も使いたい
2. タスクランナーを使ってみたい。

pnpm は言うまでもなく Node.js 用のツールです。なので他の実行環境には使えません。その点 mise なら mise.toml の書き方を覚えるだけで他の実行環境でも使えます。
また物件ビルドなどで複雑な処理をしようとしたときタスクランナーを使いたくなるかもしれません。node では package.json の scripts になるかな？ 異種の実行環境が混在するならまとめて扱える mise の tasks のほうが便利でしょうか。そんな難しいことまだまだ先のことでしょうけど💦

まぁ mise を使う場合でも pnpm のような関連ツールの挙動は知っていたほうがいいなって感じたお話でした。もっというなら仕事で使う場合は mise や pnpm のどちらで管理するかなど管理方法は運用ルールをしっかり決めないと面倒が起きそうですね。くわばらくわばら。

## まとめ

- pnpm
  - pnpm はコマンド実行時に `package.jspn` で指定されたバージョンを実行する
  - 指定したバージョンの実行媒体がなければ自動的にダウンロード、pnpm 管理下に配置され実行される
  - プロジェクトで使う pnpm をバージョンアップするには package.json を変更する
  - 不要なバージョンの pnpm をアンインストールするときは `~/Library/pnpm/.tool` にある不要なバージョンのフォルダを削除する

- mise
  - mise で pnpm のバージョンを指定して導入するには、使用するバージョンを指定する
    - `mise use pnpm@10.8.0` など
  - これで導入された pnpm は mise 管理化に配置され実行される
  - mise で pnpm を導入しても `package.json` のバージョン指定が有効で、該当バージョンでなければ pnpm 管理下に実行媒体がダウンロード、配置され、実行される。

- mise と pnpm で実行媒体の管理場所が異なることを意識しておく。

## 補足 : mise での pnpm の最新バージョン

現在 2025 年 5 月 3 日、 pnpm は 5 日前に v10.10.0 にバージョンアップされてます。

空のプロジェクトフォルダで試したところ

- `mise use pnpm@latest` → 10.9.0
- `mise use npm:pnpm@latest` → 10.10.0

と、miseのバックエンドで最新バージョンは異なるみたい。いち早く最新バージョンを使いたい場合は後者を使うのがいいかもです。

## さいごに

まずは素晴らしいプロダクトを開発されている方々に感謝を。ありがとうございます。

あとこの記事ですが、Node.js や pnpm を触り始めたばかりの初学者が書きました。不適切だったり間違いがあったりしたらご指摘お願いします。

さいごに。
この記事が僕のような初学者のお役に立てば、また mise や pnpm の普及の一助になれば幸いです。