依赖项选择器语法和查询

依赖项选择器语法和查询

选择 CLI 版本

描述

The npm query command exposes a new dependency selector syntax (informed by & respecting many aspects of the CSS Selectors 4 Spec) 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 ...)

依赖项选择器语法

概述

  • 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

组合器

  • > direct descendant/child
  • any descendant/child
  • ~ sibling

选择器

  • * universal selector
  • #<name> dependency selector (equivalent to [name="..."])
  • #<name>@<version> (equivalent to [name=<name>]:semver(<version>))
  • , selector list delimiter
  • . dependency type selector
  • : pseudo selector

依赖项类型选择器

  • .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 section of package.json
  • .bundled dependency found in the bundleDependencies section of package.json, or is a child of said dependency

伪选择器

  • :not(<selector>)
  • :has(<selector>)
  • :is(<selector list>)
  • :root matches the root node/dependency
  • :scope matches node/dependency it was queried against
  • :empty when a dependency has no dependencies
  • :private when a dependency is private
  • :link when a dependency is linked (for instance, workspaces or packages manually linked
  • :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)
  • :overridden when a dependency has been overridden
  • :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>, [selector], [function]) 匹配有效 node-semver 版本或范围到选择器
  • :path(<path>) glob 匹配基于相对于项目的依赖路径
  • :type(<type>) 基于当前识别的类型
  • :outdated(<type>) 当依赖项过时时
  • :vuln(<selector>) 当依赖项存在已知漏洞时
:semver(<spec>, [selector], [function])

The :semver() 伪选择器允许使用 semver 方法比较每个节点的 package.json 中的字段。它最多接受 3 个参数,除了第一个参数之外都是可选的。

  • spec 一个 semver 版本或范围
  • selector 每个节点的属性选择器(默认 [version])
  • function 要应用的 semver 方法,以下之一:satisfies, intersects, subset, gt, gte, gtr, lt, lte, ltr, eq, neq 或特殊函数 infer(默认 infer)

当使用特殊函数 infer 时,会比较 spec 和来自节点的实际值。如果两者都是版本,根据 semver.valid(),将使用 eq。如果两个值都是范围,根据 !semver.valid(),将使用 intersects。如果值是混合类型,将使用 satisfies

一些示例

  • :semver(^1.0.0) 返回每个 version 满足提供的范围 ^1.0.0 的节点
  • :semver(16.0.0, :attr(engines, [node])) 返回每个 engines.node 属性满足版本 16.0.0 的节点
  • :semver(1.0.0, [version], lt) 每个 version 小于 1.0.0 的节点
:outdated(<type>)

The :outdated 伪选择器从注册表检索数据,并返回有关哪些依赖项过时的信息。类型参数可以是以下之一

  • any(默认)存在一个版本大于当前版本
  • in-range 存在一个版本大于当前版本,并且至少满足其父级的依赖项之一
  • out-of-range 存在一个版本大于当前版本,不满足其父级的依赖项之一
  • major 存在一个版本是 semver 主版本大于当前版本
  • minor 存在一个版本是 semver 次版本大于当前版本
  • patch 存在一个版本是 semver 修补程序版本大于当前版本

除了伪选择器执行的过滤之外,还会向结果对象添加一些额外的信息。以下数据可以在每个节点的 queryContext 属性下找到。

  • versions 给定节点的所有可用版本的数组
  • outdated.inRange 对象数组,每个对象都有一个 fromversions,其中 from 是依赖于当前节点的节点的磁盘位置,而 versions 是满足该依赖项的所有可用版本的数组。仅当使用 :outdated(in-range) 时才会填充此信息。
  • outdated.outOfRange 对象数组,形状与 inRange 相同,但 versions 数组是所有不满足依赖项的可用版本。仅当使用 :outdated(out-of-range) 时才会填充此信息。

一些示例

  • :root > :outdated(major) 返回每个具有新 semver 主版本的直接依赖项
  • .prod:outdated(in-range) 返回具有满足其父级依赖项之一的新版本的生产依赖项
:vuln

The :vuln 伪选择器从注册表检索数据,并返回有关您的依赖项中是否存在已知漏洞的信息。仅当依赖项的当前版本与漏洞匹配时,才会返回依赖项。例如,如果您在树中具有 [email protected],则针对 semver 影响版本 <=6.3.1 的漏洞将不会匹配。

您还可以按咨询中的某些属性过滤结果。目前这包括 severitycwe。请注意,严重性过滤是按严重性执行的,不包括指定严重性“更高”或“更低”的严重性。

除了伪选择器执行的过滤之外,有关每个相关咨询的信息将添加到每个节点的 queryContext 属性下的 advisories 属性中。

一些示例

  • :root > .prod:vuln 返回具有任何已知漏洞的直接生产依赖项
  • :vuln([severity=high]) 仅返回具有 high 严重性的漏洞的依赖项。
  • :vuln([severity=high],[severity=moderate]) 仅返回具有 highmoderate 严重性的漏洞的依赖项。
  • :vuln([cwe=1333]) 仅返回包含 CWE-1333(ReDoS)的漏洞的依赖项

属性选择器

属性选择器评估 package.json 中的键值对,如果它们是 String

  • [] 属性选择器(即属性的存在)
  • [attribute=value] 属性值等效于...
  • [attribute~=value] 属性值包含单词...
  • [attribute*=value] 属性值包含字符串...
  • [attribute|=value] 属性值等于或以...开头
  • [attribute^=value] 属性值以...开头
  • [attribute$=value] 属性值以...结尾

Array & Object 属性选择器

通用 :attr() 伪选择器标准化了一种模式,该模式可用于 ObjectArrayArrayObject 属性选择,这些属性可以通过 ArboristNode.package 元数据访问。这允许进行迭代属性选择,超出顶层 String 评估。传递给 :attr() 的最后一个参数必须是 attribute 选择器或嵌套的 :attr()。请参阅下面的示例

对象

/* return dependencies that have a `scripts.test` containing `"tap"` */
*: attr(scripts, [test~=tap]);

嵌套的 Objects

嵌套对象表示为 :attr() 的顺序参数。

/* return dependencies that have a testling config for opera browsers */
*: attr(testling, browsers, [~=opera]);

数组

Array 特别是在典型属性名称的位置使用特殊的/保留的 . 字符。 Array 还支持当将 String 传递给选择器时,进行精确的 value 匹配。

Array 属性选择示例
/* 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]))
Array 直接匹配值的示例
/* return dependencies that have the exact keyword "react" */
/* this is equivalent to `*:keywords([value="react"])` */
*: attr([keywords=react]);
ArrayObject 示例
/* returns */
*: attr(contributors, [email=ruyadorno @github.com]);

依赖项组由包与其祖先的关系定义(即在 package.json 中定义的依赖项类型)。这种方法以用户为中心,因为生态系统已经被教导首先从这些组中思考依赖项。依赖项被允许包含在多个组中(例如,prod 依赖项也可能是 dev 依赖项(因为它也被另一个 dev 依赖项需要)并且也可能是 bundled - 针对这种类型依赖项的选择器将如下所示:*.prod.dev.bundled)。

  • .prod
  • .dev
  • .optional
  • .peer
  • .bundled
  • .workspace

请注意,目前 workspace 依赖项始终是 prod 依赖项。此外,.root 依赖项也被视为 prod 依赖项。

编程方式使用

  • ArboristNode 类具有 .querySelectorAll() 方法
    • 该方法将根据有效的查询选择器返回过滤后的扁平化依赖项 Arborist Node 列表
const Arborist = require("@npmcli/arborist");
const arb = new Arborist({});
// root-level
arb.loadActual().then(async (tree) => {
// query all production dependencies
const results = await tree.querySelectorAll(".prod");
console.log(results);
});
// iterative
arb.loadActual().then(async (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);
});

另请参阅