pnpm's strictness helps to avoid silly bugs
2017-05-14 18:30:00 +0200
When using npm, you have access to packages that you don't reference in your project's
This is possible because npm creates a flat
Let me explain this by an example.
- create an empty project:
mkdir project && npm init -y
npm install express --save
- go to the
node_modulesfolder that was created
You'll get an output similar to this one:
accepts array-flatten content-disposition content-type cookie cookie-signature debug depd destroy ee-first encodeurl escape-html etag express finalhandler forwarded fresh http-errors inherits ipaddr.js media-typer merge-descriptors methods mime mime-db mime-types ms negotiator on-finished parseurl path-to-regexp proxy-addr qs range-parser send serve-static setprototypeof statuses type-is unpipe utils-merge vary
As you can see, even though you installed only
express, a lot more packages are available in
node_modules folder. Node.js doesn't care what is in your
you will have access to all the packages that are in the root of
node_modules. So you can start
using all those packages without even installing them explicitly.
Let's say you need
debug in your project. You can start using it and forget to add it to
Your project will work fine. You will commit your changes and publish it to production. It will work well
even in production. However, after a few weeks or months, one of two things can very likely happen:
- a new major version of
debugwith a new API can get published. Your code was written for
debug@1and it needs some updates to work with
debug@2. You are safe till
debug@1is installed to the root of
expresscan update its dependency in any moment, fix all usages of
debugand publish a new patch (a patch, because
expressdidn't have any breaking changes). Next time you do
expresswill be updated and
debug@2installed into the root of your
node_modules. The project will break, even though you did not make any changes!
- another possibility is that
debug. It can just remove it from dependencies and publish a new version. When you'll update
express, your code will break because the
debugpackage won't be in
Now imagine that your project is not a web app but a package used by many other people. If you don't
include a package used in code into the
package.json, your package is a timed bomb. Do you remember left-pad? I can imagine a similar catastrophe
happening in the future because of a forgotten dependency.
How does pnpm help with avoiding these type of bugs?
Unlike npm, pnpm does not try to move everything to the root of
node_modules. If you remove the
folder created by npm in the previous example and run
pnpm install express, you'll see that
ls -1 will print
only one symlink in the
On next run, your app will immediately fail, because it won't be able to find
debug. Which is the way it should be!
To fix the project, you'll just run
pnpm install debug and
debug will be added to your
package.json (pnpm saves packages
package.json by default, even when the
--save parameter is not passed). With
debug in your
can be sure that it will always be installed into
node_modules and it will work with your code as expected.
pnpm gets a lot of issues opened because some packages/toolings don't work.
These issues are mostly happening because the packages/toolings have
package.jsons that miss dependencies.
Some developers even think that it is fine to not include dependencies in
because "it works with npm/yarn". It is not OK! It might work today but it will break tomorrow.
You might not use pnpm. But please, publish valid packages. If you don't use pnpm, use some tooling like dependency-check to validate your package before publishing it to the registry.
npm’s global style installation
A month later after publishing this article, I have found out that it is possible to avoid these kind of bugs with npm as well! It is not the default behavior but npm can install
node_modules in a different layout, in which
Only your direct dependencies will show in node_modules and everything they depend on will be flattened in their node_modules folders.
To achieve this, just run npm with the --global-style option or change its value in the configs via
npm c set global-style true.
Do you want to give pnpm a try?
Just install pnpm via npm:
npm install -g pnpm. And use it instead of npm whenever you want to install something:
pnpm i foo.