There are three major players in the package manager space:
npm
Yarn
High-Performance npm (pnpm)
We actually have basically similar functionality implemented in all package managers, so you will most likely decide which one to use based on non-functional requirements Package manager, such as installation speed, storage consumption or practicality.
Of course, the way you choose to use each package manager will be different, but they all have basically consistent concepts. All of the above package managers can perform the following commands:
However, despite this, package managers are different under the hood. Traditionally npm
and Yarn
installed dependencies in a flat node_modules folder. (Pay attention to the order here, yarn
is tiled first, and npm
was recursive before). But tiling can also cause a series of safety issues.
Dependence structure uncertainty .
The flattening algorithm itself is very complex and time-consuming.
Packages with declared dependencies
can still be illegally accessed in the project.
Therefore, pnpm
has introduced some new concepts in the node_modules
folder to store dependencies more efficiently. Yarn Berry
even goes further by abandoning the (PnP) mode of node_modules
entirely (more on this in another article).
The earliest released package manager was npm
, back in January 2010. It established the core principles of how package managers work today. But since npm
has been around for over 10 years, why is there any other option? Here are some key reasons why this might be the case:
node_modules
folder structure has different dependency resolution algorithms (nested & tiled, node_modules
vs. PnP mode) andhoisting
).locking
formats (different performance, such as the one written by yarn
itself)workspaces
) which affects the maintainability and speed of monorepos
Let’s dive into the history of how these aspects were determined after the rise of npm
, how Yarn Classic
solved some of these problems, how pnpm
extended these concepts, and how Yarn Berry
, as the successor to Yarn Classic
breaks these Traditional concepts and processes.
npm
is the originator of package managers. Many people mistakenly believe that npm
is an acronym for "Node package manager", but this is not the case.
Its release constituted a revolution because before, project dependencies were downloaded and managed manually. npm
introduced things like files and their metadata fields, storing dependencies in node_modules
, custom scripts, public and private packages, and more.
In 2020, GitHub acquired npm, so in principle npm
is now managed by Microsoft. At the time of writing, the latest major version is v8, released in October 2021.
In October 2016, Facebook announced a partnership with Google and a number of other companies to develop a new package manager (engineering.fb.com/2016/10/11/…) to address the then-existing consistency of npm , security and performance issues. They named the replacement Yarn.
Although Yarn
is still architected and designed based on many concepts and processes of npm
, Yarn
has had a significant impact on the package manager field. Compared to npm
, Yarn
parallelizes operations to speed up the installation process, which has been a major pain point with earlier versions of npm
.
Yarn
set higher standards for reading and writing, security and performance, and invented many concepts (later npm
also made many improvements for this), including:
monorepo
supportsLocking
)Yarn v1 in Entering maintenance mode in 2020. Since then, the v1.x series is considered legacy and renamed Yarn Classic
. Its successor Yarn v2 (Berry) is now the more active development branch.
pnpm
Version 1 of pnpm
was released in 2017 by Zoltan Kochan. It's a replacement for npm
, so if you have an npm
project, you can use pnpm
right away!
The main reason pnpm
was created is that npm
and Yarn
are very redundant in terms of dependency storage structures used across projects. Although Yarn Classic
has a speed advantage over npm
, it uses the same dependency resolution method that pnpm
does not: npm
and Yarn Classic
use hoisting
to flatten their node_modules
.
Instead of optimizing the previous structure, pnpm
introduces Another dependency resolution strategy is proposed: a content-addressed storage structure. The node_modules
folder generated by this method actually relies on the ~/.pnpm-store/
directory that is globally stored on the main folder. Each version of dependencies is physically stored once in this directory folder, forming a single source address to save considerable disk space.
The node_modules
structure is a nested structure of dependencies created by using symlinks
(where each file/package within a folder is stored through a hard link). The following figure from the official documentation illustrates this. (Pictures to be filled: soft and hard links)
pnpm
's influence is visible in the 2021 report: Because of their innovations in content-addressable storage, competitors are looking to adopt pnpm
concepts, such as the structure of symbolic links and efficient disk management of packages.
Yarn 2 was released in January 2020 and was billed as a major upgrade to the original Yarn
. The Yarn
team calls it Yarn Berry
to make it more obvious that it is essentially a new package manager with a new code base and new principles.
Yarn Berry
's main innovation is its plug-and-play (PnP) approach as a strategy for repairing node_modules. Instead of the strategy of generating node_modules
, generate a file .pnp.cjs
with a dependency lookup table, which allows for more efficient handling of dependencies since it is a single file rather than a nested folder structure. In addition, each package is stored in a folder in the form of a zip file instead of .yarn/cache/
, and takes up less disk space than node_modules
.
All of these changes happened so quickly that they caused a lot of controversy after they were released. Such breaking breaking changes to PnP require maintainers to update their existing packages to be compatible with it. Using the new PnP approach by default and reverting to node_modules
was not straightforward initially, which led to many well-known developers not considering it and publicly criticizing Yarn 2.
Since then, the Yarn Berry
team has addressed many issues in its subsequent releases. To resolve PnP incompatibility issues, the team has provided a way to easily change the default operating mode. With the help of the node_modules plugin, switching back to the traditional node_modules
approach only requires one line of configuration.
Additionally, the JavaScript ecosystem has provided increasing support for PnP over time, and as you can see in this compatibility table, some large projects have begun adopting Yarn Berry
.
Even though Yarn Berry
is still young, it's already making an impact on the package manager space - pnpm
adopted the PnP approach in late 2020.
The package manager must first be installed on each developer's local and CI/CD systems.
npm
ships with Node.js
, so no extra steps are required. In addition to downloading the Node.js installer for your operating system, it has become a common practice to use CLI tools to manage software versions. In the context of Node, Node Version Manager (nvm) or Volta has become a very convenient utility.
You can install Yarn 1 in different ways, for example, as an npm
package: .$ npm i -g yarn
To migrate from Yarn Classic to Yarn Berry, the recommended method is:
Install or update Yarn Classic
to
version
use the command
yarn set version berry.
However, the recommended way to install Yarn Berry here is through Corepack.
Corepack was created by the developers of Yarn Berry. The initiative was originally named Package Manager Manager (pmm) and was merged with Node in LTS v16.
With the help of Corepack, Node is an alternative package manager to npm
that you don’t have to install “separately” since it includes Yarn Classic
, Yarn Berry
, and pnpm
binaries. These shims allow users to run Yarn and pnpm commands without explicitly installing them first and without messing up the Node distribution.
Corepack comes pre-installed with Node.js ≥ v16.9.0. However, for older Node versions, you can use ⬇️
npm install -g corepack
to enable Corepack before using it. This example shows how to activate it in Yarn Berry v3.1.1.
# you need to opt-in first $ corepack enable # shim installed but concrete version needs to be activated $ corepack prepare [email protected] --activate
You can install pnpm
as an npm
package: $ npm i -g pnpm
. You can also install pnpm using Corepack:
$ corepack prepare [email protected] --activate
In this section you will see at a glance the main features of different package managers. You can easily discover which files are involved in configuring a specific package manager and which files are generated by the installation step.
All package managers store all important meta-information in the project manifest package.json file. Additionally, root-level configuration files can be used to set up different private packages or different dependency resolution configurations.
During the installation step, dependencies
are stored in a file structure such as node_modules
and a locking
file is generated. This section does not consider workspace settings, so all examples only show a single location where dependencies are stored.
using $ npm install
or the shorter $ npm i
will generate a package-lock.json
file and a node_modules
folder. There are also configurable files like .npmrc
that can be placed in the root level directory. See the next section for more information on locking
files.
. ├── node_modules/ ├── .npmrc ├── package-lock.json └── package.json
running $ yarn
will create a yarn.lock
file and a node_modules
folder. .yarnrc
files can also be configuration options, and Yarn Classic
also supports .npmrc
files. Alternatively you can use the cache folder .yarn/cache/
and the latest Yarn Classic
version stored locally in .yarn/releases/
.
. ├── .yarn/ │ ├── cache/ │ └── releases/ │ └── yarn-1.22.17.cjs ├── node_modules/ ├── .yarnrc ├── package.json └── yarn.lock
Because of this special installation mode, you have to deal with more files and folders in your Yarn Berry
project than with other package managers. Some are optional and some are mandatory.
Yarn Berry
no longer supports .npmrc
or .yarnrc
; it requires a .yarnrc.yml. For the traditional workflow of generating node_modules
folders, you must provide nodeLinker
configuration to use node_modules
or pnpm
configuration (I don’t understand this part).
# .yarnrc.yml nodeLinker: node-modules # or pnpm
running $ yarn
will install all dependencies in a node_modules
folder. And a yarn.lock
file is generated, which is newer but not compatible with Yarn Classic
. In addition, a .yarn/cache/
folder is generated for offline installation. This folder is optional and is used to store the version of Yarn Berry
used by the project.
. ├── .yarn/ │ ├── cache/ │ └── releases/ │ └── yarn-3.1.1.cjs ├── node_modules/ ├── .yarnrc.yml ├── package.json └── yarn.lock
Whether it is strict mode or loose mode for PnP, executing $ yarn
with .pnp.cjs
and yarn.lock
will generate a .yarn/cache/
and .yarn/unplugged
. PnP strict is the default mode. If you want to configure loose mode, you need to enable it in the following form⬇️:
# .yarnrc.yml nodeLinker: pnp pnpMode: loose
In a PnP project, in addition to the releases
folder, the .yarn
folder is likely to also contain an sdk/
folder that provides IDE support. Depending on your use case, .yarn
can even contain more folders.
. ├── .yarn/ │ ├── cache/ │ ├── releases/ │ │ └── yarn-3.1.1.cjs │ ├── sdk/ │ └── unplugged/ ├── .pnp.cjs ├── .pnp.loader.mjs ├── .yarnrc.yml ├── package.json └── yarn.lock`The initial state of
is the same as npm
or Yarn Classic
project. pnpm
also requires the package.json
file. After installing dependencies using $ pnpm i
results in a node_modules
folder, but its structure is completely different since its contents are addressable storage.
pnpm
also generates its own lock file pnp-lock.yml
. You can provide additional configuration using the optional .npmrc
file.
. ├── node_modules/ │ └── .pnpm/ ├── .npmrc ├── package.json └── pnpm-lock.yml
As mentioned in the previous section, every package manager creates lock files.
The lock
file stores the exact version of every dependency installed by your project, allowing for a more predictable and deterministic installation. This lock
file is important because dependent versions are likely to be declared with a version range (e.g., ≥ v1.2.5) and if you don't "lock" your version, the actual version installed may be different.
Lock files sometimes also store checksums (a hash as I recall), which we'll cover in more depth in the security section.
Starting from npm
v5+, locking files has always been the main function of npm
( package-lock.json
). In pnpm
, it is pnpm-lock.yaml
. yarn.lock
in Yarn Berry
appears in the new YAML format.
In the previous section we saw the traditional approach of installing dependencies in the node_modules
folder structure. This is the solution used by npm
, Yarn Classic and pnpm ( pnpm
is more efficient than the others).
Yarn Berry
does things differently in PnP mode. Instead of the node_modules
folder, dependencies are stored in zip files as a combination of .yarn/cache/
and .pnp.cjs
files.
It's better to put these lock files under version control, since every team member installs the same version, so it solves the "works on your machine and mine" problem.
The following table compares the different CLI commands available in npm
, Yarn Classic
, Yarn Berry
, and pnpm
. This is by no means a complete list, but rather a cheat sheet. This section does not cover workflow-related commands.
npm
and pnpm
have many ad hoc command and option aliases, which means commands can have different names, i.e. $ npm install
vs. $ npm add
. Additionally, many command options have abbreviated versions, such as -D
instead of --save-dev
. In the table, I call all abbreviated versions aliases. Using each of these package managers, you can add, update, or remove multiple dependencies.
This table covers the dependency management commands used to install or update all dependencies specified in package.json
.
Action | npm | Yarn Classic | Yarn Berry | pnpm |
---|---|---|---|---|
install deps in package.json | npm install alias: i, add | yarn install or yarn | like Classic | pnpm install alias: i |
update deps in package.json acc. semver | npm update alias: up, upgrade | yarn upgrade | yarn semver up (via plugin) | pnpm update alias: up |
update deps in package.json to latest | N/A | yarn upgrade --latest | yarn up | pnpm update --latest alias: -L |
update deps acc. semver | npm update react | yarn upgrade react | yarn semver up react | pnpm up react |
update deps to latest | npm update react@latest | yarn upgrade react --latest | yarn up react | pnpm up -L react |
update deps interactively | N/A | yarn upgrade-interactive | yarn upgrade-interactive (via plugin) | $ pnpm up --interactive alias: -i |
add runtime deps | npm i react | yarn add react | like Classic | pnpm add react |
add dev deps | npm i -D babel alias: --save-dev | yarn add -D babel alias: --dev | like Classic | pnpm add -D babel alias: --save-dev |
add deps to package.json without semver | npm i -E react alias: --save-exact | yarn add -E react alias: --exact | like Classic | pnpm add -E react alias: - -save-exact |
uninstall deps and remove from package.json | npm uninstall react alias: remove, rm, r, un, unlink | yarn remove react | like Classic | pnpm remove react alias: rm, un, uninstall |
uninstall deps w/o update of package. json | npm uninstall --no-save | N/A | N/A | N/A |
The following example shows how to manage packages during development. Terms used in the table:
- Package: dependency or binary
- Binary: An execution tool from
node_modules/.bin/
or.yarn/cache/
(PnP)
It is important to understand that Yarn Berry
only allows us to execute in package.json
or Expose the specified binary files in the bin/
file.
Action | npm | Yarn Classic | Yarn Berry | pnpm |
---|---|---|---|---|
install packages globally | npm i -g ntl alias: --global | yarn global add ntl | N/A (global removed) | pnpm add --global ntl |
update packages globally | npm update -g ntl | yarn global upgrade ntl | N /A | pnpm update --global ntl |
remove packages globally | npm uninstall -g ntl | yarn global remove ntl | N/A | pnpm remove --global ntl |
run binaries from terminal | npm exec ntl | yarn ntl | yarn ntl | pnpm ntl |
run binaries from script | ntl | ntl | ntl | ntl |
dynamic package execution | npx ntl | N/A | yarn dlx ntl | pnpm dlx ntl |
add runtime deps | npm i react | yarn add react | like Classic | pnpm add react |
add dev deps | npm i -D babel alias: --save-dev | yarn add -D babel alias: --dev | like Classic | pnpm add -D babel alias: --save-dev |
add deps to package.json without semver | npm i -E react alias: --save-exact | yarn add -E react alias: --exact | like Classic | pnpm add -E react alias: --save-exact |
uninstall deps and remove from package.json | npm uninstall react alias: remove, rm, r, un, unlink | yarn remove react | like Classic | pnpm remove react alias: rm, un, uninstall |
uninstall deps w/o update of package.json | npm uninstall --no-save | N/A | N/A | N/A |
This table covers some useful built-in commands. If there is no official command, third-party commands can usually be used through npm
packages or Yarn Berry
plugins.
Action | npm | Yarn Classic | Yarn Berry | pnpm |
---|---|---|---|---|
publish | npm publish | yarn publish | yarn npm publish | pnpm publish |
list installed deps | npm ls alias: list, la, ll | yarn list | pnpm list alias: ls | |
list outdated deps | npm outdated | yarn outdated | yarn upgrade-interactive | pnpm outdated |
print info about deps | npm explain ntl alias: why | yarn why ntl | like Classic | pnpm why ntl |
init project | npm init -y npm init (interactive) alias: - -yes | yarn init -y yarn init (interactive) alias: --yes | yarn init | pnpm init -y pnpm init (interactive) alias: --yes |
print licenses info | N/A (via third-party package) | yarn licenses list | N/ A (or via plugin, other plugin) | N/A (via third-party package) |
update package manager version | N/A (with third-party tools, eg, nvm) | with npm: yarn policies set-version 1.13.0 | with Corepack : yarn set version 3.1.1 | N/A (with npm, Corepack) |
perform security audit | npm audit | yarn audit | yarn npm audit | pnpm audit |
add deps to package.json without semver | npm i -E react alias: --save-exact | yarn add -E react alias: --exact | like Classic | pnpm add -E react alias: --save-exact |
uninstall deps and remove from package.json | npm uninstall react alias: remove, rm, r, un, unlink | yarn remove react | like Classic | pnpm remove react alias: rm, un, uninstall |
uninstall deps w/o update of package.json | npm uninstall --no-save | N/A | N/A | N/A |
Configuring the package manager occurs in your package.json
and dedicated configuration files.
monorepo
Most configuration occurs in the private configuration file .npmrc
.
If you want to use npm
's workspaces
feature, you must add the workspaces metadata field in package.json
to tell npm where to find the subproject or workspace folder.
// ... "workspaces": [ "hooks", "utils" ] }
Every package manager can use the public npm
registry. You probably want to reuse them without publishing them to a public registry. You can configure this to private the registry in your .npmrc
file. (Basically all have private sources now)
# .npmrc @doppelmutzi:registry=https://gitlab.doppelmutzi.com/api/v4/projects/41/packages/npm/
There are many configuration options for npm
, it is best to check them out in the documentation.
You can set yarn
's workspaces
in package.json
(must be a private package).
{ // ... "private": true, "workspaces": ["workspace-a", "workspace-b"] }
Any optional configuration goes into a .yarnrc
file. A common configuration option is to set a yarn-path:
it forces each team member to use a specified binary version. yarn-path
points to the folder containing a specific Yarn
version (e.g. .yarn/releases/
). You can install the unified Yarn Classic
version using the command (classic.yarnpkg.com/en/docs/cli…).
Configuring workspaces
in Yarn Berry
is similar to the configuration in Yarn Classic
( package.json
). Most Yarn Berry
configuration occurs in .yarnrc.yml
, and there are many configuration options available.
# .yarnrc.yml yarnPath: .yarn/releases/yarn-3.1.1.cjs
yarn berry
can use the import method $> yarn plugin import <name>
to extend the plug-in (yarnpkg.com/cli/plugin/…), this command will also be updated .yarnrc.yml
.
# .yarnrc.yml plugins: - path: .yarn/plugins/@yarnpkg/plugin-semver-up.cjs spec: "https://raw.githubusercontent.com/tophat/yarn-plugin-semver-up/master/bundles/%40yarnpkg/plugin-semver-up.js"
As mentioned in the history section, due to compatibility reasons, There may be some issues with dependencies in PnP strict mode. There is a typical solution to this type of PnP problem: packet extension configuration policy.
# .yarnrc.yml packageExtensions: "styled-components@*": dependencies: react-is: "*"
pnpm
uses the same configuration mechanism as npm
, so you can use .npmrc
files. Configuring a private registry also works the same as using npm
. Multi-package projects can be supported with the workspace feature of pnpm. To initialize monorepo
, you must specify the location of the package in the pnpm-workspace.yaml
file.
# pnpm-workspace.yaml packages: - 'packages/**'
(There are actually three concepts here, single warehouse and multiple projects, single warehouse and single project, and multiple warehouses and multiple projects)
monorepo
is a repository containing multiple projects. These projects are called workspace
or packages. Keeping everything in one place instead of using multiple repositories is a project organization strategy.
Of course, this introduces additional complexity. Yarn Classic
was the first to enable this feature, but now every major package manager offers workspace functionality. This section shows how to configure your workspace using each of the different package managers.
The npm
team has released the long-awaited npm workspace feature in v7. It contains many CLI commands to help manage multi-package projects from the root package. Most commands can be used with workspace
related options to tell npm
whether it should be run against a specific, multiple, or all workspaces.
# Installing all dependencies for all workspaces $ npm i --workspaces. # run against one package $ npm run test --workspace=hooks # run against multiple packages $ npm run test --workspace=hooks --workspace=utils # run against all $ npm run test --workspaces #ignore all packages missing test $ npm run test --workspaces --if-present
tips: Compared to other package managers, npm
v8 currently does not support advanced filtering or parallel execution of multiple workspace-related commands.
In August 2017, the Yarn
team announced monorepo
support for workspace functionality. Previously, package managers could only be used in multi-package projects with third-party software such as Lerna. This new addition to Yarn
also paves the way for other package managers to implement such functionality.
If you are interested, you can refer to how to use the workspace function of Yarn Classic with and without Lerna. But this article will only introduce some necessary commands to help you manage dependencies in Yarn Classic
workspace setup.
# Install all dependencies $yarn for all workspaces # Show dependency tree $ yarn workspaces info # Run another package to start $ yarn workspace awesome - package start # Add Webpack to package $ yarn workspace awesome - package add - D webpack # add React for all packages $ yarn add react -W
Yarn Berry
has featured workspaces from the beginning, as its implementation is built on the concepts of Yarn Classic
. In a Reddit comment, Yarn Berry's lead developer provided a brief overview of workspace-oriented features, including:
Yarn Berry
uses a number of protocols that can be used in dependencies
or devDependencies
field of the package.json
file. Among them is the workspace
protocol.
In contrast to Yarn Classic
's workspace, Yarn Berry
clearly defines that a dependency must be one of the packages in this monorepo
. Otherwise if the versions do not match, Yarn Berry
may try to get its version from the remote registry.
{ // ... "dependencies": { "@doppelmutzi/hooks": "workspace:*", "http-server": "14.0.0", // ... } }
Through the workspace
protocol, pnpm
has contributed to a monorepo
project similar to Yarn Berry
. Many pnpm
commands accept --recursive (-r)
or --filter options which are particularly useful in monorepo
context. Its native filtering commands are also a great complement to Lerna
.
# prune all workspaces pnpm -r exec -- rm -rf node_modules && rm pnpm-lock.yaml # run all tests for all workspaces with scope @doppelmutzi pnpm recursive run test --filter @doppelmutzi/`
Performance is a key part of the selection decision. This section presents benchmarks based on a small and a medium-sized project. Here are some notes on the sample project:
I measured each of our package manager variants once using three use cases (UC). For detailed evaluation and explanation, please view the results for Project 1 (P1) and Project 2 (P2).
node_modules
or .pnp.cjs
node_modules
or .pnp.cjs
node_modules
or .pnp.cjs
I used the tool gnomon to measure the time consumed by the installation ( yarn
| gnomon
). Additionally I measured the size of the generated files $ du -sh node_modules
.
Performance results for Project 1 | |||||||
---|---|---|---|---|---|---|---|
Method | npm v8.1.2 | Yarn Classic v1.23.0 | pnpm v6.24.4 | Yarn Berry PnP loose v3.1.1 | Yarn Berry PnP strict v3.1.1 | Yarn Berry node_modules v3.1.1 | Yarn Berry pnpm v3.1.1 |
UC 1 | 86.63s | 108.89s | 43.58s | 31.77s | 30.13s | 56.64s | 60.91s |
UC 2 | 41.54s | 65.49s | 26.43s | 12.46s | 12.66s | 46.36s | 40.74s |
UC 3 | 23.59s | 40.35s | 20.32s | 1.61s | 1.36s | 28.72s | 31.8 9s |
Files and size | package-lock.json: 1.3M node_modules : 467M | node_modules: 397M yarn.lock: 504K | pnpm-lock.yaml: 412K node_modules: 319M | yarn.lock: 540K cache: 68M unplugged: 29M .pnp.cjs: 1.6M | yarn.lock: 540K cache: 68M unplugged: 29M . pnp.cjs: 1.5M | node_modules: 395M yarn.lock: 540K cache: 68M | node_modules: 374M yarn.lock: 540K cache: 68M |
Performance results for Project 2 | |||||||
---|---|---|---|---|---|---|---|
Method | npm v8.1.2 | Yarn Classic v1.23.0 | pnpm v6.24.4 | Yarn Berry PnP loose v3.1.1 | Yarn Berry PnP strict v3.1.1 | Yarn Berry node_modules v3.1.1 | Yarn Berry pnpm v3.1.1 |
UC 1 | 34.91s | 43.26s | 15.6s | 13.92s | 6.44s | 23.62s | 20.09s |
UC 2 | 7.92s | 33.65s | 8.86s | 7.09s | 5.63s | 15.12s | 14.93s |
UC 3 | 5.09s | 15.64s | 4.73s | 0.93s | 0.79s | 8.18s | 6.02s |
Files and size | package-lock .json: 684K node_modules: 151M | yarn.lock: 268K node_modules: 159M | pnpm-lock.yaml: 212K node_modules: 141M | .pnp.cjs: 1.1M .pnp.loader.mjs: 8.0K yarn.lock: 292K .yarn: 38M | .pnp.cjs: 1.0 M .pnp.loader.mjs: 8.0K yarn.lock: 292K .yarn: 38M | yarn.lock: 292K node_modules: 164M cache: 34M | yarn.lock: 292K node_modules: 156M cache: 34M |
npm
is a bit too lenient when it comes to handling bad packages and has encountered some security vulnerabilities that directly affected many projects. For example, in version 5.7.0, when you execute the sudo npm
command on a Linux operating system, you can change the ownership of system files, rendering the operating system unusable.
Another incident occurred in 2018 involving the theft of Bitcoins. The Node.js package EventStream added a malicious dependency in its 3.3.6 version. This malicious package contains a cryptographic method that attempts to steal Bitcoins from the developer's machine.
To help solve these problems, new npm
versions use cryptographic algorithms to check the integrity of your installed packages. SHA-512.
Yarn Classic
and Yarn Berry
use checksums to verify the integrity of every package from the beginning. Yarn
also tries to prevent you from retrieving malicious packages that are not declared in package.json
: if a mismatch is found, the installation is aborted.
Yarn Berry
in PnP mode does not have the security issues of the traditional node_modules
method. Compared with Yarn Classic
, Yarn Berry
improves the security of command execution. You can only execute packages that have been declared in package.json
. This security feature is similar to pnpm
, which I describe below.
pnpm
still uses checksums to verify the integrity of each installed package before executing its code.
As we mentioned above, both npm
and Yarn Classic
have security issues due to promotion. pnpm
avoids this situation because its management model does not use elevation; instead, it generates nested node_modules
folders, thus eliminating the risk of illegal dependency access. This means that dependencies are declared in .package.json
.
As we discussed, this is especially important in a monorepo
setting, since boosting algorithms can sometimes lead to dependency nondeterminism.
npm | Yarn Classic | Yarn Berry | pnpm |
Svelte | React | Jest (with node_modules) | Vue 3 |
Preact | Angular | Storybook (with node_modules) | Browserlist |
Express.js | Ember | Babel (with node_modules) | Prisma |
Meteor | Next.js | Redux Toolkit (with node_modules) | SvelteKit |
Apollo Server | Gatsby | ||
Nuxt | |||
Create React App | |||
webpack-cli | |||
Emotion |
There are indeed big differences in the principles of different package managers.
pnpm
initially looks like npm
in that their CLI usage is similar but managing dependencies is very different; pnpm
's approach results in better performance and optimal disk space efficiency. Yarn Classic
is still popular, but it is considered legacy software and support may be dropped in the near future. Yarn Berry PnP
is brand new, but its potential to revolutionize the package manager world yet again is not yet realized.
Over the years, many users have asked who uses which package managers, and overall people seem to be particularly interested in the maturity and adoption of Yarn Berry PnP
.
The purpose of this article is to provide you with multiple perspectives to decide which package manager to use for yourself. I'd like to point out that I don't recommend a specific package manager. It depends on how you weigh the different requirements - so you can still choose whatever you like!
English original address: https://blog.logrocket.com/javascript-package-managers-compared/