diff --git a/workspaces/arborist/lib/arborist/build-ideal-tree.js b/workspaces/arborist/lib/arborist/build-ideal-tree.js index f20a554bd5ee8..ec6933303b3f5 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 2c058a6a3283e..a07d3b2f6cd15 100644 --- a/workspaces/arborist/test/arborist/build-ideal-tree.js +++ b/workspaces/arborist/test/arborist/build-ideal-tree.js @@ -2094,6 +2094,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 }),