WindowsのNode.jsバージョン管理をNodistからfnmに変えてみる
これまでWindowsでNode.jsのバージョン管理はNodistを使っていましたが、改めて環境構築を構築をしていくにあたってMicrosoftのドキュメントを眺めていたところ、Nodist以外にもそういったツールがあることを知りました。
WSL2(Linux)向けの方で掲載されているパッケージ管理ツールのほうが多いですが、その中のいくつかはマルチプラットフォームで、ネイティブWindowsでも使えそうなものもあります。
2020 年ではもう使えない Nodist はアンインストールする (Windows)
この記事に触発されたというわけでもないのですが、最近WSLを触り始めたこともあり、Windows/Mac/Linux(WSL)で同じように使えるツールにしておけると迷うことも少ないかなと思い、Nodistから乗り換えてみることにしました。
マルチプラットフォームなNode.jsバージョン管理ツール
Windowsをサポートしているマルチプラットフォームなものをピックアップしてみました。
それぞれのリポジトリや公式サイトを眺めてみての個人的に気になった点を挙げてみました。
名前 | 対応プラットフォーム | ポイント |
---|---|---|
nvm-windows | Windows | ・ nvm のWindows版、なのかと思いきやが互換性があるわけではないみたい ・Go製 ・自動バージョン切り替えはない |
nvs | Windows/Mac/Linux | ・VSCode連携あり ・コマンドプロンプトのサポートが弱い(PowerShellだと自動バージョン切り替えができる) ・wingetでインストールできる |
Volta | Windows/Mac/Linux | ・Rust製、速度重視 ・他のツールと若干使い方が違う印象 ・package.jsonに使うNodeのバージョンなどを記録する ・wingetでインストールできる |
fnm | Windows/Mac/Linux | ・Rust製、こちらも速度重視 ・自動バージョン切り替えに対応 |
(番外)asdf-nodejs | Mac/Linux | ・asdf公式 ・自動バージョン切り替えに対応 |
Nodistも備えていましたが、プロジェクトディレクトリにある .node-version
や .nvmrc
などを参照して使うNodeのバージョンを切り替えてくれる機能は欲しいところ。
Linux(WSL)とMacだけで考えれば asdf公式のNode.jsプラグイン が自動切り替えにも対応していて良さそうではありますが、Windowsでも使えるものからとなると、nvs、Volta、fnmあたりが選択肢になりそうです。
(nvm-windowsは紛らわしいですが、本家nvmのクローンということではないらしい。コマンドも微妙に使い方が違っていたりと今回の目的に合わないため選外)
その中でnvsとVoltaについては以下のような点が自分的に気になったので、今回はfnmを使ってみることにしました。
- nvsはコマンドプロンプト主体で考えると切り替え機能がサポートされない
- Voltaは他のツールと違ってpackage.jsonにバージョンを記録する(業務利用を考えたときに、自分の会社ではそのあたりのツールが統一されているわけではないため、自分のためだけに編集はしにくい)
Nodistをアンインストールする
まずは既存環境のNodistを削除する手順で、この点については先のNodistアンインストールの記事がとても参考になりました。
2020 年ではもう使えない Nodist はアンインストールする (Windows)
以下、その記事の手順をなぞったときのメモ。
- Nodistのアンインストール
- 記事ではコントロールパネルから実施していましたが、私はChocolateyでインストールしていたので、
choco uninstall nodist
で削除しました
- 記事ではコントロールパネルから実施していましたが、私はChocolateyでインストールしていたので、
- Nodistインストール先フォルダの削除
- 確かに残っていて、npmの各バージョンが残っていたと1GB近くありました
- npm-cacheフォルダの削除
- これも残っていて、3.5GBもありました…
- .npmrcの削除
- これについては、私の環境ではNodistに関するもの以外の設定もあったので、該当行のみ削除してファイル自体は残しました
fnm (Fast Node Manager)をインストールする
インストールは簡単で、 Releases · Schniz/fnm から利用しているプラットフォーム向けのバイナリをダウンロードして、PATHが通っているところに置くだけでも使えます。
またWindows向けにはChocolateyからもインストールすることもできるので、私はこちらの方法を使いました。
# 要管理者権限 > choco install fnm -y or # Use gsudo > gsudo choco install fnm -y
gerardog/gsudo を使うと、わざわざ管理者権限でターミナルを開き直さなくても良いので便利です。
コマンドプロンプト向けシェルセットアップ
さてインストールが完了したら、使っているシェルに合わせてfnmを使うための初期化処理を設定する必要があります。
Bash向けなどは .bashrc
に eval "$(fnm env)"
を追記すれば良いという感じで非常に簡単なのですが、Windows特にコマンドプロンプト向けのセットアップは一筋縄ではいきませんでした。
Windows Command Prompt aka Batch aka WinCMD
コマンドプロンプトを起動したときに何か処理をおこないたい場合、レジストリの HKEY_CURRENT_USER\SOFTWARE\Microsoft\Command Processor
の AutoRun
キーに設定することで実行させることができます。
fnmのREADMEによれば、そこに以下のコマンドを登録することでセットアップがおこなわれるようです。
FOR /f "tokens=*" %i IN ('fnm env --use-on-cd') DO CALL %i
ただ自分の場合は、他にも初期化処理でやりたいことがあったので、バッチファイルを登録して、その中で上記の処理を呼び出すことにしました。
[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Command Processor] "AutoRun"="C:\Users\<user>\mystartup.cmd"
ところがこれがうまくいかず、コマンドプロンプトを起動すると i was unexpected at this time.
というメッセージが表示されました。
これはfnmのREADMEにも対処法が書かれていますが、原因としてはバッチファイルの中で FOR
を使う場合の変数の書き方を %i
ではなく %%i
としなければいけないからでした。
よって以下のように書き換えます。
FOR /f "tokens=*" %%i IN ('fnm env --use-on-cd') DO CALL %%i
これでうまくいくかと思いきや、確かに先のメッセージは出なくなったものの、今度は処理が戻ってこない状態になりました。
いろいろ調べた結果、次のことがわかりました。
for /f
はサブプロセス実行時にcmd.exe
を起動するため、そこでまたAutoRunが走ってしまうことで無限ループに陥る可能性があるcmd /d
を指定することでAutoRunを無効にできるが、for
の中では効かないっぽい
参考:
- batch file - CMD AutoRun hang - Stack Overflow
- cmd - Make batch FOR /F ignore Registry AutoRun - Stack Overflow
- 汝、コマンドプロンプトを愛せよ - Qiita
fnmのREADMEにはCmderというターミナルアプリの起動スクリプト機能を使うことで回避する方法が載っていますが、これだとWindows Terminalが使えないので個人的にはイマイチ。
そこで起動バッチのなかで多重実行を防ぐような仕組みを入れたところ、無限ループにならずに初期化処理が実行できるようになりました。
具体的には初回実行時にある環境変数に値を入れるようにして、実行時にその環境変数に値がある場合はそこで処理を終了するというもので、以下のような実装です。
mystartup.cmd
@ECHO OFF IF "%MYSTARTUP_INIT%"=="OK" ( EXIT /b ) ECHO ... Running startup script SET MYSTARTUP_INIT=OK REM Setup fnm FOR /f "tokens=*" %%z IN ('fnm env --use-on-cd') DO CALL %%z ECHO ... Complete startup script
これでfnmを使うことができるようになりました。
基本的な使い方
# インストール可能なバージョンの一覧 > fnm ls-remote v0.1.14 v0.1.15 v0.1.16 ... # 省略 v16.8.0 v16.9.0 v16.9.1 v16.10.0 # 指定したバージョンをインストール > fnm install v16.10.0 Installing Node v16.10.0 (x64) # インストール済のバージョン一覧 > fnm ls * v14.17.6 * v16.10.0 default * system # 現在のデフォルトで使われるバージョン > fnm current v16.10.0 # デフォルトバージョンの設定 > fnm default v14.17.6 > node -v v14.17.6
自動バージョン切り替えを試してみる
あらかじめ、16系と14系をインストールしておきます。現在のデフォルトは16。
> fnm install 14 Installing Node v14.17.6 (x64) > fnm list * v14.17.6 * v16.10.0 default * system
わかりやすく node14 と node16 フォルダを作り、それぞれに対応するバージョンを書いた .node-version ファイルを配置します。
> type node14\.node-version v14 > type node16\.node-version v16.10.0
それぞれのフォルダに移動してみます。
> cd node14 Using Node v14.17.6 > fnm current v14.17.6 > node -v v14.17.6 > cd ..\node16 Using Node v16.10.0 > fnm current v16.10.0 > node -v v16.10.0
node14 フォルダに入るとカレントバージョンが14に、 node16 フォルダに移動するとカレントバージョンは16になりました。
.node-version ファイルの内容に合わせて使うNodeバージョンが切り替わってくれることが確認できました。
※2021/10/06追記
プロジェクトのバージョン指定に使うファイルは .node-version だけでなく、nvm由来の .nvmrc を使うツールもあります。
fnmは .nvmrc にも対応しているようだったので試してみたところ、同じようにバージョンが切り替わってくれました。
> type node14\.nvmrc 14 > type node16\.nvmrc v16.10.0 > node -v v14.17.6 > cd node16 Using Node v16.10.0 node16> fnm current v16.10.0 node16> node -v v16.10.0 node16> cd ..\node14 Using Node v14.17.6 node14> fnm current v14.17.6 node14> node -v v14.17.6
おわり
Rust製で売りの一つにスピードとあるだけに、たしかに速い気がします。