Skip to content

Commit

Permalink
fix: add fallback for time-based resolution (#5302)
Browse files Browse the repository at this point in the history
  • Loading branch information
zkochan committed Sep 3, 2022
1 parent b462502 commit 7fac3b4
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/fast-games-grow.md
@@ -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).
25 changes: 21 additions & 4 deletions packages/npm-resolver/src/pickPackage.ts
Expand Up @@ -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>
Expand All @@ -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)

Expand Down Expand Up @@ -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,
}
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions packages/npm-resolver/test/fixtures/bad-dates.json
@@ -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 changes: 68 additions & 0 deletions packages/npm-resolver/test/publishedBy.test.ts
@@ -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')
})
11 changes: 8 additions & 3 deletions packages/plugin-commands-script-runners/test/testRecursive.ts
Expand Up @@ -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([
{
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
2 changes: 0 additions & 2 deletions packages/plugin-commands-store/test/storeStatus.ts
Expand Up @@ -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',
Expand All @@ -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()
Expand Down

0 comments on commit 7fac3b4

Please sign in to comment.