diff --git a/.changeset/fix-outdated-minimum-release-age.md b/.changeset/fix-outdated-minimum-release-age.md new file mode 100644 index 0000000000..5d5fb09ea4 --- /dev/null +++ b/.changeset/fix-outdated-minimum-release-age.md @@ -0,0 +1,6 @@ +--- +"@pnpm/outdated": patch +"pnpm": patch +--- + +Fixed `pnpm outdated` crashing with `ERR_PNPM_NO_MATCHING_VERSION` when `minimumReleaseAge` is set and all versions of a package are newer than the threshold [#10605](https://github.com/pnpm/pnpm/issues/10605). diff --git a/reviewing/outdated/src/createManifestGetter.ts b/reviewing/outdated/src/createManifestGetter.ts index 99dca44fae..defe0ee832 100644 --- a/reviewing/outdated/src/createManifestGetter.ts +++ b/reviewing/outdated/src/createManifestGetter.ts @@ -63,8 +63,16 @@ export async function getManifest ( }) return resolution?.manifest ?? null } catch (err) { - if ((err as { code?: string }).code === 'ERR_PNPM_NO_MATURE_MATCHING_VERSION' && opts.publishedBy) { - // No versions found that meet the minimumReleaseAge requirement + const code = (err as { code?: string }).code + if (opts.publishedBy && ( + code === 'ERR_PNPM_NO_MATURE_MATCHING_VERSION' || + code === 'ERR_PNPM_NO_MATCHING_VERSION' + )) { + // No versions found that meet the minimumReleaseAge requirement. + // This can happen when all published versions (including the one the + // "latest" dist-tag points to) are newer than the minimumReleaseAge + // threshold, causing the resolver to throw NO_MATCHING_VERSION instead + // of NO_MATURE_MATCHING_VERSION. return null } throw err diff --git a/reviewing/outdated/test/getManifest.spec.ts b/reviewing/outdated/test/getManifest.spec.ts index 5785758b8e..cc5abe0a0a 100644 --- a/reviewing/outdated/test/getManifest.spec.ts +++ b/reviewing/outdated/test/getManifest.spec.ts @@ -100,7 +100,8 @@ test('getManifest() does not convert non-latest specifiers', async () => { expect(resolve).toHaveBeenCalledTimes(1) }) -test('getManifest() handles NO_MATCHING_VERSION error gracefully', async () => { +// https://github.com/pnpm/pnpm/issues/10605 +test('getManifest() returns null for NO_MATCHING_VERSION when publishedBy is set', async () => { const opts = { dir: '', lockfileDir: '', @@ -110,17 +111,36 @@ test('getManifest() handles NO_MATCHING_VERSION error gracefully', async () => { const publishedBy = new Date(Date.now() - 10080 * 60 * 1000) const resolve: ResolveFunction = jest.fn(async function () { + // When all versions of a package are newer than minimumReleaseAge and the + // latest dist-tag points to a pre-release, the resolver may throw + // NO_MATCHING_VERSION instead of NO_MATURE_MATCHING_VERSION. const error = new Error('No matching version found') as Error & { code?: string } - error.code = 'ERR_PNPM_NO_MATURE_MATCHING_VERSION' + error.code = 'ERR_PNPM_NO_MATCHING_VERSION' throw error }) const result = await getManifest({ ...opts, resolve, publishedBy }, 'foo', 'latest') - // Should return null when no version matches minimumReleaseAge expect(result).toBeNull() }) +// When publishedBy is NOT set, NO_MATCHING_VERSION should still throw. +test('getManifest() throws NO_MATCHING_VERSION when publishedBy is not set', async () => { + const opts = { + dir: '', + lockfileDir: '', + rawConfig: {}, + } + + const resolve: ResolveFunction = jest.fn(async function () { + const error = new Error('No matching version found') as Error & { code?: string } + error.code = 'ERR_PNPM_NO_MATCHING_VERSION' + throw error + }) + + await expect(getManifest({ ...opts, resolve }, 'foo', 'latest')).rejects.toThrow('No matching version found') +}) + test('getManifest() with minimumReleaseAgeExclude', async () => { const opts = { dir: '',