約束
此頁面說明新的基於 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) {
// ...
}
將所有內容組合在一起
此區段重新組合幾個約束範例。我們考慮稍後提供其中一些範例作為內建的輔助程式,儘管它們通常包含一些對每個團隊/公司而言獨特的邏輯。
限制工作區之間的相依性
此程式碼確保專案中的兩個工作區無法在其 dependencies
或 devDependencies
欄位中列出相同的套件,但具有不同的關聯參照。
// @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);
},
});