【超難問】npmのバージョン指定方法、全パターン分かるか！？

npmでインストールするパッケージのバージョン指定をするときに、以下の`foo`のように特定のバージョンを固定したり、`bar`のように範囲で指定することができます。

```package.json
{
  "dependencies": {
    "foo": "2.0.1",
    "bar": ">=1.0.2 <2.1.2"
  }
}
```

この記述方法が結構いろいろあり、混乱したので整理しました。

ここからは、こういう場合にどういう書き方があるかをクイズ形式でまとめていくので、考えながら読んでください。
他にもこういう書き方あるよ！というのがあれば、コメントか編集リクエストをお願いします！

## 0. 例題「version 1.2.3」を指定する

package.jsonのdependenciesに記述する時の方法を列挙していきます。
この場合は、以下の書き方が当てはまります。

- `"1.2.3"`
- `"=1.2.3"`

こんな感じで次から問題になります。

## 1. 「1.2.3以上、1.3.0より小さいもの」を指定したい

<details><summary>表し方2種類はこちら</summary>


1. `">=1.2.3 <1.3.0"`
1. `"~1.2.3"`

### 解説

1についてですが、「>=1.2.3」かつ「<1.3.0」という複数のcomparatorを結合したいときは間に空白をいれます。

2のように`~`をつけてパッチバージョンまで指定している場合は、そのバッチバージョン以上のパッチレベルでの変更が許可されます。

</details>

## 2. 「1.2.0以上、1.3.0より小さいもの」を指定したい

<details><summary>表し方4種類はこちら</summary>

1. `">=1.2.0 <1.3.0"`
1. `"~1.2"` / `"~1.2.0"`
1. `"1.2.x"`
1. `"1.2"`

### 解説

2ですが、`~`をつけてパッチバージョンを指定していないときも、マイナーバージョンが固定されてパッチレベルで変更が許可されます。`"~1.2.0"`でもOKです。

3の`x`ですが、同様の意味として、`X`と`*`を使って以下のように書くこともできます。

- `"1.2.X"`
- `"1.2.*"`

これ以降で`x`を使うときは、`X`と`*`は省略させてもらいます。

4のようにパッチバージョンを指定していないときは、3と同様にX-Rangeとして扱われ、`"1.2.x"`と同じ意味になります。

</details>

## 3. 「1.2.0以上、1.2.9以下」を指定したい

ヒント: 答え3種類といいながら、2つ目と3つ目は似た指定の方法です。

<details><summary>表し方3種類はこちら</summary>

1. `">=1.2.0 <=1.2.9"`
1. `"1.2.0 - 1.2.9"`
1. `"1.2 - 1.2.9"`

### 解説

2のように`-`でつなぐと1つ目と同じ意味になります。
また、3のように左側の数字の1パッチバージョンを省略すると、省略された部分は「0」として扱われます。

</details>

## 4. 「1.2.2もしくは、1.2.4以上かつ1.2.8より小さいもの」を指定したい

個人的には使ったことがないですが、こんなパターンも指定できるようです。

<details><summary>表し方1種類はこちら</summary>

1. `"1.2.2 || >=1.2.4 <1.2.8"`

### 解説

`||`でつなぐと、どちらかの条件にマッチしたものを表せます。

</details>

## 5. 「1.2.3以上でメジャーバージョンアップは制限」したい

<details><summary>表し方3種類はこちら</summary>

1. `">=1.2.3 <2.0.0-0"` (`">=1.2.3 <2.0.0"`)
1. `"1.2.3 - 1"`
1. `"^1.2.3"`

### 解説

1について、`">=1.2.3 <2.0.0-0"`としているのは、`2.0.0`のプレスリリースバージョン(`2.0.0-beta`など)も制限したいときです。

2の`-`を使った指定ですが、`-`の右側の部分で一部を省略すると、指定されたもので始まるすべてのバージョンが許可されるため、1つ目と同じ意味になります。
`1.2.3 - 2`ではなく、`1.2.3 - 1`なので注意しましょう。

そして3のように`^`を使うとメジャーバージョンを固定できます。

</details>

## 6. 「1.0.0以上でメジャーバージョンアップは制限」したい

<details><summary>表し方6種類はこちら</summary>

1. `">=1.0.0 <2.0.0-0"`
1. `"1.0.0 - 1"` / `"1 - 1"`
1. `"1"`
1. `"1.x"` / `"1.x.x"`
1. `"~1"`
1. `"^1"` / `"^1.0.0"` / `"^1.x"`

### 解説

5について、`~`を使う場合は、`"~1.0.0"`のようにはかけません。この場合は`">=1.0.0 <1.1.0-0`の意味になってしまいます。

</details>

## 7. 「0.1.0以上 0.2.0より小さいもの」を指定したい

ヒント: これを最後に持ってきたのはメジャーバージョンが0のときに注意点があるからです。

<details><summary>表し方4種類はこちら</summary>

1. `">=0.1.0 <0.2.0-0"`
1. `"0.1.x"`
1. `"~0.1"` / `"~0.1.0"`
1. `"^0.1"` / `"^0.1.0"`

### 解説

`0.x`系のマイナーバージョンアップデートはマイナーバージョンでありながらbraking changeがあることが想定されます。そのため、4の`"^0.1"`は`">=0.1.0 <1.0.0-0"`ではなく`">=0.1.2 <0.2.0-0"`を表します。

</details>

## 補足

これらのバージョンのバリデーションは`semver`を使って、以下のように確認できます。

```js
import semver from 'semver'
const range = semver.validRange('^0.1')
console.log({ range })
// -> { range: '>=0.1.0 <0.2.0-0' }
```

https://www.npmjs.com/package/semver

## まとめ

同じことを表現したいのに、表現方法が多すぎる...
6番に関しては6種類といいながら10パターンも書き方があって、瞬時に理解するのが難しい..

## Ref

- https://www.npmjs.com/package/semver
- https://docs.npmjs.com/cli/v9/configuring-npm/package-json#dependencies

## おまけ

この記事を書いていて、以下の記事を思い出しました。
こちらも面白かったので個人的におすすめです！

https://qiita.com/segur/items/8ab94f4f99f26d31999c