From 2e03d95d283e070795af1610b4107e1fe91a7022 Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 28 Sep 2020 14:10:37 -0700 Subject: [PATCH] Save provided range if not a subset of savePrefix If a user installs `foo@1.x <1.2.3`, and we resolve to `1.2.2`, then we should not save it as `^1.2.2`, since that would allow versions outside of the requested range. Explicit versions and tags are still saved using the savePrefix, since those are not ranges, and users can set `--save-exact` if they wish it to be saved exactly. Fix: https://github.com/npm/arborist/issues/127 Fix: https://github.com/npm/cli/pull/193 Fix: https://npm.community/t/7005 --- lib/arborist/reify.js | 14 ++++++++++++-- tap-snapshots/test-arborist-reify.js-TAP.test.js | 2 +- test/arborist/reify.js | 6 +++--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/arborist/reify.js b/lib/arborist/reify.js index f1241556c..b4fd84d50 100644 --- a/lib/arborist/reify.js +++ b/lib/arborist/reify.js @@ -4,6 +4,7 @@ const pacote = require('pacote') const rpj = require('read-package-json-fast') const { orderDeps, updateDepSpec } = require('../dep-spec.js') const AuditReport = require('../audit-report.js') +const {subset} = require('semver') const {dirname, resolve, relative} = require('path') const {depth: dfwalk} = require('treeverse') @@ -788,12 +789,21 @@ module.exports = cls => class Reifier extends cls { const root = this.idealTree const pkg = root.package for (const req of this[_resolvedAdd]) { - const {name} = req + const {name, rawSpec, subSpec} = req + const spec = subSpec ? subSpec.rawSpec : rawSpec const child = root.children.get(name) if (req.registry) { const version = child.version - const range = this[_savePrefix] + version + const prefixRange = this[_savePrefix] + version + // if we installed a range, then we save the range specified + // if it is not a subset of the ^x.y.z. eg, installing a range + // of `1.x <1.2.3` will not be saved as `^1.2.0`, because that + // would allow versions outside the requested range. Tags and + // specific versions save with the save-prefix. + const isRange = (subSpec || req).type === 'range' + const range = !isRange || subset(prefixRange, spec, { loose: true }) + ? prefixRange : spec const pname = child.package.name const alias = name !== pname updateDepSpec(pkg, name, (alias ? `npm:${pname}@` : '') + range) diff --git a/tap-snapshots/test-arborist-reify.js-TAP.test.js b/tap-snapshots/test-arborist-reify.js-TAP.test.js index 241143e50..5427dd786 100644 --- a/tap-snapshots/test-arborist-reify.js-TAP.test.js +++ b/tap-snapshots/test-arborist-reify.js-TAP.test.js @@ -27051,7 +27051,7 @@ Object { "dependencies": Object { "a": "github:foo/bar#baz", "b": "^1.2.3", - "d": "npm:c@^1.2.3", + "d": "npm:c@1.x <1.9.9", }, "devDependencies": Object { "c": "git+ssh://git@githost.com:a/b/c.git#master", diff --git a/test/arborist/reify.js b/test/arborist/reify.js index ab144323a..4cbd8671f 100644 --- a/test/arborist/reify.js +++ b/test/arborist/reify.js @@ -816,7 +816,7 @@ t.test('saving the ideal tree', t => { dependencies: { a: 'git+ssh://git@github.com:foo/bar#baz', b: '', - d: 'd@npm:c@1.x', + d: 'd@npm:c@1.x <1.9.9', }, devDependencies: { c: `git+ssh://git@githost.com:a/b/c.git#master`, @@ -870,7 +870,7 @@ t.test('saving the ideal tree', t => { a[kResolvedAdd] = [ npa('a@git+ssh://git@github.com:foo/bar#baz'), npa('b'), - npa('d@npm:c@1.x'), + npa('d@npm:c@1.x <1.9.9'), npa(`c@git+ssh://git@githost.com:a/b/c.git#master`), ] return a[kSaveIdealTree]({ @@ -883,7 +883,7 @@ t.test('saving the ideal tree', t => { dependencies: { a: 'github:foo/bar#baz', b: '^1.2.3', - d: 'npm:c@^1.2.3', + d: 'npm:c@1.x <1.9.9', }, devDependencies: { c: 'git+ssh://git@githost.com:a/b/c.git#master',