From a1bdd12624adb7a056053f05c0c1b9bf60c67fc1 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Sat, 3 Sep 2022 01:00:19 +0300 Subject: [PATCH 1/4] fix: add fallback for time-based resolution --- packages/npm-resolver/src/pickPackage.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/npm-resolver/src/pickPackage.ts b/packages/npm-resolver/src/pickPackage.ts index 332a9d72318..1f19ae61c4b 100644 --- a/packages/npm-resolver/src/pickPackage.ts +++ b/packages/npm-resolver/src/pickPackage.ts @@ -123,9 +123,13 @@ export default async ( if (opts.publishedBy) { metaCachedInStore = metaCachedInStore ?? await limit(async () => loadMeta(pkgMirror)) if (metaCachedInStore?.cachedAt && new Date(metaCachedInStore.cachedAt) >= opts.publishedBy) { + let pickedPackage = _pickPackageFromMeta(spec, opts.preferredVersionSelectors, metaCachedInStore, opts.publishedBy) + if (!pickedPackage) { + pickedPackage = pickPackageFromMeta(pickLowestVersionByVersionRange, spec, opts.preferredVersionSelectors, metaCachedInStore) + } return { meta: metaCachedInStore, - pickedPackage: _pickPackageFromMeta(spec, opts.preferredVersionSelectors, metaCachedInStore, opts.publishedBy), + pickedPackage, } } } From d699aa553c0572d1c5b6b70d76d8fdbbbce6986b Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Sat, 3 Sep 2022 16:22:04 +0300 Subject: [PATCH 2/4] test: npm-resolver --- packages/npm-resolver/src/pickPackage.ts | 22 ++++++++---- .../npm-resolver/test/fixtures/bad-dates.json | 23 ++++++++++++ .../npm-resolver/test/publishedBy.test.ts | 35 +++++++++++++++++++ 3 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 packages/npm-resolver/test/fixtures/bad-dates.json create mode 100644 packages/npm-resolver/test/publishedBy.test.ts diff --git a/packages/npm-resolver/src/pickPackage.ts b/packages/npm-resolver/src/pickPackage.ts index 1f19ae61c4b..bdca41bdcc8 100644 --- a/packages/npm-resolver/src/pickPackage.ts +++ b/packages/npm-resolver/src/pickPackage.ts @@ -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 @@ -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,13 +137,9 @@ export default async ( if (opts.publishedBy) { metaCachedInStore = metaCachedInStore ?? await limit(async () => loadMeta(pkgMirror)) if (metaCachedInStore?.cachedAt && new Date(metaCachedInStore.cachedAt) >= opts.publishedBy) { - let pickedPackage = _pickPackageFromMeta(spec, opts.preferredVersionSelectors, metaCachedInStore, opts.publishedBy) - if (!pickedPackage) { - pickedPackage = pickPackageFromMeta(pickLowestVersionByVersionRange, spec, opts.preferredVersionSelectors, metaCachedInStore) - } return { meta: metaCachedInStore, - pickedPackage, + pickedPackage: _pickPackageFromMeta(spec, opts.preferredVersionSelectors, metaCachedInStore, opts.publishedBy), } } } diff --git a/packages/npm-resolver/test/fixtures/bad-dates.json b/packages/npm-resolver/test/fixtures/bad-dates.json new file mode 100644 index 00000000000..996ae30a43b --- /dev/null +++ b/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" +} diff --git a/packages/npm-resolver/test/publishedBy.test.ts b/packages/npm-resolver/test/publishedBy.test.ts new file mode 100644 index 00000000000..726b27b7423 --- /dev/null +++ b/packages/npm-resolver/test/publishedBy.test.ts @@ -0,0 +1,35 @@ +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(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, + }) + 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') +}) From b7a1bdefe161e7054fcb5bad69f56ffcb1e719df Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Sat, 3 Sep 2022 16:44:23 +0300 Subject: [PATCH 3/4] fix: npm-resolver --- .changeset/fast-games-grow.md | 5 +++ packages/npm-resolver/src/pickPackage.ts | 9 +++-- .../npm-resolver/test/publishedBy.test.ts | 33 +++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 .changeset/fast-games-grow.md diff --git a/.changeset/fast-games-grow.md b/.changeset/fast-games-grow.md new file mode 100644 index 00000000000..79e8bc3c074 --- /dev/null +++ b/.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). diff --git a/packages/npm-resolver/src/pickPackage.ts b/packages/npm-resolver/src/pickPackage.ts index bdca41bdcc8..67061bb02ed 100644 --- a/packages/npm-resolver/src/pickPackage.ts +++ b/packages/npm-resolver/src/pickPackage.ts @@ -137,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, + } } } } diff --git a/packages/npm-resolver/test/publishedBy.test.ts b/packages/npm-resolver/test/publishedBy.test.ts index 726b27b7423..5d8fb5fa03e 100644 --- a/packages/npm-resolver/test/publishedBy.test.ts +++ b/packages/npm-resolver/test/publishedBy.test.ts @@ -1,3 +1,5 @@ +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' @@ -24,6 +26,37 @@ test('fall back to a newer version if there is no version published by the given 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, From 2e0e89798a903379e94ff1173a0ecf76a704e3c1 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Sat, 3 Sep 2022 16:53:29 +0300 Subject: [PATCH 4/4] test: fix --- .../test/testRecursive.ts | 11 ++++++++--- packages/plugin-commands-store/test/storeStatus.ts | 2 -- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/plugin-commands-script-runners/test/testRecursive.ts b/packages/plugin-commands-script-runners/test/testRecursive.ts index d0ef2bd7e3e..8e3c4ccba60 100644 --- a/packages/plugin-commands-script-runners/test/testRecursive.ts +++ b/packages/plugin-commands-script-runners/test/testRecursive.ts @@ -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', diff --git a/packages/plugin-commands-store/test/storeStatus.ts b/packages/plugin-commands-store/test/storeStatus.ts index cff706e8312..f7227d7fc0d 100644 --- a/packages/plugin-commands-store/test/storeStatus.ts +++ b/packages/plugin-commands-store/test/storeStatus.ts @@ -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()