From 81a460f6e6317aca2288d16cda591aa6541540c6 Mon Sep 17 00:00:00 2001 From: Gar Date: Wed, 23 Aug 2023 11:26:14 -0700 Subject: [PATCH] feat: add package-lock-only mode to npm query --- docs/lib/content/commands/npm-query.md | 6 ++ lib/commands/query.js | 15 ++++- .../test/lib/commands/query.js.test.cjs | 48 +++++++++++++++ tap-snapshots/test/lib/docs.js.test.cjs | 3 +- test/lib/commands/query.js | 58 +++++++++++++++++++ 5 files changed, 128 insertions(+), 2 deletions(-) diff --git a/docs/lib/content/commands/npm-query.md b/docs/lib/content/commands/npm-query.md index 42b2d086f3bbe..9907d4aa2c79c 100644 --- a/docs/lib/content/commands/npm-query.md +++ b/docs/lib/content/commands/npm-query.md @@ -133,6 +133,12 @@ npm query ":type(git)" | jq 'map(.name)' | xargs -I {} npm why {} }, ... ``` +### Package lock only mode + +If package-lock-only is enabled, only the information in the package +lock (or shrinkwrap) is loaded. This means that information from the +package.json files of your dependencies will not be included in the +result set (e.g. description, homepage, engines). ### Package lock only mode diff --git a/lib/commands/query.js b/lib/commands/query.js index ba39f004fae23..68aa1fa2c06a5 100644 --- a/lib/commands/query.js +++ b/lib/commands/query.js @@ -2,6 +2,7 @@ const { resolve } = require('path') const BaseCommand = require('../base-command.js') +const log = require('../utils/log-shim.js') class QuerySelectorItem { constructor (node) { @@ -48,6 +49,7 @@ class Query extends BaseCommand { 'workspace', 'workspaces', 'include-workspace-root', + 'package-lock-only', ] get parsedResponse () { @@ -64,7 +66,18 @@ class Query extends BaseCommand { forceActual: true, } const arb = new Arborist(opts) - const tree = await arb.loadActual(opts) + let tree + if (this.npm.config.get('package-lock-only')) { + try { + tree = await arb.loadVirtual() + } catch (err) { + log.verbose('loadVirtual', err.stack) + /* eslint-disable-next-line max-len */ + throw this.usageError('A package lock or shrinkwrap file is required in package-lock-only mode') + } + } else { + tree = await arb.loadActual(opts) + } const items = await tree.querySelectorAll(args[0], this.npm.flatOptions) this.buildResponse(items) diff --git a/tap-snapshots/test/lib/commands/query.js.test.cjs b/tap-snapshots/test/lib/commands/query.js.test.cjs index a6dbfcf7c693c..e732135468965 100644 --- a/tap-snapshots/test/lib/commands/query.js.test.cjs +++ b/tap-snapshots/test/lib/commands/query.js.test.cjs @@ -99,6 +99,54 @@ exports[`test/lib/commands/query.js TAP linked node > should return linked node ] ` +exports[`test/lib/commands/query.js TAP package-lock-only with package lock > should return valid response with only lock info 1`] = ` +[ + { + "name": "project", + "dependencies": { + "a": "^1.0.0" + }, + "pkgid": "project@", + "location": "", + "path": "{CWD}/prefix", + "realpath": "{CWD}/prefix", + "resolved": null, + "from": [], + "to": [ + "node_modules/a" + ], + "dev": false, + "inBundle": false, + "deduped": false, + "overridden": false, + "queryContext": {} + }, + { + "version": "1.2.3", + "resolved": "https://dummy.npmjs.org/a/-/a-1.2.3.tgz", + "integrity": "sha512-dummy", + "engines": { + "node": ">=14.17" + }, + "name": "a", + "_id": "a@1.2.3", + "pkgid": "a@1.2.3", + "location": "node_modules/a", + "path": "{CWD}/prefix/node_modules/a", + "realpath": "{CWD}/prefix/node_modules/a", + "from": [ + "" + ], + "to": [], + "dev": false, + "inBundle": false, + "deduped": false, + "overridden": false, + "queryContext": {} + } +] +` + exports[`test/lib/commands/query.js TAP recursive tree > should return everything in the tree, accounting for recursion 1`] = ` [ { diff --git a/tap-snapshots/test/lib/docs.js.test.cjs b/tap-snapshots/test/lib/docs.js.test.cjs index 102204d2c1a3f..a97753c84b3ce 100644 --- a/tap-snapshots/test/lib/docs.js.test.cjs +++ b/tap-snapshots/test/lib/docs.js.test.cjs @@ -3797,7 +3797,7 @@ npm query Options: [-g|--global] [-w|--workspace [-w|--workspace ...]] -[-ws|--workspaces] [--include-workspace-root] +[-ws|--workspaces] [--include-workspace-root] [--package-lock-only] Run "npm help query" for more info @@ -3809,6 +3809,7 @@ npm query #### \`workspace\` #### \`workspaces\` #### \`include-workspace-root\` +#### \`package-lock-only\` ` exports[`test/lib/docs.js TAP usage rebuild > must match snapshot 1`] = ` diff --git a/test/lib/commands/query.js b/test/lib/commands/query.js index 2b9a5b4976323..adf617316772e 100644 --- a/test/lib/commands/query.js +++ b/test/lib/commands/query.js @@ -179,3 +179,61 @@ t.test('global', async t => { await npm.exec('query', ['[name=lorem]']) t.matchSnapshot(joinedOutput(), 'should return global package') }) + +t.test('package-lock-only', t => { + t.test('no package lock', async t => { + const { npm } = await loadMockNpm(t, { + config: { + 'package-lock-only': true, + }, + prefixDir: { + 'package.json': JSON.stringify({ + name: 'project', + dependencies: { + a: '^1.0.0', + }, + }), + }, + }) + await t.rejects(npm.exec('query', [':root, :root > *']), { code: 'EUSAGE' }) + }) + + t.test('with package lock', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + config: { + 'package-lock-only': true, + }, + prefixDir: { + 'package.json': JSON.stringify({ + name: 'project', + dependencies: { + a: '^1.0.0', + }, + }), + 'package-lock.json': JSON.stringify({ + name: 'project', + lockfileVersion: 3, + requires: true, + packages: { + '': { + dependencies: { + a: '^1.0.0', + }, + }, + 'node_modules/a': { + version: '1.2.3', + resolved: 'https://dummy.npmjs.org/a/-/a-1.2.3.tgz', + integrity: 'sha512-dummy', + engines: { + node: '>=14.17', + }, + }, + }, + }), + }, + }) + await npm.exec('query', ['*']) + t.matchSnapshot(joinedOutput(), 'should return valid response with only lock info') + }) + t.end() +})