node_modulesのクローンがダウンしましたか?ファイルシステムの重複排除

2019年3月14日)

[email protected]のコピー数あなたは好きですか? ( Unsplash 浮萍闪電気による写真)

更新:以下で説明する実装には、依存関係のあるファイルに問題があります(require / importステートメント)。 node\_modules/の構造により、2つの場所に存在する同一のファイルが、それ自体の依存関係の異なるバージョンを取り込む可能性があるため、安全性を高めるために、ファイルの複製をゼロに制限する必要があります。依存関係。

いくつかの大きなNode.js / React Native / webアプリプロジェクトでTradleで発生した問題の1つは、node\_modulesのコピーが重複してしまうことです。特定の依存関係。アプリが起動すると、同じモジュールを複数回解析して実行し、メモリを浪費します。

通常の古いNode.jsでは、通常、パフォーマンスの低下はそれほど大きくありません。しかし、それが起こっていることを知ることはまだ痛いです。モバイル/ウェブでは、泣き言は必須です。ある時点で、ReactNativeアプリにreadable-streamのバージョンのコピーが10個あったと思いますが、絵文字が苦手なので、Watに言わせてください。

ワットはチョーサーの英語を教えています

痛みを感じたいですか?問題の原因となる依存関係構造の例を次に示します。

# assume for simplicity that deps specify exact versions, i.e.:
# myProject"s package.json specifies "lodash": "4.0.0"
# depA and depB"s package.json specifies "lodash": "3.0.0"myProject/
node\_modules/
lodash # 4.0.0
depA
node\_modules/lodash # 3.0.0
depB
node\_modules/lodash # 3.0.0 <- not cool

プロジェクトは[email protected]に依存していますが、そのうちの2つは依存関係は[email protected]に依存します。 [email protected]がルートにあるため、パッケージマネージャーは[email protected]をルートまで持ち上げて重複排除することはできません。したがって、[email protected]のコピーが2つ作成されます。1つはdepAの下に、もう1つはdepBの下にあります。 。それらは同一です。また、depAdepBの作成者にPRを送信してアップグレードする時間がない限り、それについてできることは何もありません。 [email protected](マージされるまでに数か月かかる可能性のあるPR)、それらがマージされるのを待ちます。それまたはそれらのモジュールの両方のフォークを維持します。だからあなたは他の問題を解決しに行きます。

彼がそれをすべて知っていた方法がわかりません当時

おめでとうございます!これで、重複排除の問題をバンドラーに延期しました。 Webpack、Browserify、Metroは、それぞれ個別に解決する必要があります。これがWebpackの未解決の問題です。 2017年からオープンしており、もし私がWebpackだったら、それを自分の問題にしようとしている人たちに腹を立てるでしょう。

この問題は新しいものではなく、多くの人が傍観者から辛抱強く耳を傾けてきました。 Node.jsとブラウザのパッケージマネージャーとバンドラーの化身。心のこもったデジタルゴキブリのように、おそらくもうしばらくは続くでしょう。

幸いなことに、最近の減税により、トンネルの終わりで電気をつける余裕ができました。 (ただし、金持ちのためだけです。)

トンネルの終わりの光

TradleのReactNativeアプリのバンドル内の依存関係の同一のコピーを削除するために、 Metro Packagerにサポートを追加するか、MetroPackagerチームにアピールします。また、WebpackにバンドルされているReact Native for Webにアプリを同梱しており、両方で機能するトリックが必要でした。

すべてのパッケージマネージャーとバンドラーに共通するものをターゲットにすることにしました。 Node.jsのresolve()アルゴリズム。これは、require("some-module")を実行して、ファイルシステム上のモジュールの絶対パスを見つけるときに実行されるアルゴリズムです。パッケージマネージャーとバンドラーの両方がこのアルゴリズムに依存しています。最初にnode\_modulesディレクトリ構造を構築し、2番目にトラバースしてバンドルを構築します。

解決策は簡単:node\_modulesをトラバースし、すべての重複を識別し、そのうちの1つを正規(最初のアルファベット順など)として選択し、*.jsファイルを次のように書き換えます。正規のものを指す他のすべて。重複フォルダー内の*.jsファイルは次のようになります。

// deduped by dedupe-deps
module.exports = require("../../../changes-feed/node\_modules/readable-stream/duplex.js")

これは少し汚い感じがしますが、それは機能し、バンドルを小さくしてロードを高速化します。このモジュールはオープンソースです。お気軽にお試しください: https://github.com/tradle/dedupe-deps

もっと良い方法があるはずです

おそらく今目を転がして、「pnpmを使用してください」と叫んでいる人もいます。そこに注意してください、目を転がすことは中毒性があります。そして、あなたは彼らがあなたに目を見張るような撤退のエッジを取り除くためにあなたに何を処方するのか知りたくありません。

pnpmは比較的新しいパッケージャーです町。ハードリンクとシンボリックリンクの組み合わせを使用して、モジュールが特定のプロジェクトのnode\_modulesに1回だけ存在することを保証します。実際、ファイルシステム全体に1回だけ存在することが保証されています。これはとてもクールです。いつか私たち全員がpnpmを使用し、すべてが魔法のように機能しますが、今日でも一部のバンドラーとの互換性の問題が残っていると感じています。

…はい、確認したところ、Metroではまだ問題が発生していますが、解決策に近づいているようです: https://github.com/facebook/metro/ pull / 257

さらに一歩進んで

ファイルシステムベースのソリューションに戻りましょう。私たちは物事をさらに一歩進めて、同じ石でより多くの鳥を殺すことができます。間違いなく、これらは元の鳥よりもさらに大きな鳥です。この依存関係ツリーがあると想像してみましょう:

myProject/
node\_modules/
lodash # 4.0.0
depA
node\_modules/lodash # 3.0.5
depB
node\_modules/lodash # 3.0.0 <- not cool

ここに、2つの異なるバージョンの[email protected]があります。ただし、depAlodashに指定された semver の範囲によって異なります。およびdepBの場合、重複排除が可能です。現在、dedupe-depsモジュールはこのシナリオに対応していませんが、PRを喜んで受け入れます。