feat: improve error message for versions not meeting minimumReleaseAge (#10350)

close #10307
This commit is contained in:
月正海角
2025-12-23 00:28:52 +08:00
committed by GitHub
parent 4015eeb8e9
commit a297ebc9f6
5 changed files with 35 additions and 4 deletions

View File

@@ -0,0 +1,7 @@
---
"@pnpm/npm-resolver": patch
"pnpm": patch
---
Improve error message when a package version exists but does not meet the `minimumReleaseAge` constraint. The error now clearly states that the version exists and shows a human-readable time since release (e.g., "released 6 hours ago") [#10307](https://github.com/pnpm/pnpm/issues/10307).

View File

@@ -398,5 +398,5 @@ test('dlx should fail when the requested package does not meet the minimum age r
default: 'https://registry.npmjs.org/',
},
}, ['shx@0.3.4'])
).rejects.toThrow('No matching version found for shx@0.3.4 published by')
).rejects.toThrow(/Version 0\.3\.4 \(released .+\) of shx does not meet the minimumReleaseAge constraint/)
})

View File

@@ -412,7 +412,7 @@ test('minimumReleaseAge makes install fail if there is no version that was publi
dir: path.resolve('project'),
minimumReleaseAge,
linkWorkspacePackages: false,
}, ['is-odd@0.1.1'])).rejects.toThrow(/No matching version found for is-odd@0\.1\.1.*satisfies the specs but/)
}, ['is-odd@0.1.1'])).rejects.toThrow(/Version 0\.1\.1 \(released .+\) of is-odd does not meet the minimumReleaseAge constraint/)
})
describeOnLinuxOnly('filters optional dependencies based on pnpm.supportedArchitectures.libc', () => {

View File

@@ -70,7 +70,9 @@ export class NoMatchingVersionError extends PnpmError {
let errorMessage: string
if (opts.publishedBy && opts.immatureVersion && opts.packageMeta.time) {
const time = new Date(opts.packageMeta.time[opts.immatureVersion])
errorMessage = `No matching version found for ${dep} published by ${opts.publishedBy.toString()} while fetching it from ${opts.registry}. Version ${opts.immatureVersion} satisfies the specs but was released at ${time.toString()}`
const releaseAgeText = formatTimeAgo(time)
const pkgName = opts.wantedDependency.alias ?? opts.packageMeta.name
errorMessage = `Version ${opts.immatureVersion} (released ${releaseAgeText}) of ${pkgName} does not meet the minimumReleaseAge constraint`
} else {
errorMessage = `No matching version found for ${dep} while fetching it from ${opts.registry}`
}
@@ -80,6 +82,28 @@ export class NoMatchingVersionError extends PnpmError {
}
}
function formatTimeAgo (date: Date): string {
const now = Date.now()
const diffMs = now - date.getTime()
// Handle clock skew (future dates) and very recent releases (< 1 minute)
if (diffMs < 60 * 1000) {
return 'just now'
}
const diffMinutes = Math.floor(diffMs / (60 * 1000))
const diffHours = Math.floor(diffMs / (60 * 60 * 1000))
const diffDays = Math.floor(diffMs / (24 * 60 * 60 * 1000))
if (diffHours >= 48) {
return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`
}
if (diffMinutes >= 90) {
return `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`
}
return `${diffMinutes} minute${diffMinutes === 1 ? '' : 's'} ago`
}
export {
parseBareSpecifier,
workspacePrefToNpm,

View File

@@ -116,7 +116,7 @@ test('do not pick version that does not satisfy the date requirement even if it
})
await expect(resolveFromNpm({ alias: 'foo', bareSpecifier: '1.0.0' }, {
publishedBy: new Date('2015-08-17T19:26:00.508Z'),
})).rejects.toThrow('No matching version found')
})).rejects.toThrow(/Version 1\.0\.0 \(released .+\) of foo does not meet the minimumReleaseAge constraint/)
})
test('should skip time field validation for excluded packages', async () => {