Edited at

package-lock.jsonについて知りたくても聞けなかったこと


はじめに

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.jsondependenciesのように見えますが、より冗長です。ひとまず無視してプロジェクトを進めていくでしょう。そして依存性の問題に結果的にぶつかります。どこが悪いか見つからず、もしくは間違ったバージョンをインストールしてしまったのかもしれません。多くの人が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.jsonpackage-lock.jsonを奥の手として持っており、悩ましさを軽減してくれます。


  • package-lock.jsonを削除する必要はなく、npm installで再生成できる

  • APIを提供している場合、セマンティックバージョン管理に従う


Background


Semantic Versioning

package-lock.jsonpackage.jsonを理解する前に、セマンティックバージョン(sember)について知る必要があります。これがとても優れており、npmの繁栄に導いたものでもあります。npmがどのようにこれを扱っているかはこちらの記事を参照ください。一言で言えば、他のアプリと連動するアプリを作っている場合、あなたが行う変更がどのような影響をもたらし、反映するかを明らかにしなければなりません。これがセマンティックバージョン管理で行われます。X(メジャー), Y(マイナー), Z(パッチ)という3つのバージョンパーツから成り立っております。例えば1.2.3というバージョンがあるとします。1がメジャーバージョンで、2がマイナーバージョン、がパッチがとなります。パッチの変更は特に大きな影響を与えないバグ修正を、マイナーバージョンの変更も大きな影響はない機能的な変更を、メジャーバージョンの変更は互換性に影響を与える大きな変更を表します。ユーザーはメジャーバージョンの変更に対応しなければ、うまく動かなくなります。


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 — saveexpressをインストールします。この記事を書いている現時点では、expressの最新バージョンは、4.15.4ですので、"express": "^4.15.4"package.jsondependenciesに追加され、コンピューターにも同じバージョンがインストールされます。そして翌日、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.jsonnpm version 5.x.xで追加されました。ですので、もしメジャーバージョンが5以上であれば、無効化しない限り、package-lock.jsonが生成されるのを確認できます。(↑↑ここ大事)


The Format

package lockpackage.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.jsonpackage-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がインストールする際に参照するものであると考えます。この議論の解決はnpmPR #17508にあります。NPMの管理者たちはpackage.jsonをアップデートしたら、package-lock.jsonを上書きするという変更を追加しました。よって今では両方の場合で、ユーザーが期待するパッケージが正確にインストールされるようになりました。この変更はnpm v5.1.0でリリースされ、2017年7月5日(当時)でも有効です。


最後に


まとめ


  • 依存パッケージが依存するパッケージ(ネストした依存状態)のバージョン情報が変わる場合がある


  • package.jsonだけでは、node_modulesを完璧に再現できるとは限らない(勝手に違うバージョンのライブラリがインストールされてしまう可能性)


  • package-lock.jsonはバージョン情報をすべて正確に記録する


  • package-lock.json に書き込まれたバージョンのパッケージがインストールされる


ひとこと

今回初めて技術系記事を翻訳しました。意訳の部分も多く、原文の意図と違った訳し方の箇所もあるかと思います。もし間違い等発見されましたら、ぜひご指摘ください!

また、ここがわかりづらい等ありましたら、コメント下さい。読んで頂き有難うございます!


その他オススメ参考記事