Dependency Selector Syntax & Querying
Select CLI Version:
See Details
Table of contents
Description
Thenpm query
commmand exposes a new dependency selector syntax (informed by & respecting many aspects of theCSS Selectors 4 Spec) which:
- Standardizes the shape of, & querying of, dependency graphs with a robust object model, metadata & selector syntax
- Leverages existing, known language syntax & operators from CSS to make disparate package information broadly accessible
- Unlocks the ability to answer complex, multi-faceted questions about dependencies, their relationships & associative metadata
- Consolidates redundant logic of similar query commands in
npm
(ex.npm fund
,npm ls
,npm outdated
,npm audit
...)
Dependency Selector Syntaxv1.0.0
Overview:
- there is no "type" or "tag" selectors (ex.
div, h1, a
) as a dependency/target is the only type ofNode
that can be queried - the term "dependencies" is in reference to any
Node
found in atree
returned byArborist
Combinators
>
direct descendant/childany descendant/child
~
sibling
Selectors
*
universal selector#<name>
dependency selector (equivalent to[name="..."]
)#<name>@<version>
(equivalent to[name=<name>]:semver(<version>)
),
selector list delimiter.
dependency type selector:
pseudo selector
Dependency Type Selectors
.prod
dependency found in thedependencies
section ofpackage.json
, or is a child of said dependency.dev
dependency found in thedevDependencies
section ofpackage.json
, or is a child of said dependency.optional
dependency found in theoptionalDependencies
section ofpackage.json
, or has"optional": true
set in its entry in thepeerDependenciesMeta
section ofpackage.json
, or a child of said dependency.peer
dependency found in thepeerDependencies
section ofpackage.json
.workspace
dependency found in theworkspaces
section ofpackage.json
.bundled
dependency found in thebundleDependencies
section ofpackage.json
, or is a child of said dependency
Pseudo Selectors
:not(<selector>)
:has(<selector>)
:is(<selector list>)
:root
matches the root node/dependency:scope
matches node/dependency it was queried against:empty
when a dependency has no dependencies:private
when a dependency is private:link
when a dependency is linked (for instance, workspaces or packages manuallylinked
:deduped
when a dependency has been deduped (note that this doesnot always mean the dependency has been hoisted to the root of node_modules):overridden
when a dependency has been overridden:extraneous
when a dependency exists but is not defined as a dependency of any node:invalid
when a dependency version is out of its ancestors specified range:missing
when a dependency is not found on disk:semver(<spec>)
matching a validnode-semver
spec:path(<path>)
glob matching based on dependencies path relative to the project:type(<type>)
based on currently recognized types
Attribute Selectors
The attribute selector evaluates the key/value pairs inpackage.json
if they areString
s.
[]
attribute selector (ie. existence of attribute)[attribute=value]
attribute value is equivalant...[attribute~=value]
attribute value contains word...[attribute*=value]
attribute value contains string...[attribute|=value]
attribute value is equal to or starts with...[attribute^=value]
attribute value starts with...[attribute$=value]
attribute value ends with...
Array
&Object
Attribute Selectors
The generic:attr()
pseudo selector standardizes a pattern which can be used for attribute selection ofObject
s,Array
s orArrays
ofObject
s accessible viaArborist
'sNode.package
metadata. This allows for iterative attribute selection beyond top-levelString
evaluation. The last argument passed to:attr()
must be anattribute
selector or a nested:attr()
. See examples below:
Objects
/* return dependencies that have a `scripts.test` containing `"tap"` */*:attr(scripts, [test~=tap]);
NestedObjects
Nested objects are expressed as sequential arguments to:attr()
.
/* return dependencies that have a testling config for opera browsers */*:attr(testling, browsers, [~=opera]);
Arrays
Array
s specifically uses a special/reserved.
character in place of a typical attribute name.Arrays
also support exactvalue
matching when aString
is passed to the selector.
Example of anArray
Attribute Selection:
/* removes the distinction between properties & arrays *//* ie. we'd have to check the property & iterate to match selection */*:attr([keywords^=react])*:attr(contributors,:attr([name~=Jordan]))
Example of anArray
matching directly to a value:
/* return dependencies that have the exact keyword "react" *//* this is equivalent to `*:keywords([value="react"])` */*:attr([keywords=react]);
Example of anArray
ofObject
s:
/* returns */*:attr(contributors, [email=ruyadorno@github.com]);
Groups
Dependency groups are defined by the package relationships to their ancestors (ie. the dependency types that are defined inpackage.json
). This approach is user-centric as the ecosystem has been taught to think about dependencies in these groups first-and-foremost. Dependencies are allowed to be included in multiple groups (ex. aprod
dependency may also be adev
dependency (in that it's also required by anotherdev
dependency) & may also bebundled
- a selector for that type of dependency would look like:*.prod.dev.bundled
).
.prod
.dev
.optional
.peer
.bundled
.workspace
Please note that currentlyworkspace
deps are alwaysprod
dependencies. Additionally the.root
dependency is also considered aprod
dependency.
Programmatic Usage
Arborist
'sNode
Class has a.querySelectorAll()
method- this method will return a filtered, flattened dependency Arborist
Node
list based on a valid query selector
- this method will return a filtered, flattened dependency Arborist
constArborist=require("@npmcli/arborist");const arb=newArborist({});
// root-levelarb.loadActual().then(async(tree)=>{// query all production dependenciesconst results=await tree.querySelectorAll(".prod");console.log(results);});
// iterativearb.loadActual().then(async(tree)=>{// query for the deduped version of reactconst results=await tree.querySelectorAll("#react:not(:deduped)");// query the deduped react for git depsconst deps=await results[0].querySelectorAll(":type(git)");console.log(deps);});