Skip to content

Commit 8ab8668

Browse files
watildeowlstronaut
authored andcommitted
fix(query): support package-lock-only in workspaces
1 parent 0765289 commit 8ab8668

File tree

3 files changed

+160
-9
lines changed

3 files changed

+160
-9
lines changed

lib/commands/query.js

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,16 @@ class Query extends BaseCommand {
5757
}
5858

5959
async exec (args) {
60-
const packageLock = this.npm.config.get('package-lock-only')
60+
const packageLockOnly = this.npm.config.get('package-lock-only')
6161
const Arborist = require('@npmcli/arborist')
6262
const arb = new Arborist({
6363
...this.npm.flatOptions,
6464
// one dir up from wherever node_modules lives
6565
path: resolve(this.npm.dir, '..'),
66-
forceActual: !packageLock,
66+
forceActual: !packageLockOnly,
6767
})
6868
let tree
69-
if (packageLock) {
69+
if (packageLockOnly) {
7070
try {
7171
tree = await arb.loadVirtual()
7272
} catch (err) {
@@ -84,20 +84,33 @@ class Query extends BaseCommand {
8484

8585
async execWorkspaces (args) {
8686
await this.setWorkspaces()
87+
const packageLockOnly = this.npm.config.get('package-lock-only')
8788
const Arborist = require('@npmcli/arborist')
8889
const arb = new Arborist({
8990
...this.npm.flatOptions,
9091
path: this.npm.prefix,
92+
forceActual: !packageLockOnly,
9193
})
92-
// FIXME: Workspace support in query does not work as expected so this does not
93-
// do the same package-lock-only check as this.exec().
94-
// https://github.com/npm/cli/pull/6732#issuecomment-1708804921
95-
const tree = await arb.loadActual()
94+
let tree
95+
if (packageLockOnly) {
96+
try {
97+
tree = await arb.loadVirtual()
98+
} catch (err) {
99+
log.verbose('loadVirtual', err.stack)
100+
throw this.usageError(
101+
'A package lock or shrinkwrap file is required in package-lock-only mode'
102+
)
103+
}
104+
} else {
105+
tree = await arb.loadActual()
106+
}
96107
for (const path of this.workspacePaths) {
97108
const wsTree = path === tree.root.path
98109
? tree // --includes-workspace-root
99-
: await tree.querySelectorAll(`.workspace:path(${path})`).then(r => r[0].target)
100-
await this.#queryTree(wsTree, args[0])
110+
: await tree.querySelectorAll(`.workspace:path(${path})`).then(r => r[0]?.target)
111+
if (wsTree) {
112+
await this.#queryTree(wsTree, args[0])
113+
}
101114
}
102115
this.#output()
103116
}

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ exports[`test/lib/commands/query.js TAP missing > should return missing node 1`]
124124
]
125125
`
126126

127+
exports[`test/lib/commands/query.js TAP package-lock-only missing workspace in tree > should return empty array for missing workspace 1`] = `
128+
[]
129+
`
130+
127131
exports[`test/lib/commands/query.js TAP package-lock-only with package lock > should return valid response with only lock info 1`] = `
128132
[
129133
{
@@ -172,6 +176,48 @@ exports[`test/lib/commands/query.js TAP package-lock-only with package lock > sh
172176
]
173177
`
174178

179+
exports[`test/lib/commands/query.js TAP package-lock-only with package lock and workspace > should return workspace object with package-lock-only 1`] = `
180+
[
181+
{
182+
"name": "root",
183+
"workspaces": [
184+
"packages/*"
185+
],
186+
"pkgid": "root@",
187+
"location": "",
188+
"path": "{CWD}/prefix",
189+
"realpath": "{CWD}/prefix",
190+
"resolved": null,
191+
"from": [],
192+
"to": [
193+
"node_modules/a"
194+
],
195+
"dev": false,
196+
"inBundle": false,
197+
"deduped": false,
198+
"overridden": false,
199+
"queryContext": {}
200+
},
201+
{
202+
"name": "a",
203+
"version": "1.0.0",
204+
205+
"pkgid": "[email protected]",
206+
"location": "packages/a",
207+
"path": "{CWD}/prefix/packages/a",
208+
"realpath": "{CWD}/prefix/packages/a",
209+
"resolved": null,
210+
"from": [],
211+
"to": [],
212+
"dev": false,
213+
"inBundle": false,
214+
"deduped": false,
215+
"overridden": false,
216+
"queryContext": {}
217+
}
218+
]
219+
`
220+
175221
exports[`test/lib/commands/query.js TAP recursive tree > should return everything in the tree, accounting for recursion 1`] = `
176222
[
177223
{

test/lib/commands/query.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,98 @@ t.test('package-lock-only', t => {
236236
await npm.exec('query', ['*'])
237237
t.matchSnapshot(joinedOutput(), 'should return valid response with only lock info')
238238
})
239+
240+
t.test('with package lock and workspace', async t => {
241+
const { npm, joinedOutput } = await loadMockNpm(t, {
242+
config: {
243+
'package-lock-only': true,
244+
workspace: ['a'],
245+
},
246+
prefixDir: {
247+
'package.json': JSON.stringify({
248+
name: 'root',
249+
workspaces: ['packages/*'],
250+
}),
251+
'package-lock.json': JSON.stringify({
252+
name: 'root',
253+
lockfileVersion: 3,
254+
requires: true,
255+
packages: {
256+
'': {
257+
name: 'root',
258+
workspaces: ['packages/*'],
259+
},
260+
'node_modules/a': {
261+
resolved: 'packages/a',
262+
link: true,
263+
},
264+
'packages/a': {
265+
name: 'a',
266+
version: '1.0.0',
267+
},
268+
},
269+
}),
270+
packages: {
271+
a: {
272+
'package.json': JSON.stringify({
273+
name: 'a',
274+
version: '1.0.0',
275+
}),
276+
},
277+
},
278+
},
279+
})
280+
await npm.exec('query', ['*'])
281+
t.matchSnapshot(joinedOutput(), 'should return workspace object with package-lock-only')
282+
})
283+
284+
t.test('no package lock and workspace', async t => {
285+
const { npm } = await loadMockNpm(t, {
286+
config: {
287+
'package-lock-only': true,
288+
workspace: ['a'],
289+
},
290+
prefixDir: {
291+
'package.json': JSON.stringify({
292+
name: 'root',
293+
workspaces: ['packages/*'],
294+
}),
295+
packages: {
296+
a: {
297+
'package.json': JSON.stringify({
298+
name: 'a',
299+
version: '1.0.0',
300+
}),
301+
},
302+
},
303+
},
304+
})
305+
await t.rejects(npm.exec('query', ['*']), { code: 'EUSAGE' })
306+
})
307+
308+
t.test('missing workspace in tree', async t => {
309+
const { npm, joinedOutput } = await loadMockNpm(t, {
310+
config: {
311+
workspace: ['a'],
312+
},
313+
prefixDir: {
314+
'package.json': JSON.stringify({
315+
name: 'root',
316+
workspaces: ['packages/*'],
317+
}),
318+
packages: {
319+
a: {
320+
'package.json': JSON.stringify({
321+
name: 'a',
322+
version: '1.0.0',
323+
}),
324+
},
325+
},
326+
},
327+
})
328+
await npm.exec('query', ['*'])
329+
t.matchSnapshot(joinedOutput(), 'should return empty array for missing workspace')
330+
})
239331
t.end()
240332
})
241333

0 commit comments

Comments
 (0)