npm query
for better dependency managementAvailable on the npm CLI as of v8.16.0, query
is a new sub-command that exposes a powerful new way to inspect and understand the dependencies of your Node.js project. With it comes a powerful new query
syntax, based on a familiar paradigm: CSS. That’s right, you can now use special CSS selectors with npm query
to better understand your project’s dependency tree.
In this article, we’ll discuss why it’s important to inspect dependencies, review npm query
examples, dive into the syntax for this new command, and explore different ways to format and manipulate its output.
Jump ahead:
npm query
First of all, why is any of this useful? Here are some of the reasons folks are wanting to have a clear picture of their dependencies:
Before diving into the nitty-gritty of the command arguments and CSS-based syntax, here are some basic examples to demonstrate the power and precision this new npm query
command provides.
Suppose you’re writing closed-source, for-profit software and need to ensure that none of your dependencies are released under the GPL license (which requires downstream users to also release their software under the GPL or compatible license). To surface potentially problematic packages, you could use the following query
command:
npm query "[license=GPL]"
N.B., this is not intended as legal advice; you should consult an attorney if you have specific questions about software licenses
Some packages run scripts after installation, and you may want to inspect these scripts to ensure that they aren’t doing anything nefarious. With npm query
, you can easily find any dependencies that register a postinstall
script:
npm query ":attr(scripts, [postinstall])"
See the MDN docs for a refresher on how the CSS :attr()
function works.
The primary means of selecting specific dependencies is analogous to the CSS ID selector. This command will list all copies of lodash
installed:
npm query "#lodash"
And if you want to verify that a particular version, say 4.17.21
, is installed, just modify the command like so:
npm query "#[email protected]"
npm query
also supports semver ranges, as long as you use the more verbose version of the above command (which uses a name
attribute selector and the semver
pseudo-selector):
npm query "[name=lodash]:semver(^4)"
Already, we begin to see the flexibility this CSS-based syntax provides. With just a couple more core concepts (outlined below), you’ll be able to bring your prior CSS experience to construct queries that answer highly specific questions about the dependency tree.
Dependency groups (prod
dependencies vs. dev
dependencies, for example) are expressed with the familiar CSS class syntax. So here’s how to query
for all dev
dependencies:
npm query ".dev"
This query
returns all transitive dependencies in the dev
group as well. But, suppose you’d like to limit the list to just the direct dev
dependencies of your project.
You can utilize the :root
pseudo-selector, which represents your project, and the CSS child combinator, >
, to limit the scope of the query
:
npm query ":root > .dev"
In addition to the standard CSS pseudo-selectors (such as :not()
, :has()
, :empty
, etc.), npm query
adds a few special pseudo-selectors that are specific to querying a dependency tree:
:private
– Selects dependencies marked as private in their package.json files ("private": true
):deduped
– Selects deduped dependencies:overridden
– Selects dependencies that have been overridden:extraneous
– Selects dependencies that may be left over from previous states as they no longer are depended on by any package in the dependency tree:missing
– Selects dependencies that are requested by other packages but are not found on disk:semver(<spec>)
– Selects dependencies matching the specified semantic version spec:path(<path>)
– Selects dependencies matching the specified path:type(<type>)
– Selects dependencies of the given type, such as git
(for a git repo), directory
(for a local directory), etc.; see this comprehensive list of possible typesWith the special pseudo-selectors listed above, the full power of CSS can be leveraged to unlock some really powerful queries deep into the dependency tree. Here’s a refresher on some particularly helpful CSS concepts:
>
combinator we used previously selects dependencies matching the selector on the right as long as they are direct descendants (children) of the selector on the left. The
combinator is similar but operates on all descendants (children, grandchildren, and beyond). The ~
combinator allows for selecting siblings[key=value]
, select dependencies that have an attribute called key
that is set to value
. This only works for string values in package.json
; for more powerful attribute selection, use the :attr()
CSS function*
is a special selector that, just as in CSS, selects all dependencies. Multiple sequences of selectors can be joined together using the ,
selector, which produces the union of results from the selectors on either sideBy default, npm query
outputs large swaths of JSON, formatted with newlines and an indentation level of two spaces. This is perfect for sending the data to a JSON file for use by other programs, but it’s not great for people who’d like to read the output in the console.
jq
jq
is a popular and fast JSON processor that’s typically used directly in the command line. There are lots of great tutorials and examples online for how to use jq
, including the official jq manual, but here is a quick example to demonstrate its power.
Let’s reuse our query
from earlier in the article for selecting all dependencies that employ a postinstall
script (npm query ":attr(scripts, [postinstall])"
), this time displaying only their name, version, and path on disk:
npm query ":attr(scripts, [postinstall])" | jq '.[] | "\(.name)@\(.version) - \(.path)"'
Here’s some sample output:
"[email protected] - /workspaces/proutils.dev/node_modules/esbuild" "[email protected] - /workspaces/proutils.dev/node_modules/svelte-preprocess"
For even greater control, as well as the ability to run advanced processing and analysis on the results, npm has released a package called Arborist for use within a Node.js program. It features a promise-based API and supports the same CSS selectors as the CLI version. For more details on Arborist, take a look at the official docs.
npm query
In this article, we’ve taken a quick look at the powerful new npm query
CLI command. With a carefully crafted query
, you can more easily achieve deep insights into the dependency tree.
There are a lot of use cases, including a great list in the npm query
documentation. To get your mental wheels turning on the possibilities, here are just a few more use cases phrased as questions that development teams might have about their dependency stack. Also provided are the queries that will help answer those questions:
npm install
because I’m missing some dependencies my teammates added?
npm query
"*:missing
"
lodash
and underscore
?
npm query "*:has(> #lodash ~ #underscore)"
npm query "*:not(*:has(> *))"
What other useful queries can you come up with?
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third-party services are successful, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.