diff --git a/.changeset/clean-catalog-overrides.md b/.changeset/clean-catalog-overrides.md new file mode 100644 index 0000000000..80439bb65b --- /dev/null +++ b/.changeset/clean-catalog-overrides.md @@ -0,0 +1,6 @@ +--- +"@pnpm/workspace.workspace-manifest-writer": patch +"pnpm": patch +--- + +Prevent catalog entries from being removed by `cleanupUnusedCatalogs` when they are referenced only from workspace `overrides` in `pnpm-workspace.yaml`. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 88477f736e..0527b5c036 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9112,6 +9112,9 @@ importers: '@pnpm/catalogs.types': specifier: workspace:* version: link:../../catalogs/types + '@pnpm/config.parse-overrides': + specifier: workspace:* + version: link:../../config/parse-overrides '@pnpm/constants': specifier: workspace:* version: link:../../core/constants diff --git a/workspace/workspace-manifest-writer/package.json b/workspace/workspace-manifest-writer/package.json index 9901e50c41..1494b9b8c0 100644 --- a/workspace/workspace-manifest-writer/package.json +++ b/workspace/workspace-manifest-writer/package.json @@ -32,6 +32,7 @@ }, "dependencies": { "@pnpm/catalogs.types": "workspace:*", + "@pnpm/config.parse-overrides": "workspace:*", "@pnpm/constants": "workspace:*", "@pnpm/lockfile.types": "workspace:*", "@pnpm/object.key-sorting": "workspace:*", diff --git a/workspace/workspace-manifest-writer/src/index.ts b/workspace/workspace-manifest-writer/src/index.ts index 72389260b9..0b8297a6d3 100644 --- a/workspace/workspace-manifest-writer/src/index.ts +++ b/workspace/workspace-manifest-writer/src/index.ts @@ -3,6 +3,7 @@ import path from 'node:path' import util from 'node:util' import type { Catalogs } from '@pnpm/catalogs.types' +import { parsePkgAndParentSelector } from '@pnpm/config.parse-overrides' import { type GLOBAL_CONFIG_YAML_FILENAME, WORKSPACE_MANIFEST_FILENAME } from '@pnpm/constants' import type { ResolvedCatalogEntry } from '@pnpm/lockfile.types' import { sortKeysByPriority } from '@pnpm/object.key-sorting' @@ -168,14 +169,24 @@ function removePackagesFromWorkspaceCatalog (manifest: Partial !packageReferences[pkg]?.has('catalog:') @@ -226,3 +237,10 @@ function removePackagesFromWorkspaceCatalog (manifest: Partial>, pkgName: string, version: string): void { + if (!packageReferences[pkgName]) { + packageReferences[pkgName] = new Set() + } + packageReferences[pkgName].add(version) +} diff --git a/workspace/workspace-manifest-writer/test/removeCatalogs.test.ts b/workspace/workspace-manifest-writer/test/removeCatalogs.test.ts index d381b2592b..7ed093f78c 100644 --- a/workspace/workspace-manifest-writer/test/removeCatalogs.test.ts +++ b/workspace/workspace-manifest-writer/test/removeCatalogs.test.ts @@ -415,3 +415,97 @@ test('when allProjects is undefined should not cleanup unused catalogs', async ( }, }) }) + +test('keep catalogs referenced only in workspace overrides', async () => { + const dir = tempDir(false) + const filePath = path.join(dir, WORKSPACE_MANIFEST_FILENAME) + writeYamlFileSync(filePath, { + catalog: { + foo: '1.0.0', + }, + catalogs: { + bar: { + '@scope/def': '2.0.0', + }, + }, + overrides: { + foo: 'catalog:', + '@scope/parent@1>@scope/def': 'catalog:bar', + }, + }) + + prepare({ + dependencies: { + zoo: '^1.0.0', + }, + }, { tempDir: dir }) + const allProjects = await findPackages(dir) + + await updateWorkspaceManifest(dir, { + cleanupUnusedCatalogs: true, + allProjects, + }) + + expect(readYamlFileSync(filePath)).toStrictEqual({ + catalog: { + foo: '1.0.0', + }, + catalogs: { + bar: { + '@scope/def': '2.0.0', + }, + }, + overrides: { + foo: 'catalog:', + '@scope/parent@1>@scope/def': 'catalog:bar', + }, + }) +}) + +test('remove catalogs unused by dependencies and workspace overrides', async () => { + const dir = tempDir(false) + const filePath = path.join(dir, WORKSPACE_MANIFEST_FILENAME) + writeYamlFileSync(filePath, { + catalog: { + foo: '1.0.0', + unusedDefault: '2.0.0', + }, + catalogs: { + bar: { + def: '2.0.0', + unusedNamed: '3.0.0', + }, + }, + overrides: { + foo: 'catalog:', + def: 'catalog:bar', + }, + }) + + prepare({ + dependencies: { + zoo: '^1.0.0', + }, + }, { tempDir: dir }) + const allProjects = await findPackages(dir) + + await updateWorkspaceManifest(dir, { + cleanupUnusedCatalogs: true, + allProjects, + }) + + expect(readYamlFileSync(filePath)).toStrictEqual({ + catalog: { + foo: '1.0.0', + }, + catalogs: { + bar: { + def: '2.0.0', + }, + }, + overrides: { + foo: 'catalog:', + def: 'catalog:bar', + }, + }) +}) diff --git a/workspace/workspace-manifest-writer/tsconfig.json b/workspace/workspace-manifest-writer/tsconfig.json index 415a296f3d..16fdff7018 100644 --- a/workspace/workspace-manifest-writer/tsconfig.json +++ b/workspace/workspace-manifest-writer/tsconfig.json @@ -18,6 +18,9 @@ { "path": "../../catalogs/types" }, + { + "path": "../../config/parse-overrides" + }, { "path": "../../core/constants" },