feat: add cleanupUnusedCatalogs config (#9793)

This commit is contained in:
btea
2025-08-14 18:26:51 +08:00
committed by GitHub
parent b84c71d6bf
commit 8747b4e7f6
10 changed files with 472 additions and 228 deletions

View File

@@ -0,0 +1,7 @@
---
"@pnpm/plugin-commands-installation": minor
"@pnpm/workspace.manifest-writer": minor
"pnpm": minor
---
Added the `cleanupUnusedCatalogs` configuration. When set to `true`, pnpm will remove unused catalog entries during installation [#9793](https://github.com/pnpm/pnpm/pull/9793).

View File

@@ -152,6 +152,7 @@ export interface Config extends OptionsFromRootManifest {
workspacePackagePatterns?: string[]
catalogs?: Catalogs
catalogMode?: 'strict' | 'prefer' | 'manual'
cleanupUnusedCatalogs?: boolean
reporter?: string
aggregateOutput: boolean
linkWorkspacePackages: boolean | 'deep'

View File

@@ -29,6 +29,7 @@ export interface StrictInstallOptions {
autoInstallPeersFromHighestMatch: boolean
catalogs: Catalogs
catalogMode: 'strict' | 'prefer' | 'manual'
cleanupUnusedCatalogs: boolean
frozenLockfile: boolean
frozenLockfileIfExists: boolean
enableGlobalVirtualStore: boolean
@@ -244,6 +245,7 @@ const defaults = (opts: InstallOptions): StrictInstallOptions => {
!process.setgid ||
process.getuid?.() !== 0,
catalogMode: 'manual',
cleanupUnusedCatalogs: false,
useLockfile: true,
saveLockfile: true,
useGitBranchLockfile: false,

View File

@@ -54,6 +54,7 @@ export type InstallDepsOptions = Pick<Config,
| 'bin'
| 'catalogs'
| 'catalogMode'
| 'cleanupUnusedCatalogs'
| 'cliOptions'
| 'dedupePeerDependents'
| 'depth'
@@ -321,6 +322,8 @@ when running add/update with the --workspace option')
writeProjectManifest(updatedProject.manifest),
updateWorkspaceManifest(opts.workspaceDir ?? opts.dir, {
updatedCatalogs,
cleanupUnusedCatalogs: opts.cleanupUnusedCatalogs,
allProjects: opts.allProjects,
}),
])
}
@@ -346,6 +349,8 @@ when running add/update with the --workspace option')
writeProjectManifest(updatedManifest),
updateWorkspaceManifest(opts.workspaceDir ?? opts.dir, {
updatedCatalogs,
cleanupUnusedCatalogs: opts.cleanupUnusedCatalogs,
allProjects,
}),
])
}

View File

@@ -82,6 +82,7 @@ export type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
| 'lockfileIncludeTarballUrl'
| 'sharedWorkspaceLockfile'
| 'tag'
| 'cleanupUnusedCatalogs'
> & {
include?: IncludedDependencies
includeDirect?: IncludedDependencies
@@ -293,6 +294,8 @@ export async function recursive (
})
promises.push(updateWorkspaceManifest(opts.workspaceDir, {
updatedCatalogs,
cleanupUnusedCatalogs: opts.cleanupUnusedCatalogs,
allProjects,
}))
await Promise.all(promises)
}
@@ -441,6 +444,8 @@ export async function recursive (
await updateWorkspaceManifest(opts.workspaceDir, {
updatedCatalogs,
cleanupUnusedCatalogs: opts.cleanupUnusedCatalogs,
allProjects,
})
if (

282
pnpm-lock.yaml generated
View File

@@ -812,7 +812,7 @@ importers:
version: link:../packages/logger
'@pnpm/meta-updater':
specifier: 'catalog:'
version: 2.0.6(@types/node@22.15.29)(typanion@3.14.0)
version: 2.0.6(@types/node@18.19.34)(typanion@3.14.0)
'@pnpm/object.key-sorting':
specifier: workspace:*
version: link:../object/key-sorting
@@ -8496,6 +8496,9 @@ importers:
'@pnpm/object.key-sorting':
specifier: workspace:*
version: link:../../object/key-sorting
'@pnpm/types':
specifier: workspace:*
version: link:../../packages/types
'@pnpm/workspace.read-manifest':
specifier: workspace:*
version: link:../read-manifest
@@ -8506,6 +8509,12 @@ importers:
specifier: 'catalog:'
version: 5.0.0
devDependencies:
'@pnpm/fs.find-packages':
specifier: workspace:*
version: link:../../fs/find-packages
'@pnpm/prepare':
specifier: workspace:*
version: link:../../__utils__/prepare
'@pnpm/prepare-temp-dir':
specifier: workspace:*
version: link:../../__utils__/prepare-temp-dir
@@ -8840,10 +8849,6 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/runtime@7.27.4':
resolution: {integrity: sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA==}
engines: {node: '>=6.9.0'}
'@babel/runtime@7.28.2':
resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==}
engines: {node: '>=6.9.0'}
@@ -15885,8 +15890,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/runtime@7.27.4': {}
'@babel/runtime@7.28.2': {}
'@babel/template@7.27.2':
@@ -16585,14 +16588,14 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0(@babel/types@7.26.10)
'@jest/types': 29.6.3
'@types/node': 18.19.34
'@types/node': 22.15.29
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.9.0
exit: 0.1.2
graceful-fs: 4.2.11(patch_hash=68ebc232025360cb3dcd3081f4067f4e9fc022ab6b6f71a3230e86c7a5b337d1)
jest-changed-files: 29.7.0
jest-config: 29.7.0(@babel/types@7.26.10)(@types/node@18.19.34)(ts-node@10.9.2(@types/node@18.19.34)(typescript@5.5.4))
jest-config: 29.7.0(@babel/types@7.26.10)(@types/node@22.15.29)(ts-node@10.9.2(@types/node@18.19.34)(typescript@5.5.4))
jest-haste-map: 29.7.0
jest-message-util: 29.7.0
jest-regex-util: 29.6.3
@@ -16730,7 +16733,7 @@ snapshots:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 18.19.34
'@types/node': 22.15.29
'@types/yargs': 17.0.33
chalk: 4.1.2
@@ -16796,7 +16799,7 @@ snapshots:
'@npmcli/fs@4.0.0':
dependencies:
semver: 7.7.1
semver: 7.7.2
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -16866,28 +16869,6 @@ snapshots:
- supports-color
- typanion
'@pnpm/cli-utils@1000.1.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/cli-meta': 1000.0.8
'@pnpm/config': 1003.1.1(@pnpm/logger@1001.0.0)
'@pnpm/config.deps-installer': 1000.0.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/default-reporter': 1002.0.1(@pnpm/logger@1001.0.0)
'@pnpm/error': 1000.0.2
'@pnpm/logger': 1001.0.0
'@pnpm/manifest-utils': 1001.0.1(@pnpm/logger@1001.0.0)
'@pnpm/package-is-installable': 1000.0.10(@pnpm/logger@1001.0.0)
'@pnpm/pnpmfile': 1001.2.2(@pnpm/logger@1001.0.0)
'@pnpm/read-project-manifest': 1000.0.11
'@pnpm/store-connection-manager': 1002.0.3(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/types': 1000.6.0
chalk: 4.1.2
load-json-file: 6.2.0
transitivePeerDependencies:
- '@pnpm/worker'
- domexception
- supports-color
- typanion
'@pnpm/client@1000.0.19(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@18.19.34))(typanion@3.14.0)':
dependencies:
'@pnpm/default-resolver': 1002.0.2(@pnpm/logger@1001.0.0)
@@ -16907,25 +16888,6 @@ snapshots:
- supports-color
- typanion
'@pnpm/client@1000.0.19(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/default-resolver': 1002.0.2(@pnpm/logger@1001.0.0)
'@pnpm/directory-fetcher': 1000.1.7(@pnpm/logger@1001.0.0)
'@pnpm/fetch': 1000.2.2(@pnpm/logger@1001.0.0)
'@pnpm/fetching-types': 1000.1.0
'@pnpm/git-fetcher': 1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/network.auth-header': 1000.0.3
'@pnpm/resolver-base': 1003.0.1
'@pnpm/tarball-fetcher': 1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/types': 1000.6.0
ramda: '@pnpm/ramda@0.28.1'
transitivePeerDependencies:
- '@pnpm/logger'
- '@pnpm/worker'
- domexception
- supports-color
- typanion
'@pnpm/colorize-semver-diff@1.0.1':
dependencies:
chalk: 4.1.2
@@ -16959,28 +16921,6 @@ snapshots:
- domexception
- supports-color
'@pnpm/config.deps-installer@1000.0.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))':
dependencies:
'@pnpm/config.config-writer': 1000.0.5
'@pnpm/core-loggers': 1001.0.1(@pnpm/logger@1001.0.0)
'@pnpm/error': 1000.0.2
'@pnpm/fetch': 1000.2.2(@pnpm/logger@1001.0.0)
'@pnpm/logger': 1001.0.0
'@pnpm/network.auth-header': 1000.0.3
'@pnpm/npm-resolver': 1004.0.1(@pnpm/logger@1001.0.0)
'@pnpm/package-store': 1002.0.4(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/parse-wanted-dependency': 1001.0.0
'@pnpm/pick-registry-for-package': 1000.0.8
'@pnpm/read-modules-dir': 1000.0.0
'@pnpm/read-package-json': 1000.0.9
'@pnpm/types': 1000.6.0
'@zkochan/rimraf': 3.0.2
get-npm-tarball-url: 2.1.0
transitivePeerDependencies:
- '@pnpm/worker'
- domexception
- supports-color
'@pnpm/config.env-replace@1.1.0': {}
'@pnpm/config.env-replace@3.0.1': {}
@@ -17290,19 +17230,6 @@ snapshots:
- supports-color
- typanion
'@pnpm/git-fetcher@1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/fetcher-base': 1000.0.11
'@pnpm/fs.packlist': 2.0.0
'@pnpm/logger': 1001.0.0
'@pnpm/prepare-package': 1000.0.16(@pnpm/logger@1001.0.0)(typanion@3.14.0)
'@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.29)
'@zkochan/rimraf': 3.0.2
execa: safe-execa@0.1.2
transitivePeerDependencies:
- supports-color
- typanion
'@pnpm/git-resolver@1001.0.2(@pnpm/logger@1001.0.0)':
dependencies:
'@pnpm/fetch': 1000.2.2(@pnpm/logger@1001.0.0)
@@ -17449,24 +17376,6 @@ snapshots:
- supports-color
- typanion
'@pnpm/meta-updater@2.0.6(@types/node@22.15.29)(typanion@3.14.0)':
dependencies:
'@pnpm/find-workspace-dir': 1000.1.0
'@pnpm/logger': 1001.0.0
'@pnpm/types': 1000.6.0
'@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.29)
'@pnpm/workspace.find-packages': 1000.0.25(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/workspace.read-manifest': 1000.1.5
load-json-file: 7.0.1
meow: 11.0.0
print-diff: 2.0.0
write-json-file: 5.0.0
transitivePeerDependencies:
- '@types/node'
- domexception
- supports-color
- typanion
'@pnpm/network.agent@2.0.3':
dependencies:
'@pnpm/network.config': 2.1.0
@@ -17647,30 +17556,6 @@ snapshots:
semver: 7.7.2
ssri: 10.0.5
'@pnpm/package-requester@1004.0.2(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))':
dependencies:
'@pnpm/core-loggers': 1001.0.1(@pnpm/logger@1001.0.0)
'@pnpm/dependency-path': 1000.0.9
'@pnpm/error': 1000.0.2
'@pnpm/fetcher-base': 1000.0.11
'@pnpm/graceful-fs': 1000.0.0
'@pnpm/logger': 1001.0.0
'@pnpm/package-is-installable': 1000.0.10(@pnpm/logger@1001.0.0)
'@pnpm/pick-fetcher': 1000.0.0
'@pnpm/read-package-json': 1000.0.9
'@pnpm/resolver-base': 1003.0.1
'@pnpm/store-controller-types': 1003.0.2
'@pnpm/store.cafs': 1000.0.13
'@pnpm/types': 1000.6.0
'@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.29)
p-defer: 3.0.0
p-limit: 3.1.0
p-queue: 6.6.2
promise-share: 1.0.0
ramda: '@pnpm/ramda@0.28.1'
semver: 7.7.2
ssri: 10.0.5
'@pnpm/package-store@1002.0.4(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@18.19.34))':
dependencies:
'@pnpm/create-cafs-store': 1000.0.14(@pnpm/logger@1001.0.0)
@@ -17687,22 +17572,6 @@ snapshots:
ramda: '@pnpm/ramda@0.28.1'
ssri: 10.0.5
'@pnpm/package-store@1002.0.4(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))':
dependencies:
'@pnpm/create-cafs-store': 1000.0.14(@pnpm/logger@1001.0.0)
'@pnpm/fetcher-base': 1000.0.11
'@pnpm/logger': 1001.0.0
'@pnpm/package-requester': 1004.0.2(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/resolver-base': 1003.0.1
'@pnpm/store-controller-types': 1003.0.2
'@pnpm/store.cafs': 1000.0.13
'@pnpm/types': 1000.6.0
'@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.29)
'@zkochan/rimraf': 3.0.2
load-json-file: 6.2.0
ramda: '@pnpm/ramda@0.28.1'
ssri: 10.0.5
'@pnpm/parse-overrides@1000.0.2':
dependencies:
'@pnpm/catalogs.resolver': 1000.0.2
@@ -17919,25 +17788,6 @@ snapshots:
- supports-color
- typanion
'@pnpm/store-connection-manager@1002.0.3(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/cli-meta': 1000.0.8
'@pnpm/client': 1000.0.19(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/config': 1003.1.1(@pnpm/logger@1001.0.0)
'@pnpm/error': 1000.0.2
'@pnpm/logger': 1001.0.0
'@pnpm/package-store': 1002.0.4(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))
'@pnpm/server': 1001.0.4(@pnpm/logger@1001.0.0)
'@pnpm/store-path': 1000.0.2
'@zkochan/diable': 1.0.2
delay: 5.0.0
dir-is-case-sensitive: 2.0.0
transitivePeerDependencies:
- '@pnpm/worker'
- domexception
- supports-color
- typanion
'@pnpm/store-controller-types@1001.0.3':
dependencies:
'@pnpm/fetcher-base': 1000.0.5
@@ -18011,28 +17861,6 @@ snapshots:
- supports-color
- typanion
'@pnpm/tarball-fetcher@1001.0.8(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/core-loggers': 1001.0.1(@pnpm/logger@1001.0.0)
'@pnpm/error': 1000.0.2
'@pnpm/fetcher-base': 1000.0.11
'@pnpm/fetching-types': 1000.1.0
'@pnpm/fs.packlist': 2.0.0
'@pnpm/graceful-fs': 1000.0.0
'@pnpm/logger': 1001.0.0
'@pnpm/prepare-package': 1000.0.16(@pnpm/logger@1001.0.0)(typanion@3.14.0)
'@pnpm/worker': 1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.29)
'@zkochan/retry': 0.2.0
lodash.throttle: 4.1.1
p-map-values: 1.0.0
path-temp: 2.1.0
ramda: '@pnpm/ramda@0.28.1'
rename-overwrite: 6.0.3
transitivePeerDependencies:
- domexception
- supports-color
- typanion
'@pnpm/tarball-resolver@1002.0.2':
dependencies:
'@pnpm/fetching-types': 1000.1.0
@@ -18080,26 +17908,6 @@ snapshots:
transitivePeerDependencies:
- '@types/node'
'@pnpm/worker@1000.1.7(@pnpm/logger@packages+logger)(@types/node@22.15.29)':
dependencies:
'@pnpm/cafs-types': 1000.0.0
'@pnpm/create-cafs-store': 1000.0.14(@pnpm/logger@1001.0.0)
'@pnpm/crypto.polyfill': 1000.1.0
'@pnpm/error': 1000.0.2
'@pnpm/exec.pkg-requires-build': 1000.0.8
'@pnpm/fs.hard-link-dir': 1000.0.1(@pnpm/logger@1001.0.0)
'@pnpm/graceful-fs': 1000.0.0
'@pnpm/logger': link:packages/logger
'@pnpm/store.cafs': 1000.0.13
'@pnpm/symlink-dependency': 1000.0.9(@pnpm/logger@1001.0.0)
'@rushstack/worker-pool': 0.4.9(@types/node@22.15.29)
is-windows: 1.0.2
load-json-file: 6.2.0
p-limit: 3.1.0
shell-quote: 1.8.3
transitivePeerDependencies:
- '@types/node'
'@pnpm/workspace.find-packages@1000.0.15(@pnpm/logger@1000.0.0)':
dependencies:
'@pnpm/cli-utils': 1000.0.15(@pnpm/logger@1000.0.0)
@@ -18123,20 +17931,6 @@ snapshots:
- supports-color
- typanion
'@pnpm/workspace.find-packages@1000.0.25(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)':
dependencies:
'@pnpm/cli-utils': 1000.1.5(@pnpm/logger@1001.0.0)(@pnpm/worker@1000.1.7(@pnpm/logger@1001.0.0)(@types/node@22.15.29))(typanion@3.14.0)
'@pnpm/constants': 1001.1.0
'@pnpm/fs.find-packages': 1000.0.11
'@pnpm/logger': 1001.0.0
'@pnpm/types': 1000.6.0
'@pnpm/util.lex-comparator': 3.0.2
transitivePeerDependencies:
- '@pnpm/worker'
- domexception
- supports-color
- typanion
'@pnpm/workspace.manifest-writer@1000.1.4':
dependencies:
'@pnpm/constants': 1001.1.0
@@ -18338,7 +18132,7 @@ snapshots:
'@types/isexe@2.0.2':
dependencies:
'@types/node': 18.19.34
'@types/node': 22.15.29
'@types/istanbul-lib-coverage@2.0.6': {}
@@ -18461,7 +18255,7 @@ snapshots:
'@types/touch@3.1.5':
dependencies:
'@types/node': 18.19.34
'@types/node': 22.15.29
'@types/treeify@1.0.3': {}
@@ -19817,7 +19611,7 @@ snapshots:
date-fns@2.30.0:
dependencies:
'@babel/runtime': 7.27.4
'@babel/runtime': 7.28.2
dayjs@1.11.7: {}
@@ -19922,7 +19716,7 @@ snapshots:
didyoumean2@6.0.1:
dependencies:
'@babel/runtime': 7.27.4
'@babel/runtime': 7.28.2
fastest-levenshtein: 1.0.16
lodash.deburr: 4.1.0
@@ -21529,6 +21323,38 @@ snapshots:
- babel-plugin-macros
- supports-color
jest-config@29.7.0(@babel/types@7.26.10)(@types/node@22.15.29)(ts-node@10.9.2(@types/node@18.19.34)(typescript@5.5.4)):
dependencies:
'@babel/core': 7.26.10
'@jest/test-sequencer': 29.7.0
'@jest/types': 29.6.3
babel-jest: 29.7.0(@babel/core@7.26.10)(@babel/types@7.26.10)
chalk: 4.1.2
ci-info: 3.9.0
deepmerge: 4.3.1
glob: 7.2.3
graceful-fs: 4.2.11(patch_hash=68ebc232025360cb3dcd3081f4067f4e9fc022ab6b6f71a3230e86c7a5b337d1)
jest-circus: 29.7.0(@babel/types@7.26.10)
jest-environment-node: 29.7.0
jest-get-type: 29.6.3
jest-regex-util: 29.6.3
jest-resolve: 29.7.0
jest-runner: 29.7.0(@babel/types@7.26.10)
jest-util: 29.7.0
jest-validate: 29.7.0
micromatch: 4.0.8
parse-json: 5.2.0
pretty-format: 29.7.0
slash: 3.0.0
strip-json-comments: 3.1.1
optionalDependencies:
'@types/node': 22.15.29
ts-node: 10.9.2(@types/node@18.19.34)(typescript@5.5.4)
transitivePeerDependencies:
- '@babel/types'
- babel-plugin-macros
- supports-color
jest-diff@29.7.0:
dependencies:
chalk: 4.1.2
@@ -21713,7 +21539,7 @@ snapshots:
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/node': 18.19.34
'@types/node': 22.15.29
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11(patch_hash=68ebc232025360cb3dcd3081f4067f4e9fc022ab6b6f71a3230e86c7a5b337d1)
@@ -22350,7 +22176,7 @@ snapshots:
make-fetch-happen: 14.0.3
nopt: 8.1.0
proc-log: 5.0.0
semver: 7.7.1
semver: 7.7.2
tar: 7.4.3
which: 5.0.0
transitivePeerDependencies:
@@ -23266,7 +23092,7 @@ snapshots:
semver-range-intersect@0.3.1:
dependencies:
'@types/semver': 6.2.7
semver: 7.7.1
semver: 7.7.2
semver-utils@1.1.4: {}
@@ -24203,7 +24029,7 @@ snapshots:
version-selector-type@3.0.0:
dependencies:
semver: 7.7.1
semver: 7.7.2
vfile-message@2.0.4:
dependencies:

View File

@@ -35,11 +35,14 @@
"@pnpm/constants": "workspace:*",
"@pnpm/lockfile.types": "workspace:*",
"@pnpm/object.key-sorting": "workspace:*",
"@pnpm/types": "workspace:*",
"@pnpm/workspace.read-manifest": "workspace:*",
"ramda": "catalog:",
"write-yaml-file": "catalog:"
},
"devDependencies": {
"@pnpm/fs.find-packages": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/prepare-temp-dir": "workspace:*",
"@pnpm/workspace.manifest-writer": "workspace:*",
"@types/ramda": "catalog:",

View File

@@ -7,6 +7,9 @@ import { WORKSPACE_MANIFEST_FILENAME } from '@pnpm/constants'
import writeYamlFile from 'write-yaml-file'
import equals from 'ramda/src/equals'
import { sortKeysByPriority } from '@pnpm/object.key-sorting'
import {
type Project,
} from '@pnpm/types'
async function writeManifestFile (dir: string, manifest: Partial<WorkspaceManifest>): Promise<void> {
manifest = sortKeysByPriority({
@@ -25,9 +28,14 @@ async function writeManifestFile (dir: string, manifest: Partial<WorkspaceManife
export async function updateWorkspaceManifest (dir: string, opts: {
updatedFields?: Partial<WorkspaceManifest>
updatedCatalogs?: Catalogs
cleanupUnusedCatalogs?: boolean
allProjects?: Project[]
}): Promise<void> {
const manifest = await readWorkspaceManifest(dir) ?? {} as WorkspaceManifest
let shouldBeUpdated = opts.updatedCatalogs != null && addCatalogs(manifest, opts.updatedCatalogs)
if (opts.cleanupUnusedCatalogs) {
shouldBeUpdated = removePackagesFromWorkspaceCatalog(manifest, opts.allProjects ?? []) || shouldBeUpdated
}
for (const [key, value] of Object.entries(opts.updatedFields ?? {})) {
if (!equals(manifest[key as keyof WorkspaceManifest], value)) {
@@ -90,3 +98,83 @@ function addCatalogs (manifest: Partial<WorkspaceManifest>, newCatalogs: Catalog
return shouldBeUpdated
}
function removePackagesFromWorkspaceCatalog (manifest: Partial<WorkspaceManifest>, packagesJson: Project[]): boolean {
let shouldBeUpdated = false
if (manifest.catalog == null && manifest.catalogs == null) {
return shouldBeUpdated
}
const packageReferences: Record<string, Set<string>> = {}
for (const pkg of packagesJson) {
const pkgManifest = pkg.manifest
const dependencyTypes = [
pkgManifest.dependencies,
pkgManifest.devDependencies,
pkgManifest.optionalDependencies,
pkgManifest.peerDependencies,
]
for (const deps of dependencyTypes) {
if (!deps) continue
for (const [pkgName, version] of Object.entries(deps)) {
if (!packageReferences[pkgName]) {
packageReferences[pkgName] = new Set()
}
packageReferences[pkgName].add(version)
}
}
}
if (manifest.catalog) {
const packagesToRemove = Object.keys(manifest.catalog).filter(pkg =>
!packageReferences[pkg]?.has('catalog:')
)
for (const pkg of packagesToRemove) {
delete manifest.catalog![pkg]
shouldBeUpdated = true
}
if (Object.keys(manifest.catalog).length === 0) {
delete manifest.catalog
shouldBeUpdated = true
}
}
if (manifest.catalogs) {
const catalogsToRemove: string[] = []
for (const [catalogName, catalog] of Object.entries(manifest.catalogs)) {
if (!catalog) continue
const packagesToRemove = Object.keys(catalog).filter(pkg => {
const references = packageReferences[pkg]
return !references?.has(`catalog:${catalogName}`) && !references?.has('catalog:')
})
for (const pkg of packagesToRemove) {
delete catalog[pkg]
shouldBeUpdated = true
}
if (Object.keys(catalog).length === 0) {
catalogsToRemove.push(catalogName)
shouldBeUpdated = true
}
}
for (const catalogName of catalogsToRemove) {
delete manifest.catalogs[catalogName]
}
if (Object.keys(manifest.catalogs).length === 0) {
delete manifest.catalogs
shouldBeUpdated = true
}
}
return shouldBeUpdated
}

View File

@@ -0,0 +1,298 @@
import path from 'path'
import fs from 'fs'
import { WORKSPACE_MANIFEST_FILENAME } from '@pnpm/constants'
import { tempDir } from '@pnpm/prepare-temp-dir'
import { prepare } from '@pnpm/prepare'
import { updateWorkspaceManifest } from '@pnpm/workspace.manifest-writer'
import { sync as readYamlFile } from 'read-yaml-file'
import { sync as writeYamlFile } from 'write-yaml-file'
import { findPackages } from '@pnpm/fs.find-packages'
test('remove the default catalog if it is empty', async () => {
const dir = tempDir(false)
const filePath = path.join(dir, WORKSPACE_MANIFEST_FILENAME)
prepare({
dependencies: {
foo: '^0.1.2',
},
}, { tempDir: dir })
const allProjects = await findPackages(dir)
await updateWorkspaceManifest(dir, {
updatedCatalogs: {
default: {},
},
allProjects,
})
await updateWorkspaceManifest(dir, {
updatedCatalogs: {
default: {
foo: '^0.1.2',
},
},
})
expect(readYamlFile(filePath)).toStrictEqual({
catalog: {
foo: '^0.1.2',
},
})
await updateWorkspaceManifest(dir, {
cleanupUnusedCatalogs: true,
allProjects,
})
expect(fs.existsSync(filePath)).toBeFalsy()
})
test('remove the unused default catalog', async () => {
const dir = tempDir(false)
const filePath = path.join(dir, WORKSPACE_MANIFEST_FILENAME)
writeYamlFile(filePath, {
catalog: {
bar: '3.2.1',
},
})
await updateWorkspaceManifest(dir, {
updatedCatalogs: {
default: {
foo: '^0.1.2',
},
},
})
expect(readYamlFile(filePath)).toStrictEqual({
catalog: {
bar: '3.2.1',
foo: '^0.1.2',
},
})
prepare({
dependencies: {
foo: '^0.1.2',
bar: 'catalog:',
},
}, { tempDir: dir })
const allProjects = await findPackages(dir)
await updateWorkspaceManifest(dir, {
cleanupUnusedCatalogs: true,
allProjects,
})
expect(readYamlFile(filePath)).toStrictEqual({
catalog: {
bar: '3.2.1',
},
})
})
test('remove the unused default catalog with catalogs', async () => {
const dir = tempDir(false)
const filePath = path.join(dir, WORKSPACE_MANIFEST_FILENAME)
writeYamlFile(filePath, {
catalogs: {
default: {
bar: '3.2.1',
},
},
})
await updateWorkspaceManifest(dir, {
updatedCatalogs: {
default: {
foo: '^0.1.2',
},
},
})
expect(readYamlFile(filePath)).toStrictEqual({
catalogs: {
default: {
bar: '3.2.1',
foo: '^0.1.2',
},
},
})
prepare({
dependencies: {
foo: '^0.1.2',
bar: 'catalog:',
},
}, { tempDir: dir })
const allProjects = await findPackages(dir)
await updateWorkspaceManifest(dir, {
cleanupUnusedCatalogs: true,
allProjects,
})
expect(readYamlFile(filePath)).toStrictEqual({
catalogs: {
default: {
bar: '3.2.1',
},
},
})
})
test('remove the unused named catalog', async () => {
const dir = tempDir(false)
const filePath = path.join(dir, WORKSPACE_MANIFEST_FILENAME)
await updateWorkspaceManifest(dir, {
updatedCatalogs: {
foo: {
abc: '0.1.2',
},
bar: {
def: '3.2.1',
},
},
})
expect(readYamlFile(filePath)).toStrictEqual({
catalogs: {
foo: {
abc: '0.1.2',
},
bar: {
def: '3.2.1',
},
},
})
prepare({
dependencies: {
abc: '0.1.2',
def: 'catalog:bar',
},
}, { tempDir: dir })
const allProjects = await findPackages(dir)
await updateWorkspaceManifest(dir, {
cleanupUnusedCatalogs: true,
allProjects,
})
expect(readYamlFile(filePath)).toStrictEqual({
catalogs: {
bar: {
def: '3.2.1',
},
},
})
})
test('remove all unused named catalogs', async () => {
const dir = tempDir(false)
const filePath = path.join(dir, WORKSPACE_MANIFEST_FILENAME)
writeYamlFile(filePath, {
catalogs: {
foo: {
ghi: '7.8.9',
},
},
})
await updateWorkspaceManifest(dir, {
updatedCatalogs: {
foo: {
abc: '0.1.2',
},
bar: {
def: '3.2.1',
},
},
})
expect(readYamlFile(filePath)).toStrictEqual({
catalogs: {
foo: {
abc: '0.1.2',
ghi: '7.8.9',
},
bar: {
def: '3.2.1',
},
},
})
prepare({
dependencies: {
def: 'catalog:bar',
ghi: 'catalog:foo',
},
}, { tempDir: dir })
const allProjects = await findPackages(dir)
await updateWorkspaceManifest(dir, {
cleanupUnusedCatalogs: true,
allProjects,
})
expect(readYamlFile(filePath)).toStrictEqual({
catalogs: {
bar: {
def: '3.2.1',
},
foo: {
ghi: '7.8.9',
},
},
})
prepare({
dependencies: {
def: '3.2.1',
},
}, { tempDir: dir })
const _allProjects = await findPackages(dir)
await updateWorkspaceManifest(dir, {
cleanupUnusedCatalogs: true,
allProjects: _allProjects,
})
expect(fs.existsSync(filePath)).toBeFalsy()
})
test('same pkg with different version', async () => {
const dir = tempDir(false)
const filePath = path.join(dir, WORKSPACE_MANIFEST_FILENAME)
writeYamlFile(filePath, {
catalogs: {
foo: {
ghi: '7.8.9',
},
},
})
await updateWorkspaceManifest(dir, {
updatedCatalogs: {
foo: {
abc: '0.1.2',
},
bar: {
def: '3.2.1',
abc: '1.2.3',
},
},
})
expect(readYamlFile(filePath)).toStrictEqual({
catalogs: {
foo: {
abc: '0.1.2',
ghi: '7.8.9',
},
bar: {
def: '3.2.1',
abc: '1.2.3',
},
},
})
prepare({
dependencies: {
def: 'catalog:bar',
ghi: 'catalog:foo',
abc: 'catalog:foo',
},
optionalDependencies: {
abc: 'catalog:bar',
},
}, { tempDir: dir })
const allProjects = await findPackages(dir)
await updateWorkspaceManifest(dir, {
cleanupUnusedCatalogs: true,
allProjects,
})
expect(readYamlFile(filePath)).toStrictEqual({
catalogs: {
bar: {
abc: '1.2.3',
def: '3.2.1',
},
foo: {
abc: '0.1.2',
ghi: '7.8.9',
},
},
})
})

View File

@@ -9,12 +9,18 @@
"../../__typings__/**/*.d.ts"
],
"references": [
{
"path": "../../__utils__/prepare"
},
{
"path": "../../__utils__/prepare-temp-dir"
},
{
"path": "../../catalogs/types"
},
{
"path": "../../fs/find-packages"
},
{
"path": "../../lockfile/types"
},
@@ -24,6 +30,9 @@
{
"path": "../../packages/constants"
},
{
"path": "../../packages/types"
},
{
"path": "../read-manifest"
}