跳至主要内容

约束

信息

此页面记录了新的基于 JavaScript 的约束。基于 Prolog 的旧约束仍然受支持,但应视为已弃用。其文档可以在 此处 找到。

概述

约束是对一个非常基本需求的解决方案:您有很多 工作区,并且需要确保它们使用其依赖项的相同版本。或者它们不依赖于特定软件包。或者它们使用特定类型的依赖项。无论确切的逻辑是什么,您的目标都是相同的:您希望自动对所有工作区强制执行某种规则。这正是约束的用途。

我们可以强制执行什么?

我们的约束引擎目前支持两个主要目标

  • 工作区依赖项
  • 任意 package.json 字段

它目前不支持以下内容,但未来可能会支持(欢迎提交 PR!)

  • 传递依赖项
  • 项目结构

创建约束

通过在项目的根目录(存储库)中添加一个 yarn.config.cjs 文件来创建约束。此文件应导出一个带有 constraints 方法的对象。此方法将由约束引擎调用,并且必须使用提供的 API 定义要对项目强制执行的规则。

例如,以下 yarn.config.cjs 将强制所有 react 依赖项都设置为 18.0.0

module.exports = {
async constraints({Yarn}) {
for (const dep of Yarn.dependencies({ ident: 'react' })) {
dep.update(`18.0.0`);
}
},
};

以下内容将强制在所有工作区中正确设置 engines.node 字段

module.exports = {
async constraints({Yarn}) {
for (const workspace of Yarn.workspaces()) {
workspace.set('engines.node', `20.0.0`);
}
},
};

声明式模型

尽可能使用声明式模型定义约束:声明预期状态应是什么,Yarn 检查它是否与现实相符。如果不符,Yarn 将抛出一个错误(在不带参数调用 yarn constraints 时),或尝试自动修复问题(在调用 yarn constraints --fix 时)。

由于采用此声明式模型,您无需自己检查实际值。例如,此处的 if 条件是多余的,应将其删除

module.exports = {
async constraints({Yarn}) {
for (const dep of Yarn.dependencies({ ident: 'ts-node' })) {
// No need to check for the actual value! Just always call `update`.
if (dep.range !== `18.0.0`) {
dep.update(`18.0.0`);
}
}
},
};

TypeScript 支持

Yarn 提供类型,使编写约束变得更容易。要使用它们,请将依赖项添加到您的项目

$ yarn add @yarnpkg/types

然后,在您的 yarn.config.cjs 文件中,导入类型,特别是 defineConfig 函数,该函数会自动键入配置方法

/** @type {import('@yarnpkg/types')} */
const { defineConfig } = require('@yarnpkg/types');

module.exports = defineConfig({
async constraints({Yarn}) {
// `Yarn` is now well-typed ✨
},
});

您还可以手动检索类型,如果您将一些规则提取到帮助器函数中,这会很有用

/** @param {import('@yarnpkg/types').Yarn.Constraints.Workspace} dependency */
function expectMyCustomRule(dependency) {
// ...
}

您可以给类型设置别名,以便更轻松地使用它们

/**
* @typedef {import('@yarnpkg/types').Yarn.Constraints.Workspace} Workspace
* @typedef {import('@yarnpkg/types').Yarn.Constraints.Dependency} Dependency
*/

/** @param {Workspace} dependency */
function expectMyCustomRule(dependency) {
// ...
}

将所有内容放在一起

本节重新组合了一些约束示例。我们正在考虑稍后将其中一些作为内置帮助器提供,尽管它们往往包含一些对每个团队/公司来说都是独一无二的逻辑。

限制工作空间之间的依赖关系

此代码确保项目中的任何两个工作空间都不能在其 dependenciesdevDependencies 字段中列出相同的软件包,但具有不同的关联引用。

// @ts-check

/** @type {import('@yarnpkg/types')} */
const {defineConfig} = require(`@yarnpkg/types`);

/**
* This rule will enforce that a workspace MUST depend on the same version of
* a dependency as the one used by the other workspaces.
*
* @param {Context} context
*/
function enforceConsistentDependenciesAcrossTheProject({Yarn}) {
for (const dependency of Yarn.dependencies()) {
if (dependency.type === `peerDependencies`)
continue;

for (const otherDependency of Yarn.dependencies({ident: dependency.ident})) {
if (otherDependency.type === `peerDependencies`)
continue;

dependency.update(otherDependency.range);
}
}
}

module.exports = defineConfig({
constraints: async ctx => {
enforceConsistentDependenciesAcrossTheProject(ctx);
},
});