Why does, for example, a Grunt plugin define its dependency on grunt as "peer dependencies"?
Why can't the plugin just have Grunt as its own dependency in grunt-plug/node_modules?
Peer dependencies are described here: https://nodejs.org/en/blog/npm/peer-dependencies/
But I don't really get it.
Example
I'm working with AppGyver Steroids at the moment which uses Grunt tasks to build my source files into a /dist/ folder to be served on a local device. I'm quite new at npm and grunt so I want to fully comprehend what is going on.
So far I get this:
[rootfolder]/package.json tells npm it depends on the grunt-steroids
npm package for development:
"devDependencies": {
"grunt-steroids": "0.x"
},
Okay. Running npm install in [rootfolder] detects the dependency and installs grunt-steroids in [rootfolder]/node_modules/grunt-steroids.
Npm then reads [rootfolder]/node_modules/grunt-steroids/package.json so it can install grunt-steroids
own dependencies.:
"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"
},
The "dependencies" packages are installed into [rootfolder]/node_modules/grunt-steroids/node_modules which is logical for me.
The "devDependencies" aren't installed, which I'm sure is controlled by npm detecting I'm just trying to use grunt-steroids
, and not develop on it.
But then we have the "peerDependencies".
These are installed in [rootfolder]/node_modules, and I don't understand why there and not in [rootfolder]/node_modules/grunt-steroids/node_modules so that conflicts with other grunt plugins (or whatever) are avoided?
TL;DR:
peerDependencies
is for dependencies that are exposed to (and expected to be used by) the consuming code, as opposed to "private" dependencies that are not exposed, and are only an implementation detail.The problem peer dependencies solve
npm's module system is hierarchical. One big advantage for simpler scenarios is that when you install an npm package, that package brings its own dependencies with it so it will work out of the box.
But problems arise when:
Example
Let's say you are building
YourCoolProject
and are usingJacksModule 1.0
andJillsModule 2.0
. And let's suppose thatJacksModule
also depends onJillsModule
, but on a different version, say1.0
. As long as those 2 versions don't meet, there is no problem. The fact thatJacksModule
is usingJillsModule
below the surface is just an implementation detail. We are bundlingJillsModule
twice, but that's a small price to pay when we get stable software out of the box.But now let's suppose that
JacksModule
exposes its dependency onJillsModule
in some way. It accepts an objectinstanceof
JillsClass
for example... What happens when we create anew JillsClass
using version2.0
of the library and pass it along tojacksFunction
? All hell will break loose! Simple things likejillsObject instanceof JillsClass
will suddenly returnfalse
, becausejillsObject
is actually an instance of anotherJillsClass
, the2.0
version.How peer dependencies solve this
They tell npm
When npm sees that your package is being installed into a project that does not have that dependency, or that has an incompatible version of it, it will warn the user during the installation process.
When should you use peer dependencies?
Common scenarios are plugins for larger frameworks. Think of things like Gulp, Grunt, Babel, Mocha etc. If you write a Gulp plugin, you want that plugin to work with the same Gulp that the user's project is using, not with your own private version of Gulp.
As the official Blueprint says If you see UNMET PEER DEPENDENCY errors, you should manually install React:
npm install --save react react-dom react-addons-css-transition-group
you could find it here
I would recommend you to read the article again first. It's a bit confusing but the example with winston-mail shows you the answer why:
In this case it is possible to have multiple versions of a package which would cause some issues. The peerDependencies allows npm developers to make sure that the user has the specific module (in the rootfolder). But you're correct with the point that describing one specific version of a package would lead to issues with other packages using other versions. This issue has to do with npm developers, as the articles states
Therefore developers should follow semver for defining peerDependencies. You should open an issue for the grunt-steriods package on github...