Available 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.
- Why inspect dependencies?
- Basic examples
- Formatting and manipulating the output
- How to improve dependency management with
Why inspect dependencies?
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:
- Security audits: Staying on top of security vulnerabilities (and subsequent patches) is critical to shipping high-quality modern software. Good tooling around dependency inspection makes fixing these issues easier and faster
- Stability: When bugs in a project’s dependencies cause problems, it’s crucial to be able to quickly identify the affected versions and roll out fixes
- Bundle size: Powerful dependency inspection tooling can aid software teams in their efforts to ship smaller bundles to end users and improve performance
- Clarity: Having a better grasp of which software packages are installed and their intended purpose helps engineers make better decisions and write better code
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
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
Post-install script inspection
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
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
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
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
npm query ".dev"
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
npm query ":root > .dev"
In addition to the standard CSS pseudo-selectors (such as
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 (
: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 types
Familiar CSS paradigms
With 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:
- Combinators: The
>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 allows for selecting siblings
- Attribute selectors: Attribute selectors, like
[key=value], select dependencies that have an attribute called
keythat is set to
value. This only works for string values in
package.json; for more powerful attribute selection, use the
- Advanced selectors:
*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 side
Formatting and manipulating the output
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.
Usage in the terminal with
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.
How to improve dependency management with
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:
- Do I need to run
npm installbecause I’m missing some dependencies my teammates added?
- Are there any packages that depend on both
npm query "*:has(> #lodash ~ #underscore)"
- Which packages are leaf nodes in the dependency tree?
npm query "*:not(*:has(> *))"
What other useful queries can you come up with?
200’s only Monitor failed and slow network requests in productionDeploying 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. https://logrocket.com/signup/
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.