とりあえずで npm install しないために

みなさん package.json や package-lock.json が更新されたらとりあえず `npm install` を実行してませんか？
package-lock.json や node_modules が存在するかで `npm install` の挙動が違います。この辺り、実際にコマンドを実行してみて想定していた動きと違くて混乱する方も多いかもしれません。挙動が意外と知られていないようなので、記事にしてみました。

package-lock.json がなんなのかよくわかってない方も、これを読むとどういうファイルなのかわかると思います。ちなみにこれは yarn においても同じですので読み替えていただければと思います。

## 挙動の違い

挙動は、package-lock.json があるか、node_modules があるかで変わります。

ここでいう挙動とは 「package.json に記載されてるバージョン範囲・指定によるアップデートが行われるか」です。なお、package-lock.json がなく、node_modules がある場合はおかしな状態なため割愛します。

| package-lock.json が存在するか | node_modules が存在するか | アップデートされるか |
| ------------------------------ | ------------------------- | -------------------- |
| ❌                             | ❌                        | ⭕️                  |
| ⭕️                            | ❌                        | ❌                   |
| ⭕️                            | ⭕️                       | ⭕️                  |

## 解説

### 1. package-lock.json がなく、node_modules もないとき

この状態での `npm install` は、どのバージョンでインストールすれば良いのか書かれているのが package.json のみとなりますので、package.json に記載されてるバージョン範囲・指定によるインストールが行われます。

初期構築時のインストールは当然この状態ですので、その時はあまり気にしないかもしれませんが、インストールしてから一ヶ月経って package-lock.json と node_modules を消して再インストールしようとすると、package.json に記載されてるバージョン範囲・指定によるインストールとなるため、patch バージョンや minor バージョン（書き方によっては major バージョンも）が自動的に更新されます。実行しているコマンドが "install" コマンドなので、どうして更新されるんだと混乱するかもしれませんが、こういった仕様に基づいて動作してるわけです。

逆に、package.json のバージョン指定で再インストールが可能ですので、フレームワークの major アップデートガイドによく「package.json のバージョン書き直しを行い、package-lock.json と node_modules を消して npm install してください」と書かれてあることが多いのはこのためです。

### 2. package-lock.json はあるが、node_modules がないとき

この状態での `npm install` は、package-lock.json が存在しているため、package.json ではなく package-lock.json が優先されインストールが行われます。

package-lock.json は、解説 1 の状態でインストールすると生成されるファイルで、インストール時の package バージョンで固定するためのファイルです。

基本的には package-lock.json が優先されインストールされますが、node_modules が存在するかで挙動が変わります。node_modules は、`npm install`したら必ず生成されるインストール先のディレクトリです。node_modules が存在しない限り、`npm install`は package-lock.json による固定バージョンでインストールが行われます。

node_modules は膨大なファイルデータなため git 管理しないわけですが、package-lock.json も存在しないとそれはつまり解説 1 の状態ですので、package.json のバージョン指定でインストールが行われるようになってしまいます。なので開発メンバーが新たに `git clone` した場合や、CICD など `git clone` から始まる環境ですと、`npm install`を実行した日時によりバージョンがコロコロ変わってしまいますので、開発間のバージョン統一やデプロイ先それぞれが違うバージョンとなり動作保証ができなくなります。必ず package-lock.json は git 管理しましょう。

### 3. package-lock.json はあり、node_modules もあるとき

この状態での `npm install` は、package.json が優先されます。

package-lock.json が存在するので、こちらが優先されると思いがちですが、優先されません。package-lock.json は、node_modules のない未インストール状態時におけるバージョン保証なだけです。

ですので、他メンバーが新たに package を追加したりアップデートして更新した package-lock.json を `git pull` で取り込んだ時に、そのまま `npm install` してしまうと、package.json でインストールされてしまうので、忘れずに node_modules を削除してから実行しましょう。

とは言いつつも、わざわざ node_modules を消さないと package-lock.json を優先してインストールできないのかというとそうではありません。ちゃんとコマンドがあります。`npm ci` (yarn の場合は `yarn install --frozen-lockfile`) です。このコマンドを使用すると、node_modules があっても package-lock.json による再インストールが可能です。

## まとめ

いかがだったでしょうか。少しでも理解の手助けになりましたでしょうか。

意外にもフロントを長く触っていても知らないという方が多いなと思いましたので、改めて書き記してみました。

もちろん package-lock.json によりバージョンが固定化されますので、知らないうちにバージョンが古くなりすぎてメンテが追いつけないなんてことにならないよう、脆弱性診断やアップデート有無のチェックを自動化しておくと良いですね。

ちなみに`npm update`によるアップデートは、package.json に記載されているバージョン値まで更新をしてくれないので、私はよく[ncu コマンド](https://www.npmjs.com/package/npm-check-updates)を使って更新したりします。