diff options
author | Ruy Adorno <ruyadorno@hotmail.com> | 2022-08-01 21:29:56 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-01 21:29:56 +0300 |
commit | 3c024ace60904c69e61da00e1fb56c0c1735804a (patch) | |
tree | 20455ad5af7395f9a25943127d6d4ed36421eab7 /docs | |
parent | 050284d2abb6aa91a0f9ffad5b0c4f074e5dbf6d (diff) |
feat: add npm query cmd (#5000)
Co-authored-by: Gar <gar+gh@danger.computer>
Diffstat (limited to 'docs')
-rw-r--r-- | docs/content/commands/npm-query.md | 236 | ||||
-rw-r--r-- | docs/content/using-npm/dependency-selectors.md | 168 | ||||
-rw-r--r-- | docs/nav.yml | 6 |
3 files changed, 410 insertions, 0 deletions
diff --git a/docs/content/commands/npm-query.md b/docs/content/commands/npm-query.md new file mode 100644 index 000000000..424b9e7e4 --- /dev/null +++ b/docs/content/commands/npm-query.md @@ -0,0 +1,236 @@ +--- +title: npm-query +section: 1 +description: Dependency selector query +--- + +### Synopsis + +<!-- AUTOGENERATED USAGE DESCRIPTIONS START --> +<!-- automatically generated, do not edit manually --> +<!-- see lib/commands/query.js --> + +```bash +npm query <selector> +``` + +<!-- automatically generated, do not edit manually --> +<!-- see lib/commands/query.js --> + +<!-- AUTOGENERATED USAGE DESCRIPTIONS END --> + +### Description + +The `npm query` command allows for usage of css selectors in order to retrieve +an array of dependency objects. + +### 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 {} +``` + +### 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 --> +<!-- automatically generated, do not edit manually --> +<!-- see lib/utils/config/definitions.js --> +#### `global` + +* Default: false +* Type: Boolean + +Operates in "global" mode, so that packages are installed into the `prefix` +folder instead of the current working directory. See +[folders](/configuring-npm/folders) for more on the differences in behavior. + +* packages are installed into the `{prefix}/lib/node_modules` folder, instead + of the current working directory. +* bin files are linked to `{prefix}/bin` +* man pages are linked to `{prefix}/share/man` + +<!-- automatically generated, do not edit manually --> +<!-- see lib/utils/config/definitions.js --> + +#### `workspace` + +* Default: +* Type: String (can be set multiple times) + +Enable running a command in the context of the configured workspaces of the +current project while filtering by running only the workspaces defined by +this configuration option. + +Valid values for the `workspace` config are either: + +* Workspace names +* Path to a workspace directory +* Path to a parent workspace directory (will result in selecting all + workspaces within that folder) + +When set for the `npm init` command, this may be set to the folder of a +workspace which does not yet exist, to create the folder and set it up as a +brand new workspace within the project. + +This value is not exported to the environment for child processes. + +<!-- automatically generated, do not edit manually --> +<!-- see lib/utils/config/definitions.js --> + +#### `workspaces` + +* Default: null +* Type: null or Boolean + +Set to true to run the command in the context of **all** configured +workspaces. + +Explicitly setting this to false will cause commands like `install` to +ignore workspaces altogether. When not set explicitly: + +- Commands that operate on the `node_modules` tree (install, update, etc.) +will link workspaces into the `node_modules` folder. - Commands that do +other things (test, exec, publish, etc.) will operate on the root project, +_unless_ one or more workspaces are specified in the `workspace` config. + +This value is not exported to the environment for child processes. + +<!-- automatically generated, do not edit manually --> +<!-- see lib/utils/config/definitions.js --> + +#### `include-workspace-root` + +* Default: false +* Type: Boolean + +Include the workspace root when workspaces are enabled for a command. + +When false, specifying individual workspaces via the `workspace` config, or +all workspaces via the `workspaces` flag, will cause npm to operate only on +the specified workspaces, and not on the root project. + +This value is not exported to the environment for child processes. + +<!-- automatically generated, do not edit manually --> +<!-- see lib/utils/config/definitions.js --> + +<!-- AUTOGENERATED CONFIG DESCRIPTIONS END --> +## See Also + +* [dependency selector](/using-npm/dependency-selector) + diff --git a/docs/content/using-npm/dependency-selectors.md b/docs/content/using-npm/dependency-selectors.md new file mode 100644 index 000000000..9d65baf63 --- /dev/null +++ b/docs/content/using-npm/dependency-selectors.md @@ -0,0 +1,168 @@ +--- +title: Dependency Selector Syntax & Querying +section: 7 +description: Dependency Selector Syntax & Querying +--- + +### Description + +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 +- Unlocks the ability to answer complex, multi-faceted questions about dependencies, their relationships & associative metadata +- Consolidates redundant logic of similar query commands in `npm` (ex. `npm fund`, `npm ls`, `npm outdated`, `npm audit` ...) + +### Dependency Selector Syntax `v1.0.0` + +#### Overview: + +- there is no "type" or "tag" selectors (ex. `div, h1, a`) as a dependency/target is the only type of `Node` that can be queried +- the term "dependencies" is in reference to any `Node` found in a `tree` returned by `Arborist` + +#### Combinators + +- `>` direct descendant/child +- ` ` any descendant/child +- `~` sibling + +#### Selectors + +- `*` universal selector +- `#<name>` dependency selector (equivalent to `[name="..."]`) +- `#<name>@<version>` (equivalent to `[name=<name>]:semver(<version>)`) +- `,` selector list delimiter +- `.` dependency type selector +- `:` pseudo selector + +#### Dependency Type Selectors + +- `.prod` dependency found in the `dependencies` section of `package.json`, or is a child of said dependency +- `.dev` dependency found in the `devDependencies` section of `package.json`, or is a child of said dependency +- `.optional` dependency found in the `optionalDependencies` section of `package.json`, or has `"optional": true` set in its entry in the `peerDependenciesMeta` section of `package.json`, or a child of said dependency +- `.peer` dependency found in the `peerDependencies` section of `package.json` +- `.workspace` dependency found in the [`workspaces`](https://docs.npmjs.com/cli/v8/using-npm/workspaces) section of `package.json` +- `.bundled` dependency found in the `bundleDependencies` section of `package.json`, or is a child of said dependency + +#### Pseudo Selectors +- [`:not(<selector>)`](https://developer.mozilla.org/en-US/docs/Web/CSS/:not) +- [`:has(<selector>)`](https://developer.mozilla.org/en-US/docs/Web/CSS/:has) +- [`:is(<selector list>)`](https://developer.mozilla.org/en-US/docs/Web/CSS/:is) +- [`:root`](https://developer.mozilla.org/en-US/docs/Web/CSS/:root) matches the root node/dependency +- [`:scope`](https://developer.mozilla.org/en-US/docs/Web/CSS/:scope) matches node/dependency it was queried against +- [`:empty`](https://developer.mozilla.org/en-US/docs/Web/CSS/:empty) when a dependency has no dependencies +- [`:private`](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#private) when a dependency is private +- `:link` when a dependency is linked (for instance, workspaces or packages manually [`linked`](https://docs.npmjs.com/cli/v8/commands/npm-link) +- `:deduped` when a dependency has been deduped (note that this does *not* always mean the dependency has been hoisted to the root of node_modules) +- `:override` when a dependency is an override (not implemented yet) +- `:extraneous` when a dependency exists but is not defined as a dependency of any node +- `:invalid` when a dependency version is out of its ancestors specified range +- `:missing` when a dependency is not found on disk +- `:semver(<spec>)` matching a valid [`node-semver`](https://github.com/npm/node-semver) spec +- `:path(<path>)` [glob](https://www.npmjs.com/package/glob) matching based on dependencies path relative to the project +- `:type(<type>)` [based on currently recognized types](https://github.com/npm/npm-package-arg#result-object) + +#### [Attribute Selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) + +The attribute selector evaluates the key/value pairs in `package.json` if they are `String`s. + +- `[]` 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 starts with... +- `[attribute$=value]` attribute value ends with... + +#### `Array` & `Object` Attribute Selectors + +The generic `:attr()` pseudo selector standardizes a pattern which can be used for attribute selection of `Object`s, `Array`s or `Arrays` of `Object`s accessible via `Arborist`'s `Node.package` metadata. This allows for iterative attribute selection beyond top-level `String` evaluation. The last argument passed to `:attr()` must be an `attribute` selector or a nested `:attr()`. See examples below: + +#### `Objects` + +```css +/* return dependencies that have a `scripts.test` containing `"tap"` */ +*:attr(scripts, [test~=tap]) +``` + +#### Nested `Objects` + +Nested objects are expressed as sequential arguments to `:attr()`. + +```css +/* return dependencies that have a testling config for opera browsers */ +*:attr(testling, browsers, [~=opera]) +``` + +#### `Arrays` + +`Array`s specifically uses a special/reserved `.` character in place of a typical attribute name. `Arrays` also support exact `value` matching when a `String` is passed to the selector. + +##### Example of an `Array` Attribute Selection: +```css +/* removes the distinction between properties & arrays */ +/* ie. we'd have to check the property & iterate to match selection */ +*:attr([keywords^=react]) +*:attr(contributors, :attr([name~=Jordan])) +``` + +##### Example of an `Array` matching directly to a value: +```css +/* return dependencies that have the exact keyword "react" */ +/* this is equivalent to `*:keywords([value="react"])` */ +*:attr([keywords=react]) +``` + +##### Example of an `Array` of `Object`s: +```css +/* returns */ +*:attr(contributors, [email=ruyadorno@github.com]) +``` + +### 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 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` +- `.optional` +- `.peer` +- `.bundled` +- `.workspace` + +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 has a `.querySelectorAll()` method + - this method will return a filtered, flattened dependency Arborist `Node` list based on a valid query selector + +```js +const Arborist = require('@npmcli/arborist') +const arb = new Arborist({}) +``` + +```js +// root-level +arb.loadActual((tree) => { + // query all production dependencies + const results = await tree.querySelectorAll('.prod') + console.log(results) +}) +``` + +```js +// iterative +arb.loadActual((tree) => { + // query for the deduped version of react + const results = await tree.querySelectorAll('#react:not(:deduped)') + // query the deduped react for git deps + const deps = await results[0].querySelectorAll(':type(git)') + console.log(deps) +}) +``` + +## See Also + +* [npm query](/commands/npm-query) +* [@npmcli/arborist](https://npm.im/@npmcli/arborist] diff --git a/docs/nav.yml b/docs/nav.yml index bc39b969e..565537054 100644 --- a/docs/nav.yml +++ b/docs/nav.yml @@ -132,6 +132,9 @@ - title: npm publish url: /commands/npm-publish description: Publish a package + - title: npm query + url: /commands/npm-query + description: Retrieve a filtered list of packages - title: npm rebuild url: /commands/npm-rebuild description: Rebuild a package @@ -253,6 +256,9 @@ - title: Organizations url: /using-npm/orgs description: Working with teams & organizations + - title: Dependency Selectors + url: /using-npm/dependency-selectors + description: Dependency Selector Syntax & Querying - title: Developers url: /using-npm/developers description: Developer guide |