Skip to content

Commit a1ed199

Browse files
committed
fix: implement deduped
1 parent ca4857f commit a1ed199

File tree

6 files changed

+124
-19
lines changed

6 files changed

+124
-19
lines changed

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

Lines changed: 4 additions & 4 deletions
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

Lines changed: 6 additions & 6 deletions
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

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ exports[`test/lib/commands/query.js TAP global > should return global package 1`
2121
],
2222
"to": [],
2323
"dev": false,
24-
"inBundle": false
24+
"inBundle": false,
25+
"deduped": false
2526
}
2627
]
2728
`
@@ -49,7 +50,8 @@ exports[`test/lib/commands/query.js TAP include-workspace-root > should return w
4950
"node_modules/b"
5051
],
5152
"dev": false,
52-
"inBundle": false
53+
"inBundle": false,
54+
"deduped": false
5355
},
5456
{
5557
"name": "c",
@@ -63,7 +65,8 @@ exports[`test/lib/commands/query.js TAP include-workspace-root > should return w
6365
"from": [],
6466
"to": [],
6567
"dev": false,
66-
"inBundle": false
68+
"inBundle": false,
69+
"deduped": false
6770
}
6871
]
6972
`
@@ -82,7 +85,8 @@ exports[`test/lib/commands/query.js TAP linked node > should return linked node
8285
"from": [],
8386
"to": [],
8487
"dev": false,
85-
"inBundle": false
88+
"inBundle": false,
89+
"deduped": false
8690
},
8791
{
8892
"name": "a",
@@ -96,7 +100,61 @@ exports[`test/lib/commands/query.js TAP linked node > should return linked node
96100
"from": [],
97101
"to": [],
98102
"dev": false,
99-
"inBundle": false
103+
"inBundle": false,
104+
"deduped": false
105+
}
106+
]
107+
`
108+
109+
exports[`test/lib/commands/query.js TAP recursive tree > should return everything in the tree, accounting for recursion 1`] = `
110+
[
111+
{
112+
"name": "project",
113+
"dependencies": {
114+
"a": "^1.0.0",
115+
"b": "^1.0.0"
116+
},
117+
"pkgid": "project@",
118+
"location": "",
119+
"path": "{CWD}/test/lib/commands/tap-testdir-query-recursive-tree/prefix",
120+
"realpath": "{CWD}/test/lib/commands/tap-testdir-query-recursive-tree/prefix",
121+
"resolved": null,
122+
"from": [],
123+
"to": [
124+
"node_modules/a",
125+
"node_modules/b"
126+
],
127+
"dev": false,
128+
"inBundle": false,
129+
"deduped": false
130+
},
131+
{
132+
"pkgid": "a@",
133+
"location": "node_modules/a",
134+
"path": "{CWD}/test/lib/commands/tap-testdir-query-recursive-tree/prefix/node_modules/a",
135+
"realpath": "{CWD}/test/lib/commands/tap-testdir-query-recursive-tree/prefix/node_modules/a",
136+
"resolved": null,
137+
"from": [
138+
""
139+
],
140+
"to": [],
141+
"dev": false,
142+
"inBundle": false,
143+
"deduped": false
144+
},
145+
{
146+
"pkgid": "b@",
147+
"location": "node_modules/b",
148+
"path": "{CWD}/test/lib/commands/tap-testdir-query-recursive-tree/prefix/node_modules/b",
149+
"realpath": "{CWD}/test/lib/commands/tap-testdir-query-recursive-tree/prefix/node_modules/b",
150+
"resolved": null,
151+
"from": [
152+
""
153+
],
154+
"to": [],
155+
"dev": false,
156+
"inBundle": false,
157+
"deduped": false
100158
}
101159
]
102160
`
@@ -109,6 +167,9 @@ exports[`test/lib/commands/query.js TAP simple query > should return root object
109167
"a": "^1.0.0",
110168
"b": "^1.0.0"
111169
},
170+
"peerDependencies": {
171+
"c": "1.0.0"
172+
},
112173
"pkgid": "project@",
113174
"location": "",
114175
"path": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix",
@@ -120,7 +181,8 @@ exports[`test/lib/commands/query.js TAP simple query > should return root object
120181
"node_modules/b"
121182
],
122183
"dev": false,
123-
"inBundle": false
184+
"inBundle": false,
185+
"deduped": false
124186
},
125187
{
126188
"pkgid": "a@",
@@ -133,7 +195,8 @@ exports[`test/lib/commands/query.js TAP simple query > should return root object
133195
],
134196
"to": [],
135197
"dev": false,
136-
"inBundle": false
198+
"inBundle": false,
199+
"deduped": false
137200
},
138201
{
139202
"pkgid": "b@",
@@ -146,7 +209,8 @@ exports[`test/lib/commands/query.js TAP simple query > should return root object
146209
],
147210
"to": [],
148211
"dev": false,
149-
"inBundle": false
212+
"inBundle": false,
213+
"deduped": false
150214
}
151215
]
152216
`
@@ -165,7 +229,8 @@ exports[`test/lib/commands/query.js TAP workspace query > should return workspac
165229
"from": [],
166230
"to": [],
167231
"dev": false,
168-
"inBundle": false
232+
"inBundle": false,
233+
"deduped": false
169234
}
170235
]
171236
`

test/lib/commands/query.js

Lines changed: 31 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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)