プラグインのnpmでピアの依存関係を使用する理由


217

たとえば、Gruntプラグインがgrunt への依存関係を「ピア依存関係」として定義するのはなぜですか?

なぜプラグインはgrunt-plug / node_modulesの独自の依存関係としてGruntを使用できないのですか?

ピアの依存関係については、https//nodejs.org/en/blog/npm/peer-dependencies/で説明しています

しかし、私は本当にそれを理解していません。

現在、Gruntタスクを使用してソースファイルを/ dist /フォルダーにビルドし、ローカルデバイスで提供されるAppGyver Steroidsを使用しています。私はnpmとうなり声で非常に新しいので、何が起こっているのかを完全に理解したいと思います。

これまでのところ私はこれを得ます:

[rootfolder] /package.jsonはnpmにgrunt-steroids、開発がnpmパッケージに依存していることを伝えます。

  "devDependencies": {
    "grunt-steroids": "0.x"
  },

はい。[rootfolder]でnpm installを実行すると、依存関係が検出され、[rootfolder] / node_modules / grunt-steroidsに grunt-steroidsがインストールされます。

次に、Npmは[rootfolder] /node_modules/grunt-steroids/package.jsonを読み取り、grunt-steroids独自の依存関係をインストールできるようにします。

"devDependencies": {
    "grunt-contrib-nodeunit": "0.3.0",
    "grunt": "0.4.4"
  },
"dependencies": {
    "wrench": "1.5.4",
    "chalk": "0.3.0",
    "xml2js": "0.4.1",
    "lodash": "2.4.1"
  },
"peerDependencies": {
    "grunt": "0.4.4",
    "grunt-contrib-copy": "0.5.0",
    "grunt-contrib-clean": "0.5.0",
    "grunt-contrib-concat": "0.4.0",
    "grunt-contrib-coffee": "0.10.1",
    "grunt-contrib-sass": "0.7.3",
    "grunt-extend-config": "0.9.2"
  },

依存関係」パッケージは、[rootfolder] / node_modules / grunt-steroids / node_modulesにインストールされています。

devDependencies」がインストールされていません。これは、使用しようとしているnpm検出によって制御されてgrunt-steroidsおり、開発を行っていません。

しかし、「peerDependencies」があります。

これらは[rootfolder] / node_modulesにインストールされていますが、他のgruntプラグイン(または何でも)との競合が回避されるように、[rootfolder] / node_modules / grunt-steroids / node_modulesにないのはなぜですか?

回答:


420

TL; DR:[1] peerDependenciesは、公開されていない「プライベート」依存関係とは対照的に、消費コードに公開されている(および使用されることが予想される)依存関係用であり、実装の詳細にすぎません。

ピアの依存関係が解決する問題

NPMのモジュールシステムは階層的です。より単純なシナリオの大きな利点の1つは、npmパッケージをインストールすると、そのパッケージに独自の依存関係がもたらされるため、そのまま使用できることです。

ただし、次の場合に問題が発生します。

  • プロジェクトと使用中のモジュールの両方が別のモジュールに依存しています。
  • 3つのモジュールは互いに通信する必要があります。

例では

ビルドYourCoolProjectしていて、JacksModule 1.0との両方を使用しているとしJillsModule 2.0ます。そして、それがにJacksModuleも依存しているとしましょう。これら2つのバージョンが一致しない限り、問題はありません。表面下を使用しているという事実は、実装の詳細にすぎません。私たちは2回バンドルしていますが、箱から出してすぐに安定したソフトウェアを入手する場合、それはわずかな代償です。JillsModule1.0JacksModuleJillsModuleJillsModule

しかし、どうすればJacksModule依存関係JillsModuleが何らかの形で公開されたとしたらどうでしょう。それは例えばのインスタンスを受け入れますJillsClass... ライブラリのnew JillsClass使用バージョンを作成して2.0それに渡すとjacksFunctionどうなりますか?すべての地獄が解けます!のような単純なものは、実際にはがのバージョンのバージョンであるため、jillsObject instanceof JillsClass突然戻ります。falsejillsObject JillsClass2.0

ピアの依存関係がこれを解決する方法

彼らはnpmに伝えます

このパッケージが必要ですが、モジュールのプライベートバージョンではなく、プロジェクトの一部であるバージョンが必要です。

npmは、パッケージが依存関係のないプロジェクトにインストールされている、または互換性のないバージョンのプロジェクトにインストールされていることを検出すると、インストールプロセス中にユーザーに警告します。

ピアの依存関係を使用する必要があるのはいつですか?

  • あなたは他のプロジェクトで使用するライブラリを構築している場合、および
  • このライブラリは、いくつかの他のライブラリを使用している、
  • ユーザーが他のライブラリも操作することを期待/必要とする

一般的なシナリオは、より大きなフレームワーク用のプラグインです。Gulp、Grunt、Babel、Mochaなどのことを考えてみてください。Gulpプラグインを作成する場合、そのプラグインを、自分のプライベートバージョンのGulpではなく、ユーザーのプロジェクトが使用しているのと同じGulpで動作させる必要があります。


注釈

  1. 長すぎる; 読みませんでした。長すぎると思われるテキストの短い要約を示すために使用されます。

2
プラグインを構築しているときに、ピアの依存関係について、パッケージの依存関係の複製が必要ですか?OPの例では、それが"grunt": "0.4.4"devDependenciesとpeerDependenciesの両方にあることがわかります。そこに複製があることは私にとって理にかなっていgruntます。これは、自分が使用するためにそのパッケージが必要であるだけでなく、私のユーザーもライブラリは、peerDependenciesバージョンロックを尊重する限り、独自のバージョンを使用できます。あれは正しいですか?または、OPの例は非常に悪い例ですか?
Vadorequest

4
私は人々がGruntのファンであるGruntプラグインを作成していることを想像できます:)そのため、プラグインのビルドプロセスに自分自身でGruntを使用するのは自然なことだと思います。彼らがそれを作成するために使用するビルドプロセスで?開発依存として追加することで、これを切り離すことができます。基本的に2つのフェーズがあります。ビルド時と実行時です。ビルド時に開発依存関係が必要です。実行時に定期的およびピア依存関係が必要です。もちろん、依存関係の依存関係があると、すべてが速く混乱します:)
Stijn de Witt '27

1
この回答ありがとうございます!明確にするために、あなたの例でJacksModuleは、JillsModule ^1.0.0JillsModuleのピア依存関係に依存しJacksModuleYourCoolProjectいて、を使用JacksModuleしていたJillsModule ^2.0.0場合、NPMからピア依存関係の警告が表示され、JillsModule ^1.0.0同様にインストールするようにアドバイスされます。しかし、その後何が起こりますか?YourCoolProjectこれで、2つのバージョンのJillsModuleインポート可能なものがありimport jillsModule from "..."ますか?そして、使用するときにJacksModuleインスタンスを渡す必要があることをどのように覚えていますJillsModule v1.0.0か?
tonix

1
@tonixさて、バージョンの非互換性があるのは確かに問題でしょう。peerDependenciesはそれを解決しません。しかし、それは問題を明確にするのに役立ちます。2つのバージョンをサイレントモードで使用するのではなく、バージョンの不一致を明確に示すからです。ライブラリを選択しているアプリ開発者は、解決策を見つける必要があります。
Stijn de Witt

2
@tonixまたは3番目のオプション:JacksModuleリポジトリのクローンを作成し、それをアップグレードJillsModule ^2.0.0してプロジェクトメンテナーにPRを提供します。最初に、この依存関係が古くなっていることを示すバグを提出することが役立つ場合があり、それを更新したいと考えています。優れたPRを作成すると、ほとんどのライブラリメンテナがそれをマージして、感謝します。メンテナが応答しない場合は、自分の名前で名前空間が指定されたNPMにフォークを公開し、代わりにフォークを使用できます。いずれにせよ、解決策はありますが、それだけでpeerDependenciesは解決しません。
Stijn de Witt

26

最初にもう一度記事を読むことをお勧めします。少しわかりにくいかもしれませんが、winston-mailの例では、その理由がわかります。

たとえば、それがテストされた最新バージョンであるため、オブジェクトでwinston-mail@0.2.3指定さ"winston": "0.5.x"れた"dependencies"ものを偽装してみましょう。アプリの開発者は、最新かつ最高のものを求めているので、winstonおよびの最新バージョンを調べて、winston-mailpackage.jsonに次のように配置します。

{
  "dependencies": {  
    "winston": "0.6.2",  
    "winston-mail": "0.2.3"  
  }  
}

しかし、今、npm installを実行すると、予期しない依存関係グラフが表示されます

├── winston@0.6.2  
└─┬ winston-mail@0.2.3                
  └── winston@0.5.11

この場合、いくつかの問題を引き起こすパッケージの複数のバージョンが存在する可能性があります。ピアの依存関係により、npm開発者は、ユーザーが特定のモジュール(ルートフォルダー内)を持っていることを確認できます。ただし、パッケージの特定のバージョンを1つ記述すると、他のバージョンを使用する他のパッケージで問題が発生するという点は正しいです。この問題は、記事に記載されているように、npm開発者に関係しています。

アドバイスの1つ:ピアの依存関係の要件は、通常の依存関係の要件とは異なり、寛大なければなりません。ピアの依存関係を特定のパッチバージョンに限定しないでください。

したがって、開発者はpeerDependenciesを定義するためにsemverに従う必要があります。GitHubでgrunt-steroidsパッケージの問題を開く必要があります...


1
あなたはそう言いますが、multiple versions of a package which would cause some issuesそれはパッケージマネージャーの全体のポイントではありませんか?彼らはさらに、同じパッケージの2つのバージョンがプロジェクトにある同じ記事で、これについてさらに説明します。1つは開発者によって提供され、もう1つはサードパーティライブラリによって提供されます。
Adam Beck、

1
ピアの依存関係のポイントは理解していると思いますが、winston例ではwinston-mail、バージョンがピアの依存関係と一致しないため、ライブラリを使用できなくなりましたか?ライブラリをまったく使用できないようにするよりも、1ライブラリの最新かつ最高のものから一時的にダウングレードしたほうがいいです。
Adam Beck、

1
最初のコメントについては、私が理解して使用する限り、テストと関係があります。たとえば、特定のサードパーティパッケージについてテストされたパッケージがある場合、依存関係の変更(バグ修正、主要機能の更新)のうち、パッケージが機能すること。したがって、特定のプラグインバージョンを指定でき、テストとともに保存されます。
Ferから

1
あなたの2番目のコメント:それが彼らがドキュメントで開発者がパッケージの依存関係に寛容でなければならず、例えば「0.2.1」の代わりにsemverを使用すべきであると言う理由です、「〜0.2.1」->は「0.2.x」を許可しますが「0.3.x」または「> = 0.2.1」ではなく、「0.2.x」から「1.x」または「x.2。」までのすべて。...(しかし、〜となるだろうNPMパッケージのため、実際には好ましくない
FERは

15

peerDependencies 可能な最も簡単な例で説明します:

{
  "name": "myPackage",
  "dependencies": {
    "foo": "^4.0.0",
    "react": "^15.0.0"
  }
}


{
  "name": "foo"
  "peerDependencies": {
    "react": "^16.0.0"
  }
}

myPackageでnpm installを実行すると、Reactバージョン^15.0.0foo、Reactとのみ互換性のあるバージョンをインストールしようとするため、エラーがスローされます^16.0.0

peerDependenciesがインストールされていません。


foo内にdepとしてreact 16を入れないのはなぜですか?そうすれば、15と16の両方が利用可能になり、fooは16を使用でき、mypackageは15を使用できますか?
nitinsh99

Reactは実行時にブートストラップされるフレームワークです。React15とReact 16の両方が同じページに存在するためには、両方を同時にブーストラップする必要があります。これは非常に重く、エンドユーザーにとって問題になります。fooReact 15とReact 16の両方で動作する場合、peerDependencyをとしてリストできます>=15 < 17
Jens Bodal

nitinsh99私の答えは、peerDependenciesによってスローされたエラーを取り除く方法ではなく、可能な限り単純な例でpeerDependenciesの目的を説明することでした
Christopher

@ nitinsh99パッケージの依存関係内にリアクションを追加すると、フックのような問題が発生します
Masood
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.