---
# System prepended metadata

title: Dependency Selector Syntax

---

# Dependency Selector Syntax

## Summary

A new dependency object model & selector syntax, informed by & respecting many aspects of the [CSS Selectors 4 Spec](https://dev.w3.org/csswg/selectors4/#relational).

## Motivation

- Standardize a dependency object & selector syntax
- Make it easy to anwser complex questions about your dependencies
- Unblock users from requiring new & redundant query commands in npm (ex. `npm fund`, `npm ls`, `npm outdated`, `npm audit` ...)

## Detailed Explanation

Should only be limited to whatever is possible within our current understanding of CSS Selectors today.


## Implementation

- `Arborist`'s `Node`s have a new `querySelectorAll()` method
- net new implementation of the query selection language described below, available through the `npm query` command.

### Selectors
    
- `*` all packages
- `>` direct dependants
- `#` id
- `#<name>`
- `#<name @ semver>`
- `.` class
- `:` pseudo class
- `~` sibling selector
- `+` preceding selector
- `[]` 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 begins with...
- `[attribute$=value]` attribute value ends with...
- `:not(<selector>)`
- `:has(<selector>)`
- `:nth-child(<num>)` 
- `:nth-of-type(<num>)` 
- `:root`
- `:scope`
- `:empty`
- `:private`
- `:linked`
- `:deduped`
- `:override`
- `:extraneous`
- `:outdated`
- `:vulnerable`
- `:advisory(<id>)`
- `:cwe(<id>)`
- `:semver(<spec>)`
 
### Classes

Classifications of dependencies; notably, packages can have multiple classes.

- `.prod`
- `.dev`
- `.optional`
- `.peer`
- `.bundled`
- `.workspace`
 
### Attributes

- `[name]`
- `[version]`
- `[description]`
- `[homepage]`
- `[bugs]`
- `[author]`
- `[license]`
- `[funding]`
- `[files]`
- `[main]`
- `[browser]`
- `[bin]`
- `[man]`
- `[directories]`
- `[repository]`
- `[scripts]`
- `[config]`
- `[workspaces]`
- `[dependencies]`
- `[devDependencies]`
- `[optionalDependencies]`
- `[bundledDependencies]`
- `[peerDependencies]`
- `[peerDependenciesMeta]`
- `[engines]`
- `[os]`
- `[cpu]`
- `[keywords]`
- `[path]`

## New Command

### `npm query "<selector>"` (alias `q`)

#### Options:

- `output` (alias `o`)
  - Default: `parseable`
  - Type: `parseable`, `json`, `list`, `explain`, `outdated`, `funding`, `audit`, `audit-explain`, `duplicates`, `file`
- `destination` (alias `dest`)
  - Default: `./npm-query-{date-time}.json`
  - Type: File path

### Command Mapping to `output`

Previous commands with similar behaivours will now all utilize `query` under-the-hood.

### `npm list`

```bash
npm list # equivalent to...
npm query ":root > *"

npm list --all # equivalent to...
npm query "*" --output list

npm list <pkg> # equivalent to...
npm query "#<pkg>" --output list
```

### `npm explain`

```bash
npm explain <pkg> # equivalent to...
npm query "#<pkg>" --output explain
```

### `npm outdated`

```bash
npm outdated # equivalent to...
npm query ":root > *:outdated" --output outdated

npm outdated --all # equivalent to...
npm query "*:outdated" --output outdated
```

### `npm fund`

```bash
npm fund # equivalent to...
npm query ":root > *[funding]" --output funding

npm fund --all # equivalent to...
npm query "*[funding]" --output funding

npm fund <pkg> # equivalent to...
npm query "#<pkg>" --output funding
```

### `npm audit`

```bash
npm audit # equivalent to...
npm query ":root > *:vulnerable" --output audit

npm audit --all # equivalent to...
npm query "*:vulnerable" --output audit

npm audit <pkg> # equivalent to...
npm query "#<pkg>" --output audit
```

### `npm audit explain`

```bash
npm audit explain # equivalent to...
npm query ":root > *:vulnerable" --output audit-explain

npm audit explain --all # equivalent to...
npm query "*:vulnerable" --output audit-explain

npm audit explain <pkg> # equivalent to...
npm query "#<pkg>" --output audit-explain
```

### `npm find-dupes`

```bash
npm find-dupes # equivalent to...
npm query "*:vulnerable" --output duplicates
```

### `npm view`

```bash
npm view # equivalent to...
npm query ":root" --output view

npm view <pkg> # equivalent to...
npm query "#<pkg>" --output view
```

## Example Queries & Use Cases

```sass
// all deps
*

// all direct deps
:root > *

// direct production deps
:root > .prod

// direct development deps
:root > .dev 

// any peer dep of a direct deps
:root > * > .peer  
  
// any workspace dep
.workspace

// all workspaces that depend on another workspace
.workspace > .workspace
    
// all workspaces that have peer deps
.workspace:has(*.peer)

// any dep named "lodash"
// equivalent to [name="lodash"]
#lodash 
  
// any dep named "lodash" & version "1.2.3"
// equivalent to [name="lodash"][version="1.2.3"]
#lodash@1.2.3 
[name="lodash"]:semver(1.2.3)

// querying by semver
#lodash@^2
[name="lodash"][version="^2"]
  
// all deps living alongside vulnerable deps
*:vulnerable ~ *:not(:vulnerable)

// has any deps
*:has(*)

// has any vulnerable deps
*:has(*:vulnerable)

// deps with no other deps (ie. "leaf" nodes)
*:empty

// all vulnerable deps that aren't dev deps & that aren't vulnerable to CWE-1333
*:vulnerable:not(.dev:cwe(1333))
  
// manually querying git dependencies
*[repository^="github:"],
*[repository^="git:"],
*[repository^="https://github.com"],
*[repository^="http://github.com"],
*[repository^="https://github.com"],
*[repository^="+git:..."]
  
// querying git dependencies
*:git

// find all references to "install" scripts
*[scripts=install], 
*[scripts=postinstall],
*[scripts=preinstall]

// ------------------------------
// brainstorming w/ fritzy

*:dep(.node_module, :has(.package.vulnerability))

:root>(node_module).package

// assumptions:
// querying on-disk representation...
// everything is a package...

// direct dependents
:root > *

// any package I can access in this scope...
* 

// :not()
.prod:not(.dev)

.prod:not([name="lodash"])
    
// license querying (RFC)
*[license="MIT"], *[license="ISC"]
```

## Example Output

```bash
npm query ":root > .workspace > *"  # get all workspace direct deps
```

```json
[
  {
    "hash": "...",
    "name": "react",
    "version": "1.0.1",
    "description": "",
    "homepage": "",
    "bugs": "",
    "author": "",
    "license": "",
    "funding": "",
    "files": "",
    "main": "",
    "browser": "",
    "bin": "",
    "man": "",
    "directories": "",
    "repository": "",
    "scripts": "",
    "config": "",
    "dependencies": "",
    "devDependencies": "",
    "optionalDependencies": "",
    "bundledDependencies": "",
    "peerDependencies": "",
    "peerDependenciesMeta": "",
    "engines": "",
    "os": "",
    "cpu": "",
    "workspaces": "",
    "keywords": "",
    "vulnerabilities": "",
    "cwe": "",
    "path": ""
  },
  ...
```

## Prior Art

- https://pnpm.io/filtering
- [`HTML`](https://html.spec.whatwg.org/) & [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)
- [`CSS`](https://www.w3.org/Style/CSS/specs.en.html), [Selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) & [Pseudo Classes](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes)
- [`estree`](https://github.com/estree/estree)
- [`abstract-syntax-tree`](https://www.npmjs.com/package/abstract-syntax-tree)
- [`postcss-selector-parser`](https://www.npmjs.com/package/postcss-selector-parser) / [API](https://github.com/postcss/postcss-selector-parser/blob/master/API.md) 
- [`css-selector-parser`](https://www.npmjs.com/package/css-selector-parser)
- [`@npmcli/arborist`'s  `Node` Class](https://github.com/npm/cli/tree/latest/workspaces/arborist)
- https://github.com/nodejs/Gzemnid

## Unresolved Questions and Bikeshedding

- Q. Is there any such thing as a bare specifier?
  - A. No. Unlike CSS Selectors, there's no `"element"`, or `"package"` in this context, equivalent. The selector syntax presuposes all entities are packages.
- Q. Should this syntax cover **all** possible queries of a dependency graph?
  - A. No. This spec is meant to provide a sufficently mature mechanism/building block for  (re. 80/20 rule applies here)

---

# [RFC] Commands read from `stdin`

```
audit, bugs, ci, config, deprecate, diff, dist-tag, docs, edit, exec, 
explain, explore, find-dupes, fund, get, install, install-ci-test, 
install-test, link, list, outdated, pack, pkg, prune, publish, rebuild, 
repo, restart, run-script, set, set-script, shrinkwrap, star, stars,
start, stop, team, test, token, uninstall, unpublish, unstar, update,
version, view
```

All of the above commands would add the ability to read from `stdin` & execute as if they were passed package specs.

## Example piping output to other commands

This is predicated/dependant on [RFC #]()
```bash=
# list workspaces w/ peer deps
npm query ".workspace:has(*.peer)" | npm ls

# install the same dev deps across all workspaces
npm query ":root > *.dev" | npm install --workspaces

# find and enforce a single version of dependencies
npm query ""

# find cve
npm query "*:cve(<id>)"

# show audit details for vulnerable deps that aren't ReDoS dev deps
npm query "*:vulnerable:not(.dev[cwe~=1333]:not(cwe=))" | npm audit explain
```

---

# [RFC] `npm audit` Improvements

## Commands

### `npm audit [<pkg>]` 

### `npm audit fix` 

### `npm audit explain [<pkg>]` 

---

# [RFC] Scoped Command Config

- must use canonical command name (ie. does not support alias')

### Examples
```ini
omit=dev

[npm-install]
audit=false
fund=false

[npm-audit]
production=true

[npm-audit-fix]
force=true

[npm-list]
all=true

[npm-publish]
loglevel=silly
```