The npm community forum has been discontinued.
To discuss usage of npm, visit the GitHub Support Community.
What is the current state of peer dependencies in npm?
I’ve just built a thing that feels like it needs a peer dependency (for the first time in like 7 years working with node!) but I’m wondering if there is any guidance on if we should be using peer dependancies in 2018?
In the main documentation, it mentions “In the next major version of npm (npm@3)” and that is a little bit out of date now especially because of the overhaul that came in npm@5
Any guidance would be appreciated
In the post npm@3 world:
peerDependencies are assertions that result in warnings if they fail. So a pretty soft assertion. They instruct folks to install the missing peer dep, but make no attempt to install it themselves.
There is one lingering issue relating to them, as this assertion framework is a little brittle: If your applciation depends on library A which depends in turn depends on B and C. And C has a peer dep on B. It’s possible for B to get installed under A, but C installed at top level, resulting in an unresolved peer dep when it otherwise should be resolved.
I have some thoughts on how we can address this, but it will require some deep changes to how we resolve module trees and so is scheduled for post npm@7 (so, end of this year, probably).
In the meantime, authors of module A can ensure this doesn’t happen by shipping an
npm-shrinkwrap.json or by bundling B and C.
One problem I have with peerDependencies that sometimes you want to surpress warnings about peerDependencies. The effects of this are:
- In a large team the warnings that need to be ignored create confusion and people learn to ignore them completely, even though some are valid. For example, bootstrap has a peerDependency on jquery, but if I never use the JS part of bootstrap, the warning is misleading.
- Some packages start not using / removing peerDependencies just because the dependency is not always required and the warning annoys some users. The result is that there is no way to statically verify the version constraint anymore (e.g. for a tool that does automatic dependency update PRs like Renovate). The version check is sometimes done at runtime, meaning the feedback time is longer (you won’t find out until you run the thing and hit that codepath).
- An example of a package that does not use peerDependencies because most warnings would be irrelevant is Sequelize, which also has felt the pain of losing the ability to define version ranges because of that (new major versions of DB adapters often require work first to make Sequelize compatible, in the meantime people by accident install the latest version and file countless issues).
- An example of a package that removed a peerDependency because downstream consumers were annoyed is sass-loader, and the removal was extremely controversial.
Now all these examples could be solved if all the packages were architected in a different way, but unfortunately it’s not the reality.
An example of a package that does not use peerDependencies because most warnings would be irrelevant is Sequelize
If the DB adapters had peerDeps on Sequelize, why would there be extraneous warnings? Do folks use the DB adapters independently of Sequalize?
It’s the other way around, Sequelize needs one of the low-level DB adapters to be installed (e.g. pg, mysql2, …). Yes, they are not Sequelize-specific. If Sequelize had peerDependencies on all of these, you would only ever satisfy one of them.
Now as said, this could be solved if Sequelize had independent packages that acted as a bridge between Sequelize and the adapters, and you provide an instance of one of these bridges to the core. But that would entail a lot of refactoring, problems with versioning and testing, etc that the Sequelize team chose not to deal with. This is the unfortunate reality with many packages.
Ah, ok, gotcha. I don’t think peerDependencies are an appropriate tool for that class of problems. The one thing their good for is letting plugins depend on the version of the thing they plug into.
It does point to a need for another kind of assertion or constraint, but I don’t think that it should be
It may be that we need some kind of more general assertion system. Relatedly: folks would like some way to declare something only working as a singleton.
I think I need to see this as a graph to follow But essentially you’re talking about deep peer dependencies (not quite the same as the diamond require issue but I put it in the same mental bucket).
My particular use case is very “top level” and I’m pretty sure this won’t be a problem.
My take away is this: It’s cool to use peer dependencies now but just be aware of what you’re doing.
I was going to ask if we should update the documentation but it sounds like the overhaul is in the roadmap and I assume it will get updated then
Thanks for the help folks
We absolutely should update the docs. You’re right though, left to our own devices we likely won’t do so till an overhaul. I would happily take a PR though!
And as you say, it is on the roadmap, albeit a bit in passing:
Two-pass installer where resolution of the logical tree is distinct from computing the physical tree. With this we may be able to, at long last, end the pain around peerDependencies.