PeerDependencies, one of the terms that brought up confusion at least to me when I got a PeerDependency warning in my terminal like the following:
Recent happenings about a malicious code attack in a node package that heavily include the topic of PeerDependencies finally made me that curious about this topic to start some deeper investigation about how PeerDependencies work. In this blog post I'll write down what I found out about NodeJS PeerDependencies in a way that also might help you to better understand this topic.
Searching for "What are peer dependencies"
using Google - of course - returns some results. Nevertheless none of the main references Google returned made me understand PeerDependencies in way I was satisfied with. After some time I found this Stackoverflow Page including a great PeerDependency explanation of Stijn De Witt. His explanation came quite close to a version that made me understand the basics of PeerDependencies and brought up some imaginary "Aha!" moments (Thank you Stijn!). But somehow and as I am more a visual learning type Stijn's "text-driven" Stackoverflow explanation did not bring up that imaginary last-mile satisfaction for me in terms of understanding PeerDependencies. As a result I draw some code around his explanation (you can see quoted below) and suddenly things became more clear to me.
What's the problem?
Upfront: in the upcoming example, JillsModule
will be the tricky part (subsequently the PeerDependency) of the process. That's why I added fictional version appends (@1.0, @2.0) when using it.
Let's say we are building
OurCoolProject
and are usingJacksModule
andJillsModule@2.0
. Also let's suppose thatJacksModule
also depends on JillsModule, but on a different version, sayJillsModule@1.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
(as the code uses 2 different versions, but not related to each other (!)) twice, but that's a small price to pay when we get stable software out of the box.
In code this means something like
// OurCoolProcject.js
import JacksModule from 'jacksmodule';
import JillsModule(@2.0) from 'jillsmodule(@2.0)';
const OurCoolProcject = () => {
// do some stuff with JacksModule
// do some stuff with JillsModule(@2.0). stuff won't break as we have the compatible @2.0 version of JillsModule available in this scope.
}
export default OurCoolProject;
// jacksmodule.js (an npm module)
import JillsModule(@1.0) from 'jillsmodule(@1.0)';
const JacksModule = () => {
// do some stuff with JillsModule(@1.0). stuff won't break as we have the compatible @1.0 version of JillsModule available in this scope.
}
export default JacksModule;
But next this dependency relationship gets more tricky.
Now let's suppose that
JacksModule
exposes its dependency onJillsModule
in some way. It accepts anobject instanceof JillsClass
for example... What happens when we create anew JillsClass
using version 2.0 of the library and pass it along tojacksFunction
(that excepts the 1.0 version as we know!)? All hell will break loose! Simple things likejillsObject instanceof JillsClass
will suddenly returnfalse
, becausejillsObject
is actually an instance of anotherJillsClass
, the 2.0 version.
In code this means something like this:
// OurCoolProcject.js
import jacksFunction from 'jacksmodule';
import JillsModule(@2.0) from 'jillsmodule(@2.0)'; // node resolves to OUR dependency of JillsModule which is 2.0!
const OurCoolProcject = () => {
const jillsObject = new JillsModule(@2.0).JillsClass;
// next the beginning of all evil, we'll pass a jillsObject of version 2.0
// to jacksFunction (that would expect jillsObject of version 1.0 🤦♀️)
jacksFunction(jillsObject);
}
export default OurCoolProject;
// jacksmodule.js (an npm module)
import JillsModule(@1.0) from 'jillsmodule(@1.0)';
const jacksFunction = (jillsObject) => {
// make sure jillsObject is compatible for further usage in this function
const jillsObjectRocks = jillsObject instanceOf JillsModule(@1.0).JillsClass;
// └─> 🔥🔥🔥 `jillsObjectRocks` will be a big, fat FALSE
// as the JillsModule dependencies actively used in this function and
// passed to this function differ in versions (1.0 vs. 2.0) 🤦♀️
...
}
export default jacksFunction;
Do you notice what's going on here? jacksFunction
receives an incompatible jillsObject
as the object was constructed from JillsModule(2.0) and not from JillsModule(1.0) JacksModule
is be compatible with. So far, this only shows the problem that in the worst case, leads to non-working software.
How PeerDependencies solve this problem
Luckily npm has some built-in intelligence trying to solve this. If JacksModule declares JillsModule(@1.0) as a PeerDependency, npm can warn the user about this when installing dependencies of your project. So JacksModule's package.json
should include this declaration:
{
"name": "JacksModule",
...
"peerDependencies": {
"JillsModule": "1.x"
},
...
}
So npm's PeerDepenedency intelligence basically triggers a console output notifying us developers with a warning saying this:
"Hey, this is JacksModule speaking here. Let me tell you: I need this specific package of JillsModule, but I really need the version that is part of my JacksModule project and listed in my package.json file. So please make sure it's installed and make sure it's not some other version of JillsModule you might have installed for your own usage somewhere else in your application."
So in the end - thinking this further - depending on npm packages that require PeerDependencies can be tricky. In case you need a new version of the package X for separated usage in your application this might lead to problems if another dependency you use in your application has a PeerDependency on another version of package X. If this pops up - and in the worst case also leads to problems with your software - you are on your own to decide which package to use or which code maybe needs refactoring to meet all requirements.
I hope those explanations and code examples made sense for you and closed the last thought gap you've had about PeerDependencies. If you've questions or want to suggest some article optimization, feel free to contact me or leave a comment.
This post was originally posted here.
Top comments (17)
Nice article. I got several questions about it.
Hey!
Thx for your questions.
what you've described, is what the maintainer of the react-redux package has already done, see here github.com/reduxjs/react-redux/blo.... That's the reason why the warning is thrown here. The solution to this warning is, that the developer of that project (that uses react-redux) has to add react@0.14.0 on its own as a project dependency.
"... I assume the person who call jacksFunction already know the exact version it needs." > the person who calls jacksFunction might not necessarily know this when installing and initially using the npm package
jacksmodule
. The definition of a peer dependency in jacksmodule's package.json will throw the helping hint that the person should install the proper version of jillsModule.Hope that helped to answer your questions (?).
Thx
I specially signed in to write a comment for this article. Really thank you, man. It's already 2 months I can't really understand and even find a good article related to peerDependencies. One small thing I couldn't understand from StackOverflow answer and from this article is it.
From StackOverflow -
How peer dependencies solve this.
They tell npm
I need this package, but I need the version that is part of the project,
not some version private to my module.
From this article -
"Hey, this is JacksModule speaking here. Let me tell you: I need this
specific package of JillsModule, but I really need the version that is
part of my JacksModule project and listed in my package.json file. So
please make sure it's installed and make sure it's not some other version
of JillsModule you might have installed for your own usage somewhere else
in your application."
Note the difference. You say that
I need this
and StackOverflow saysspecific package of JillsModule, but I really need the version that is
part of my JacksModule project and listed in my package.json file.
I need this package, but I need the version that is part of the project,
not some version private to my module.
Could you please explain it ?
This is pretty cool and awesome.
thank you!
No online resource was clear enough for me to understand peerDependencies except this one. Great Article!
thank you! happy I could help!
Nice article! Thanks!
thx!
Finally found an explanation that makes me feel confortable, thanks
nice, glad it helped you!
Thank you, great overview, it certainly helped clarify things for me. Keep up the great work! :)
glad to read that, thx!
Some comments may only be visible to logged-in visitors. Sign in to view all comments.