Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve heuristics when dist-tags.latest is in range #33

Merged
merged 1 commit into from
Jul 9, 2024

Conversation

isaacs
Copy link

@isaacs isaacs commented Jul 28, 2021

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: npm/rfcs#405

References

@isaacs
Copy link
Author

isaacs commented Jul 8, 2024

rebased onto main (can't force push, no longer have access to this repo)

From 0637cf6083d8038494ddd4d2cbf57bef4f5dac4c Mon Sep 17 00:00:00 2001
From: isaacs <i@izs.me>
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..682c8d5 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')
-- 
2.39.3 (Apple Git-146)

@wraithgar wraithgar force-pushed the isaacs/do-not-default-to-latest-on-engine-mismatch branch from 66b46ab to 5029341 Compare July 9, 2024 16:41
@wraithgar wraithgar requested a review from a team as a code owner July 9, 2024 16:41
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: npm/rfcs#405
@wraithgar wraithgar force-pushed the isaacs/do-not-default-to-latest-on-engine-mismatch branch from 5029341 to e170e96 Compare July 9, 2024 16:43
@wraithgar
Copy link
Member

Rebased, fixed linting (which ironically would have prevented the merge conflict. I begrudgingly have accepted that trailing commas on every line make sense).

@ljharb
Copy link

ljharb commented Jul 9, 2024

i'd consider this a semver-minor, fwiw.

also the farther back in npm majors this can be backported (i understand nobody wants to do that), the sooner the ecosystem may be able to actually treat dropping an engine as a nonbreaking change.

@wraithgar wraithgar changed the title fix: improve heuristics when dist-tags.latest is in range feat: improve heuristics when dist-tags.latest is in range Jul 9, 2024
@wraithgar wraithgar merged commit 9f5560f into main Jul 9, 2024
24 checks passed
@wraithgar wraithgar deleted the isaacs/do-not-default-to-latest-on-engine-mismatch branch July 9, 2024 17:01
@github-actions github-actions bot mentioned this pull request Jul 9, 2024
wraithgar pushed a commit that referenced this pull request Jul 9, 2024
🤖 I have created a release *beep* *boop*
---


##
[9.1.0](v9.0.1...v9.1.0)
(2024-07-09)

### Features

*
[`9f5560f`](9f5560f)
[#33](#33) improve
heuristics when dist-tags.latest is in range (#33) (@isaacs)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants