When we install packages using npm i <package> --save-dev
, we are specifically telling npm to install our package as a development dependency.:
- This adds our package to the
devDependencies
section in thepackage.json
file. - Usage: This means the package is intended only for development purposes, not for production builds. When deploying the application to production (with
npm install --production
or similar), packages listed underdevDependencies
won't be installed. - Example: Cypress, as a testing framework, is usually not needed in production environments, so it makes sense to include it in the
devDependencies
section.
- No parameter (
npm i <package>
): This installs our package and adds it to thedependencies
section inpackage.json
, meaning it will be installed in both development and production environments. This is less ideal, for example, for a testing tool like Cypress. --global
(npm i -g <package>
): This installs our package globally on our system, making it accessible from any project without having to install it locally within a project folder. It doesn't modify thepackage.json
and won’t be linked to the specific project.
-
Yarn: Similar to npm, Yarn supports:
yarn add <package> --dev
(equivalent to--save-dev
in npm).yarn add <package>
to add Cypress to production dependencies.yarn global add <package>
for global installation.
-
pnpm: Another package manager with similar options:
pnpm add <package> --save-dev
for development dependencies.pnpm add <package>
for production.pnpm add -g <package>
for global installation.
In summary, the --save-dev
parameter ensures our package is installed only as a development dependency, which is typical, for example, for testing tools such as Cypress. Other package managers like Yarn and pnpm offer nearly identical functionality for specifying where a package should be installed (production vs. development).
Other package managers such as Yarn, pnpm, Bun, or Rush offer features or options for package installation that npm doesn't provide. The following comparison focuses on unique offerings of each package manager:
Yarn has some features and flags that differ from npm, giving developers more control or improved workflows:
-
Offline cache: Yarn can install packages without needing to hit the network if they have been installed before. This is not natively supported by npm, though npm has caching mechanisms (like
npm ci
for a clean install using thepackage-lock.json
), but it doesn’t work entirely offline.- Example: If we install Cypress once, Yarn caches it locally, and we can reinstall it offline using the cache without needing internet access.
-
Workspaces: Yarn has native support for workspaces, making it easy to manage monorepos (projects with multiple packages) and share dependencies between them. npm added support for workspaces later, but Yarn’s workspaces are considered more mature and widely used.
-
Selective versions: Yarn allows installing a specific version of a package using the
resolutions
field inpackage.json
. This feature is particularly useful in scenarios where we want to force a particular subdependency version across the project. npm lacks an easy native mechanism for this, thoughnpm-force-resolutions
can be used for similar functionality. When we talk about forcing a specific subdependency version across the project in Yarn, we're referring to any package that is a dependency of another package (i.e., not a direct dependency listed inpackage.json
but a nested dependency that comes from another package). Yarn offers a feature calledresolutions
, which allows us to force a specific version of a subdependency, even if it conflicts with what the package’s author specified. This is useful in scenarios where we are using a package (e.g., Cypress) that depends on a specific version of another package (let's say a package likechalk
), but we want to override that subdependency with a different version (perhaps due to a bug in the version that the parent package requests). In this case, the technology whose version we want to force is the subdependency package itself (likechalk
in this example). So, the focus is on a nested package in the dependency tree rather than a package we directly install.
{
"resolutions": {
"chalk": "4.1.0"
}
}
This forces every version of chalk
(even if it's required by other dependencies) to resolve to version 4.1.0
.
-
Zero-installs: This allows projects to be distributed with their
node_modules
folder already included via source control. When the project is cloned, noyarn install
is necessary, unlike npm which requires a clean installation. Yarn's Zero-installs means that we store all the project dependencies (in thenode_modules
folder) directly within the repository, alongside the Yarn cache files. This allows the project to be ready to run without needing to executeyarn install
after cloning. Let’s address how this interacts with the build and deployment process:-
Excluding
node_modules
in the build: Yes, when using Yarn Zero-installs, you can still exclude thenode_modules
folder from the final build (for example, when preparing a production deployment). Thenode_modules
folder is used for development and testing, but when building the final app, you would typically bundle the code (e.g., using Webpack, Rollup, etc.), and thenode_modules
would not be included in the distributed build. -
Is
node_modules
included in the repository?:- Yes: In the Zero-installs pattern,
node_modules
and the.yarn/cache
files are stored in the repo itself. This makes it so that after cloning the repository, we don’t need to runyarn install
to have the dependencies available.
- Yes: In the Zero-installs pattern,
-
Is
node_modules
included in the final app distributed?:- No: Typically, the
node_modules
folder is not included in the build artifact that is distributed. The final build might include only the necessary bundled code. For example, in a Nuxt3 project, we might use Webpack or Vite to bundle everything into a few files, and these are the files we would deploy to production. Thenode_modules
would stay out of the deployment or build artifact.
- No: Typically, the
-
- Subdependency version forcing: Refers to forcing the version of a nested package (subdependency) across the entire project via Yarn’s
resolutions
feature. - Zero-installs: The
node_modules
folder is included in the repo for development purposes, but it can (and typically should) be excluded from the final build artifact when distributing the app. This keeps the build lean and focused only on the bundled code required to run the app.
pnpm is known for its disk space efficiency and some unique behaviors:
-
Symlinked node_modules: Unlike npm or Yarn, pnpm creates a global store of packages and symlinks dependencies to the project folder, saving disk space and improving installation speed. This approach differs fundamentally from npm, where each project has a fully installed
node_modules
directory.- For example, if multiple projects depend on Cypress, pnpm uses symlinks to avoid multiple copies of the same package, reducing redundancy.
-
Strictness: pnpm is more strict in enforcing dependency rules. For example, it ensures that packages can only access dependencies they explicitly declare, preventing accidental dependency hoisting issues seen in npm or Yarn.
-
Fast install via hard linking: Like Yarn’s offline mode, pnpm also caches packages globally but installs them much faster due to hard links from the global store rather than duplicating files. This allows quick installations across projects that share dependencies.
-
Bun (an emerging package manager):
- Native performance: Bun is built in a highly optimized environment (Zig), offering much faster package installations than npm, Yarn, or pnpm.
- Out-of-the-box support for TypeScript: Bun also provides a built-in runtime for TypeScript and other modern languages, which can reduce the complexity of setting up and configuring builds compared to npm.
-
Rush: Focuses on large monorepos and dependency management in large-scale projects. It provides enhanced monorepo support, which can be more powerful than npm or even Yarn Workspaces for enterprise-level projects.
- Yarn offers offline caching, more mature workspace support, and features like selective version resolution.
- pnpm stands out for its efficient disk usage, symlinked node_modules, and strict dependency resolution.
- npm has caught up in areas like workspaces but lacks some of the fine-tuning features offered by Yarn and pnpm.
In short, Yarn and pnpm provide features like more efficient installations (pnpm), better monorepo management (Yarn), and performance improvements (pnpm, Bun), which npm lacks or implements differently.