pnpm is a JavaScript package manager that differs from npm and Yarn in many ways. If you haven’t heard about it yet, I recommend checking it out. In this article, I would love to write about the design system that we use to report during installation.

When I first started contributing to pnpm (around v0.15 ), this is how an installation was reported:

It wasn’t really useful but some users of pnpm liked it. They thought it was beautiful. But then as we started adding more features, we realized that it is very important to print the right amount of information in a nice readable format.

So let’s see how pnpm has evolved and how it reports in different scenarios as of v2.13.6 .

Reporting installation in a single project

When you first install pnpm and you run pnpm install in a project, you'll see an output like this:

Unlike the old output, this one is very static and minimalistic but it contains a lot more useful information.

We see that:

one of the installed packages is deprecated 117 new packages were added to node_modules Installation slowed down a bit because the huge typescript tarball was being downloaded 0 packages were available in the store, so all 117 packages were downloaded (pnpm saves one version of a package only ever once on a disk, so when a package is available in the store, it is just hard linked to the node_modules ) express@4.16.0 was added as a production dependency a newer version of express is available in the registry babel-preset-es2015@6.24.1 and typescript@3.0.1 were added as dev dependencies

Now lets update express to the latest version and see what we get:

5 packages were removed from node_modules 5 packages were added to node_modules all 5 packages were downloaded from the registry the newest express was added to the project

Reporting installation in a multi-package repository

pnpm has a set of commands for working with multi-package repositories (MPR). When installing dependencies in an MPR, the amount of information that is being processed is so big that printing all of it would just make an unreadable mess. In order to provide some basic information anyway, we came out with the concept of zoomed-out reporting. A zoomed-out reporting contains just the most important pieces of information.

Every package in the MPR is printed with the number of added/removed packages (inspired by Git):

Zoomed-out reporting also prints warnings (only warnings, no info messages):

When we came out with the concept of zoomed-out reporting for the recursive commands, we realized that there are other scenarios in which they are useful. For instance, when packages are linked in, it should be a mixture of zoomed-out and zoomed-in reporting. Packages that are linked in should be reported briefly and the package in the current working directory should be in focus:

Implementation details

Although the output seems minimalistic and simple, it is produced by a very complex system. pnpm consists of many components and many operations may happen in random order (this is one of the reasons pnpm is so fast). That is why reporting is performed by a specialized part of pnpm called “reporter” (code).

The reporter is a package that listens for logs, filters them, combines and forms an output from them. pnpm uses bole to pass the logs from the loggers to the reporter. This modularization is great because we can mock the logs and cover reporting with tests!

For printing the output to the console, we use ansi-diff. ansi-diff is great because it accepts "frames" of output and it updates only those parts of the console that are changed (and it is fast). Before we switched to ansi-diff , we used another popular library for updating the console output but it was doing the update with noticeable flickering.

P.S.

It is very hard to implement good CLI reporting. But good reporting allows developers to focus on the important things and possibly notice issues earlier.

Of course, pnpm’s reporting can improved a lot and we have many open issues in that area. Give pnpm a try and don’t hesitate to let us know if there are things that can be improved further.