From 502934132b69e15f2b64c62e65ba7f026206b874 Mon Sep 17 00:00:00 2001 From: isaacs Date: Wed, 28 Jul 2021 12:34:15 -0700 Subject: [PATCH] fix: improve heuristics when dist-tags.latest is in range Previously, there were two problems. First problem, even though we heuristically choose the version that best suits the stated 'engines' restriction, we were skipping that check when the 'defaultTag' (ie, 'latest', typically) version was a semver match. Second problem, the heuristic was improperly being set for 'staged' and 'restricted' packages, resulting in failure to sort those versions properly. Only choose the defaultTag version if it both a semver match, _and_ passes the engines/staged/restricted heuristics, and apply those heuristics properly in the sort that comes later, if the defaultTag version is not used. Related-to: https://github.com/npm/rfcs/pull/405 --- lib/index.js | 16 +++++++++++----- test/index.js | 13 ++++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/index.js b/lib/index.js index 42e41b1..8280797 100644 --- a/lib/index.js +++ b/lib/index.js @@ -123,9 +123,15 @@ const pickManifest = (packument, wanted, opts) => { const defaultVer = distTags[defaultTag] if (defaultVer && (range === '*' || semver.satisfies(defaultVer, range, { loose: true })) && + !restricted[defaultVer] && !shouldAvoid(defaultVer, avoid)) { const mani = versions[defaultVer] - if (mani && isBefore(verTimes, defaultVer, time)) { + const ok = mani && + isBefore(verTimes, defaultVer, time) && + engineOk(mani, npmVersion, nodeVersion) && + !mani.deprecated && + !staged[defaultVer] + if (ok) { return mani } } @@ -155,10 +161,10 @@ const pickManifest = (packument, wanted, opts) => { const [verb, manib] = b const notavoida = !shouldAvoid(vera, avoid) const notavoidb = !shouldAvoid(verb, avoid) - const notrestra = !restricted[a] - const notrestrb = !restricted[b] - const notstagea = !staged[a] - const notstageb = !staged[b] + const notrestra = !restricted[vera] + const notrestrb = !restricted[verb] + const notstagea = !staged[vera] + const notstageb = !staged[verb] const notdepra = !mania.deprecated const notdeprb = !manib.deprecated const enginea = engineOk(mania, npmVersion, nodeVersion) diff --git a/test/index.js b/test/index.js index 366bcca..6666f5b 100644 --- a/test/index.js +++ b/test/index.js @@ -130,6 +130,9 @@ test('ETARGET if range does not match anything', t => { test('E403 if version is forbidden', t => { const metadata = { + 'dist-tags': { + latest: '2.1.0' // do not default the latest if restricted + }, policyRestrictions: { versions: { '2.1.0': { version: '2.1.0' }, @@ -141,6 +144,9 @@ test('E403 if version is forbidden', t => { '2.0.5': { version: '2.0.5' }, }, } + t.equal(pickManifest(metadata, '2').version, '2.0.5') + t.equal(pickManifest(metadata, '').version, '2.0.5') + t.equal(pickManifest(metadata, '1 || 2').version, '2.0.5') t.throws(() => { pickManifest(metadata, '2.1.0') }, { code: 'E403' }, 'got correct error on match failure') @@ -423,6 +429,9 @@ test('accepts opts.before option to do date-based cutoffs', t => { test('prefers versions that satisfy the engines requirement', t => { const pack = { + 'dist-tags': { + latest: '1.5.0' // do not default latest if engine mismatch + }, versions: { '1.0.0': { version: '1.0.0', engines: { node: '>=4' } }, '1.1.0': { version: '1.1.0', engines: { node: '>=6' } }, @@ -430,7 +439,9 @@ test('prefers versions that satisfy the engines requirement', t => { '1.3.0': { version: '1.3.0', engines: { node: '>=10' } }, '1.4.0': { version: '1.4.0', engines: { node: '>=12' } }, '1.5.0': { version: '1.5.0', engines: { node: '>=14' } }, - }, + // not tagged as latest, won't be chosen by default + '1.5.1': { version: '1.5.0', engines: { node: '>=14' } } + } } t.equal(pickManifest(pack, '1.x', { nodeVersion: '14.0.0' }).version, '1.5.0')