お、お、お前は僕の知っているnpmじゃないなっ!!!!!

# nodeをアップデートしたのに、npmだけなぜかアップデートされない罠

## まえがき

少し前のことですが、、、
あるプロジェクトにて[nodist](https://github.com/marcelklehr/nodist)を使って``node.js``を動かしてるWindowsマシンに、nodistじゃない通常のインストーラーを使用して別のnodeを入れることになりました。
nodeコマンド自体は新たに入れた方の``node.js``を参照するようになったのですが、npmはnodist側で入れたnpmを見たまま、、、
なぜなの？ということで対応したときのことをメモとして残しておきます。
(何かの参考になることはないと思いますが、npm内の処理について少しだけ勉強になりました)

## その前に前置き(モヤモヤ払いのため)

##### nodistのnode.js -> node.js !?
こういうケースはなかなかないように思えますし、そもそもそういうやり方ってどうなの？(nodistでversion入れ替えればいいじゃん!)と突っ込まれるかもしれません:fearful:
もちろん私含め、誰もが突っ込みたい状況でした、、、が、
ここについては``THE 大人の事情``の一言で片付けてもらえたらと思います、、、:cry:

モヤモヤ払い、終了。

## 起こった事象

では、まず状況からまとめます。

* windowsマシンにてnodistを使ってnodeをインストールしていた。
* 今回、とある事情により、nodistを使わないでnodeをインストールし、そちらのnodeを使うことになった。
* nodeのインストール時にpathの設定は自動で行われたようで、node自体はコマンドプロンプト上で``node -v``と叩くと、新しく入れた方のnodeのバージョン番号が出てくる。
* ところが``npm -v``と打っても、nodist使用時に使っていたnpmのバージョン番号が出てきてしまう。
* 新しくインストールした方のnode側のnpmのディレクトリに移動して直接``npm.cmd -v``と叩いても、古いnpmのバージョン番号(nodist使用時のもの)が出てきてしまう。
* なぜっっっ！？？？？？？？？？？？？？
* ちなみに新しく入れた方のnpmのbin配下で``node npm-cli.js -v``と叩いてみると、意図したバージョン番号が出てくる。


## 問題の原因

で、結論から言うとホームディレクトリ配下にある``.npmrc``内にて、
prefixに古い方のnpmのパスが記載されていたために起こっていた事象でした。

ここらへんも問題の背景を話し出すとモヤモヤした感じになっちゃうので割愛しますが:cry:、要は``npm prefix -g``って打つと、nodist側のnpmを指すように``.npmrc``に設定されていたために起きていたのです。

[npm prefix](https://docs.npmjs.com/cli/prefix)

```.npmrc
prefix=hoge
```

例えば、上のように``.npmrc``に書くと、

```bash
npm prefix -g
/Users/username/hoge
```

となります。
上記は自身のmac環境で叩いていますが、windowsでも同じような動作をします。
これにより、prefix側で設定されているnpmを叩きに行くような動作をしていたために今回の事象が起きていました。

とりあえず原因も分かり、問題自体はこれで解決したのですが、ちょっと面白い(?)と思ったのが、
例えば``.npmrc``側で``prefix``を書いてしまうと、インストール先のnpn.cmdを直接叩こうが、問答無用でprefixに設定されているnpmを見に行ってしまうという点です。

ここの部分が最大のハマるポイントだったように思います。
(ま、自身のwindowsバッチファイルの読解力が低すぎたというのもありますが、、、)

## 実際にnpm.cmdを読んでみる

実際にどういうことか内部のソースを少しだけ追ってみようと思います。
これはversionが``9.7.1``の``npm/bin/npm.cmd``になります。

```bat

FOR /F "delims=" %%F IN ('CALL "%NODE_EXE%" "%NPM_CLI_JS%" prefix -g') DO (
  SET "NPM_PREFIX_NPM_CLI_JS=%%F\node_modules\npm\bin\npm-cli.js"
)
```
割愛しまくって話すと、この``CALL "%NODE_EXE%" "%NPM_CLI_JS%" prefix -g'``という箇所で``npm prefix -g``で出力されるパスにあるnpmを指すように設定しています。
そのため、直接``npm.cmd``を叩いているにも関わらず、nodist側のnpmが呼ばれるようになっていたのです。

## 疑問点

問題の原因は無事に判明しました。
が、このように直接実行ファイルを叩いているのにもかかわらず、違うバージョンのnpmが実行されてしまうということに、同時に**違和感**のようなものも感じました。

せめて、対象のnpmの実行ファイルを直接叩いた場合は、直接叩いた先のnpmが動くべきなのではないでしょうか？

以上の疑問点から、他のパッケージマネージャーの実装を見てみようと思います。
(といっても時間がないので、ひとまず``yarn``だけ。他の実装も後々確認してみたい)

## yarnにおける実装

npmオルタナティヴである[yarn](https://github.com/yarnpkg/yarn)の``bin/yarn.cmd``を見てみます。

```js
@echo off
node "%~dp0\yarn.js" %*
```

yarnでは実装内容はこの２行だけでした。
一応``grep``でprefix使ってないか確認してみましたが、少なくともbin配下では使われていません。
(bin配下で実行される処理で、js側に処理が移るのでprefixを調べるのは``bin``のみで良いと判断しています。)


ちなみに``%~dp0``については下記の記事で以下のように解説されています。

[Windowsバッチまとめ](https://qiita.com/tomotagwork/items/5b9e08f28d5925d96b5f)


> %~dp0は、当該バッチが配置されているディレクトリのフルパスを示します。

つまり``yarn.cmd``の場合、同じディレクトリ内に在る``yarn.js``に対してnodeコマンドを叩いているだけということになります。

こちらのほうが直感的で分かりやすいと思ったのですが、何か意図があるのかもしれません。
後々、機会を見つけて``npm.cmd``のcommit logを追ってみようと思います。

## 実装の意図を調べる(TODO:まだ、未実施)

該当するコミットログは下記のようです。

[windows: search for a user-installed npm](https://github.com/npm/npm/commit/89320ba9d27e25ea3f8c88d7089d9fcf0bf0d440#diff-b0ef0fda468f793db22d92b3efc2b56e)

時間を見つけて調べてみようと思います。