To go further: Yarn PnP
These steps are completely optional!
While we recommend to use Yarn Plug'n'Play for new projects, enabling it on existing projects may require a time investment. Feel free to skip this part if you prefer, and to come back to it whenever you have more time and/or a concrete benefit to get from it.
Calling the Doctor
Plug'n'Play enforces strict dependency rules. You'll get errors should something in your application rely on unlisted dependencies which could cause your application to become unstable.
To quickly detect which places may rely on unsafe patterns, Yarn provides a tool called the Doctor. Just run yarn dlx @yarnpkg/doctor
in your project and the Doctor will start looking at your source files to detect any potentially problematic pattern.
Example
For example, here's what the Doctor used to say about webpack-dev-server
:
➤ YN0000: Found 1 package(s) to process
➤ YN0000: For a grand total of 236 file(s) to validate
➤ YN0000: ┌ /webpack-dev-server/package.json
➤ YN0000: │ /webpack-dev-server/test/testSequencer.js:5:19: Undeclared dependency on @jest/test-sequencer
➤ YN0000: │ /webpack-dev-server/client-src/default/webpack.config.js:12:14: Webpack configs from non-private packages should avoid referencing loaders without require.resolve
➤ YN0000: │ /webpack-dev-server/test/server/contentBase-option.test.js:68:8: Strings should avoid referencing the node_modules directory (prefer require.resolve)
➤ YN0000: └ Completed in 5.12s
➤ YN0000: Failed with errors in 5.12s
We can see that the Doctor spotted a couple of legitimate issues:
-
testSequencer.js
depends on@jest/test-sequencer
without listing it as a proper dependency - which would be reported as an error at runtime under Yarn Plug'n'Play, as nothing guarantees that the version of@jest/test-sequencer
would match what the package has been tested with. -
webpack.config.js
references a loader without passing its name throughrequire.resolve
- this is unsafe, as it means the loader will be resolved relative to thewebpack
package, rather thanwebpack-dev-server
's dependencies. -
contentBase-option.test.js
checks the content of thenode_modules
folder - which wouldn't exist anymore under Plug'n'Play.
Enabling Yarn PnP
- Look into your
.yarnrc.yml
file for thenodeLinker
setting. - If you don't find it, or if it's set to
pnp
, then it's all good: you're already using Yarn Plug'n'Play! - Otherwise, remove it from your configuration file and run
yarn install
. - Commit the changes.
What to look for
Now you should have a working Yarn Plug'n'Play setup, but your repository might still need some extra care. Some things to keep in mind:
- There are no
node_modules
folders. Userequire.resolve
instead. - There are no
.bin
folders. If you relied on them, useyarn run bin
instead. - Replace any calls to
node
that are not inside thescripts
field byyarn node
. - Custom pre-hooks (e.g.
prestart
) need to be called manually now (yarn prestart
).
All of this and more is documented in the following sections. In general, we advise you at this point to try to run your application and see what breaks, then check here to find out tips on how to correct your install.
Editor support
We only cover VSCode here, but we have a dedicated documentation page covering more IDEs!
Make sure that typescript
, eslint
, prettier
, ... all dependencies typically used by your IDE extensions are listed at the top level of the project (rather than in an arbitrary workspace).
- Install the ZipFS VSCode extension.
- Run
yarn dlx @yarnpkg/sdks vscode
and commit the changes. - For TypeScript, don't forget to select Use Workspace Version in VSCode.
General Advices
Fix dependencies with packageExtensions
Packages sometimes forget to list their dependencies. In the past it used to cause many subtle issues, so Yarn now defaults to prevent such unsound accesses. Still, we don't want it to prevent you from doing your work as long as you can do it in a safe and predictable way, so we came up with the packageExtensions
setting.
For example, if react
was to forget to list a dependency on prop-types
, you'd fix it like this:
packageExtensions:
"react@*":
dependencies:
prop-types: "*"
And if a Babel plugin was missing its peer dependency on @babel/core
, you'd fix it with:
packageExtensions:
"@babel/plugin-something@*":
peerDependencies:
"@babel/core": "*"
Should you use dependencies or peer dependencies? It depends on the context; as a rule of thumb, if the package is a singleton (for example react
, or react-redux
which also relies on the React context), you'll want to make it a peer dependency. In other cases, where the package is just a collection of utilities, using a regular dependency should be fine (for example tslib
, lodash
, etc).
Call binaries using yarn run bin
rather than node_modules/.bin
The node_modules/.bin
folder is an implementation detail, and the PnP installs don't generate it at all. Rather than relying on its existence, just use the yarn run bin
command which can start both scripts and binaries:
yarn run jest
# or, using the shortcut:
yarn jest
Call your scripts through yarn node
rather than node
We now need to inject some variables into the environment for Node to be able to locate your dependencies. In order to make this possible, we ask you to use yarn node
which transparently does the heavy lifting.
this section only applies to the shell CLI. The commands defined in your scripts
are unaffected, as we make sure that node
always points to the right location, with the right variables already set.
Setup your IDE for PnP support
Since Yarn Plug'n'Play doesn't generate node_modules
folders, some IDE integrations may not work out of the box. Check our guide to see how to fix them.
Take a look at our end-to-end tests
We now run daily end-to-end tests against various popular JavaScript tools in order to make sure that we never regress - or be notified when third-party project ship incompatible changes.
Consulting the sources for those tests is a great way to check whether some special configuration values have to be set when using a particular toolchain.
Troubleshooting
Cannot find module [...]
This error doesn't come from Yarn: it's emitted by the Node.js resolution pipeline, telling you a package cannot be found on disk.
If you have enabled Plug'n'Play, then the Node.js resolution pipeline is supposed to forward resolution requests to Yarn - meaning that if you get this message, it's that this forwarding didn't occur, and your first action should be to figure out why.
Usually, it'll be because you called a Node.js script using node ./my-script
instead of yarn node ./my-script
.
A package is trying to access [...]
Although rare, some packages don't list all their dependencies. Now that we enforce boundaries between the various branches of the dependency tree, this kind of issue is more apparent than it used to be (although it's always been problematic).
The long term fix is to submit a pull request upstream to add the missing dependency to the package listing. Given that it sometimes might take some time before they get merged, we also have a more short-term fix available: create .yarnrc.yml
in your project, then use the packageExtensions
setting to add the missing dependency to the relevant packages. Run yarn install
to apply your changes, and voilà!
Should you choose to open a PR on the upstream repository, you will also be able to contribute your package extension to our plugin-compat
database, helping the whole ecosystem move forward.