Node.js を使うときには、使用するライブラリのバージョンをピン留めしておきましょう

Qiita Engineer Festa 2024 記事投稿2本目！
本記事はQmonus Value Streamの投稿キャンペーン記事です。

https://qiita.com/official-events/3648bc4e6cedb674491c

## 📌 はじめに

 `npm install` コマンドを使用して外部ライブラリのインストールをする際に、バージョンが固定化されなくてもやもやした経験はありませんか？
さらにバージョンが固定化されないのは開発する上では些細な問題だと思い、スルーした経験はないですか？

```json-doc:package.json
// 例
{
  "dependencies": {
    // 心の声　（先頭のキャレット(^)いらないけど、大した問題じゃないしそのままでいいか〜）
    "express": "^4.19.0"
  }
}
```

私はわりと最近まで、スルーしてきました ///

そんな過去の私を含め、package.json の外部ライブラリを固定化する方法がわからない（もしくは、調べるのをサボっている）方向けに、Tipsとしてまとめておきたいと思います！

## 📌 バージョンをピン留めすると何が嬉しいの？
そもそも、バージョンをピン留めした場合のメリットについて紹介します！

バージョンをピン留めするメリットについて紹介します！

- 可視性の向上
    - `package.json` を見るだけで、プロジェクトで使用しているライブラリのバージョンが一覧でわかります
- バージョン特定の容易さ
    - キャレット付きのバージョン指定では、実際に使用しているバージョンを特定するために package-lock.json を確認したり、`npm ci` コマンド実行後に `npm list` コマンドでバージョンを確認する必要がありますが、バージョンを固定することでこの手間が省けます
- 競合解決の簡略化
    - 親ブランチの内容をバックマージする際 `package-lock.json` に競合が発生しても、バージョンが固定されているため競合解決が容易になります
- 一貫性の確保
    - バージョンが固定化されているため、`package-lock.json` を削除して `npm install` を実行しても、常に同じバージョンのライブラリがインストールされます



## 📌 バージョンをピン留めするにはどうすればいいの？
私が試したことのある、3つの方法をご紹介したいと思います！
ほかにも方法があるとおもいますので、自分自身や組織にあったやり方を探してみてください！

### 💡 インストール時にコマンドオプションを指定する

最もシンプルなやり方です！
`npm install` コマンドにバージョン固定用のオプション(`-E` or `--save-exact`)をつけるだけです！

> `npm install` saves any specified packages into `dependencies` by default. Additionally, you can control where and how they get saved with some additional flags:
　（...)
When using any of the above options to save dependencies to your package.json, there are two additional, optional flags:
> -  _**`-E, --save-exact`: Saved dependencies will be configured with an exact version**_ rather than using npm's default semver range operator.
> - (...)

引用元: https://docs.npmjs.com/cli/v10/commands/npm-install

```shell
# 例1: express をインストールする場合
npm install express -E

#例2: @types/express をインストールする場合(devDependenciesに追加するパターン）
npm install @types/express --save-exact --save-dev
```
なんと！簡単ですね

これで、最新のバージョンをピン留めしてインストールできます！

```json-doc:package.json
// 例
{
  "dependencies": {
    "express": "4.19.2"
  },
  "devDependencies": {
    "@types/express": "4.17.21"
  }
}
```
できました！

### 💡 `.npmrc` ファイルに設定値を追加する

１つ前に紹介したやり方は悪くないですが、バージョン固定用のオプションを忘れてしまった場合、キャレットつきでバージョン指定されてしまいます。
`npm install` のオプションを指定しなかった場合でも、バージョンを固定してライブラリをインストール方法があります。
`package.json` と同じ階層に`.npmrc`ファイルを作成して、バージョン固定の設定値を追加することです

https://docs.npmjs.com/cli/v10/configuring-npm/npmrc

https://qiita.com/terrierscript/items/86dbfd26abe6905756c0#save-exacttrue

`save-exact`の値を`true`に設定しておけばOKです！

```properties:.npmrc
save-exact=true
```

こちらの設定をしておくことで、`npm install` をするとバージョンを固定してインストールできます。

```shell
# nodemon を　devDependencies に追加する
npm install nodemon -D
```

こんな感じでいけますね！

```json-doc
// 例
{
  "devDependencies": {
    "nodemon": "3.1.3"
  }
}
```


### 💡 Github で Renovate を設定する

3つ目のやり方は、Githubを使ってリモートでライブラリのバージョン管理をする場合に使用できる選択肢です。
Renovateというサービスを、リモートで管理しているGithubリポジトリに組み込む方法です！

https://docs.renovatebot.com/

Renovateは、主にバージョン管理を効率化する目的でリポジトリに組み込んでいることが多いと思います！

Renovateの設定プリセットのうち `:pinAllExceptPeerDependencies` を設定しておくと、ライブラリのバージョンを固定化する設定が有効になります！（`config:js-app` には、`:pinAllExceptPeerDependencies`が含まれているため、`config:js-app` でも代用可）

https://docs.renovatebot.com/presets-default/#pinallexceptpeerdependencies


```json-doc:renovate.json
{
  "extends": [
    ":pinAllExceptPeerDependencies"  // もしくは "config:js-app"
  ]
}
```

上の設定をしておくとピン留めされていないライブラリがあれば、Renovateがバージョンを固定化するように、package.json と package-lock.json の書き換えをやってくれます！

## 📌 まとめ
Node.js でバージョン管理する際にライブラリのバージョンをピン留めすることで、１つのもやもやを解消することができます！
他にもっといいバージョンのピン留め方法がありましたら、コメントいただけると幸いです！

今回取り上げたトピックは些細な問題でしたが、こうしたもやもやが積み重なって開発に集中できないといった悲しい状況になってしまったらイヤですよねー

今回紹介したケースの他にも些細な問題が、開発に注力できなくなる要因となっているかもしれません！
ほかにも「これだ！」と思い当たるものがあったら、また同じテーマで投稿してみようかなと思います！


さいごに、
Renovateのドキュメントにも本記事に関連するバージョン固定化についての記載がありました

https://docs.renovatebot.com/dependency-pinning/

では〜