Skip to main content

进一步了解:Yarn PnP

信息

这些步骤完全是可选的!

¥These steps are completely optional!

虽然我们建议在新项目中使用 Yarn Plug'n'Play,但在现有项目上启用它可能需要时间投入。如果你愿意,可以跳过此部分,并在你有更多时间和/或从中获得具体好处时再回来。

¥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 强制执行严格的 依赖规则。如果应用中的某些内容依赖于未列出的依赖,你将收到错误,这可能会导致你的应用变得不稳定。

¥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.

为了快速检测哪些地方可能依赖不安全的模式,Yarn 提供了一个名为 Doctor 的工具。只需在你的项目中运行 yarn dlx @yarnpkg/doctor,Doctor 就会开始查看你的源文件以检测任何潜在的问题模式。

¥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

例如,这是 Doctor 过去对 webpack-dev-server 的说法:

¥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

我们可以看到 Doctor 发现了几个合法的问题:

¥We can see that the Doctor spotted a couple of legitimate issues:

  • testSequencer.js 依赖于 @jest/test-sequencer,但未将其列为适当的依赖 - 在 Yarn Plug'n'Play 下,这将在运行时报告为错误,因为没有什么可以保证 @jest/test-sequencer 的版本与包测试过的版本相匹配。

    ¥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 引用加载器而不将其名称传递给 require.resolve - 这是不安全的,因为这意味着加载器将相对于 webpack 包而不是 webpack-dev-server 的依赖进行解析。

    ¥webpack.config.js references a loader without passing its name through require.resolve - this is unsafe, as it means the loader will be resolved relative to the webpack package, rather than webpack-dev-server's dependencies.

  • contentBase-option.test.js 检查 node_modules 文件夹的内容 - 在 Plug'n'Play 下将不再存在。

    ¥contentBase-option.test.js checks the content of the node_modules folder - which wouldn't exist anymore under Plug'n'Play.

启用 Yarn PnP

¥Enabling Yarn PnP

  1. 查看你的 .yarnrc.yml 文件中的 nodeLinker 设置。

    ¥Look into your .yarnrc.yml file for the nodeLinker setting.

  2. 如果你找不到它,或者它被设置为 pnp,那么一切都很好:你已经在使用 Yarn Plug'n'Play!

    ¥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!

  3. 否则,将其从配置文件中删除并运行 yarn install

    ¥Otherwise, remove it from your configuration file and run yarn install.

  4. 提交更改。

    ¥Commit the changes.

要查找什么

¥What to look for

现在你应该有一个可以正常工作的 Yarn Plug'n'Play 设置,但你的存储库可能仍需要一些额外的注意。需要记住的一些事项:

¥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:

  • 没有 node_modules 文件夹。改用 require.resolve

    ¥There are no node_modules folders. Use require.resolve instead.

  • 没有 .bin 文件夹。如果你依赖它们,请改用 yarn run bin

    ¥There are no .bin folders. If you relied on them, use yarn run bin instead.

  • 将不在 scripts 字段内的任何对 node 的调用替换为 yarn node

    ¥Replace any calls to node that are not inside the scripts field by yarn node.

  • 现在需要手动调用自定义预钩子(例如 prestart)(yarn prestart)。

    ¥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

提示

我们在这里只介绍 VSCode,但我们有一个专门的 文档页面 涵盖更多 IDE!

¥We only cover VSCode here, but we have a dedicated documentation page covering more IDEs!

警告

确保 typescripteslintprettier、...IDE 扩展通常使用的所有依赖都列在项目的顶层(而不是任意工作区中)。

¥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).

  1. 安装 ZipFS VSCode 扩展。

    ¥Install the ZipFS VSCode extension.

  2. 运行 yarn dlx @yarnpkg/sdks vscode 并提交更改。

    ¥Run yarn dlx @yarnpkg/sdks vscode and commit the changes.

  3. 对于 TypeScript,不要忘记在 VSCode 中选择 使用工作区版本

    ¥For TypeScript, don't forget to select Use Workspace Version in VSCode.

常规建议

¥General Advices

修复与 packageExtensions 的依赖

¥Fix dependencies with packageExtensions

包有时会忘记列出它们的依赖。过去它曾经导致许多细微的问题,因此 Yarn 现在默认防止这种不合理的访问。不过,只要你能够以安全且可预测的方式完成工作,我们不希望它妨碍你工作,因此我们提出了 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.

例如,如果 react 忘记列出对 prop-types 的依赖,你可以像这样修复它:

¥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: "*"

如果 Babel 插件缺少对 @babel/core 的对等依赖,你可以使用以下方法修复它:

¥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": "*"

你应该使用依赖还是对等依赖?这取决于上下文;根据经验,如果包是单例(例如 reactreact-redux,它们也依赖于 React 上下文),则需要将其设为对等依赖。在其他情况下,如果包只是实用程序的集合,则使用常规依赖应该没问题(例如 tsliblodash 等)。

¥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).

使用 yarn run bin 而不是 node_modules/.bin 调用二进制文件

¥Call binaries using yarn run bin rather than node_modules/.bin

node_modules/.bin 文件夹是一个实现细节,PnP 安装根本不会生成它。不要依赖它的存在,只需使用可以启动脚本和二进制文件的 yarn run 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

通过 yarn node 而不是 node 调用脚本

¥Call your scripts through yarn node rather than node

我们现在需要向环境中注入一些变量,以便 Node 能够找到你的依赖。为了实现这一点,我们要求你使用 yarn 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.

注意

本节仅适用于 shell CLI。scripts 中定义的命令不受影响,因为我们确保 node 始终指向正确的位置,并且已设置正确的变量。

¥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.

设置你的 IDE 以支持 PnP

¥Setup your IDE for PnP support

由于 Yarn Plug'n'Play 不会生成 node_modules 文件夹,因此某些 IDE 集成可能无法开箱即用。查看我们的 guide 以了解如何修复它们。

¥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

我们现在每天针对各种流行的 JavaScript 工具运行 端到端测试,以确保我们永远不会退步 - 或者当第三方项目发布不兼容的更改时收到通知。

¥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 [...]

此错误不是来自 Yarn:它由 Node.js 解析管道触发,告诉你无法在磁盘上找到包。

¥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.

如果你已启用 Plug'n'Play,则 Node.js 解析管道应该将解析请求转发给 Yarn - 这意味着如果你收到此消息,则表示此转发未发生,你的第一个操作应该是找出原因。

¥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.

通常,这是因为你使用 node ./my-script 而不是 yarn node ./my-script 调用了 Node.js 脚本。

¥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).

长期修复是向上游提交拉取请求,以将缺少的依赖添加到软件包列表中。鉴于有时可能需要一些时间才能合并,我们还提供了一个更短期的修复方法:在你的项目中创建 .yarnrc.yml,然后使用 packageExtensions 设置 将缺少的依赖添加到相关包中。运行 yarn install 以应用你的更改,然后就好了!

¥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à!

如果你选择在上游存储库上打开 PR,你还可以将你的包扩展贡献给我们的 plugin-compat 数据库,帮助整个生态系统向前发展。

¥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.