よくある質問 (FAQ)
なぜ、 node_modules
フォルダは、パッケージがグローバルストアに保存されている場合にディスク容量を使用するのですか?
pnpm は、 ハードリンク を、グローバルストアからプロジェクトの node_modules
フォルダーに作成します。 ハードリンクは、元の ファイルがあるディスク上の同じ場所を指します。 例えば、1MBを占める foo
を依存に持つプロジェクトでは、そのプロジェクト配下の node_modules
フォルダが、リンク元のグローバルストアと同じ 1MB を消費しているように見えます。 ただし、2つの異なるリンクが示す、占有されている 1MB はディスク上の 同じ位置 にあります。 そのため、全体で foo
は 2MB ではなく 1MB のみ占有しています。
このテーマの詳細について:
Windowsで動作しますか?
簡単な答え:はい。 詳細な答え: Windows 上でシンボリックリンクを使用することに関しては少なくとも問題はありますが、しかし、pnpm には回避策があります。 Windows では、代わりに ジャンクション を使用します。
しかし、ネストされた node_modules
アプローチは Windows と互換性がありますか?
npmの初期のバージョンでは、すべての node_module
をネストするため、問題がありました。(こちらのIssueをご覧ください) しかし、現在 pnpm は依存関係ツリーの構造を作成するのに深いフォルダを作成するのではなく、パッケージをフラットに保存し、それをシンボリックリンクを通じて使用します。
循環シンボリックリンクはどうですか?
pnpm はリンクを使って依存関係を node_modules
フォルダに置きますが、親パッケージは依存関係があるのと同じ node_modules
フォルダに置かれるため、循環するシンボリックリンクは避けられます。 つまり、foo
の依存関係は foo/node_modules
にあるのではなく、foo
は自身の依存関係とともに node_modules
にあるのです。
なぜハードリンクを使うのですか? 直接グローバルストアへのシンボリックリンクを作ってはどうですか?
1つのパッケージは、1台のマシンで異なる依存関係のセットを持ち得ます。
プロジェクト A では foo@1.0.0
の依存関係は bar@1.0.0
に解決されますが、プロジェクト B では foo
の同じ依存関係は bar@1.1.0
に解決するかもしれません。そこで、pnpm は foo@1.0.0
をそれが使われるすべてのプロジェクトにハードリンクし、 そのために異なる依存関係のセットを作成できるようにしています。
Node の --preserve-symlinks
フラグを使えばグローバルストアへの直接のシンボリックリンクも可能ですが、この方法には多くの問題があるので、ハードリンクにこだわることにしました。 この決定の理由については、こちらのIssueをご覧ください。
pnpmは複数のドライブまたはファイルシステムをまたいで機能しますか?
パッケージストアは、インストール先と同じドライブおよびファイルシステム上にある必要があります そうでない場合、パッケージはリンクされずにコピーされます。 これは、あるファイルシステム上のファイルは、他のファイルシステム上の場所を扱うことができないというハードリンクの仕組みによる制限です。 詳細については、Issue #712 を参照してください。
pnpmは、以下の2つの場合で異なる動作をします:
ストアのパスが指定された場合
ストアの設定でストアのパスが指定されると、違うディスクにあるストアとプロジェクトの間ではコピーが発生します。
ディスクA
で pnpm install
を実行する場合、pnpm ストアはディスクA
にある必要があります。 もし pnpm ストアがディスクB
にある場合、すべての必要なパッケージがプロジェクトにリンクされる代わりにコピーされます。 これは、pnpm のストレージとパフォーマンスに関する利点を阻害します。
ストアのパスが指定されていない場合
ストアのパスが指定されていない場合、一つのドライブやファイルシステムごとに、複数のストアが作成されます。
インストールがディスクA
で実行された場合、ストアはA
のファイルシステムのルートの .pnpm-store
に作成されます。 その後、ディスクB
でインストールが実行される場合、 B
の .pnpm-store
に独立したストアが作成されます。 プロジェクトは引き続きpnpmの利点を維持しますが、各ドライブには重複したパッケージが含まれる場合があります。
pnpm
は何の略ですか?
pnpm
は performant npm
の略です。 @rstacruz がこの名前を考案しました。
pnpm
が私のプロジェクトでうまく動きません
ほとんどの場合、これは依存関係のどれかが、package.json
で宣言されていないパッケージを使用していることを意味します。 これはフラットな node_modules
によって引き起される一般的な誤ちの一つです。 これが発生した場合、これは依存パッケージの問題であり、依存パッケージを修正する必要があります。 ただし、これには時間がかかる場合があるため、pnpmには、バグのあるパッケージを機能させるための回避策があります。
解決策1
問題がある場合は、 node-linker=hoisted
設定を使用できます。 この設定をすると、npm
が作るのと同様なフラットな構造のnode_modules
を作ります。
解決策2
以下の例では、ある依存関係が iterall
というモジュールをそのパッケージ自身の依存関係として持っていません。
このようなバグのあるパッケージで依存関係が足りていないことを修正する最も簡単な方法は、 iterall
パッケージをあなたのプロジェクトの package.json
に追加することです。
pnpm add iterall
を実行することであなたのプロジェクトの package.json
に自動で追加され、これを行うことができます。
"dependencies": {
...
"iterall": "^1.2.2",
...
}
解決策3
解決策の一つは、依存パッケージの package.json
に足りていない依存関係を追加するために フック を利用することです。
例として、pnpm
でうまく動作していなかった Webpack Dashboard があります。 その後、 pnpm
で動作するように修正されました。
そのときはこのようなエラーを出していました:
Error: Cannot find module 'babel-traverse'
at /node_modules/inspectpack@2.2.3/node_modules/inspectpack/lib/actions/parse
webpack-dashboard
で使われていた inspectpack
では babel-traverse
が使われていましたが、 babel-traverse
が inspectpack
の package.json
で指定されていなかったことがこの原因です。 これはフラットな node_modules
を生成する npm
や yarn
では動作しました。
これの解決策は次のような .pnpmfile.cjs
を作成することでした:
module.exports = {
hooks: {
readPackage: (pkg) => {
if (pkg.name === "inspectpack") {
pkg.dependencies['babel-traverse'] = '^6.26.0';
}
return pkg;
}
}
};
.pnpmfile.cjs
を作成後、pnpm-lock.yaml
のみを削除してください。pnpmのフックはモジュール解決のみに影響するため、 node_modules
を削除する必要はありません。 そして依存関係を再ビルドすれば、うまく動くようになっているはずです。