npmではなくpnpmを使う理由¶
このリポジトリは pnpm を前提 に設計されています。npm はサポート対象外です。
結論を先に言うと、プラグインシステムを成立させるために「hoisting問題を避ける必要がある」ので、pnpm を採用しています。
このリポジトリが求める前提¶
プラグインは 独立パッケージとして運用できること が前提です。具体的には以下を満たす必要があります。
- プラグインごとの依存関係が明示され、未宣言依存が混入しない
- プラグイン単位で依存を更新し、影響範囲を局所化できる
workspace:*による内部参照が確実に解決される- CI とローカルで依存解決が一致し、再現性がある
npmのhoistingがプラグインシステムと相性が悪い理由¶
npm(および Yarn v1)は依存関係を できるだけ上位に持ち上げる(hoist) 方針です。
これにより次の問題が起きやすくなります。
1) phantom dependencies(幽霊依存)¶
本来は依存として宣言していないパッケージが、
hoist によって 偶然見えてしまう 状態が発生します。
- プラグインAが
lodashを依存に書いていない - 別のプラグインBが
lodashを依存している - hoist によりルートに
node_modules/lodashができる - プラグインAから
lodashが import できてしまう
結果として「プラグインAは依存を宣言していないのに動く」状態になり、
リポジトリ内では動くが、単体で配布すると壊れるという事態を生みます。
2) プラグイン単位の更新が局所化できない¶
hoist されると、依存の実体がワークスペース全体で共有されがちです。
そのため、プラグインAの更新がB/Cにも影響しやすくなります。
さらに npm の lockfile は ワークスペース全体で解決されるため、 「Aだけ上げたつもり」が、結果として 全体アップデートに近い挙動になりがちです。
これが、プラグインシステムで避けたい「境界の崩壊」です。
pnpmが提供する解決策¶
pnpmは依存を content-addressable store に一度だけ保存し、
各パッケージの node_modules には 必要なものだけをシンボリックリンクで見せます。
これにより以下が成立します。
- 依存を宣言していないパッケージは 見えない
- プラグイン単位の依存更新がしやすい
- 依存境界が明確になり、配布・互換性検証が安全になる
- 依存解決が固定され、CI とローカルの一致が保たれる
プラグインシステムの前提を満たすには、依存境界が厳密であることが必須であり、
pnpm はそれを実現できるため採用しています。
このリポジトリでpnpmが必須な理由(運用面)¶
技術的な理由に加えて、以下の運用上の事情もあります。
pnpm-workspace.yamlにplugins/*を含めて運用しているpnpm --filterやpnpm -rなど pnpm固有のコマンドをスクリプトで使用しているpnpm-lock.yamlとpackageManagerにより 解決結果とツールバージョンを固定している
npm を使うと、これらが機能しない、または挙動が変わります。
npmを使うと起きやすい問題(まとめ)¶
- pnpm前提のスクリプトが動かない
package-lock.jsonが生成され、依存解決が分岐する- hoisting により 環境差分・幽霊依存が発生しやすい
- プラグイン単位の互換性検証が難しくなる
推奨手順¶
corepack が利用できない場合は、pnpm を別途インストールしてから pnpm install を実行してください。
代替ツールについて¶
Yarn v2/v3 や Bun でも hoisting 問題を避ける構成は可能ですが、
このリポジトリの 運用スクリプト・ロックファイル・ワークスペース設定は pnpm を前提にしています。
そのため、サポート対象は pnpm のみです。