Flat node_modules is not the only way
Tags: pnpm, npm, javascript, node
Published: 2018-06-24 18:24:17

---
title: Flat node_modules is not the only way
published: true
description: How pnpm's node_modules works
tags: pnpm, npm, javascript, node
---

**This article covers an old version of pnpm. For an updated version of the article [go here](https://pnpm.js.org/blog/2020/05/27/flat-node-modules-is-not-the-only-way).**

New users of [pnpm](https://pnpm.js.org/) frequently ask me about the weird structure of `node_modules` that pnpm creates. Why is it not flat? Where are all the sub-dependencies?

> I am going to assume that readers of the article are already familiar with flat `node_modules` created by npm and Yarn. If you don't understand why npm 3 had to start using flat `node_modules` in v3, you can find some prehistory in [Why should we use pnpm?](https://www.kochan.io/nodejs/why-should-we-use-pnpm.html).

So why is pnpm's `node_modules` unusual? Let's create two directories and run `npm install express` in one of them and `pnpm install express` in the other one. Here's the top of what you get in the first directory's `node_modules`:

![](https://thepracticaldev.s3.amazonaws.com/i/4iw9vko9g7l5mhcwrfj1.png)

You can see the whole directory [here](https://github.com/zkochan/comparing-node-modules/tree/master/npm-example/node_modules).

And this is what you get in the `node_modules` created by pnpm:

![pnpm node_modules](https://thepracticaldev.s3.amazonaws.com/i/3n9yg9ead1tfcfxugrgd.png)

You can check it [here](https://github.com/zkochan/comparing-node-modules/tree/master/pnpm-example/node_modules).

So where are all the dependencies? There is only one folder in the `node_modules` called `.registry.npmjs.org` and a symlink called `express`. Well, we installed only `express`, so that is the only package that your application has to have access to 

> Read more about why pnpm's strictness is a good thing [here](https://medium.com/pnpm/pnpms-strictness-helps-to-avoid-silly-bugs-9a15fb306308)

Let's see what is inside `express`:

![](https://thepracticaldev.s3.amazonaws.com/i/8pf5cwjo8ndr7xzxu0x7.png)

`express` has no `node_modules`? Where are all the dependencies of `express`?

The trick is that `express` is just a symlink. When Node.js resolves dependencies, it uses their real locations, so it does not preserve symlinks. But where is the real location of `express`, you might ask?

Here: [node_modules/.registry.npmjs.org/express/4.16.3/node_modules/express](https://github.com/zkochan/comparing-node-modules/tree/master/pnpm-example/node_modules/.registry.npmjs.org/express/4.16.3/node_modules/express).

OK, so now we know the purpose of the `.registry.npmjs.org/` folder. `.registry.npmjs.org/` stores all the packages in a flat folder structure, so every package can be found in a folder named by this pattern:

```
.registry.npmjs.org/<name>/<version>/node_modules/<name>
```

This flat structure avoids the long path issues that were caused by the nested `node_modules` created by npm v2 but keeps packages isolated unlike the flat `node_modules` created by npm v3,4,5,6.

Now let's look into the real location of `express`:

![](https://thepracticaldev.s3.amazonaws.com/i/53zr83hig44wti1zpwy8.png)

Is it a scam? It still lacks `node_modules`! The second trick of pnpm's `node_modules` structure is that the dependencies of packages are on the same directory level on which the real location of the dependent package. So dependencies of `express` are not in `/express/4.16.4/node_modules/express/node_modules/` but in [/express/4.16.4/node_modules/](https://github.com/zkochan/comparing-node-modules/tree/master/pnpm-example/node_modules/.registry.npmjs.org/express/4.16.3/node_modules):

![](https://thepracticaldev.s3.amazonaws.com/i/09a98isedcqvhciwk2z7.png)

All the dependencies of `express` are symlinks to appropriate directories in `node_modules/.registry.npmjs.org/`. Placing dependencies of `express` one level up allows avoiding circular symlinks.

So as you can see, even though pnpm's `node_modules` structure seems unusual at first

1. it is completely Node.js compatible
2. packages are nicely grouped with their dependencies

The structure is a little bit [more complex](https://pnpm.js.org/docs/en/how-peers-are-resolved.html) for packages with peer dependencies but the idea is the same: using symlinks to create a nesting with a flat directory structure.

***

If you'd like to try out [pnpm](https://github.com/pnpm/pnpm/), you can easily install it with npm: `npm i -g pnpm`. Then just run it instead of npm when you need to install something: `pnpm install foo bar`.