跳至主要内容

插件教程

从 Yarn 2 开始,Yarn 现在支持插件。有关插件的更多信息以及在何种情况下需要使用插件,请参阅专用页面。我们将在本文中讨论编写插件所需的具体步骤。这真的很简单!

插件是什么样的?

插件是 Yarn 在运行时加载的脚本,可以向 Yarn 中注入新行为。它们还可以要求 Yarn 本身提供的一些包,例如 @yarnpkg/core。这使你可以使用与当前使用的 Yarn 二进制文件完全相同的核心 API,就像它是对等依赖项一样!

信息

由于插件是在 Yarn 启动之前加载的(因此在你进行首次安装之前),强烈建议以不依赖的方式编写插件。如果这样做有困难,请知道我们提供了一个功能强大的工具(@yarnpkg/builder),它可以将你的插件捆绑到一个 JavaScript 文件中,以便发布。

编写我们的第一个插件

在文本编辑器中打开一个名为 plugin-hello-world.js 的新文件,然后键入以下代码

module.exports = {
name: `plugin-hello-world`,
factory: require => ({
// What is this `require` function, you ask? It's a `require`
// implementation provided by Yarn core that allows you to
// access various packages (such as @yarnpkg/core) without
// having to list them in your own dependencies - hence
// lowering your plugin bundle size, and making sure that
// you'll use the exact same core modules as the rest of the
// application.
//
// Of course, the regular `require` implementation remains
// available, so feel free to use the `require` you need for
// your use case!
})
};

我们有插件,但现在我们需要注册它,以便 Yarn 知道在哪里找到它。为此,我们只需在存储库根目录的 .yarnrc.yml 文件中添加一个条目

plugins:
- ./plugin-hello-world.js

就是这样!您拥有了第一个插件,恭喜!当然,它没有做太多事情(或者根本没有做任何事情),但我们将看到如何对其进行扩展以使其更强大。

一体化插件构建器

正如我们所见,插件旨在成为独立的 JavaScript 源文件。手动编写它们非常可行,特别是如果您只需要一个小的插件,但一旦开始添加多个命令,它可能会变得有点复杂。为了简化此过程,我们维护了一个名为 @yarnpkg/builder 的包。此构建器对于 Yarn 来说就像 Next.js 对于 Web 开发一样 - 它是一个旨在帮助创建、构建和管理用 TypeScript 编写的复杂插件的工具。

其文档可以在 专用页面 上找到,但请记住,您不必使用它。有时,好的旧脚本就足够了!

添加命令

插件还可以注册自己的命令。为此,我们只需使用 clipanion 库编写它们 - 甚至不必将其添加到我们的依赖项中!我们来看一个例子

module.exports = {
name: `plugin-hello-world`,
factory: require => {
const {BaseCommand} = require(`@yarnpkg/cli`);

class HelloWorldCommand extends BaseCommand {
static paths = [[`hello`]];

async execute() {
this.context.stdout.write(`This is my very own plugin 😎\n`);
}
}

return {
commands: [
HelloWorldCommand,
],
};
}
};

现在,尝试运行 yarn hello。您会看到您的消息出现!请注意,您可以使用 clipanion 提供的全套功能,包括短选项、长选项、可变参数列表,... 您甚至可以使用我们提供的 typanion 库验证您的选项。以下是一个示例,其中我们仅接受数字作为参数

module.exports = {
name: `plugin-addition`,
factory: require => {
const {BaseCommand} = require(`@yarnpkg/cli`);
const {Command, Option} = require(`clipanion`);
const t = require(`typanion`);

class AdditionCommand extends BaseCommand {
static paths = [[`addition`]];

// Show descriptive usage for a --help argument passed to this command
static usage = Command.Usage({
description: `hello world!`,
details: `
This command will print a nice message.
`,
examples: [[
`Add two numbers together`,
`yarn addition 42 10`,
]],
});

a = Option.String({validator: t.isNumber()});
b = Option.String({validator: t.isNumber()});

async execute() {
this.context.stdout.write(`${this.a}+${this.b}=${this.a + this.b}\n`);
}
}

return {
commands: [
AdditionCommand,
],
};
},
};

使用钩子

插件可以注册到 Yarn 生命周期中的各种事件,并为其提供附加信息以改变其行为。为此,您只需在插件中声明一个新的 hooks 属性,并为要监听的每个钩子添加成员

module.exports = {
name: `plugin-hello-world`,
factory: require => ({
hooks: {
setupScriptEnvironment(project, scriptEnv) {
scriptEnv.HELLO_WORLD = `my first plugin!`;
},
},
})
};

在此示例中,我们注册到 setupScriptEnvironment 钩子,并使用它将参数注入到环境中。现在,每次运行脚本时,你都会看到你的 env 中包含一个名为 HELLO_WORLD 的新值!

钩子有很多,我们仍在研究它们。根据你的反馈,可能会添加、删除或更改一些钩子。因此,如果你想做一些钩子还不允许你做的事情,请告诉我们!

使用 Yarn API

大多数 Yarn 钩子都使用各种参数调用,这些参数会告诉你更多有关调用钩子的上下文的信息。每个钩子的确切参数列表不同,但通常它们是 @yarnpkg/core 库中定义的类型。

在此示例中,我们将与 afterAllInstalled 钩子集成,以便在每次安装后打印有关依赖项树的一些基本信息。此钩子使用附加参数调用,该参数是公共 Project 实例,其中包含 Yarn 收集的大多数有关项目的信息:依赖项、包清单、工作空间信息等。

const fs = require(`fs`);
const util = require(`util`);

module.exports = {
name: `plugin-project-info`,
factory: require => {
const {structUtils} = require(`@yarnpkg/core`);

return {
default: {
hooks: {
afterAllInstalled(project) {
let descriptorCount = 0;
for (const descriptor of project.storedDescriptors.values())
if (!structUtils.isVirtualDescriptor(descriptor))
descriptorCount += 1;

let packageCount = 0;
for (const pkg of project.storedPackages.values())
if (!structUtils.isVirtualLocator(pkg))
packageCount += 1;

console.log(`This project contains ${descriptorCount} different descriptors that resolve to ${packageCount} packages`);
}
}
}
};
}
};

这变得有趣了。如你所见,我们从项目实例中访问了 storedDescriptorsstoredPackages 字段,并对其进行迭代以获取非虚拟项的数量(虚拟包在此处有更详细的描述此处)。这是一个非常简单的用例,但我们可以做更多的事情:项目根目录位于 cwd 属性中,工作空间公开为 工作空间,可以通过 storedResolutions 建立描述符和包之间的链接,等等。

请注意,我们只触及了 Project 类实例的表面!Yarn 核心提供了许多其他类(和钩子),允许你使用缓存、下载包、触发 http 请求等等。下次你想编写插件时,不妨看看,几乎肯定有一个实用程序可以让你避免重新实现轮子。

使用 YARN_PLUGINS 环境变量动态加载插件

虽然插件通常在 .yarnrc.yml 配置文件中声明,但它们表示用户界面配置,第三方工具不应在未经用户许可的情况下修改这些配置。

YARN_PLUGINS 环境变量是一个分号分隔的插件路径列表,Yarn 在调用时会动态加载这些路径。路径相对于 Yarn 被调用的 startingCwd 进行解析。

包可以使用此机制动态注册插件,并使用命令查询 Yarn API,而无需显式依赖 Yarn 包并处理潜在的版本不匹配问题。

官方钩子

我们的新网站还不支持生成钩子列表;抱歉 :(