From 8a9a7cda907508ad8393d2db85604595e0a6c8f3 Mon Sep 17 00:00:00 2001 From: Gar Date: Mon, 18 Jul 2022 10:00:45 -0700 Subject: [PATCH] fix: implement deduped --- .../content/using-npm/dependency-selectors.md | 8 +- lib/commands/query.js | 12 +- .../test/lib/commands/query.js.test.cjs | 111 ++---------------- test/lib/commands/query.js | 31 +++++ workspaces/arborist/lib/query-selector-all.js | 4 + .../arborist/test/query-selector-all.js | 5 + 6 files changed, 63 insertions(+), 108 deletions(-) diff --git a/docs/content/using-npm/dependency-selectors.md b/docs/content/using-npm/dependency-selectors.md index d2cc73be290f1..9d65baf631a7e 100644 --- a/docs/content/using-npm/dependency-selectors.md +++ b/docs/content/using-npm/dependency-selectors.md @@ -41,7 +41,7 @@ The [`npm query`](/commands/npm-query) commmand exposes a new dependency selecto - `.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` +- `.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 @@ -52,9 +52,9 @@ The [`npm query`](/commands/npm-query) commmand exposes a new dependency selecto - [`: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 -- `:deduped` when a dependency has been deduped -- `:override` when a dependency is an override +- `: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 diff --git a/lib/commands/query.js b/lib/commands/query.js index 5dba4b2bea3a0..5cd28e98dfbc6 100644 --- a/lib/commands/query.js +++ b/lib/commands/query.js @@ -19,11 +19,14 @@ class QuerySelectorItem { this.to = [] this.dev = node.target.dev this.inBundle = node.target.inBundle + this.deduped = this.from.length > 1 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) + if (edge.to) { + this.to.push(edge.to.location) + } } } } @@ -89,11 +92,8 @@ class Query extends BaseCommand { // builds a normalized inventory buildResponse (items) { for (const node of items) { - if (!this.#seen.has(node.target.location)) { - const item = new QuerySelectorItem(node) - this.#response.push(item) - this.#seen.add(item.realpath) - } + const item = new QuerySelectorItem(node) + this.#response.push(item) } } } diff --git a/tap-snapshots/test/lib/commands/query.js.test.cjs b/tap-snapshots/test/lib/commands/query.js.test.cjs index ef5574ee2ccac..6e8282e2d5636 100644 --- a/tap-snapshots/test/lib/commands/query.js.test.cjs +++ b/tap-snapshots/test/lib/commands/query.js.test.cjs @@ -6,24 +6,7 @@ */ 'use strict' exports[`test/lib/commands/query.js TAP global > should return global package 1`] = ` -[ - { - "name": "lorem", - "version": "2.0.0", - "_id": "lorem@2.0.0", - "pkgid": "lorem@2.0.0", - "location": "node_modules/lorem", - "path": "{CWD}/test/lib/commands/tap-testdir-query-global/global/node_modules/lorem", - "realpath": "{CWD}/test/lib/commands/tap-testdir-query-global/global/node_modules/lorem", - "resolved": null, - "from": [ - "" - ], - "to": [], - "dev": false, - "inBundle": false - } -] + ` exports[`test/lib/commands/query.js TAP include-workspace-root > should return workspace object and root object 1`] = ` @@ -49,7 +32,8 @@ exports[`test/lib/commands/query.js TAP include-workspace-root > should return w "node_modules/b" ], "dev": false, - "inBundle": false + "inBundle": false, + "deduped": false }, { "name": "c", @@ -63,92 +47,22 @@ exports[`test/lib/commands/query.js TAP include-workspace-root > should return w "from": [], "to": [], "dev": false, - "inBundle": false + "inBundle": false, + "deduped": false } ] ` exports[`test/lib/commands/query.js TAP linked node > should return linked node res 1`] = ` -[ - { - "name": "a", - "version": "1.0.0", - "_id": "a@1.0.0", - "pkgid": "a@1.0.0", - "location": "a", - "path": "{CWD}/test/lib/commands/tap-testdir-query-linked-node/prefix/a", - "realpath": "{CWD}/test/lib/commands/tap-testdir-query-linked-node/prefix/a", - "resolved": null, - "from": [], - "to": [], - "dev": false, - "inBundle": false - }, - { - "name": "a", - "version": "1.0.0", - "_id": "a@1.0.0", - "pkgid": "a@1.0.0", - "location": "a", - "path": "{CWD}/test/lib/commands/tap-testdir-query-linked-node/prefix/a", - "realpath": "{CWD}/test/lib/commands/tap-testdir-query-linked-node/prefix/a", - "resolved": null, - "from": [], - "to": [], - "dev": false, - "inBundle": false - } -] + +` + +exports[`test/lib/commands/query.js TAP recursive tree > should return everything in the tree, accounting for recursion 1`] = ` + ` exports[`test/lib/commands/query.js TAP simple query > should return root object and direct children 1`] = ` -[ - { - "name": "project", - "dependencies": { - "a": "^1.0.0", - "b": "^1.0.0" - }, - "pkgid": "project@", - "location": "", - "path": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix", - "realpath": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix", - "resolved": null, - "from": [], - "to": [ - "node_modules/a", - "node_modules/b" - ], - "dev": false, - "inBundle": false - }, - { - "pkgid": "a@", - "location": "node_modules/a", - "path": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix/node_modules/a", - "realpath": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix/node_modules/a", - "resolved": null, - "from": [ - "" - ], - "to": [], - "dev": false, - "inBundle": false - }, - { - "pkgid": "b@", - "location": "node_modules/b", - "path": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix/node_modules/b", - "realpath": "{CWD}/test/lib/commands/tap-testdir-query-simple-query/prefix/node_modules/b", - "resolved": null, - "from": [ - "" - ], - "to": [], - "dev": false, - "inBundle": false - } -] + ` exports[`test/lib/commands/query.js TAP workspace query > should return workspace object 1`] = ` @@ -165,7 +79,8 @@ exports[`test/lib/commands/query.js TAP workspace query > should return workspac "from": [], "to": [], "dev": false, - "inBundle": false + "inBundle": false, + "deduped": false } ] ` diff --git a/test/lib/commands/query.js b/test/lib/commands/query.js index f13777f9cd94f..fb5b4843c34ee 100644 --- a/test/lib/commands/query.js +++ b/test/lib/commands/query.js @@ -30,6 +30,9 @@ t.test('simple query', async t => { a: '^1.0.0', b: '^1.0.0', }, + peerDependencies: { + c: '1.0.0', + }, }), }, }) @@ -37,6 +40,34 @@ t.test('simple query', async t => { t.matchSnapshot(joinedOutput(), 'should return root object and direct children') }) +t.test('recursive tree', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + prefixDir: { + node_modules: { + a: { + name: 'a', + version: '1.0.0', + }, + b: { + name: 'b', + version: '^2.0.0', + dependencies: { + a: '1.0.0', + }, + }, + }, + 'package.json': JSON.stringify({ + name: 'project', + dependencies: { + a: '^1.0.0', + b: '^1.0.0', + }, + }), + }, + }) + await npm.exec('query', ['*']) + t.matchSnapshot(joinedOutput(), 'should return everything in the tree, accounting for recursion') +}) t.test('workspace query', async t => { const { npm, joinedOutput } = await loadMockNpm(t, { config: { diff --git a/workspaces/arborist/lib/query-selector-all.js b/workspaces/arborist/lib/query-selector-all.js index 857c82a2acb33..86ec06e23ca25 100644 --- a/workspaces/arborist/lib/query-selector-all.js +++ b/workspaces/arborist/lib/query-selector-all.js @@ -310,6 +310,10 @@ class Results { return found }) } + + dedupedPseudo () { + return this.initialItems.filter(node => node.target.edgesIn.size > 1) + } } // operators for attribute selectors diff --git a/workspaces/arborist/test/query-selector-all.js b/workspaces/arborist/test/query-selector-all.js index 425db952ad7fd..ec8c116122099 100644 --- a/workspaces/arborist/test/query-selector-all.js +++ b/workspaces/arborist/test/query-selector-all.js @@ -358,6 +358,11 @@ t.test('query-selector-all', async t => { [':invalid', ['lorem@1.0.0']], [':link', ['a@1.0.0', 'b@1.0.0']], [':link', ['a@1.0.0', 'b@1.0.0']], + [':deduped', [ + 'bar@2.0.0', + 'moo@3.0.0', + 'recur@1.0.0', + ]], [':missing', ['missing-dep@^1.0.0']], [':private', ['b@1.0.0']],