npm,yarnのlockファイルの恩恵

# lockファイルの恩恵を確認したい!

Nodejsのモジュールをコマンドでインストールすると、
* `npm install`の場合 => `package-lock.json`
* `yarn install`の場合 => `yarn-lock.json`
がどれぞれ生成される。

なんかバージョンを固定するのに役立つと聞くけど、いまいちどんな恩恵があるのかわかってなかったので、動作を確認してみる。

# 環境
* node : v12.13.0
* npm : 6.12.0
* yarn : 1.19.1

# 確認
`npm`, `yarn` でそれぞれ`lodash`をinstallして、色々変更しながら入るバージョン確認していく。

lodashは2019/10/24の時点で、4系はv4.17.15まで、3系はv3.10.1までがnpmに公開されている。
https://www.npmjs.com/package/lodash

## npmでの確認
### lodash v3.0.0のインストール
適当なディレクトリを作成して、npmでlodashの3.0.0指定してをインストールする。

```bash
$ npm init -y
$ npm install lodash@3.0.0 --save
```

node_moduleに入っているlodashのバージョンを確認すると、期待通りにlodashの**3.0.0**がインストールされている。

```bash
$ grep '"version":' node_modules/lodash/package.json
  "version": "3.0.0"
```

このときのpackage.jsonとpackage-lock.json下記のようになる。

```json:package.jsonのdependencies
  "dependencies": {
    "lodash": "^3.0.0"
  }
```

* lodashのバージョン指定は`^3.0.0`と記述されて、少しゆるい指定の「3系の最新ちょうだい(3.0.0 <= x < 4.0.0)」となる。

```json:package-lock.jsonのdependencies
  "dependencies": {
    "lodash": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.0.0.tgz",
      "integrity": "sha1-STNktxg6NxBNZSVfOLD+WGnL4P0="
    }
  }
```
* インストールするバージョンは`3.0.0`であると記述される（`"version": "3.0.0"`）

### node_modulesのみを削除して再インストール
`node_modules`を削除して`npm install`をすると...**3.0.0**がインストールされる。

```bash
$ rm -rf node_modules
$ npm install
$ grep '"version":' node_modules/lodash/package.json
  "version": "3.0.0"
```
### package-lock.jsonとnode_modulesを削除して再インストール
`node_modules`と**一緒に`package-lock.json`**を削除して`npm install`をすると...**3.10.1**がインストールされる。

```bash
$ rm -rf node_modules package-lock.json
$ npm install
$ grep '"version":' node_modules/lodash/package.json
  "version": "3.10.1"
```

これは`package.json`でのlodashのバージョン指定が「3系の一番新しいやつ」とゆるい指定になっているため。

```json:package.json抜粋
  "dependencies": {
    "lodash": "^3.0.0"
  }
```

`package-lock.json`がある間はしっかりバージョン指定されるが、なくなると`package.json`のゆるい指定にひっぱられる。
また、このときに`package-lock.json`はその時のlodashのバージョンにロックされたもので再生成される。

### package.jsonの記述を変更する(`~3.0.0`)
（一旦、lodashのバージョンを3.0.0で指定してインストールし直す）

`package.json`のlodashのバージョン指定方法を`^3.0.0` => `~3.0.0`へ変更する。

```json:package.json抜粋
  "dependencies": {
    "lodash": "~3.0.0"
  }
```

この状態で`node_modules`を削除して`npm install`をすると...`3.0.0`がインストールされる。

```bash
$ rm -rf node_modules
$ npm install
$ grep '"version":' node_modules/lodash/package.json
  "version": "3.0.0"
```

`~3.0.0`は 「3.0.0 <= x < 3.1.0」を取得する記述で、`package-lock.json`で指定されているバージョンが3.0.0なため、
`package-lock.json`に書かれているバージョンが採用される。

### package.jsonの記述を変更する(`^4.0.0`)

`package.json`のlodashのバージョン指定方法を`^3.0.0` => `^4.0.0`へ変更する。

```json:package.json抜粋
  "dependencies": {
    "lodash": "^4.0.0"
  }
```

この状態で`node_modules`を削除して`npm install`をすると...**4.17.15**がインストールされる。

```bash
$ rm -rf node_modules
$ npm install
$ grep '"version":' node_modules/lodash/package.json
  "version": "4.17.15"
```

`^4.0.0`は 「4.0.0 <= x < 5.0.0」を取得する記述で、`package-lock.json`で指定されているバージョンが3.0.0だけど、
`package.json`の指定の範囲外なので、`package-lock.json`の内容は無視されて、4系の最新の`4.17.15`がインストールされる。

また、このときに`package-lock.json`はその時のlodashのバージョンにロックされたもので再生成される。


## yarnでの確認
### lodash v3.0.0のインストール
適当なディレクトリを作成して、yarnでlodashの3.0.0指定してをインストールする。

```bash
$ yarn init -y
$ yarn add lodash@3.0.0
```

node_moduleに入っているlodashのバージョンを確認すると、期待通りにlodashの**3.0.0**がインストールされている。

```bash
$ grep '"version":' node_modules/lodash/package.json
  "version": "3.0.0"
```

このときのpackage.jsonとpackage-lock.json下記のようになる。

```json:package.jsonのdependencies
  "dependencies": {
    "lodash": "3.0.0"
  }
```

* lodashのバージョン指定は`3.0.0`と記述されて、厳密に指定した「3.0.0のバージョンがほしい」となる。

```:yarn.lockのdependencies
lodash@3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.0.0.tgz#493364b7183a37104d65255f38b0fe5869cbe0fd"
  integrity sha1-STNktxg6NxBNZSVfOLD+WGnL4P0=
```
* package.jsonでの「lodash指定は`3.0.0`」だった。(`lodash@3.0.0:`)
* インストールするバージョンは`3.0.0`であると記述される（`version "3.0.0"`）

### node_modulesのみを削除して再インストール
`node_modules`を削除して`yarn install`をすると...**3.0.0**がインストールされる。

```bash
$ rm -rf node_modules
$ yarn install
$ grep '"version":' node_modules/lodash/package.json
  "version": "3.0.0"
```

### yarn.lockとnode_modulesを削除して再インストール
`node_modules`と**一緒に`package-lock.json`**を削除して`npm install`をすると...**3.0.0**がインストールされる。

```bash
$ rm -rf node_modules package-lock.json
$ yarn install
$ grep '"version":' node_modules/lodash/package.json
  "version": "3.0.0"
```
`package.json`での指定がnpmとは違って厳密なので、3.0.0がインストールされる。
また、このときに`yarn.lock`がなくなっていたので、その時のlodashのバージョンにロックされたもので再生成される。（消す前と全く同じ内容）

### package.jsonの記述を変更する(`~3.0.0`)
`package.json`のlodashのバージョン指定方法を`^3.0.0` => `~3.0.0`へ変更する。

```json:package.json抜粋
  "dependencies": {
    "lodash": "~3.0.0"
  }
```

この状態で`node_modules`を削除して`yarn install`をすると...`3.0.1`がインストールされる。

```bash
$ rm -rf node_modules
$ yarn install
$ grep '"version":' node_modules/lodash/package.json
  "version": "3.0.1"
```
`yarn.lock`内での記述は、 
> * package.jsonでの「lodash指定は`3.0.0`」だった。(`lodash@3.0.0:`)

となっていたけど、package.jsonでの指定方法が`~3.0.0`に変更したせいで、`yarn.lock`の記述と一致しなくなったため、`yarn.lock`の指定が無視されるようになる。

また、このときに`yarn.lock`はその時のlodashのバージョンにロックされたもので再生成される。

### package.jsonとyarn.lockの記述を同じように変更する(`~3.0.0`)
（一旦、lodashのバージョンを3.0.0で指定してインストールし直す）

`package.json`と `yarn.lock`バージョン指定をそれぞれ`~3.0.0`へ変更する

```json:package.json抜粋
  "dependencies": {
    "lodash": "~3.0.0"
  }
```

```:yarn.lockのdependencies
lodash@~3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.0.0.tgz#493364b7183a37104d65255f38b0fe5869cbe0fd"
  integrity sha1-STNktxg6NxBNZSVfOLD+WGnL4P0=
```

この状態で`node_modules`を削除して`npm install`をすると...**3.0.0**がインストールされる。

```bash
$ rm -rf node_modules
$ yarn install
$ grep '"version":' node_modules/lodash/package.json
  "version": "4.17.15"
```

`package.json`の指定が変更されたが、一緒に`yarn.lock`の指定も変更したため、それぞれの指定がずれなかったため`yarn.lock`の指定されたバージョンがインストールされた。


# まとめ
`package-lock.json` と `yarn.lock`でバージョンをロックしてくれた。
ただ、`package.json`を変更したときの、無視する条件が異なった。

* `package-lock.json`(npm)
  * `package.json`の指定内容が`package-lock.json`の指定範囲を外れてしまったら、無視される。
    * 逆に、指定範囲内なら記述を変えても`package-lock.json`は有効
* `yarn.lock`(yarn)
  * `package.json`の指定内容が、`yarn.lock`内での記述方法が一致しなくなったら、無視される。

npmからyarnに乗り換えるときとか、`package.json`のバージョン指定を整理するときとかはこの仕様の違いに注意したほうが良さそう。

# おまけ
調べている途中で気づいたけど、 `npm install lodash@3.0.0`と`yarn add lodash@3.0.0`でpackage.jsonの記述内容が変わるのが意外だった。個人的にはバージョン指定はしっかり固定したほうが安心派なので、yarnのほうが好き。

あと、
> * `package.json`の指定内容が`package-lock.json`の指定範囲を外れてしまったら、無視される。

この挙動はnpmの`v6.12.0`あたりからの仕様っぽい。
nodeのv10とかで使われる、 npmの`v6.4.1`ではこの挙動はしないで、`package-lock.json`の指定は勝手にはずれなかった。
好みの問題かもしれないけど、nodeのバージョンアップ時(`v10` => `v12`)になにか地雷を踏みそうで怖い。