dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

package.jsonのパッケージバージョンに記載される ^ (キャレット) とは?どうしてつくのか?

$ npm install --save react

などのコマンドでパッケージをインストールすると、package.json にはインストールした(その時点での最新)バージョンが記載されますが
そのとき

"dependencies": {
  "react": "^15.3.2"
}

というように、バージョン番号の前に ^ がつきます。

これの意味と、どうしてつくのか調べてみたメモ。

前提

Node.js のパッケージのバージョンは Semantic Versioning (semver) というルールに従っている。

semver は X.Y.Z という3桁の数字で表されるバージョンで、

  • X: major version。後方互換性のない変更の場合にこのバージョンを上げる
  • Y: minor version。後方互換性のある変更の場合、このバージョンを上げる
  • Z: patch version。後方互換性のあるバグ修正などの場合、このバージョンを上げる

というルールになっている。


^ (キャレット) とは?

このあたりを読む。
https://docs.npmjs.com/misc/semver#caret-ranges-123-025-004

Allows changes that do not modify the left-most non-zero digit in the [major, minor, patch] tuple

パッケージのバージョン番号のうち、一番左の非ゼロの値を変更しないようなアップデートを許容する、という意味のよう。

つまり、

  • バージョンが 1.2.3 の場合、1.2.3 <= n < 2.0.0
  • バージョンが 0.2.3 の場合、0.1.2 <= n < 0.2.0
  • バージョンが 0.0.3 の場合、0.0.3 <= n < 0.0.4

というように、対象のバージョンによって挙動が異なる。

そして、npm install や npm update を行ったとき、許容される範囲の中で最新のバージョンをインストールしたりアップデートしたりする。
そのため、package.json だけだとバージョンを完全に固定できているわけではない


【補足】package.json でバージョンを完全に固定するには?

後述するように ^ をつけないように設定を変更するか、npm shrinkwrap コマンドを使って npm-shrinkwrap.json というファイルを生成しておく。
https://docs.npmjs.com/cli/shrinkwrap

// npm-shrinkwrap.json (例)
{
  "name": "react-es6-tutorial",
  "version": "1.0.0",
  "dependencies": {
    "jquery": {
      "version": "2.2.2",
      "from": "jquery@2.2.2",
      "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.2.tgz"
    },
    "marked": {
      "version": "0.3.5",
      "from": "marked@0.3.5",
      "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.5.tgz"
    },
    "react": {
      "version": "0.14.7",
      "from": "react@0.14.7",
      "resolved": "https://registry.npmjs.org/react/-/react-0.14.7.tgz",
      "dependencies": {
        "envify": {
          "version": "3.4.1",
          "from": "envify@>=3.0.0 <4.0.0",
          "resolved": "https://registry.npmjs.org/envify/-/envify-3.4.1.tgz",
          "dependencies": {
            "through": {
       ...


【補足】~ (チルダ)

^ と似たような記号として、~ (チルダ) もある。
https://docs.npmjs.com/misc/semver#tilde-ranges-123-12-1

Allows patch-level changes if a minor version is specified on the comparator. Allows minor-level changes if not.

minor version が明記されていれば patch version のアップデートを許容、明記されていなければ minor version のアップデートを許容。
なので、

  • ~1.2.3 の場合: 1.2.3 <= n < 1.3.0
  • ~1.2 の場合: 1.2 <= n < 1.3.0
  • ~1 の場合: 1.0.0 <= n < 2.0.0


^ (キャレット) はなぜつくのか?

package.json にデフォルトで ^ がつく挙動はどこで設定しているのだろうか?気になって調べてみた。
どうやら npm config に関係する設定があるらしい。


save-prefix

デフォルトでつく prefix を設定する。

$ npm config get save-prefix
^

# デフォルトを ~ に変更
$ npm config set save-prefix "~"
$ npm config get save-prefix
~


save-exact

--save--save-dev したときに prefix をつけるか、あるいは prefix をつけずに package.json に記載してバージョンを完全に固定するかを選べる。
デフォルトは false。なので prefix がつく。

$ npm config get save-exact
false

# デフォルトで完全なバージョン固定になるようにする
$ npm config set save-exact true


【補足】npm config で設定した値

どうやらこの設定は ~/.npmrc に保存されているらしい。
.npmrc はプロジェクトごとに置くことも可能。
https://docs.npmjs.com/misc/config#npmrc-files


^ (キャレット) をつけずにインストールする方法はないのか?

上述したように、npm config で

$ npm config set save-exact true

と設定しておくと、npm install 時の挙動を変更することができる。

あるいは、npm install 時のオプションとしても

--save-exact (略記は -E)

が用意されている。
https://docs.npmjs.com/cli/install

これをつけると、^ なしでインストールできる。

# -SE は --save --save-exact の略
npm i -SE react