Skip to content

Commit

Permalink
fix: proper depType functions
Browse files Browse the repository at this point in the history
  • Loading branch information
wraithgar committed Jul 18, 2022
1 parent eccf71b commit ca4857f
Show file tree
Hide file tree
Showing 6 changed files with 386 additions and 232 deletions.
111 changes: 111 additions & 0 deletions docs/content/commands/npm-query.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,117 @@ npm query ":attr(scripts, [postinstall])" | jq 'map(.name)|join("\n")' -r | xarg
npm query ":type(git)" | jq 'map(.name)' | xargs -I {} npm why {}
```

### Extended Use Cases & Queries

```stylus
// 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 deps named "lodash" & within semver range ^"1.2.3"
#lodash@^1.2.3
// equivalent to...
[name="lodash"]:semver(^1.2.3)
// get the hoisted node for a given semver range
#lodash@^1.2.3:not(:deduped)
// querying deps with a specific version
#lodash@2.1.5
// equivalent to...
[name="lodash"][version="2.1.5"]
// has any deps
:has(*)
// deps with no other deps (ie. "leaf" nodes)
:empty
// manually querying git dependencies
[repository^=github:],
[repository^=git:],
[repository^=https://github.com],
[repository^=http://github.com],
[repository^=https://github.com],
[repository^=+git:...]
// querying for all git dependencies
:type(git)
// get production dependencies that aren't also dev deps
.prod:not(.dev)
// get dependencies with specific licenses
[license=MIT], [license=ISC]
// find all packages that have @ruyadorno as a contributor
:attr(contributors, [email=ruyadorno@github.com])
```

### Example Response Output

- an array of dependency objects is returned which can contain multiple copies of the same package which may or may not have been linked or deduped

```json
[
{
"name": "",
"version": "",
"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": [],
...
},
...
```

### Configuration

<!-- AUTOGENERATED CONFIG DESCRIPTIONS START -->
Expand Down
149 changes: 8 additions & 141 deletions docs/content/using-npm/dependency-selectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description: Dependency Selector Syntax & Querying

### Description

The `npm query` commmand exposes a new dependency selector syntax (informed by & respecting many aspects of the [CSS Selectors 4 Spec](https://dev.w3.org/csswg/selectors4/#relational)) which:
The [`npm query`](/commands/npm-query) commmand exposes a new dependency selector syntax (informed by & respecting many aspects of the [CSS Selectors 4 Spec](https://dev.w3.org/csswg/selectors4/#relational)) 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
Expand Down Expand Up @@ -121,7 +121,7 @@ Nested objects are expressed as sequential arguments to `:attr()`.

### Groups

Dependency groups are defined by the package relationships to their ancestors (ie. the dependency types that are defined in `package.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 apart of multiple groups (ex. a `workspace` may also be a `dev` dependency & may also be `bundled` - a selector for that type of dependency would look like: `*.workspace.dev.bundled`).
Dependency groups are defined by the package relationships to their ancestors (ie. the dependency types that are defined in `package.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. a `prod` dependency may also be a `dev` dependency (in that it's also required by another `dev` dependency) & may also be `bundled` - a selector for that type of dependency would look like: `*.prod.dev.bundled`).

- `.prod`
- `.dev`
Expand All @@ -130,149 +130,12 @@ Dependency groups are defined by the package relationships to their ancestors (i
- `.bundled`
- `.workspace`

### Command

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

#### Options:

- `query-output`
- Default: `list` - a human-readable subset of dependency information
- `json` - all data available

#### Example Response Output

- an array of dependency objects is returned which can contain multiple copies of the same package which may or may not have been linked or deduped

```json
[
{
"name": "",
"version": "",
"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": [],
...
},
...
```

### Usage

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

### Extended Use Cases & Queries

```stylus
// 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 deps named "lodash" & within semver range ^"1.2.3"
#lodash@^1.2.3
// equivalent to...
[name="lodash"]:semver(^1.2.3)

// get the hoisted node for a given semver range
#lodash@^1.2.3:not(:deduped)

// querying deps with a specific version
#lodash@2.1.5
// equivalent to...
[name="lodash"][version="2.1.5"]

// has any deps
:has(*)

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

// manually querying git dependencies
[repository^=github:],
[repository^=git:],
[repository^=https://github.com],
[repository^=http://github.com],
[repository^=https://github.com],
[repository^=+git:...]

// querying for all git dependencies
:type(git)

// get production dependencies that aren't also dev deps
.prod:not(.dev)

// get dependencies with specific licenses
[license=MIT], [license=ISC]

// find all packages that have @ruyadorno as a contributor
:attr(contributors, [email=ruyadorno@github.com])
```

### Piping `npm query` to other commands

```bash
# find all dependencies with postinstall scripts & uninstall them
npm query ":attr(scripts, [postinstall])" | jq 'map(.name)|join("\n")' -r | xargs -I {} npm uninstall {}

# find all git dependencies & explain who requires them
npm query ":type(git)" | jq 'map(.name)' | xargs -I {} npm why {}
```
Please note that currently `workspace` deps are always `prod` dependencies. Additionally the `.root` dependency is also considered a `prod` dependency.

### Programmatic Usage

- `Arborist`'s `Node` Class will have a new `.querySelectorAll()` method
- `Arborist`'s `Node` Class has a `.querySelectorAll()` method
- this method will return a filtered, flattened dependency Arborist `Node` list based on a valid query selector
- Introduce a new command, `npm query`, which will take a dependency selector & output a flattened dependency Node list (output is in `json` by default, but configurable)

```js
const Arborist = require('@npmcli/arborist')
Expand All @@ -299,3 +162,7 @@ arb.loadActual((tree) => {
})
```

## See Also

* [npm query](/commands/npm-query)
* [@npmcli/arborist](https://npm.im/@npmcli/arborist]
14 changes: 11 additions & 3 deletions lib/commands/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,16 @@ class QuerySelectorItem {
this.path = node.target.path
this.realpath = node.target.realpath
this.resolved = node.target.resolved
this.isLink = node.target.isLink
this.isWorkspace = node.target.isWorkspace
this.from = []
this.to = []
this.dev = node.target.dev
this.inBundle = node.target.inBundle
for (const edge of node.target.edgesIn) {
this.from.push(edge.from.location)
}
for (const [, edge] of node.target.edgesOut) {
this.to.push(edge.to.location)
}
}
}

Expand Down Expand Up @@ -81,7 +89,7 @@ class Query extends BaseCommand {
// builds a normalized inventory
buildResponse (items) {
for (const node of items) {
if (!this.#seen.has(node.target.realpath)) {
if (!this.#seen.has(node.target.location)) {
const item = new QuerySelectorItem(node)
this.#response.push(item)
this.#seen.add(item.realpath)
Expand Down
Loading

0 comments on commit ca4857f

Please sign in to comment.