fix(workspace): treat catalog refs in workspace overrides as used during cleanupUnusedCatalogs (#11075)

* fix(workspace): treat catalog refs in workspace overrides as used during cleanupUnusedCatalogs

* fix: update

* fix: update
This commit is contained in:
btea
2026-03-24 23:43:13 +08:00
committed by GitHub
parent 74cdef5e46
commit a1807b11d3
6 changed files with 129 additions and 4 deletions

View File

@@ -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`.

3
pnpm-lock.yaml generated
View File

@@ -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

View File

@@ -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:*",

View File

@@ -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<WorkspaceManifest
if (!deps) continue
for (const [pkgName, version] of Object.entries(deps)) {
if (!packageReferences[pkgName]) {
packageReferences[pkgName] = new Set()
}
packageReferences[pkgName].add(version)
addPackageReference(packageReferences, pkgName, version)
}
}
}
for (const [selector, version] of Object.entries(manifest.overrides ?? {})) {
if (!version.startsWith('catalog:')) {
continue
}
let pkgName: string
try {
pkgName = parsePkgAndParentSelector(selector).targetPkg.name
} catch {
continue
}
addPackageReference(packageReferences, pkgName, version)
}
if (manifest.catalog) {
const packagesToRemove = Object.keys(manifest.catalog).filter(pkg =>
!packageReferences[pkg]?.has('catalog:')
@@ -226,3 +237,10 @@ function removePackagesFromWorkspaceCatalog (manifest: Partial<WorkspaceManifest
return shouldBeUpdated
}
function addPackageReference (packageReferences: Record<string, Set<string>>, pkgName: string, version: string): void {
if (!packageReferences[pkgName]) {
packageReferences[pkgName] = new Set()
}
packageReferences[pkgName].add(version)
}

View File

@@ -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',
},
})
})

View File

@@ -18,6 +18,9 @@
{
"path": "../../catalogs/types"
},
{
"path": "../../config/parse-overrides"
},
{
"path": "../../core/constants"
},