まだ node.js の依存性解決で消耗してるの?

PLAID エンジニア/HUNTER
山内 雅浩
@algas

概要

node.js (npm) での依存性解決とパッケージマネージャを shrinkwrap から yarn へ移行した話。
プレイド勉強会での発表資料 も合わせてご覧ください。

対象読者

  • node.js で継続的に開発を行っているエンジニア
  • npm 依存関係地獄に苦しんでいる同志

結論

shrinkwrap を捨てて yarn を使いましょう。
ただし production に使うのはちょっと早い。
執筆時点での yarn のバージョンは 0.17.10 です。

shrinkwrap vs yarn

(npm) shrinkwrap と yarn を比較してみましょう。

項目名 Node.js Yarn
パッケージマネージャ名 npm yarn
パッケージファイル package.json package.json
パッケージ指定ファイル npm-shrinkwrap.json yarn.lock

yarn を使う利点と欠点は以下のとおりです。

PROS

  • インストール処理が高速かつ安定
    ネットワークパフォーマンス改善やオフラインモード、リトライの実装がされていてパッケージのインストールが標準の npm install に比べて高速かつ安定に動作します。
  • 依存性解決の向上
    複数のパッケージで使われている同じパッケージを1つのインデックスにまとめることでインストールするパッケージ数を減らし、ディスク容量の軽減・インストール速度の向上を実現しています。

CONS

  • yarn 自体のインストールが必要
    他のパッケージよりも先に yarn をインストールしなければなりません。shrinkwrap は npm に標準装備されているのでインストールは必要ありません。
  • npm lsyarn ls の互換性がない
    npm install 済みのパッケージを確認するコマンドである npm lsyarn install 済みのパッケージを確認する yarn ls に互換性がありません。yarn install が正常にできた環境で npm ls を実施しても OK と返らない場合があります。
  • yarn 自体の不具合
    yarn のバージョンは執筆時点で 0.x であり、正式バージョンである 1.0 以上になっていません。公開されて間もないこともあり、issue が大量に上げられていて yarn 自体の不具合に悩まされることもあります。

node パッケージの追加と削除

パッケージを追加・削除する方法にはコマンドからインストールするのと、package.json に書いてからインストールする2通りの方法があります。
CIなどのリモート環境では後者の方法で運用する場合もあります。

  • npm + shrinkwrap の場合

    1. コマンドからインストール
      1. npm install --save new_package or npm uninstall --save new_package
      2. npm ls
    2. package.json からインストール
      1. dependencies に new_package を追加/削除する
      2. npm update --save (危険)
      3. npm ls (失敗しがち)
  • yarn の場合

    1. コマンドからインストール
      1. yarn add new_package or yarn remove new_package
      2. yarn check
    2. package.json からインストール
      1. dependencies に new_package を追加/削除する
      2. yarn upgrade
      3. yarn check

shrinkwrap を使った場合には package.json に手動でパッケージを追加すると、それを削除した際に追従できずにエラーが発生することがありますが、yarn を使った場合に同じ作業を行っても問題が発生しません。

依存関係地獄 (dependency hell)

npm-shrinkwrap.json を使うことで確かに npm install で同じパッケージ群をインストールすることはできるようになります。
この環境に package.json への追加によって新しいパッケージを追加しようとした場合には shrinkwrap は正しく依存関係を更新できません。

以下の手順で依存性解決をすることになります。

  1. 本体またはサブモジュールの package.json を更新する
  2. npm install or npm update --save を実行する
  3. パッケージの更新に失敗する
  4. 1.に戻る(依存性解決できるまで繰り返す)

結局、問題が解決できずに npm-shrinkwrap.json を削除するしかない状況に陥ることも多くあります。
これが継続的に依存性問題を解決できない理由です。

依存性解決できない問題

node.js でデファクトスタンダードになっているパッケージ管理ツールである npm (node package manager) で依存性を解決するのが難しいという問題があります。
現状では shrinkwrap という npm に標準装備されている方法を使ってインストールされるパッケージを固定(バージョン指定のスナップショット)できます。
npm shrinkwrap コマンドでパッケージ指定ファイル npm-shrinkwrap.json を生成することで、他の環境で npm install を実行した際に(依存関係も含めて)同じパッケージがインストールされるようにします。
上記で書いたとおりに shrinkwrap を使えばインストールされるパッケージを固定することはできます。しかし上述の通り、実際に運用してみると shrinkwrap ではパッケージの固定はできても、継続的な依存性解決ができるわけではないことがわかります。

Yarn の不具合

  • production install できない
    yarn install --production が効かない。つまり devDependencies もインストールされる。
    https://github.com/yarnpkg/yarn/issues/761
  • postinstall を実行してない問題
    deep dependencies で preinstall, install, postinstall を実行できてない。
    https://github.com/yarnpkg/yarn/issues/2142

自作ツールの紹介

緩やかに移行したい方へ npm-shrinkwrap.json から yarn.lock を生成するツールを作りました。
https://github.com/algas/shrinkwrap2yarn
使い方は上記リポジトリの README をご参照ください。
動かなかったり不具合を見つけたりした場合には issue, PR をお待ちしております。
自社の製品では~~shrinkwrap.json の作成フローが雑だったので~~結局使いませんでした。

ハマったこと

  • npm でのインストールの挙動(サブコマンドの動作など)が把握できなくて、理解するまでにすごく時間がかかりました。
  • 自社の node.js プロジェクトの依存性解決に手間取りました。
  • うちの package.json はいろんな処理を書きすぎじゃね?

参考文献

まとめ

今すぐ shrinkwrap を捨てて yarn と旅に(ry

エンジニア募集中

ウェブ接客プラットフォーム「KARTE」を運営するプレイドでは、KARTEを使ってこんなアプリケーションが作りたい! KARTE自体の開発に興味がある!というエンジニア(インターンも!)を募集しています。
詳しくは弊社採用ページ
またはWantedly
をご覧ください。 もしくはお気軽に、下記の「話を聞きに行きたい」ボタンを押してください!