npm workspaces: an introduction
Workspaces provides tooling for managing multiple packages within a single repository. npm’s implementation aids with more efficiently managing shared depedencies, and running scripts across packages.
Setting up
Given a repository npm-workspaces with two packages a and b:
./npm-workspaces
├── package.json
└── packages
├── a
│ └── package.json
└── b
└── package.jsonWorkspaces are defined in the root package.json under the workspaces property:
{
"name": "npm-workspaces",
"workspaces": ["packages/a", "packages/b"]
}Dependencies
Running npm install at the root will search for all dependencies in the multi-package repository to install.
All dependencies are hoisted to the root node_modules unless their versions differ across packages, so dependencies shared by multiple packages are only saved once. Packages are also treated as dependencies, and are automatically symlinked in the root node_modules.
Installing the dependency lodash for a specific workspace a (from anywhere within the repository):
npm i lodash -w aThen generates the following tree:
./npm-workspaces
├── node_modules
│ ├── a -> ../packages/a
│ ├── b -> ../packages/b
│ └── lodash
├── package-lock.json
├── package.json
└── packages
├── a
│ ├── package.json
└── b
└── package.jsonIf the same dependency is shared by package b then the above tree is unchanged and the dependency is still only saved once in the root node_modules, as confirmed below:
/npm-workspaces ❯ npm ls
npm-workspaces@ /npm-workspaces
├─┬ a@1.0.0 -> ./packages/a
│ └── lodash@4.17.21
└─┬ b@1.0.0 -> ./packages/b
└── lodash@4.17.21 dedupedIf a package diverts it’s version of a shared dependency, then it will have its own node_modules within the package. Workspaces will continue to share a single package-lock.json at the root:
./npm-workspaces
├── node_modules
│ ├── a -> ../packages/a
│ ├── b -> ../packages/b
│ ├── lodash
├── package-lock.json
├── package.json
└── packages
├── a
│ ├── node_modules
│ │ └── lodash
│ └── package.json
└── b
└── package.jsonRunning scripts
npm workspaces supports running workspace scripts from anywhere within the repository. Running the script start for workspace a:
npm run start -w=aOr for multiple workspaces:
npm run start -w=a -w=b
npm run start -ws # allHowever, the above will fail if a specified workspace does not support the script start. To ignore workspaces missing the target script:
npm run dev -ws --if-present