NPM Workspaces installing packages into a workspace/node_modules

Viewed 141

I have workspaces configured and working.

When I install from the root, sometimes packages are installed in the root and sometimes they are installed in the workspace node_modules.

When and how does NPM choose to install a package at the root/node_modules vs a workspace/node_modules?

1 Answers

It turns out, when two packages rely on the same module with a compatible version number in the dependencies, npm (with workspaces) will install the package only in root/node_modules. Otherwise, it will install in the node_modules of package(s).

In this example the same react-dom version is shared between packages:

customer-ui@1.0.0 /home/ubuntu/name/customer-ui
├─┬ components@0.15.0 -> ./packages/components
│ ├─┬ react-cosmos@5.7.2
│ │ └─┬ react-cosmos-shared2@5.7.1
│ │   └─┬ react-element-to-jsx-string@14.3.4
│ │     └── react-dom@17.0.2 deduped
│ ├── react-dom@17.0.2
│ ├─┬ react-modal@3.15.1
│ │ └── react-dom@17.0.2 deduped
│ ├─┬ react-to-print@2.14.7
│ │ └── react-dom@17.0.2 deduped
│ └─┬ styled-components@5.3.5
│   └── react-dom@17.0.2 deduped
└─┬ embedded@0.1.0 -> ./packages/embedded
  └── react-dom@17.0.2 deduped

And npm installed it only in the root/node_modules dir:

❯ ls node_modules/react-dom
LICENSE    build-info.json  index.js      profiling.js       server.js       test-utils.js
README.md  cjs              package.json  server.browser.js  server.node.js  umd

❯ ls packages/embedded/node_modules/react-dom
ls: cannot access 'packages/embedded/node_modules/react-dom': No such file or directory

❯ ls packages/components/node_modules/react-dom
ls: cannot access 'packages/components/node_modules/react-dom': No such file or directory

However, for a package with incompatible version between packages, it's different:

❯ npm ls argparse
customer-ui@1.0.0 /home/ubuntu/name/customer-ui
├─┬ @components@0.15.0 -> ./packages/components
│ └─┬ eslint@8.17.0
│   └─┬ js-yaml@4.1.0
│     └── argparse@2.0.1
└─┬ embedded@0.1.0 -> ./packages/embedded
  ├─┬ eslint@6.8.0
  │ └─┬ js-yaml@3.14.1
  │   └── argparse@1.0.10
  ├─┬ jest-preset-preact@4.0.5
  │ └─┬ babel-jest@27.5.1
  │   └─┬ babel-plugin-istanbul@6.1.1
  │     └─┬ @istanbuljs/load-nyc-config@1.1.0
  │       └─┬ js-yaml@3.14.1
  │         └── argparse@1.0.10
  └─┬ microbundle@0.12.4
    ├─┬ cssnano@4.1.11
    │ ├─┬ cosmiconfig@5.2.1
    │ │ └─┬ js-yaml@3.14.1
    │ │   └── argparse@1.0.10
    │ └─┬ cssnano-preset-default@4.0.8
    │   └─┬ postcss-svgo@4.0.3
    │     └─┬ svgo@1.3.2
    │       └─┬ js-yaml@3.14.1
    │         └── argparse@1.0.10
    └─┬ rollup-plugin-postcss@2.9.0
      └─┬ postcss-load-config@2.1.2
        └─┬ cosmiconfig@5.2.1
          └─┬ js-yaml@3.14.1
            └── argparse@1.0.10

embedded package demands argparse@1.0.10 and embedded demands argparse@2.0.1.

For these, npm installs them in multiple places:

❯ ls node_modules/argparse
CHANGELOG.md  LICENSE  README.md  argparse.js  lib  package.json

❯ ls packages/components/argparse
ls: cannot access 'packages/components/argparse': No such file or directory

❯ ls packages/embedded/node_modules/argparse
CHANGELOG.md  LICENSE  README.md  index.js  lib  package.json

In case it helps, this is a snippet of root/package.json:

{
  "name": "customer-ui",
  "version": "1.0.0",
  "workspaces": [
    "packages/*"
  ]
}

Thanks to @Stf_F for answering this in the comment above.

Related