diff --git a/workspaces/arborist/lib/arborist/build-ideal-tree.js b/workspaces/arborist/lib/arborist/build-ideal-tree.js index df4f5015919db..0375e1851495a 100644 --- a/workspaces/arborist/lib/arborist/build-ideal-tree.js +++ b/workspaces/arborist/lib/arborist/build-ideal-tree.js @@ -269,6 +269,22 @@ module.exports = cls => class IdealTreeBuilder extends cls { this[_complete] = !!options.complete this[_preferDedupe] = !!options.preferDedupe this[_legacyBundling] = !!options.legacyBundling + + // validates list of update names, they must + // be dep names only, no semver ranges are supported + for (const name of update.names) { + const spec = npa(name) + const validationError = + new TypeError(`Update arguments must not contain package version specifiers + +Try using the package name instead, e.g: + npm update ${spec.name}`) + validationError.code = 'EUPDATEARGS' + + if (spec.fetchSpec !== 'latest') { + throw validationError + } + } this[_updateNames] = update.names this[_updateAll] = update.all diff --git a/workspaces/arborist/test/arborist/build-ideal-tree.js b/workspaces/arborist/test/arborist/build-ideal-tree.js index d8bfe8200c71c..be3a40042a9fd 100644 --- a/workspaces/arborist/test/arborist/build-ideal-tree.js +++ b/workspaces/arborist/test/arborist/build-ideal-tree.js @@ -2098,6 +2098,22 @@ t.test('update global', async t => { t.matchSnapshot(await printIdeal(path, { global: true, update: ['wrappy'] }), 'updating sub-dep has no effect') + + const invalidArgs = [ + 'once@1.4.0', + 'once@next', + 'once@^1.0.0', + 'once@>=2.0.0', + 'once@2', + ] + for (const updateName of invalidArgs) { + t.rejects( + printIdeal(path, { global: true, update: [updateName] }), + { code: 'EUPDATEARGS' }, + 'should throw an error when using semver ranges' + ) + } + t.matchSnapshot(await printIdeal(path, { global: true, update: ['once'] }), 'update a single dep') t.matchSnapshot(await printIdeal(path, { global: true, update: true }),