From 9a8ccf679a39a53f29fb0cf933c76cf408b8e9f4 Mon Sep 17 00:00:00 2001 From: Michael Garvin Date: Mon, 23 Feb 2026 11:06:07 -0800 Subject: [PATCH] feat: add :type(registry) to query selector syntax --- .../content/using-npm/dependency-selectors.md | 2 +- workspaces/arborist/lib/query-selector-all.js | 10 +++++++++- workspaces/arborist/test/query-selector-all.js | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/docs/lib/content/using-npm/dependency-selectors.md b/docs/lib/content/using-npm/dependency-selectors.md index b12c640c586ec..ea4a231467636 100644 --- a/docs/lib/content/using-npm/dependency-selectors.md +++ b/docs/lib/content/using-npm/dependency-selectors.md @@ -62,7 +62,7 @@ The [`npm query`](/commands/npm-query) command exposes a new dependency selector - `:missing` when a dependency is not found on disk - `:semver(, [selector], [function])` match a valid [`node-semver`](https://github.com/npm/node-semver) version or range to a selector - `:path()` [glob](https://www.npmjs.com/package/glob) matching based on dependencies path relative to the project -- `:type()` [based on currently recognized types](https://github.com/npm/npm-package-arg#result-object) +- `:type()` [based on currently recognized types](https://github.com/npm/npm-package-arg#result-object). You can also use the aggregate type of `registry` for any registry dependency (e.g. tag, version, range, alias) - `:outdated()` when a dependency is outdated - `:vuln()` when a dependency has a known vulnerability diff --git a/workspaces/arborist/lib/query-selector-all.js b/workspaces/arborist/lib/query-selector-all.js index db0d8ea2edb11..71cfee736d9cc 100644 --- a/workspaces/arborist/lib/query-selector-all.js +++ b/workspaces/arborist/lib/query-selector-all.js @@ -427,11 +427,19 @@ class Results { if (!this.currentAstNode.typeValue) { return this.initialItems } + // TODO this differs subtly with `:type()` because it now iterates on edgesIn, which means extraneous deps won't show up + // note how "@npmcli/abbrev@2.0.0-beta.45" is in the `:type()` results in the test but not in any of the other results. return this.initialItems .flatMap(node => { const found = [] + const { typeValue } = this.currentAstNode for (const edge of node.edgesIn) { - if (npa(`${edge.name}@${edge.spec}`).type === this.currentAstNode.typeValue) { + const parsedArg = npa(`${edge.name}@${edge.spec}`) + if (typeValue === 'registry') { + if (parsedArg.registry) { + found.push(edge.to) + } + } else if (parsedArg.type === typeValue) { found.push(edge.to) } } diff --git a/workspaces/arborist/test/query-selector-all.js b/workspaces/arborist/test/query-selector-all.js index 4a5a06df849c6..97a20a6f6e92e 100644 --- a/workspaces/arborist/test/query-selector-all.js +++ b/workspaces/arborist/test/query-selector-all.js @@ -569,6 +569,7 @@ t.test('query-selector-all', async t => { 'a@1.0.0', 'b@1.0.0', 'c@1.0.0', + // TODO this should show up in the other :type tests, see lib/query-selector-all.js '@npmcli/abbrev@2.0.0-beta.45', 'abbrev@1.1.1', 'bar@2.0.0', @@ -596,6 +597,22 @@ t.test('query-selector-all', async t => { 'bar@1.4.0', 'moo@3.0.0', ]], + [':type(registry)', [ + 'a@1.0.0', + 'abbrev@1.1.1', + 'b@1.0.0', + 'bar@2.0.0', + 'baz@1.0.0', + 'dash-separated-pkg@1.0.0', + 'dasher@2.0.0', + 'foo@2.2.2', + 'bar@1.4.0', + 'ipsum@npm:sit@1.0.0', + 'lorem@1.0.0', + 'moo@3.0.0', + 'recur@1.0.0', + 'sive@1.0.0', + ]], [':type(git)', []], // path pseudo