Pular para o conteúdo

version-catalog

Impõe gerenciamento centralizado de versões em monorepos exigindo notação catalog: ou workspace: para todas as dependências.

Em monorepos com muitos pacotes, duplicar strings de versão entre arquivos package.json leva à divergência de versões e dificuldades de atualização. Esta regra garante que toda referência de dependência use catalog: (resolvido a partir de um catálogo central no package.json raiz) ou workspace: (para pacotes internos), nunca uma string semver crua.

A regra possui duas verificações: catalog-usage verifica se todas as dependências usam a notação correta, e catalog-completeness verifica se toda referência catalog: resolve para uma entrada real no catálogo raiz.

packages/api/package.json
{ "dependencies": { "zod": "^3.23.0", "hono": "^4.0.0" } }

Strings semver cruas ignoram o catálogo central e vão divergir entre pacotes.

packages/api/package.json
{
"dependencies": {
"zod": "catalog:",
"hono": "catalog:",
"@myorg/shared": "workspace:*"
}
}
package.json (root)
{ "catalog": { "zod": "^3.23.0", "hono": "^4.0.0" } }

Todas as versões são gerenciadas centralmente. Atualizar zod requer alterar apenas o catálogo raiz.

/// <reference path="../rules.d.ts" />
export default {
rules: {
"catalog-usage": {
description:
'All workspace dependencies must use "catalog:" or "workspace:" notation',
async check(ctx) {
const packageJsonFiles = [
...(await ctx.glob("packages/*/package.json")),
...(await ctx.glob("packages/*/*/package.json")),
];
for (const file of packageJsonFiles) {
const pkg = (await ctx.readJSON(file)) as Record<string, unknown>;
for (const depType of [
"dependencies",
"devDependencies",
"peerDependencies",
]) {
const deps = pkg[depType] as Record<string, string> | undefined;
if (!deps) continue;
for (const [name, version] of Object.entries(deps)) {
if (
typeof version === "string" &&
!version.startsWith("catalog:") &&
!version.startsWith("workspace:")
) {
ctx.report.violation({
message: `${file}: ${depType}.${name} uses "${version}" instead of "catalog:" or "workspace:"`,
file,
fix: `Change to "catalog:" and ensure the package is listed in root package.json catalog`,
});
}
}
}
}
},
},
"catalog-completeness": {
description:
"All catalog: references must resolve to entries in root package.json catalog",
async check(ctx) {
const rootPkg = (await ctx.readJSON("package.json")) as Record<
string,
unknown
>;
const catalog = (rootPkg.catalog ?? {}) as Record<string, string>;
const catalogKeys = new Set(Object.keys(catalog));
const packageJsonFiles = [
...(await ctx.glob("packages/*/package.json")),
...(await ctx.glob("packages/*/*/package.json")),
];
for (const file of packageJsonFiles) {
const pkg = (await ctx.readJSON(file)) as Record<string, unknown>;
for (const depType of [
"dependencies",
"devDependencies",
"peerDependencies",
]) {
const deps = pkg[depType] as Record<string, string> | undefined;
if (!deps) continue;
for (const [name, version] of Object.entries(deps)) {
if (
typeof version !== "string" ||
!version.startsWith("catalog:")
)
continue;
const catalogRef =
version === "catalog:"
? name
: version.slice("catalog:".length);
if (!catalogKeys.has(catalogRef)) {
ctx.report.violation({
message: `${file}: ${depType}.${name} references catalog:${catalogRef} but it is not in root catalog`,
file,
fix: `Add "${catalogRef}" to the catalog section in the root package.json`,
});
}
}
}
}
},
},
},
} satisfies RuleSet;

Em monorepos onde múltiplos pacotes compartilham dependências e você quer uma única fonte de verdade para versões (ex.: Bun workspaces com suporte a catalog, ou configurações similares).

Em repositórios de pacote único ou monorepos que usam uma estratégia diferente de gerenciamento de versões como atualizações em grupo do Renovate.