package.jsonの^1.2.3と~1.2.3の違い:npmのバージョン範囲と0.xの落とし穴

既定では、^1.2.3は1.2.3以上2.0.0未満の正式版を、~1.2.3は1.2.3以上1.3.0未満の正式版を許可します。SemVerとnpmの違い、0.x、プレリリース、ロックファイルを整理しました。

開発npmSemVer依存関係管理

既定の設定では、npm install <パッケージ>でインストールすると、package.jsonに"^1.2.3"のような範囲が保存されます。先頭の^は、どのバージョンまでインストールできるかを表します。よく似た~とは許可する範囲が異なります。

SemVerは番号のルール、^と~はnpmの記法

Semantic Versioning 2.0.0は、バージョンをMAJOR.MINOR.PATCHで表し、どの番号をいつ上げるかを定めています。

1.0.0以上では、後方互換性のあるバグ修正だけならPATCHを上げなければなりません。後方互換性のある機能を追加した場合や、公開APIの機能を非推奨にした場合はMINORを上げます。後方互換性を壊す変更にはMAJORの更新が必要です。これらは仕様上のMUST要件です。

0.y.zは初期開発用です。仕様では、何がいつ変わってもよく、公開APIを安定しているとみなすべきではないとされています。

SemVerの仕様自体には^や~の意味は定義されていません。これらはnpmがpackage.jsonで受け付ける範囲の記法です。正確な解釈は、npmがバージョンの解析と比較に使うnode-semverで定義されています。

範囲を展開すると

記法 展開した範囲 既定で許可される正式版
^1.2.3 >=1.2.3 <2.0.0-0 マイナー・パッチの変更
~1.2.3 >=1.2.3 <1.3.0-0 パッチの変更
^0.2.3 >=0.2.3 <0.3.0-0 パッチの変更
~0.2.3 >=0.2.3 <0.3.0-0 パッチの変更
^0.0.3 >=0.0.3 <0.0.4-0 0.0.4のような後続の正式版は対象外

^は、major、minor、patchのうち最も左にある0以外の要素を変えない変更を許可します。そのため1.xでは^のほうが~より広い範囲になります。0.xでは^0.2.3と~0.2.3が同じ範囲です。^0.0.3は0.0.4のような後続の正式版を受け入れません。

~は、マイナーバージョンまで書かれていればパッチの変更を許可します。マイナーバージョンを省略した~1は>=1.0.0 <2.0.0-0に展開されます。

上限の-0とプレリリース

SemVerの優先順位では、プレリリースは同じ番号の正式版より前に並びます。プレリリース識別子を比較する場合、数値識別子は非数値識別子より優先順位が低くなります。そのため2.0.0-0は、2.0.0のほかの有効なプレリリースより前に並びます。上限を<2.0.0-0とすることで、2.0.0-betaのようなバージョンも範囲外になります。

数値上は範囲内にあるプレリリースも既定では除外されます。プレリリースがcomparator setを満たすには、同じsetの中に、同一の[major, minor, patch]とプレリリースタグを持つcomparatorが1つ以上必要です。node-semverのincludePrereleaseオプションを有効にすると、この既定の除外規則が変わります。

次の結果はNode.js v24.13.0とnode-semver 7.8.5で実行して確認しました。

const semver = require("semver");

for (const range of ["^1.2.3", "~1.2.3", "^0.2.3", "^0.0.3"]) {
  console.log(range.padEnd(7), new semver.Range(range).range);
}

console.log(semver.satisfies("1.9.0", "^1.2.3"));
console.log(semver.satisfies("1.9.0", "~1.2.3"));
console.log(semver.satisfies("0.3.0", "^0.2.3"));
console.log(semver.satisfies("1.3.0-beta.1", "^1.2.3"));
console.log(
  semver.satisfies("1.3.0-beta.1", "^1.2.3", {
    includePrerelease: true,
  }),
);

実行結果:

^1.2.3  >=1.2.3 <2.0.0-0
~1.2.3  >=1.2.3 <1.3.0-0
^0.2.3  >=0.2.3 <0.3.0-0
^0.0.3  >=0.0.3 <0.0.4-0
true
false
false
false
true

範囲と実際にインストールされるバージョン

npmのsave-prefix設定の既定値は"^"です。save-exactを有効にすると、範囲ではなく正確なバージョンを保存します。

package.jsonの範囲が広くても、実際のインストールにはロックファイルが関係します。package-lock.jsonは、npmが生成した依存関係ツリーを正確に記録するファイルで、リポジトリにコミットすることが想定されています。

引数なしでnpm installを実行すると、ロックファイルのバージョンがpackage.jsonの範囲を満たす間はそのバージョンを使います。両者が一致しない場合はpackage.jsonに従って依存関係を再計算し、ロックファイルを更新します。

npm ciにはロックファイルが必要です。package.jsonとロックファイルが一致しない場合、ロックファイルを修正せずエラーで終了します。package.jsonやロックファイルへの書き込みも行いません。

バージョン範囲が保証しないこと

^1.2.3が1.9.0を受け入れるのは、公開者がSemVerに従い、1.x内の互換性を保ち、互換性を壊す変更ではMAJORを上げたことが前提です。バージョン範囲は番号とプレリリースの規則を比較するだけで、APIが実際に互換かどうかは検査しません。

この記事のnpmの動作は、2026年10月1日にnpm CLI v12のドキュメントで確認しました。

参照した仕様とドキュメント

ブログへ