Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions workspaces/arborist/lib/arborist/build-ideal-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,22 @@ module.exports = cls => class IdealTreeBuilder extends cls {
this[_updateNames] = update.names

this[_updateAll] = update.all

// validates list of rm names, they must
// be dep names only, no semver ranges are supported
for (const name of options.rm || []) {
const spec = npa(name)
const validationError =
new TypeError(`Remove arguments must only contain package names, eg:
npm rm ${spec.name || '<pkg>'}`)
validationError.code = 'ERMARGS'

// If they gave us anything other than a bare package name
if (spec.raw !== spec.name) {
throw validationError
}
}

// we prune by default unless explicitly set to boolean false
this.#prune = options.prune !== false

Expand Down
41 changes: 41 additions & 0 deletions workspaces/arborist/test/arborist/build-ideal-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -2347,6 +2347,47 @@ t.test('remove deps when initializing tree from actual tree', async t => {
t.equal(tree.children.get('foo'), undefined, 'removed foo child')
})

t.test('remove deps with a version spec', async t => {
const path = t.testdir({
node_modules: {
foo: {
'package.json': JSON.stringify({
name: 'foo',
version: '1.2.3',
}),
},
},
})

createRegistry(t, false)
const invalidArgs = [
'foo@1.2.3',
'foo@next',
'foo@^1.0.0',
'foo@>=2.0.0',
'foo@2',
]
for (const rmName of invalidArgs) {
await t.rejects(
buildIdeal(path, { rm: [rmName] }),
{ code: 'ERMARGS', message: /npm rm foo/ },
'should throw an error when the package name has a version'
)
}

await t.rejects(
buildIdeal(path, { rm: ['@scope/foo@1.2.3'] }),
{ code: 'ERMARGS', message: /npm rm @scope\/foo/ },
'should throw an error when a scoped package name has a version'
)

await t.rejects(
buildIdeal(path, { rm: ['./foo'] }),
{ code: 'ERMARGS', message: /npm rm <pkg>/ },
'should throw an error when the package is a path'
)
})

t.test('detect conflicts in transitive peerOptional deps', async t => {
const base = resolve(fixtures, 'test-conflicted-optional-peer-dep')

Expand Down
Loading