Node.js プロジェクトで依存関係の更新を管理する

完了

Tailwind Traders の開発者として、パッケージを最新の状態に保つことが重要です。 これは、最新の機能と修正プログラムを使用していることを保証するのに役立ちます。 また、セキュリティの脆弱性を回避するのにも役立ちます。 このユニットでは、Node.js プロジェクトで依存関係を管理する方法について学習します。 パッケージの更新方法、セマンティック バージョン管理の使用方法、セキュリティの問題の管理方法について学習します。

パッケージを更新する前の考慮事項

パッケージを更新する前に、以下を検討します。

  • 更新の種類:それが小さな修正プログラムなのか、新機能なのか、またはコードを壊す可能性がある大きな変更なのかを把握します。 セマンティック バージョン管理は、これを識別するのに役立ちます。
  • プロジェクト構成:予期しない変更を回避するために、プロジェクトが必要な更新プログラムのみを受け取るように設定されていることを確認します。
  • セキュリティ: 潜在的な脆弱性を認識します。 npm の監査機能を使用して、問題のあるパッケージを特定して更新します。
  • テスト:更新後にテストが合格になることを確認します。 テストがない場合は、テストの追加を検討します。 更新後にはアプリケーションが異なった動作をする場合があり、テストは "正しい" 動作を検証します。

更新の動作を定義するためのセマンティック バージョン管理

セマンティック バージョン管理は、ソフトウェア開発における重要な標準です。 これは npm パッケージの公開と使用の両方で不可欠です。 これは、新しいバージョンにおける変更の種類を明示すことによって、更新のリスクの管理に役立ちます。 バージョン番号は、以下の変更を反映する特定のセクションを持ちます。

バージョンの種類 配置 構文 起こること 更新の方針
Major 1st x.0.0 または * 1.0.0 から 2.0.0 への変更は、破壊的変更を意味します。 コードの調整が必要となる場合があります。 コード変更の可能性を許容するなら、最新のメジャー バージョンへの即座の更新を行って構いません。
Minor 2 番目 1.x.1 または ^ 1.2.9 から 1.3.0 への変更は新しい機能を導入します。 既存のコードは引き続き動作する必要があります。 更新は通常は安全です。 新しい機能は受け入れますが、破壊的変更は受け入れません。
[Patch] 3 つ目 1.1.x または ~ 1.0.7 から 1.0.8 への変更はバグ修正を意味します。 更新は安全である必要があります。 バグ修正を受け入れます。

小規模な Node.js プロジェクトの場合は、最新バージョンに自由に更新できます。 しかし、より大規模なプロジェクトの場合、更新には慎重な検討が必要であり、更新は常に自動であるとは限りません。 一般に、更新する依存関係を小さくし、それら自体の依存関係を少なくすると、プロセスが簡単になります。

1 つ以上の依存関係を更新する前に、npm update <name of dependency> コマンドを実行したときに予測可能な動作になるように、package.json ファイルを構成する必要があります。 Node.js では、パッケージをどのように更新したいかを定義できる一連のシンボルが使用されます。

npm CLI を使用してパッケージを更新する

npm の install または update コマンドを使用してパッケージをインストールできます。 これらのコマンドは現在ではほとんど互いに代替可能となっています。 パッケージを更新するには、通常、以下を使用します。

  • 最新バージョン: npm update <package name>@latest
  • 特定のバージョン: npm update <package name>@<optional version number>

更新プロセスは、以下の 2 つの要素に依存します。

  • Version 引数:npm update コマンドでバージョン番号が指定されている場合、npm はその特定のバージョンをフェッチしてインストールします。
  • マニフェスト ファイル エントリ:package.json ファイルには、依存関係の更新に関するルールが含まれています。 たとえば、"dependencyName": "1.1.x" は npm がこのパターンに一致するバージョンをフェッチすることを意味します。

バージョン管理を理解する

依存関係のバージョン管理は、次の 3 つのファイルによって制御されます。

  • package.json: 使用するパッケージのバージョンをこのファイルで定義します。 これはプロジェクトのマニフェスト ファイルです。 依存関係を含むプロジェクトのメタデータが含まれます。
  • package-lock.json: これは生成された正確なツリーを記述するファイルです。これにより、中間の依存関係の更新とは関係なく、後続のインストールで同じツリーを生成できます。 このファイルは、ソース リポジトリにコミットすることを意図したものです。
  • shrinkwrap.json: このファイルは CLI コマンド npm shrinkwrap によって作成され、package-lock.json に似ています。 これらのコマンド間の主な違いは、npm-shrinkwrap.json で指定されたパッケージ バージョンをユーザーがオーバーライドできないことです。 さらに、npm-shrinkwrap.json ファイルは以前のバージョンの npm (バージョン 2 から 4) と互換性があります。これに対し、package-lock.json は npm バージョン 5 以降と互換性があります。 そのため、レガシ コードベースを保守する場合は npm-shrinkwrap.json が見つかります。 ほとんどの開発者は、npm-shrinkwrap.json ではなく package-lock.json を使用します。 npm-shrinkwrap.json が推奨される 1 つの例外は、開発者がデーモンとコマンド ライン ツールのグローバル インストールについて、指定された正確なバージョンのパッケージがインストールされることを確実にしたい場合です。

パッケージ バージョンを決定する例

あなたが自分のコード内でバージョン 1.2 を使用していて、その後にバージョン 1.4 がリリースされ、あなたのコードが破壊されたシナリオを考えましょう。 この時点で誰かがあなたのアプリをインストールすると、その人は機能していないアプリを手にすることになります。 しかし、バージョン 1.2 を指定する package-lock.json ファイルが存在する場合は、そのバージョンがインストールされます。

パッケージのどのバージョンがインストールされるかの決定の例を以下に示します。

  • package.json および package-lock.json ファイルがバージョン ルールについて一致している場合、競合はありません。 たとえば、package.json1.x を指定し、package-lock.json がバージョン 1.4 を指定している場合、バージョン 1.4 がインストールされます。
  • package.json1.8.x のようなより具体的なバージョンを指定している場合、これはより古いバージョンである 1.4 を指定している package-lock.json ファイルをオーバーライドします。 この場合、バージョン 1.8.0 以降のパッチ バージョンがインストールされます (利用可能な場合)。

npm outdated を使用して古くなったパッケージを検索して更新する

npm outdated コマンドは、利用可能なより新しいバージョンがあるパッケージを特定するために使用されます。 実行されると、これは以下のような古くなったパッケージの一覧を表示します。

Package       Current    Wanted   Latest     Location     Depended by
lodash        1.0.0      1.0.0    4.17.19    lock-test    main-code-file
node-fetch    1.2.0      1.2.0    2.6.0      lock-test    function-code-file

出力には次の列があります。

説明
パッケージ 古くなったパッケージ。
現行 パッケージの現在インストールされているバージョン。
Wanted package.json ファイルで指定したセマンティック パターンに一致する最新バージョン。
最も遅い パッケージの最新バージョン。
場所 パッケージの依存関係の場所。 outdated コマンドは、さまざまな node_modules フォルダーの中のインストールされているすべてのパッケージをクロールします。
Depended by 依存関係を持つパッケージ。

npm audit を使用してセキュリティの問題を管理する

パッケージをインストールまたは更新するたびに、インストールされたバージョンと何らかの脆弱性があるかどうかを示すログ応答が表示されます。 ログは脆弱性を一覧表示します。 何らかの致命的または重要度の高い脆弱性がある場合、そのパッケージを更新する必要があります。

ログ応答の例を以下に示します。

+ lodash@1.3.1
added 1 package from 4 contributors and audited 1 package in 0.949s
found 3 vulnerabilities (1 low, 2 high)
  run `npm audit fix` to fix them, or `npm audit` for details

問題を解決して更新を適用するためには、npm audit コマンドを実行します。 このコマンドにより、各脆弱性が一覧表示されます。

npm audit fix コマンドは、問題が存在しないマイナー バージョンにアップグレードすることで、脆弱性の解決を試みます。 ただし、修正プログラムが実際には次のメジャー バージョンに含まれる場合、これは利用できない可能性があります。

このような場合は、メジャー バージョンに更新することで破壊的変更を導入する可能性がある npm audit fix --force を使用する必要があるかもしれません。 このコマンドの実行は、慎重に行う必要がある決断です。 破壊的変更を認識し、npm update を使用して脆弱性を含むコードを更新する必要があります。

"脆弱性" とは、攻撃者によって悪意のあるアクションを実行するために悪用され、データやシステムが侵害される可能性があるコードの欠陥です。 このような脆弱性には迅速に対処することが重要です。

脆弱性は頻繁に検出されるので、GitHub にはリポジトリをスキャンし、より安全なバージョンへのアップグレードを提案する PR を自動的に作成する機能があります。 npm audit を定期的に実行することは、脆弱性を特定して修正するための良いプラクティスで、プロジェクトの全体的なセキュリティに貢献します。

推奨される更新のワークフローを以下に示します。

  1. npm run test:この更新プロセスを開始する前に、既存のテストが合格になることを確認します。
  2. npm audit: 使用している現在のバージョンの脆弱性を確認します。 npm audit からの情報では、メジャー バージョンへの更新が推奨される場合があります。 破壊的変更が一覧に表示されたら、それらを慎重に確認する必要があります。
  3. npm outdated: 古くなったパッケージをすべて一覧表示します。 このコマンドにより、WantedLatestLocation の各列に情報が提供されます。
  4. npm update で更新:
    • (package.json 内に数個の依存関係があるような) 小規模なプロジェクトの場合: npm update を試してすべての依存関係を更新した後に、テストを実行することができます。
    • (package.json 内に多くの依存関係があるような) 大規模なプロジェクトの場合: 1 つのパッケージまたはパッケージ ファミリ (Next.js や React など) を更新した後、テストを実行します。
  5. npm audit: 致命的または重要度の高い脆弱性がないことを確認します。 脆弱性がまだ存在する場合は、パッケージ名と npm audit で推奨されるメジャー バージョンで npm update を使用します。
  6. 再び npm run test を実行します。
  7. package.json および package-lock.json をチェックインします。