Skip to content

Commit 8a9a7cd

Browse files
committed
fix: implement deduped
1 parent ca4857f commit 8a9a7cd

File tree

6 files changed

+63
-108
lines changed

6 files changed

+63
-108
lines changed

docs/content/using-npm/dependency-selectors.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ The [`npm query`](/commands/npm-query) commmand exposes a new dependency selecto
4141
- `.dev` dependency found in the `devDependencies` section of `package.json`, or is a child of said dependency
4242
- `.optional` dependency found in the `optionalDependencies` section of `package.json`, or has `"optional": true` set in its entry in the `peerDependenciesMeta` section of `package.json`, or a child of said dependency
4343
- `.peer` dependency found in the `peerDependencies` section of `package.json`
44-
- `.workspace` dependency found in the `workspaces` section of `package.json`
44+
- `.workspace` dependency found in the [`workspaces`](https://docs.npmjs.com/cli/v8/using-npm/workspaces) section of `package.json`
4545
- `.bundled` dependency found in the `bundleDependencies` section of `package.json`, or is a child of said dependency
4646

4747
#### Pseudo Selectors
@@ -52,9 +52,9 @@ The [`npm query`](/commands/npm-query) commmand exposes a new dependency selecto
5252
- [`:scope`](https://developer.mozilla.org/en-US/docs/Web/CSS/:scope) matches node/dependency it was queried against
5353
- [`:empty`](https://developer.mozilla.org/en-US/docs/Web/CSS/:empty) when a dependency has no dependencies
5454
- [`:private`](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#private) when a dependency is private
55-
- `:link` when a dependency is linked
56-
- `:deduped` when a dependency has been deduped
57-
- `:override` when a dependency is an override
55+
- `:link` when a dependency is linked (for instance, workspaces or packages manually [`linked`](https://docs.npmjs.com/cli/v8/commands/npm-link)
56+
- `:deduped` when a dependency has been deduped (note that this does *not* always mean the dependency has been hoisted to the root of node_modules)
57+
- `:override` when a dependency is an override (not implemented yet)
5858
- `:extraneous` when a dependency exists but is not defined as a dependency of any node
5959
- `:invalid` when a dependency version is out of its ancestors specified range
6060
- `:missing` when a dependency is not found on disk

lib/commands/query.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ class QuerySelectorItem {
1919
this.to = []
2020
this.dev = node.target.dev
2121
this.inBundle = node.target.inBundle
22+
this.deduped = this.from.length > 1
2223
for (const edge of node.target.edgesIn) {
2324
this.from.push(edge.from.location)
2425
}
2526
for (const [, edge] of node.target.edgesOut) {
26-
this.to.push(edge.to.location)
27+
if (edge.to) {
28+
this.to.push(edge.to.location)
29+
}
2730
}
2831
}
2932
}
@@ -89,11 +92,8 @@ class Query extends BaseCommand {
8992
// builds a normalized inventory
9093
buildResponse (items) {
9194
for (const node of items) {
92-
if (!this.#seen.has(node.target.location)) {
93-
const item = new QuerySelectorItem(node)
94-
this.#response.push(item)
95-
this.#seen.add(item.realpath)
96-
}
95+
const item = new QuerySelectorItem(node)
96+
this.#response.push(item)
9797
}
9898
}
9999
}

tap-snapshots/test/lib/commands/query.js.test.cjs

+13-98
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,7 @@
66
*/
77
'use strict'
88
exports[`test/lib/commands/query.js TAP global > should return global package 1`] = `
9-
[
10-
{
11-
"name": "lorem",
12-
"version": "2.0.0",
13-
14-
"pkgid": "[email protected]",
15-
"location": "node_modules/lorem",
16-
"path": "{CWD}/test/lib/commands/tap-testdir-query-global/global/node_modules/lorem",
17-
"realpath": "{CWD}/test/lib/commands/tap-testdir-query-global/global/node_modules/lorem",
18-
"resolved": null,
19-
"from": [
20-
""
21-
],
22-
"to": [],
23-
"dev": false,
24-
"inBundle": false
25-
}
26-
]
9+
2710
`
2811

2912
exports[`test/lib/commands/query.js TAP include-workspace-root > should return workspace object and root object 1`] = `
@@ -49,7 +32,8 @@ exports[`test/lib/commands/query.js TAP include-workspace-root > should return w
4932
"node_modules/b"
5033
],
5134
"dev": false,
52-
"inBundle": false
35+
"inBundle": false,
36+
"deduped": false
5337
},
5438
{
5539
"name": "c",
@@ -63,92 +47,22 @@ exports[`test/lib/commands/query.js TAP include-workspace-root > should return w
6347
"from": [],
6448
"to": [],
6549
"dev": false,
66-
"inBundle": false
50+
"inBundle": false,
51+
"deduped": false
6752
}
6853
]
6954
`
7055

7156
exports[`test/lib/commands/query.js TAP linked node > should return linked node res 1`] = `
72-
[
73-
{
74-
"name": "a",
75-
"version": "1.0.0",
76-
77-
"pkgid": "[email protected]",
78-
"location": "a",
79-
"path": "{CWD}/test/lib/commands/tap-testdir-query-linked-node/prefix/a",
80-
"realpath": "{CWD}/test/lib/commands/tap-testdir-query-linked-node/prefix/a",
81-
"resolved": null,
82-
"from": [],
83-
"to": [],
84-
"dev": false,
85-
"inBundle": false
86-
},
87-
{
88-
"name": "a",
89-
"version": "1.0.0",
90-
91-
"pkgid": "[email protected]",
92-
"location": "a",
93-
"path": "{CWD}/test/lib/commands/tap-testdir-query-linked-node/prefix/a",
94-
"realpath": "{CWD}/test/lib/commands/tap-testdir-query-linked-node/prefix/a",
95-
"resolved": null,
96-
"from": [],
97-
"to": [],
98-
"dev": false,
99-
"inBundle": false
100-
}
101-
]
57+
58+
`
59+
60+
exports[`test/lib/commands/query.js TAP recursive tree > should return everything in the tree, accounting for recursion 1`] = `
61+
10262
`
10363

10464
exports[`test/lib/commands/query.js TAP simple query > should return root object and direct children 1`] = `
105-
[
106-
{
107-
"name": "project",
108-
"dependencies": {
109-
"a": "^1.0.0",
110-
"b": "^1.0.0"
111-
},
112-
"pkgid": "project@",
113-
"location": "",
114-
"path": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix",
115-
"realpath": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix",
116-
"resolved": null,
117-
"from": [],
118-
"to": [
119-
"node_modules/a",
120-
"node_modules/b"
121-
],
122-
"dev": false,
123-
"inBundle": false
124-
},
125-
{
126-
"pkgid": "a@",
127-
"location": "node_modules/a",
128-
"path": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix/node_modules/a",
129-
"realpath": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix/node_modules/a",
130-
"resolved": null,
131-
"from": [
132-
""
133-
],
134-
"to": [],
135-
"dev": false,
136-
"inBundle": false
137-
},
138-
{
139-
"pkgid": "b@",
140-
"location": "node_modules/b",
141-
"path": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix/node_modules/b",
142-
"realpath": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix/node_modules/b",
143-
"resolved": null,
144-
"from": [
145-
""
146-
],
147-
"to": [],
148-
"dev": false,
149-
"inBundle": false
150-
}
151-
]
65+
15266
`
15367

15468
exports[`test/lib/commands/query.js TAP workspace query > should return workspace object 1`] = `
@@ -165,7 +79,8 @@ exports[`test/lib/commands/query.js TAP workspace query > should return workspac
16579
"from": [],
16680
"to": [],
16781
"dev": false,
168-
"inBundle": false
82+
"inBundle": false,
83+
"deduped": false
16984
}
17085
]
17186
`

test/lib/commands/query.js

+31
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,44 @@ t.test('simple query', async t => {
3030
a: '^1.0.0',
3131
b: '^1.0.0',
3232
},
33+
peerDependencies: {
34+
c: '1.0.0',
35+
},
3336
}),
3437
},
3538
})
3639
await npm.exec('query', [':root, :root > *'])
3740
t.matchSnapshot(joinedOutput(), 'should return root object and direct children')
3841
})
3942

43+
t.test('recursive tree', async t => {
44+
const { npm, joinedOutput } = await loadMockNpm(t, {
45+
prefixDir: {
46+
node_modules: {
47+
a: {
48+
name: 'a',
49+
version: '1.0.0',
50+
},
51+
b: {
52+
name: 'b',
53+
version: '^2.0.0',
54+
dependencies: {
55+
a: '1.0.0',
56+
},
57+
},
58+
},
59+
'package.json': JSON.stringify({
60+
name: 'project',
61+
dependencies: {
62+
a: '^1.0.0',
63+
b: '^1.0.0',
64+
},
65+
}),
66+
},
67+
})
68+
await npm.exec('query', ['*'])
69+
t.matchSnapshot(joinedOutput(), 'should return everything in the tree, accounting for recursion')
70+
})
4071
t.test('workspace query', async t => {
4172
const { npm, joinedOutput } = await loadMockNpm(t, {
4273
config: {

workspaces/arborist/lib/query-selector-all.js

+4
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,10 @@ class Results {
310310
return found
311311
})
312312
}
313+
314+
dedupedPseudo () {
315+
return this.initialItems.filter(node => node.target.edgesIn.size > 1)
316+
}
313317
}
314318

315319
// operators for attribute selectors

workspaces/arborist/test/query-selector-all.js

+5
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,11 @@ t.test('query-selector-all', async t => {
358358
[':invalid', ['[email protected]']],
359359
360360
361+
[':deduped', [
362+
363+
364+
365+
]],
361366
[':missing', ['missing-dep@^1.0.0']],
362367
[':private', ['[email protected]']],
363368

0 commit comments

Comments
 (0)