fix: add fallback for time-based resolution (#5302)

This commit is contained in:
Zoltan Kochan
2022-09-03 17:17:33 +03:00
committed by GitHub
parent b462502c58
commit 7fac3b4463
6 changed files with 125 additions and 9 deletions

View 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).

View File

@@ -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,
}
}
}
}

View 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"
}

View 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')
})

View File

@@ -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',

View File

@@ -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()