Node.js 初学者、天沙士郎です。TypeScript 学習中です。
最近学習のため開発環境を mise-en-place と pnpm で構築したんです。pnpm は活動が活発なようで新バージョンのリリースがありバージョンアップしたんです。そこでなかなかに不思議な挙動に出会いました。
みなさん、なにが起こっているかわかるでしょうか? (回答あり)
実行環境
- MacBook Air Retina, 13-inch, 2018 Intel
- MacOS Sonoma 14.7.5
ツールについて
僕は mise で Python や Node.js などの各種実行環境を構築しています。mise は実行環境のバージョン制御、仮想環境準備、direnv を一手に引き受けてくれるの、便利ですもんね。
pnpm は Node.js のアプリやモジュールを使うのに、効率的かつ高速に作業ができるのがいいですよね。
準備
mise で node と pnpm を導入
では早速やっていきましょう。まずはベースとなる環境構築から。
brew install mise # mise 未インストールなら実行
mkdir project1 # プロジェクトフォルダを作り node と pnpm をインストール
cd project1
mise use node@latest
mise use pnpm@latest
pnpm init
これで mise を使って node と pnpm を使う準備ができました。ほんとにとっても簡単ですね。
確認 : バージョンと実行ファイルの場所
ここで一旦確認。
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 の場所とバージョンを確認しておきましょう。
which node
/Users/shiro/.local/share/mise/installs/node/23.11.0/bin/node
node -v
v23.11.0
which pnpm
/Users/shiro/.local/share/mise/installs/pnpm/10.8.0/pnpm
pnpm -v
10.8.0
はい、OK です。問題ないですね。
pnpm のバージョンアップ。あれぇ?
さて pnpm がバージョンアップしたので導入しましょう。mise で導入したので mise でバージョンアップします。コマンド一発!やっぱり便利。
mise upgrade
# 以下はコマンド実行結果
mise pnpm@10.9.0 ✓ installed
mise uninstall pnpm@10.8.0 ✓ remove ~/Library/Caches/mise/pnpm/10.8.0
インストールできたようなので確認です。
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
とバージョンが上がっています。では場所とバージョンを確認です。
which pnpm
/Users/shiro/.local/share/mise/installs/pnpm/10.9.0/pnpm
pnpm -v
10.8.0
は、はい?
実行媒体の場所は mise 管理下のフォルダでバージョンが 10.9.0
になってます。これはいい。
でもバージョンは pnpm -v
の結果が 10.8.0
? なにこれ?😱
さて、なぜでしょう?
ヒント
pnpm は実行時にとあるファイルをみて…
答え合わせ
サクッといきます。答えは
- pnpm は実行時にプロジェクトフォルダの
package.json
をみて実行するバージョンを決定する。 - 実行している pnpm のバージョンと異なるバージョンが指定されていた場合、設定ファイルに記載されたバージョンを実行する。
です。
実は準備の pnpm インストールで pnpm init
したときにできる設定ファイル、 package.json
は次のようになっています。
{
"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
と書いてバージョンを確認します。
pnpm -v
10.9.0
わかってしまえばなんてことない話でした。
謎を深堀り
これだけだとなんなので、少し調べたことをまとめておきます。なにかのお役に立てれば幸い。
プロジェクトの pnpm をバージョンアップするには?
これは簡単で解決方法の通り package.json
の packageManager
に利用するバージョンを指定すれば OK。
ここで最新バージョンを指定したらちゃんと mise 管理下の pnpm のバージョンが帰ってきました。
ただ本来この指定はプロジェクトで使用するパッケージのバージョンを固定化して、開発などでパッケージマネージャのバージョン違いでの不具合を起こさないようにするためのもの。すでに導入済みのプロジェクトでバージョンアップするかどうかはよーくよく検討する必要があります。
なおこの指定は Corepack というパッケージマネージャーマネージャー(ややこしい)で、npm、 yarn、 pnpm など Node.js のパッケージマネージャを管理するツールで定義されたものらしいです。Corepack は実験的なプロジェクトで、今は Node.js に含まれるようですが将来独立する可能性もあるようです。
設定ファイルでバージョン指定しただけでなんで動くの?
設定ファイルの package.json
で違うバージョンを指定しても、実際にそのバージョンの実行媒体があるわけでもないのになんで動くんだろう?って不思議じゃないですか。
調べてみたら pnpm はなんと自前で必要なバージョンをダウンロードして管理・実行してるんですね。設定ファイルでインストールされていないバージョンが指定されたとき pnpm -v
を実行すると、確かにちょっと間があってから結果が返ってくる。実はこの「間」、必要なバージョンの pnpm をダウンロードしている時間でした。
ちなみにインターネット接続がない状態で実行するとプロンプトが帰ってこなくて ⌃C
で止めたらこんなエラーが出ます。
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 本家のアンインストールのページとか色々探したんだけどどこにも見つけられませんでした。なので試してみたんだけど、このフォルダの 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つ。
- python や rust など他の環境も使いたい
- タスクランナーを使ってみたい。
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
にある不要なバージョンのフォルダを削除する
- pnpm はコマンド実行時に
-
mise
- mise で pnpm のバージョンを指定して導入するには、使用するバージョンを指定する
-
mise use pnpm@10.8.0
など
-
- これで導入された pnpm は mise 管理化に配置され実行される
- mise で pnpm を導入しても
package.json
のバージョン指定が有効で、該当バージョンでなければ pnpm 管理下に実行媒体がダウンロード、配置され、実行される。
- mise で 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 の普及の一助になれば幸いです。