Yarn 2 — the future of package managers for JavaScript?

April 3, 2020 | in Engineering
| By Liran Tal

What is Yarn 2?

Yarn 2 is the new release of the revolutionary and well-established npm package manager Yarn which features improvements, such as Plug’n’Play, Plugins architecture, Monorepos, and improved workspaces support, Zero installs. You can find the community repository on GitHub at: https://github.com/yarnpkg/berry.

Why did we need a new Yarn version?

Yarn 2 ticks a quite a few check-boxes on the board that have been keeping developers discussing on how to manage JavaScript projects:

Monorepos

Whether you’re a fan of monorepos or not, managing them requires a good set of toolchains to ensure the success and pain-free of managing large projects and complicated workflows.

A popular recipe for setting up monorepo for JavScript projects is the combination of Yarn’s workspaces (existed since Yarn 1) and Lerna as a project manager.

The good news with Yarn 2 is that now, Yarn doubles as being both a package manager as well as a project manager and aims to provide a wholesome experience for running monorepos smoothly.

Plug’n’Play

What hasn’t been said on node_modules? The new Yarn docs go into further details about the shortcomings of node_modules as a folder structure and iterate why a new way of thinking about how we manage dependencies is needed.

The Plug’n’Play strategy is not only reserved to the new Yarn version but rather something we’ve already seen before in the ecosystem. Kat Marchán has show-cased Tink in JSConf Eu 2019 during her presentation. This isn’t to say, however, that Tink’s approach to Plug’n’Play is the same as Yarn’s, but worth rehearsing that these ideas have been spreading for a while with different experiments across our JavaScript ecosystem.

An interesting security angle with how Yarn 2 implements Plug’n’Play:

  • The local cache folder .yarn/cache is mounted as read-only and so it doesn’t allow malicious attempts to modify already installed packages on the file system.
  • Due to the nature of Plug’n’Play with Yarn, it is able to apply strict checks, such as disallowing packages to refer to so-called phantom packages that aren’t defined within a package or project’s list of dependencies.

Plugins

Perhaps this is more of a developer/maintainer perspective rather than the end-user’s but still important to capture. When building Yarn 2 with TypeScript, the opportunity arose for also decreasing the complexity and maintenance of the very core, by creating a plugin architecture that allows for others to extend Yarn.

Even more features?

In his announcement of the Yarn 2 release on dev.to, Maël Nison, provided a lot of insights into the features and their perils that make Yarn 2 the right choice for future JavaScript and Node.js projects. I encourage you to read his release blog, as well as Yarn’s official features page for more reasons.

How to get started with Yarn 2?

Yarn has adopted a per-repository — or per-project if you will — install strategy which means you can have one global install of Yarn — such as the first, classic version of Yarn — and then shift to Yarn 2 for a specific project.

Getting started with Yarn 2 requires you to have an up to date version of Node.js, at minimum Node.js 10. In any case, Node.js LTS should always be your preferred version since it is supported and maintained. Node.js versions prior to 10 are not supported anymore and so no bug fixes, and no security fixes.

Switching to the new Yarn version

To get started, we’ll create a new directory to init a new project:

mkdir my-app
cd my-app

Let’s find out your current Yarn version with yarn --version. if you’re running a Yarn version below 1.22 go ahead and type-in:

yarn policies set-version berry

If your local Yarn version is 1.22 and above:

yarn set version berry

This should fetch Yarn 2 and show you an output as follows:

Resolving berry to a url...
Downloading https://github.com/yarnpkg/berry/raw/master/packages/berry-cli/bin/berry.js...
Saving it into /private/tmp/my-app/.yarn/releases/yarn-berry.js...
Updating /private/tmp/my-app/.yarnrc...
Done!

You can then verify your version of Yarn with yarn –version, for me it shows:

2.0.0-rc.30

As usual, it is advised to commit .yarnrc and .yarnrc.yml to share configuration across the repo. It’s interesting to note here the use of .yarn folder where yarn manages cache, the yarn runtime, plugins and other data.

Updating the policy for Yarn 2 downloads a copy of the latest release into the local .yarn/ folder and creates a .yarnrc which records the path on disk to the installed version.

Installing npm modules in Yarn 2

Let’s add the Node.js web framework Fastify to our dependencies:

yarn add fastify

➤ YN0000: ┌ Resolution step
➤ YN0000: └ Completed in 2.45s
➤ YN0000: ┌ Fetch step
➤ YN0013: │ string-similarity@npm:4.0.1 can't be found in the cache and will be fetched from the remote registry
➤ YN0013: │ string_decoder@npm:1.3.0 can't be found in the cache and will be fetched from the remote registry
➤ YN0013: │ tiny-lru@npm:7.0.2 can't be found in the cache and will be fetched from the remote registry
➤ YN0013: │ uri-js@npm:4.2.2 can't be found in the cache and will be fetched from the remote registry
➤ YN0013: │ util-deprecate@npm:1.0.2 can't be found in the cache and will be fetched from the remote registry
➤ YN0000: └ Completed in 1.73s
➤ YN0000: ┌ Link step
➤ YN0000: └ Completed in 0.07s
➤ YN0000: Done in 4.26s

You can already see some changes in Yarn’s new output:

  1. Every set of related tasks that happens in the install process is grouped together.
  2. Yarn provides a reference in the CLI output to each group of tasks so if you have an issue with one of them, it is easier to troubleshoot.

The lock file is still the preferred way of how Yarn manages pinning dependencies for repeatable and auditable versions of dependencies. New in Yarn 2, however, is that it is now a proper YAML syntax.

The common package management commands have remained the same with prior Yarn versions:

  • yarn add [package] --dev
  • yarn remove [package]
  • yarn up [package]

Running JavaScript or Node.js needs Yarn

Now that there’s no node_modules directory anymore, running a one-off script such as node script.js won’t work as it won’t be able to resolve the dependencies stated in that file. For that reason, you need to issue yarn node script.js in which yarn will spawn the node process with its own file that patches some of the logic to allow the PnP magic. It does so by using Node’s built-in --require CLI flag.

Also note that yarn node isn’t required inside scripts defined in the scripts field (which is how a lot of tools are invoked anyway), just calling node is enough. Thanks Mael for clarifying this! 🤗

What’s next?

Yarn 2 still needs some mileage and community vetting to see if it withstands the test of time and gets the recognition of the JavaScript community for the novel ideas it executes and builds upon.

I suggest heading over to Yarn’s website for the elaborate documentation at https://yarnpkg.com/cli/install as you try it out with your next project.