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.
Jump ahead:
- Why inspect dependencies?
- Basic examples
- Syntax
- Formatting and manipulating the output
- How to improve dependency management with
npm query
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
Basic examples
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.
License audit
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
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 postinstall
script:
npm query ":attr(scripts, [postinstall])"
See the MDN docs for a refresher on how the CSS :attr()
function works.
Syntax
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 "#lodash@4.17.21"
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
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"
Special pseudo-selectors
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 asgit
(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 is similar but operates on all descendants (children, grandchildren, and beyond). The~
combinator allows for selecting siblings - Attribute selectors: Attribute selectors, like
[key=value]
, select dependencies that have an attribute calledkey
that is set tovalue
. This only works for string values inpackage.json
; for more powerful attribute selection, use the:attr()
CSS function - 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
By 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.
Usage in the terminal with 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:
"esbuild@0.14.36 - /workspaces/proutils.dev/node_modules/esbuild" "svelte-preprocess@4.10.6 - /workspaces/proutils.dev/node_modules/svelte-preprocess"
Programmatic usage
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 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:
- Do I need to run
npm install
because I’m missing some dependencies my teammates added?npm query
"
*:missing"
- Are there any packages that depend on both
lodash
andunderscore
?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?
The post Better dependency management: npm query appeared first on LogRocket Blog.
from LogRocket Blog https://ift.tt/UPu2WJA
Gain $200 in a week
via Read more