约束
概述
¥Overview
约束允许跨 workspace 包执行规则。例如,这些规则可能包括以下内容:
¥Constraints allow enforcement of rules across workspace packages. For example, these rules might include the following:
-
确保所有软件包的依赖版本相同。
¥Ensuring the same version of dependencies across packages.
-
禁止在包中使用特定依赖。
¥Prohibiting the use of specific dependencies in packages.
我们可以执行什么?
¥What can we enforce?
我们的约束引擎目前支持两个主要目标:
¥Our constraint engine currently supports two main targets:
-
工作区依赖
¥Workspace dependencies
-
任意 package.json 字段
¥Arbitrary package.json fields
它目前不支持以下内容,但将来可能会支持(欢迎 PR!):
¥It currently doesn't support the following, but might in the future (PRs welcome!):
-
传递依赖
¥Transitive dependencies
-
项目结构
¥Project structure
创建约束
¥Creating a constraint
通过在项目(存储库)的根目录下添加 yarn.config.cjs
文件来创建约束。此文件应使用 constraints
方法导出对象。此方法将由约束引擎调用,并且必须使用提供的 API 定义要在项目上执行的规则。
¥Constraints are created by adding a yarn.config.cjs
file at the root of your project (repository). This file should export an object with a constraints
method. This method will be called by the constraints engine, and must define the rules to enforce on the project, using the provided API.
例如,以下 yarn.config.cjs
将强制将所有 react
依赖设置为 18.0.0
。
¥For example, the following yarn.config.cjs
will enforce that all react
dependencies are set to 18.0.0
.
module.exports = {
async constraints({Yarn}) {
for (const dep of Yarn.dependencies({ ident: 'react' })) {
dep.update(`18.0.0`);
}
},
};
以下内容将强制在所有工作区中正确设置 engines.node
字段:
¥And the following will enforce that the engines.node
field is properly set in all workspaces:
module.exports = {
async constraints({Yarn}) {
for (const workspace of Yarn.workspaces()) {
workspace.set('engines.node', `20.0.0`);
}
},
};
声明式模型
¥Declarative model
尽可能使用声明式模型定义约束:你声明预期状态应该是什么,Yarn 检查它是否与现实相符。如果没有,Yarn 要么会抛出错误(当调用没有参数的 yarn constraints
时),要么会尝试自动修复问题(当调用 yarn constraints --fix
时)。
¥As much as possible, constraints are defined using a declarative model: you declare what the expected state should be, and Yarn checks whether it matches the reality or not. If it doesn't, Yarn will either throw an error (when calling yarn constraints
without arguments), or attempt to automatically fix the issue (when calling yarn constraints --fix
).
由于这种声明式模型,你不需要自己检查实际值。例如,此处的 if
条件是多余的,应将其删除:
¥Because of this declarative model, you don't need to check the actual values yourself. For instance, the if
condition here is extraneous and should be removed:
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 支持
¥TypeScript support
Yarn 附带的类型使编写约束变得更加容易。要使用它们,请将依赖添加到你的项目中:
¥Yarn ships types that make it easier to write constraints. To use them, add the dependency to your project:
$ yarn add @yarnpkg/types
然后,在你的 yarn.config.cjs
文件中,导入类型,特别是自动输入配置方法的 defineConfig
函数:
¥Then, in your yarn.config.cjs
file, import the types, in particular the defineConfig
function which automatically type the configuration methods:
/** @type {import('@yarnpkg/types')} */
const { defineConfig } = require('@yarnpkg/types');
module.exports = defineConfig({
async constraints({Yarn}) {
// `Yarn` is now well-typed ✨
},
});
你还可以手动检索类型,如果你将一些规则提取到辅助函数中,这将很有用:
¥You can also retrieve the types manually, which can be useful if you extract some rules into helper functions:
/** @param {import('@yarnpkg/types').Yarn.Constraints.Workspace} dependency */
function expectMyCustomRule(dependency) {
// ...
}
你可以为类型添加别名,使它们更易于使用:
¥You can alias the types to make them a little easier to use:
/**
* @typedef {import('@yarnpkg/types').Yarn.Constraints.Workspace} Workspace
* @typedef {import('@yarnpkg/types').Yarn.Constraints.Dependency} Dependency
*/
/** @param {Workspace} dependency */
function expectMyCustomRule(dependency) {
// ...
}
将它们放在一起
¥Putting it all together
本节重新组合了几个约束示例。我们正在考虑稍后将其中一些作为内置助手提供,尽管它们往往包含每个团队/公司独有的一些逻辑。
¥This section regroups a couple of constraint examples. We are thinking to provide some of them as builtin helpers later on, although they tend to often contain some logic unique to each team / company.
限制工作区之间的依赖
¥Restrict dependencies between workspaces
此代码可确保项目中没有两个工作区可以在其 dependencies
或 devDependencies
字段中列出相同的包,但具有不同的关联引用。
¥This code ensures that no two workspaces in your project can list the same packages in their dependencies
or devDependencies
fields but with different associated references.
// @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);
},
});