version-catalog
Impõe gerenciamento centralizado de versões em monorepos exigindo notação catalog: ou workspace: para todas as dependências.
Detalhes da regra
Seção intitulada “Detalhes da regra”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.
Exemplos de código incorreto
Seção intitulada “Exemplos de código incorreto”{ "dependencies": { "zod": "^3.23.0", "hono": "^4.0.0" } }Strings semver cruas ignoram o catálogo central e vão divergir entre pacotes.
Exemplos de código correto
Seção intitulada “Exemplos de código correto”{ "dependencies": { "zod": "catalog:", "hono": "catalog:", "@myorg/shared": "workspace:*" }}{ "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.
Implementação da regra
Seção intitulada “Implementação da regra”/// <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;Quando usar
Seção intitulada “Quando usar”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).
Quando não usar
Seção intitulada “Quando não usar”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.