mirror of
https://github.com/pnpm/pnpm.git
synced 2026-02-02 11:12:58 -05:00
fix: add fallback for time-based resolution (#5302)
This commit is contained in:
5
.changeset/fast-games-grow.md
Normal file
5
.changeset/fast-games-grow.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/npm-resolver": patch
|
||||
---
|
||||
|
||||
Pick a version even if it was published after the given date (if there is no better match).
|
||||
@@ -55,6 +55,17 @@ export interface PickPackageOptions {
|
||||
dryRun: boolean
|
||||
}
|
||||
|
||||
function pickPackageFromMetaUsingTime (
|
||||
spec: RegistryPackageSpec,
|
||||
preferredVersionSelectors: VersionSelectors | undefined,
|
||||
meta: PackageMeta,
|
||||
publishedBy?: Date
|
||||
) {
|
||||
const pickedPackage = pickPackageFromMeta(pickVersionByVersionRange, spec, preferredVersionSelectors, meta, publishedBy)
|
||||
if (pickedPackage) return pickedPackage
|
||||
return pickPackageFromMeta(pickLowestVersionByVersionRange, spec, preferredVersionSelectors, meta, publishedBy)
|
||||
}
|
||||
|
||||
export default async (
|
||||
ctx: {
|
||||
fetch: (pkgName: string, registry: string, authHeaderValue?: string) => Promise<PackageMeta>
|
||||
@@ -69,7 +80,10 @@ export default async (
|
||||
opts: PickPackageOptions
|
||||
): Promise<{meta: PackageMeta, pickedPackage: PackageInRegistry | null}> => {
|
||||
opts = opts || {}
|
||||
const _pickPackageFromMeta = pickPackageFromMeta.bind(null, opts.pickLowestVersion ? pickLowestVersionByVersionRange : pickVersionByVersionRange)
|
||||
const _pickPackageFromMeta =
|
||||
opts.publishedBy
|
||||
? pickPackageFromMetaUsingTime
|
||||
: (pickPackageFromMeta.bind(null, opts.pickLowestVersion ? pickLowestVersionByVersionRange : pickVersionByVersionRange))
|
||||
|
||||
validatePackageName(spec.name)
|
||||
|
||||
@@ -123,9 +137,12 @@ export default async (
|
||||
if (opts.publishedBy) {
|
||||
metaCachedInStore = metaCachedInStore ?? await limit(async () => loadMeta(pkgMirror))
|
||||
if (metaCachedInStore?.cachedAt && new Date(metaCachedInStore.cachedAt) >= opts.publishedBy) {
|
||||
return {
|
||||
meta: metaCachedInStore,
|
||||
pickedPackage: _pickPackageFromMeta(spec, opts.preferredVersionSelectors, metaCachedInStore, opts.publishedBy),
|
||||
const pickedPackage = _pickPackageFromMeta(spec, opts.preferredVersionSelectors, metaCachedInStore, opts.publishedBy)
|
||||
if (pickedPackage) {
|
||||
return {
|
||||
meta: metaCachedInStore,
|
||||
pickedPackage,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
23
packages/npm-resolver/test/fixtures/bad-dates.json
vendored
Normal file
23
packages/npm-resolver/test/fixtures/bad-dates.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"versions": {
|
||||
"1.0.0": {
|
||||
"name": "bad-dates",
|
||||
"version": "1.0.0",
|
||||
"_hasShrinkwrap": false,
|
||||
"directories": {},
|
||||
"dist": {
|
||||
"integrity": "sha512-9cI+DmhNhA8ioT/3EJFnt0s1yehnAECyIOXdT+2uQGzcEEBaj8oNmVWj33+ZjPndMIFRQh8JeJlEu1uv5/J7pQ==",
|
||||
"shasum": "88009856b64a2f1eb7d8bb0179418424ae0452cb",
|
||||
"tarball": "https://registry.npmjs.org/bad-dates/-/bad-dates-1.0.0.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"time": {
|
||||
"1.0.0": "2017-08-17T19:26:00.508Z"
|
||||
},
|
||||
"name": "bad-dates",
|
||||
"dist-tags": {
|
||||
"latest": "1.0.0"
|
||||
},
|
||||
"modified": "2017-08-17T19:26:00.508Z"
|
||||
}
|
||||
68
packages/npm-resolver/test/publishedBy.test.ts
Normal file
68
packages/npm-resolver/test/publishedBy.test.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { createFetchFromRegistry } from '@pnpm/fetch'
|
||||
import _createResolveFromNpm from '@pnpm/npm-resolver'
|
||||
import fixtures from '@pnpm/test-fixtures'
|
||||
import loadJsonFile from 'load-json-file'
|
||||
import nock from 'nock'
|
||||
import tempy from 'tempy'
|
||||
|
||||
const f = fixtures(__dirname)
|
||||
const registry = 'https://registry.npmjs.org/'
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
const badDatesMeta = loadJsonFile.sync<any>(f.find('bad-dates.json'))
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
|
||||
const fetch = createFetchFromRegistry({})
|
||||
const getCredentials = () => ({ authHeaderValue: undefined, alwaysAuth: undefined })
|
||||
const createResolveFromNpm = _createResolveFromNpm.bind(null, fetch, getCredentials)
|
||||
|
||||
test('fall back to a newer version if there is no version published by the given date', async () => {
|
||||
nock(registry)
|
||||
.get('/bad-dates')
|
||||
.reply(200, badDatesMeta)
|
||||
|
||||
const cacheDir = tempy.directory()
|
||||
const resolve = createResolveFromNpm({
|
||||
cacheDir,
|
||||
filterMetadata: true,
|
||||
fullMetadata: true,
|
||||
})
|
||||
const resolveResult = await resolve({ alias: 'bad-dates', pref: '^1.0.0' }, {
|
||||
registry,
|
||||
publishedBy: new Date('2015-08-17T19:26:00.508Z'),
|
||||
})
|
||||
|
||||
expect(resolveResult!.resolvedVia).toBe('npm-registry')
|
||||
expect(resolveResult!.id).toBe('registry.npmjs.org/bad-dates/1.0.0')
|
||||
})
|
||||
|
||||
test('request metadata when the one in cache does not have a version satisfiyng the range', async () => {
|
||||
const cacheDir = tempy.directory()
|
||||
const cachedMeta = {
|
||||
'dist-tags': {},
|
||||
versions: {},
|
||||
time: {},
|
||||
cachedAt: '2016-08-17T19:26:00.508Z',
|
||||
}
|
||||
fs.mkdirSync(path.join(cacheDir, 'metadata-full-filtered/registry.npmjs.org'), { recursive: true })
|
||||
fs.writeFileSync(path.join(cacheDir, 'metadata-full-filtered/registry.npmjs.org/bad-dates.json'), JSON.stringify(cachedMeta), 'utf8')
|
||||
|
||||
nock(registry)
|
||||
.get('/bad-dates')
|
||||
.reply(200, badDatesMeta)
|
||||
|
||||
const resolve = createResolveFromNpm({
|
||||
cacheDir,
|
||||
filterMetadata: true,
|
||||
fullMetadata: true,
|
||||
})
|
||||
const resolveResult = await resolve({ alias: 'bad-dates', pref: '^1.0.0' }, {
|
||||
registry,
|
||||
publishedBy: new Date('2015-08-17T19:26:00.508Z'),
|
||||
})
|
||||
|
||||
expect(resolveResult!.resolvedVia).toBe('npm-registry')
|
||||
expect(resolveResult!.id).toBe('registry.npmjs.org/bad-dates/1.0.0')
|
||||
})
|
||||
@@ -5,6 +5,8 @@ import { preparePackages } from '@pnpm/prepare'
|
||||
import execa from 'execa'
|
||||
import { DEFAULT_OPTS, REGISTRY_URL } from './utils'
|
||||
|
||||
const pnpmBin = path.join(__dirname, '../../pnpm/bin/pnpm.cjs')
|
||||
|
||||
test('pnpm recursive test', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
@@ -51,7 +53,8 @@ test('pnpm recursive test', async () => {
|
||||
])
|
||||
|
||||
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
await execa('node', [
|
||||
pnpmBin,
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
@@ -106,7 +109,8 @@ test('`pnpm recursive test` does not fail if none of the packages has a test com
|
||||
])
|
||||
|
||||
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
await execa('node', [
|
||||
pnpmBin,
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
@@ -158,7 +162,8 @@ test('pnpm recursive test with filtering', async () => {
|
||||
[{ namePattern: 'project-1' }],
|
||||
{ workspaceDir: process.cwd() }
|
||||
)
|
||||
await execa('pnpm', [
|
||||
await execa('node', [
|
||||
pnpmBin,
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
|
||||
@@ -60,7 +60,6 @@ test('CLI does not fail when store status does not find modified packages', asyn
|
||||
`--store-dir=${storeDir}`,
|
||||
`--registry=${REGISTRY}`,
|
||||
'--verify-store-integrity',
|
||||
'--config.resolution-mode=highest',
|
||||
'add',
|
||||
'eslint@3.4.0',
|
||||
'gulp@4.0.2',
|
||||
@@ -79,7 +78,6 @@ test('CLI does not fail when store status does not find modified packages', asyn
|
||||
`--store-dir=${storeDir}`,
|
||||
`--registry=${REGISTRY}`,
|
||||
'--verify-store-integrity',
|
||||
'--config.resolution-mode=highest',
|
||||
])
|
||||
|
||||
const modulesState = await project.readModulesManifest()
|
||||
|
||||
Reference in New Issue
Block a user