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、プレリリース、ロックファイルを整理しました。
既定の設定では、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のドキュメントで確認しました。
参照した仕様とドキュメント
- Semantic Versioning 2.0.0(原文の Markdown)
- node-semver:範囲の記法(
^、~)とプレリリースタグの規則 - npm CLI v12 のドキュメント:package.json、config、npm install、npm ci、package-lock.json