はじめに
npm
でプロジェクトを始めると、目にするpackage-lock.json
今までこれ何だろう?って思ってたけど、放置して進めていました。しかし、曖昧なままで置いておくのはダメだなと思い、一通りググって調べてみたところ、Everything You Wanted To Know About package-lock.json But Were Too Afraid To Askという記事がわかりやすかったので、こちらを翻訳してみました。
以下内容です
内容
Introduction
Node Package Manager(npm)
をv5.x.x
にアップデートすると、全て問題ないように見えます。でもちょっと待って、何これ?package-lock.json
という新しいファイルが自動生成されました。開いてみると、package.json
のdependencies
のように見えますが、より冗長です。ひとまず無視してプロジェクトを進めていくでしょう。そして依存性の問題に結果的にぶつかります。どこが悪いか見つからず、もしくは間違ったバージョンをインストールしてしまったのかもしれません。多くの人がpackage-lock.json
を削除してnpm install
を実行することでしょう。それではなぜ、package-lock.json
があるのでしょう?なんのためにあり、何を実際にするのでしょうか?
Summary
-
npm ^5.x.x
を使っている場合、デフォルトでpackage-lock.json
が生成されます - 継続的インストールと依存関係を確実にするため
package lock
を使うべきです - ソースバージョン管理に
package lock
を使いましょう -
npm ^5.1.x
以降はpackage.json
はpackage-lock.json
を奥の手として持っており、悩ましさを軽減してくれます。 -
package-lock.json
を削除する必要はなく、npm install
で再生成できる - APIを提供している場合、セマンティックバージョン管理に従う
Background
Semantic Versioning
package-lock.json
やpackage.json
を理解する前に、セマンティックバージョン(sember)について知る必要があります。これがとても優れており、npm
の繁栄に導いたものでもあります。npm
がどのようにこれを扱っているかはこちらの記事を参照ください。一言で言えば、他のアプリと連動するアプリを作っている場合、あなたが行う変更がどのような影響をもたらし、反映するかを明らかにしなければなりません。これがセマンティックバージョン管理
で行われます。X(メジャー), Y(マイナー), Z(パッチ)
という3つのバージョンパーツから成り立っております。例えば1.2.3
というバージョンがあるとします。1
がメジャーバージョンで、2
がマイナーバージョン、3
がパッチがとなります。パッチの変更は特に大きな影響を与えないバグ修正を、マイナーバージョンの変更も大きな影響はない機能的な変更を、メジャーバージョンの変更は互換性に影響を与える大きな変更を表します。ユーザーはメジャーバージョンの変更に対応しなければ、うまく動かなくなります。
Managing Packages
npm
はパッケージ管理を簡単にするために存在します。あなたのプロジェクトは何百ものdependencies
(依存状態)を持っており、またそれぞれが何百という他のdependencies
(依存先)にも依存しています。このdependencies
地獄を回避するために、npm
は存在しており、それらを特に考えなくてもいいよう、シンプルなコマンドでインストールから管理までを行うことができます。
パッケージをnpm
コマンドでインストールする際に、名前とバージョン番号を含めた項目がpackage.json
に追加されます。(npm
はセマンティックバージョンのワイルドカードを補います)。デフォルトで、npm
は最新のバージョンをインストールし^1.2.12
のようなキャレット(^:caret)
が付け加えられます。これは最低でもバージョン1.2.12
が使われるべきであり、メジャーバージョンが同じである限り対応するということを示しています。マイナーバージョンやパッチ番号はバグ修正や大きな追加変更のみを表しているので、より最新のメジャーバージョンを使うことに問題はありません。セマンティックバージョン管理のワイルドカード、また使いやすいセマンティック計算についてはこちらを参照ください
Shared Projects
このようなdependencies
(依存関係)を明記しているpackage.json
を持つことのメリットは、そのpackage.json
にアクセスできる誰もがあなたのアプリケーションを使うために必要なモジュールを含む依存フォルダーを作成できることにあります。しかし、うまくいかない場合を見てみましょう
express
(Node.jsのフレームワーク)を使う新規プロジェクトを作るとします。npm init
をまず行い、npm install express — save
でexpress
をインストールします。この記事を書いている現時点では、express
の最新バージョンは、4.15.4
ですので、"express": "^4.15.4"
とpackage.json
のdependencies
に追加され、コンピューターにも同じバージョンがインストールされます。そして翌日、express
の管理をしている人がバグ修正を行いリリースした場合、最新バージョンは4.15.5
になります。この時、もし誰かがこのプロジェクトに参加したく、クローンしてnpm install
をするとします。すると同じメジャーバージョン内で4.15.5
が最新となるので、それがその人のコンピューターにインストールされます。お互いexpress
を持っていますが、違ったバージョンとなります。理論上は、互換性がありますが、おそらく先ほどのバグ修正が、使っている機能に影響し、express@4.15.4
を起動させた場合に、express@4.15.5
に比べ、違った結果をもたらすことがあります。(↑↑わかりやすい例)
Package-lock
The Goal
package-lock
の目的はこのような、同じpackage.json
からのモジュールインストールが、違ったインストールに陥る結果を回避するためにあります。package-lock.json
はnpm version 5.x.x
で追加されました。ですので、もしメジャーバージョンが5以上であれば、無効化しない限り、package-lock.json
が生成されるのを確認できます。(↑↑ここ大事)
The Format
package lock
はpackage.json
に記載されたそれぞれの依存先の大きなリストです。インストールされるべき厳密なバージョン番号、モジュールの場所(URI)、モジュールの統合性を確実にするハッシュ、必要とされるパッケージリスト、そして依存リストです。express
の場合を見てみましょう
対応する項目が"requires"
セクションにある記載されたパッケージそれぞれにあります。
この考え方はpackage.json
の代わりに、package-lock.json
を使ってモジュールのインストールを行うことです。package-lock
はバージョンを厳密に特定し統合ハッシュを全てのモジュールまたそれぞれの依存先に振り分け、インストール時に完全に同じ環境を構築するためです。どのようなデバイスを使おうと、将来的にどのタイミングでインストールしようとも、毎回同じ結果をもたらしてくれます。
The Controversy
では、package-lock
が一般的な問題を解決する場合、なぜググった際に(もしくは他のnpm
ドキュメントで)、package-lock.json
を無効化もしくはその役割を疑問視するようなものが見つかるのでしょうか?
npm 5.x.x
以前は、package.json
はプロジェクトの信頼できる唯一の情報源でした。package.json
に書かれていることが原則でした。npm
ユーザーはこのモデルを好みパッケージファイルをこちらに適応させ管理してきました。しかし、package-lock.json
は導入された際に、多くの人が期待していたものと正反対の動きをしました。すでに存在していたpackage
と新しいpackage lock
、多くの人が「信頼できる唯一の情報源」と考えていたpackage.json
の変更はpackage-lock.json
には反映されなかったのです。
例:ver1.0.0
のパッケージAが、package.json
とpackage-lock.json
の両方にあるとします。package.json
においてAが手動でver 1.1.0に編集されるとします。もしpackage.json
を「信頼できる唯一の情報源」と考えるユーザーはここで、npm install
を行うと、ver 1.1.0
がインストールされることを期待します。しかしながらpackage.json
にはv1.1.0
が記載されていますが、実際には1.0.0
がインストールされます。
例: あるモジュールがpackage.json
に存在し、package-lock.json
にないとします。「信頼できる唯一の情報源」として、package.json
を確認し、期待するモジュールがインストールされると考えます。しかしpackage-lock.json
には明記されていないので、実際にはインストールされません。そしてモジュールが見つからないためエラーが起きます。
ほとんどの場合、彼らはなぜ自分のdependencies
が正しくインストールされないか理解できないでしょう。よって、package-lock.json
を削除するか再インストール、もしくはpackage-lock.json
を無効化するでしょう。
この期待と現実の乖離問題はnpm
レポジトリスレッドに立った興味深い議論を引き起こしました。あるひとはpackage.json
が「信頼できる唯一の情報源」としてみる一方で、ある人は、package-lock
こそがnpm
がインストールする際に参照するものであると考えます。この議論の解決はnpm
のPR #17508にあります。NPMの管理者たちはpackage.json
をアップデートしたら、package-lock.json
を上書きするという変更を追加しました。よって今では両方の場合で、ユーザーが期待するパッケージが正確にインストールされるようになりました。この変更はnpm v5.1.0
でリリースされ、2017年7月5日(当時)でも有効です。
最後にまとめ
- 依存パッケージが依存するパッケージ(ネストした依存状態)のバージョン情報が変わる場合がある
-
package.json
だけでは、node_modules
を完璧に再現できるとは限らない(勝手に違うバージョンのライブラリがインストールされてしまう可能性) -
package-lock.json
はバージョン情報をすべて正確に記録する -
package-lock.json
に書き込まれたバージョンのパッケージがインストールされる