diff --git a/hooks/types/src/index.ts b/hooks/types/src/index.ts index 7be5e6d5dc..49d0a645a4 100644 --- a/hooks/types/src/index.ts +++ b/hooks/types/src/index.ts @@ -69,9 +69,10 @@ export interface CustomResolver { * Use this to implement custom cache invalidation logic (e.g., time-based expiry, version checks). * * @param wantedDependency - The dependency to check for force re-resolution + * @param wantedLockfile - The current lockfile contents * @returns true to force re-resolution of all dependencies */ - shouldForceResolve?: (wantedDependency: WantedDependency) => boolean | Promise + shouldForceResolve?: (wantedDependency: WantedDependency, wantedLockfile: LockfileObject) => boolean | Promise } export interface CustomFetcher { diff --git a/pkg-manager/core/README.md b/pkg-manager/core/README.md index 687466fd6b..d0b68eb9ba 100644 --- a/pkg-manager/core/README.md +++ b/pkg-manager/core/README.md @@ -146,7 +146,7 @@ interface CustomResolver { resolve?: (wantedDependency: WantedDependency, opts: ResolveOptions) => ResolveResult | Promise // Force resolution check - shouldForceResolve?: (wantedDependency: WantedDependency) => boolean | Promise + shouldForceResolve?: (wantedDependency: WantedDependency, wantedLockfile: LockfileObject) => boolean | Promise } ``` @@ -164,7 +164,7 @@ interface CustomFetcher { * `canResolve(wantedDependency)` - Returns `true` if this resolver can resolve the given package descriptor * `resolve(wantedDependency, opts)` - Resolves a package descriptor to a resolution. Should return an object with `id` and `resolution` -* `shouldForceResolve(wantedDependency)` - Return `true` to trigger full resolution of all packages (skipping the "Lockfile is up to date" optimization) +* `shouldForceResolve(wantedDependency, wantedLockfile)` - Return `true` to trigger full resolution of all packages (skipping the "Lockfile is up to date" optimization). The lockfile contents are provided to enable comparisons with the latest resolved version. **Custom Fetcher Methods:** diff --git a/pkg-manager/core/src/install/checkCustomResolverForceResolve.ts b/pkg-manager/core/src/install/checkCustomResolverForceResolve.ts index 67bdee9f4a..34ebf064e3 100644 --- a/pkg-manager/core/src/install/checkCustomResolverForceResolve.ts +++ b/pkg-manager/core/src/install/checkCustomResolverForceResolve.ts @@ -36,7 +36,7 @@ export async function checkCustomResolverForceResolve ( if (canResolve && customResolver.shouldForceResolve) { // eslint-disable-next-line no-await-in-loop - const shouldForce = await customResolver.shouldForceResolve(wantedDependency) + const shouldForce = await customResolver.shouldForceResolve(wantedDependency, wantedLockfile) if (shouldForce) { return true diff --git a/pkg-manager/core/test/install/checkCustomResolverForceResolve.test.ts b/pkg-manager/core/test/install/checkCustomResolverForceResolve.test.ts index b096c308f7..44aab9f905 100644 --- a/pkg-manager/core/test/install/checkCustomResolverForceResolve.test.ts +++ b/pkg-manager/core/test/install/checkCustomResolverForceResolve.test.ts @@ -563,4 +563,41 @@ describe('checkCustomResolverForceResolve', () => { expect(result).toBe(false) }) + + test('passes lockfile to shouldForceResolve', async () => { + let receivedLockfile: LockfileObject | undefined + const lockfile: LockfileObject = { + lockfileVersion: '9.0', + importers: { + '.': { + specifiers: { 'test-pkg': '1.0.0' }, + dependencies: { 'test-pkg': '1.0.0' }, + }, + } as any, // eslint-disable-line @typescript-eslint/no-explicit-any + packages: { + '/test-pkg@1.0.0': { + resolution: { tarball: 'http://example.com/test-pkg-1.0.0.tgz', integrity: 'sha512-test' }, + }, + } as any, // eslint-disable-line @typescript-eslint/no-explicit-any + } + const resolver: CustomResolver = { + canResolve: () => true, + shouldForceResolve: (_wantedDep, wantedLockfile) => { + receivedLockfile = wantedLockfile + return false + }, + } + const projects: ProjectWithManifest[] = [ + { + id: '.' as ProjectId, + manifest: { + dependencies: { 'test-pkg': '1.0.0' }, + } as any, // eslint-disable-line @typescript-eslint/no-explicit-any + }, + ] + + await checkCustomResolverForceResolve([resolver], lockfile, projects) + + expect(receivedLockfile).toBe(lockfile) + }) })