Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce :outdated pseudo selector #5547

Merged
merged 4 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/content/using-npm/dependency-selectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ The [`npm query`](/commands/npm-query) commmand exposes a new dependency selecto
- `:semver(<spec>, [selector], [function])` match a valid [`node-semver`](https://github.com/npm/node-semver) version or range to a selector
- `: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)
- `:outdated(<type>)` when a dependency is outdated

##### `:semver(<spec>, [selector], [function])`

Expand All @@ -78,6 +79,28 @@ Some examples:
- `:semver(16.0.0, :attr(engines, [node]))` returns every node which has an `engines.node` property satisfying the version `16.0.0`
- `:semver(1.0.0, [version], lt)` every node with a `version` less than `1.0.0`

##### `:outdated(<type>)`

The `:outdated` pseudo selector retrieves data from the registry and returns information about which of your dependencies are outdated. The type parameter may be one of the following:

- `any` (default) a version exists that is greater than the current one
- `in-range` a version exists that is greater than the current one, and satisfies at least one if its dependents
- `out-of-range` a version exists that is greater than the current one, does not satisfy at least one of its dependents
- `major` a version exists that is a semver major greater than the current one
- `minor` a version exists that is a semver minor greater than the current one
- `patch` a version exists that is a semver patch greater than the current one

In addition to the filtering performed by the pseudo selector, some extra data is added to the resulting objects. The following data can be found under the `queryContext` property of each node.

- `versions` an array of every available version of the given node
- `outdated.inRange` an array of objects, each with a `from` and `versions`, where `from` is the on-disk location of the node that depends on the current node and `versions` is an array of all available versions that satisfies that dependency. This is only populated if `:outdated(in-range)` is used.
- `outdated.outOfRange` an array of objects, identical in shape to `inRange`, but where the `versions` array is every available version that does not satisfy the dependency. This is only populated if `:outdated(out-of-range)` is used.

Some examples:

- `:root > :outdated(major)` returns every direct dependency that has a new semver major release
- `.prod:outdated(in-range)` returns production dependencies that have a new release that satisfies at least one of its edges in

#### [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.
Expand Down
5 changes: 3 additions & 2 deletions lib/commands/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class QuerySelectorItem {
this.inBundle = node.target.inBundle
this.deduped = this.from.length > 1
this.overridden = node.overridden
this.queryContext = node.queryContext
for (const edge of node.target.edgesIn) {
this.from.push(edge.from.location)
}
Expand Down Expand Up @@ -63,7 +64,7 @@ class Query extends BaseCommand {
}
const arb = new Arborist(opts)
const tree = await arb.loadActual(opts)
const items = await tree.querySelectorAll(args[0])
const items = await tree.querySelectorAll(args[0], this.npm.flatOptions)
this.buildResponse(items)

this.npm.output(this.parsedResponse)
Expand All @@ -84,7 +85,7 @@ class Query extends BaseCommand {
items = await tree.querySelectorAll(args[0])
} else {
const [workspace] = await tree.querySelectorAll(`.workspace:path(${workspacePath})`)
items = await workspace.target.querySelectorAll(args[0])
items = await workspace.target.querySelectorAll(args[0], this.npm.flatOptions)
}
this.buildResponse(items)
}
Expand Down
9 changes: 9 additions & 0 deletions node_modules/@npmcli/query/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ const fixupPaths = astNode => {
astNode.nodes.length = 0
}

const fixupOutdated = astNode => {
if (astNode.nodes.length) {
astNode.outdatedKind = String(astNode.nodes[0])
astNode.nodes.length = 0
}
}

// a few of the supported ast nodes need to be tweaked in order to properly be
// interpreted as proper arborist query selectors, namely semver ranges from
// both ids and :semver pseudo-class selectors need to be translated from what
Expand Down Expand Up @@ -196,6 +203,8 @@ const transformAst = selector => {
return fixupSemverSpecs(nextAstNode)
case ':type':
return fixupTypes(nextAstNode)
case ':outdated':
return fixupOutdated(nextAstNode)
}
})
}
Expand Down
17 changes: 10 additions & 7 deletions node_modules/@npmcli/query/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@npmcli/query",
"version": "1.2.0",
"version": "2.0.0",
"description": "npm query parser and tools",
"main": "lib/index.js",
"scripts": {
Expand All @@ -9,9 +9,6 @@
"postlint": "template-oss-check",
"template-oss-apply": "template-oss-apply --force",
"lintfix": "npm run lint -- --fix",
"preversion": "npm test",
"postversion": "npm publish",
"prepublishOnly": "git push origin --follow-tags",
"snap": "tap",
"posttest": "npm run lint"
},
Expand All @@ -38,15 +35,15 @@
"lib/"
],
"engines": {
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "3.5.0"
"version": "4.3.2"
},
"devDependencies": {
"@npmcli/eslint-config": "^3.0.1",
"@npmcli/template-oss": "3.5.0",
"@npmcli/template-oss": "4.3.2",
"tap": "^16.2.0"
},
"dependencies": {
Expand All @@ -57,5 +54,11 @@
"repository": {
"type": "git",
"url": "https://github.com/npm/query.git"
},
"tap": {
"nyc-arg": [
"--exclude",
"tap-snapshots/**"
]
}
}
10 changes: 5 additions & 5 deletions package-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -2334,16 +2334,16 @@
}
},
"node_modules/@npmcli/query": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@npmcli/query/-/query-1.2.0.tgz",
"integrity": "sha512-uWglsUM3PjBLgTSmZ3/vygeGdvWEIZ3wTUnzGFbprC/RtvQSaT+GAXu1DXmSFj2bD3oOZdcRm1xdzsV2z1YWdw==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@npmcli/query/-/query-2.0.0.tgz",
"integrity": "sha512-ef3fUALbojBvtCi6wpogwnrtChBcK8Pdso5Vbz2EU0cud7VW1jcMGqwSNeSMU8V4cjpqVgudKQ+dosKO7N903g==",
"dependencies": {
"npm-package-arg": "^9.1.0",
"postcss-selector-parser": "^6.0.10",
"semver": "^7.3.7"
},
"engines": {
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/@npmcli/run-script": {
Expand Down Expand Up @@ -13875,7 +13875,7 @@
"@npmcli/name-from-folder": "^1.0.1",
"@npmcli/node-gyp": "^2.0.0",
"@npmcli/package-json": "^2.0.0",
"@npmcli/query": "^1.2.0",
"@npmcli/query": "^2.0.0",
"@npmcli/run-script": "^4.1.3",
"bin-links": "^3.0.3",
"cacache": "^16.1.3",
Expand Down
33 changes: 22 additions & 11 deletions tap-snapshots/test/lib/commands/query.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ exports[`test/lib/commands/query.js TAP global > should return global package 1`
"dev": false,
"inBundle": false,
"deduped": false,
"overridden": false
"overridden": false,
"queryContext": {}
}
]
`
Expand Down Expand Up @@ -53,7 +54,8 @@ exports[`test/lib/commands/query.js TAP include-workspace-root > should return w
"dev": false,
"inBundle": false,
"deduped": false,
"overridden": false
"overridden": false,
"queryContext": {}
},
{
"name": "c",
Expand All @@ -69,7 +71,8 @@ exports[`test/lib/commands/query.js TAP include-workspace-root > should return w
"dev": false,
"inBundle": false,
"deduped": false,
"overridden": false
"overridden": false,
"queryContext": {}
}
]
`
Expand All @@ -90,7 +93,8 @@ exports[`test/lib/commands/query.js TAP linked node > should return linked node
"dev": false,
"inBundle": false,
"deduped": false,
"overridden": false
"overridden": false,
"queryContext": {}
}
]
`
Expand All @@ -116,7 +120,8 @@ exports[`test/lib/commands/query.js TAP recursive tree > should return everythin
"dev": false,
"inBundle": false,
"deduped": false,
"overridden": false
"overridden": false,
"queryContext": {}
},
{
"pkgid": "a@",
Expand All @@ -131,7 +136,8 @@ exports[`test/lib/commands/query.js TAP recursive tree > should return everythin
"dev": false,
"inBundle": false,
"deduped": false,
"overridden": false
"overridden": false,
"queryContext": {}
},
{
"pkgid": "b@",
Expand All @@ -146,7 +152,8 @@ exports[`test/lib/commands/query.js TAP recursive tree > should return everythin
"dev": false,
"inBundle": false,
"deduped": false,
"overridden": false
"overridden": false,
"queryContext": {}
}
]
`
Expand Down Expand Up @@ -175,7 +182,8 @@ exports[`test/lib/commands/query.js TAP simple query > should return root object
"dev": false,
"inBundle": false,
"deduped": false,
"overridden": false
"overridden": false,
"queryContext": {}
},
{
"pkgid": "a@",
Expand All @@ -190,7 +198,8 @@ exports[`test/lib/commands/query.js TAP simple query > should return root object
"dev": false,
"inBundle": false,
"deduped": false,
"overridden": false
"overridden": false,
"queryContext": {}
},
{
"pkgid": "b@",
Expand All @@ -205,7 +214,8 @@ exports[`test/lib/commands/query.js TAP simple query > should return root object
"dev": false,
"inBundle": false,
"deduped": false,
"overridden": false
"overridden": false,
"queryContext": {}
}
]
`
Expand All @@ -226,7 +236,8 @@ exports[`test/lib/commands/query.js TAP workspace query > should return workspac
"dev": false,
"inBundle": false,
"deduped": false,
"overridden": false
"overridden": false,
"queryContext": {}
}
]
`
7 changes: 5 additions & 2 deletions workspaces/arborist/lib/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ class Node {
dummy = false,
sourceReference = null,
} = options
// this object gives querySelectorAll somewhere to stash context about a node
// while processing a query
this.queryContext = {}

// true if part of a global install
this[_global] = global
Expand Down Expand Up @@ -1455,8 +1458,8 @@ class Node {

// maybe accept both string value or array of strings
// seems to be what dom API does
querySelectorAll (query) {
return querySelectorAll(this, query)
querySelectorAll (query, opts) {
return querySelectorAll(this, query, opts)
}

toJSON () {
Expand Down
Loading