diff --git a/.changeset/tired-hotels-jog.md b/.changeset/tired-hotels-jog.md new file mode 100644 index 0000000000..e03fffbe32 --- /dev/null +++ b/.changeset/tired-hotels-jog.md @@ -0,0 +1,5 @@ +--- +"@pnpm/resolving.npm-resolver": patch +--- + +Fixed a bug preventing the `clearCache` function returned by `createNpmResolver` from properly clearing metadata cache. diff --git a/resolving/npm-resolver/src/index.ts b/resolving/npm-resolver/src/index.ts index 2b539dfd74..aaaaf07541 100644 --- a/resolving/npm-resolver/src/index.ts +++ b/resolving/npm-resolver/src/index.ts @@ -33,7 +33,7 @@ import { import { resolveWorkspaceRange } from '@pnpm/workspace.range-resolver' import { LRUCache } from 'lru-cache' import normalize from 'normalize-path' -import pMemoize from 'p-memoize' +import pMemoize, { pMemoizeClear } from 'p-memoize' import { clone } from 'ramda' import semver from 'semver' import ssri from 'ssri' @@ -230,6 +230,7 @@ export function createNpmResolver ( resolveFromJsr: resolveJsr.bind(null, ctx), clearCache: () => { metaCache.clear() + pMemoizeClear(fetch) }, } } diff --git a/resolving/npm-resolver/test/clearCache.test.ts b/resolving/npm-resolver/test/clearCache.test.ts new file mode 100644 index 0000000000..1d296e2580 --- /dev/null +++ b/resolving/npm-resolver/test/clearCache.test.ts @@ -0,0 +1,84 @@ +import { createFetchFromRegistry } from '@pnpm/network.fetch' +import { createNpmResolver } from '@pnpm/resolving.npm-resolver' +import type { PackageMeta } from '@pnpm/resolving.registry.types' +import type { Registries } from '@pnpm/types' +import nock from 'nock' +import { temporaryDirectory } from 'tempy' + +const registries: Registries = { + default: 'https://registry.npmjs.org/', +} + +const fetch = createFetchFromRegistry({}) +const getAuthHeader = () => undefined +const createResolveFromNpm = createNpmResolver.bind(null, fetch, getAuthHeader) + +beforeEach(() => { + nock.disableNetConnect() +}) + +afterEach(() => { + // https://github.com/nock/nock?tab=readme-ov-file#resetting-netconnect + nock.cleanAll() + nock.enableNetConnect() +}) + +test('metadata is fetched again after calling clearCache()', async () => { + const name = 'test-package' + const meta: PackageMeta = { + name, + versions: { + '3.0.0': { + name, + version: '3.0.0', + // Generated locally through: echo '1.1.0-beta' | sha1sum + dist: { shasum: '8c6981d7f982c3e2986fda2f34282264a4db344c', tarball: `https://registry.npmjs.org/${name}/-/${name}-3.0.0.tgz` }, + }, + }, + 'dist-tags': { + latest: '3.0.0', + }, + time: { + '3.0.0': '2020-02-01T00:00:00.000Z', + }, + } + + nock(registries.default) + .get(`/${name}`) + .reply(200, meta) + + const cacheDir = temporaryDirectory() + const { resolveFromNpm, clearCache } = createResolveFromNpm({ + cacheDir, + fullMetadata: true, + registries, + storeDir: temporaryDirectory(), + }) + + const res = await resolveFromNpm({ alias: name, bareSpecifier: 'latest' }, {}) + expect(res?.id).toBe(`${name}@3.0.0`) + + // Simulate publishing a new 3.1.0 version. + meta.versions['3.1.0'] = { + name, + version: '3.1.0', + dist: { shasum: '5f022945150b402cb3e470acc3818847b3dc5e00', tarball: `https://registry.npmjs.org/${name}/-/${name}-3.1.0.tgz` }, + } + meta['dist-tags'].latest = '3.1.0' + + const scope = nock(registries.default) + .get(`/${name}`) + .reply(200, meta) + + // Until the cache is cleared, the resolver will still return 3.0.0. + const res2 = await resolveFromNpm({ alias: name, bareSpecifier: 'latest' }, {}) + expect(res2?.id).toBe(`${name}@3.0.0`) + expect(scope.isDone()).toBe(false) + + clearCache() + + // After clearing cache, the resolver should start returning 3.1.0. + const res3 = await resolveFromNpm({ alias: name, bareSpecifier: 'latest' }, {}) + expect(res3?.id).toBe(`${name}@3.1.0`) + expect(scope.isDone()).toBe(true) +})