Using Yarn Plug'n'Play

Plug'n'Play (PnP) is an innovative installation strategy for Node that tries to solve the challenges of using node_modules for storing installed packages:

  • slow installation process
  • slow node runtime cold start
  • expensive packages diffing when adding new packages
  • no ability to restrict access and enforce hoisting/nesting

Instead of using the node_modules folder, PnP creates a map from package names to hoisted package versions and creates dependency edges between packages. The packages are kept in zip archives which makes caching and restoring faster.

Read more about PnP on Yarn's official docs.

Switching to Yarn v2+ (aka Berry)

When you run create-nx-workspace with the optional --pm=yarn flag, Nx uses the default yarn version set in your global .yarnrc.yml file (usually in the root of your user folder). To check your current yarn version, run:

yarn --version

If the version is in the 1.x.x range, the workspace will be created with yarn classic. To migrate an existing yarn classic workspace to the modern version of yarn, also known as Berry, run:

yarn set version stable

This command will update .yarnrc.yml with the path to the local yarn bin of the correct version and will set the packageManager property in the root package.json:

package.json
{ // ... "packageManager": "yarn@3.6.1" }

Switching to PnP

Once you are on the modern version of yarn, you can use the following command to switch to PnP:

yarn config set nodeLinker pnp

Your .yarnrc.yml will now have nodeLinker set to a proper method:

.yarnrc.yml
nodeLinker: pnp

Once the config is changed you need to run the install again:

yarn install

Running install generates a .pnp.cjs file that contains a mapping of external packages and strips all the packages from the node_modules. The contents of your node_modules should now look like this:

node_modules/ └── .cache └── nx

Dealing with Inaccessible Dependencies

When using Yarn Berry, Nx will set the nodeLinker property to a backward compatible node-modules value by default. This is to ensure your repository works out of the box.

Some packages come with broken dependency requirements so using strict PnP can lead to broken builds. This results in errors similar to the one below:

Error: [BABEL]: @babel/plugin-transform-react-jsx tried to access @babel/core (a peer dependency) but it isn't provided by your application; this makes the require call ambiguous and unsound. Required package: @babel/core

The problem above is something we need to fix by ensuring the requested peer dependency exists and is available:

yarn add -D @babel/core

Sometimes, simply adding a missing package doesn't work. In those cases, the only thing we can do is contact the author of the package causing the issues and notify them of the missing dependency requirement. The alternative is to switch back to using the node-modules node linker.