diff --git a/workspaces/arborist/lib/arborist/build-ideal-tree.js b/workspaces/arborist/lib/arborist/build-ideal-tree.js index 2ea66ac336414..0d936d8ef7768 100644 --- a/workspaces/arborist/lib/arborist/build-ideal-tree.js +++ b/workspaces/arborist/lib/arborist/build-ideal-tree.js @@ -1243,7 +1243,7 @@ This is a one-time fix-up, please be patient... if (isWorkspace) { const existingNode = this.idealTree.edgesOut.get(spec.name).to if (existingNode && existingNode.isWorkspace && existingNode.satisfies(edge)) { - return edge.to + return existingNode } } diff --git a/workspaces/arborist/tap-snapshots/test/arborist/build-ideal-tree.js.test.cjs b/workspaces/arborist/tap-snapshots/test/arborist/build-ideal-tree.js.test.cjs index 3559497804752..3fb3901e52c04 100644 --- a/workspaces/arborist/tap-snapshots/test/arborist/build-ideal-tree.js.test.cjs +++ b/workspaces/arborist/tap-snapshots/test/arborist/build-ideal-tree.js.test.cjs @@ -159849,6 +159849,94 @@ ArboristNode { } ` +exports[`test/arborist/build-ideal-tree.js TAP workspaces should allow cyclic peer dependencies between workspaces and packages from a repository > must match snapshot 1`] = ` +ArboristNode { + "children": Map { + "foo" => ArboristNode { + "edgesIn": Set { + EdgeIn { + "from": "workspace-a", + "name": "foo", + "spec": ">=1.0.0", + "type": "prod", + }, + }, + "edgesOut": Map { + "workspace-a" => EdgeOut { + "name": "workspace-a", + "spec": "1.0.0", + "to": "node_modules/workspace-a", + "type": "peer", + }, + }, + "location": "node_modules/foo", + "name": "foo", + "path": "{CWD}/test/arborist/tap-testdir-build-ideal-tree-workspaces-should-allow-cyclic-peer-dependencies-between-workspaces-and-packages-from-a-repository/node_modules/foo", + "version": "1.0.0", + }, + "workspace-a" => ArboristLink { + "edgesIn": Set { + EdgeIn { + "from": "", + "name": "workspace-a", + "spec": "file:{CWD}/test/arborist/tap-testdir-build-ideal-tree-workspaces-should-allow-cyclic-peer-dependencies-between-workspaces-and-packages-from-a-repository/workspace-a", + "type": "workspace", + }, + EdgeIn { + "from": "node_modules/foo", + "name": "workspace-a", + "spec": "1.0.0", + "type": "peer", + }, + }, + "isWorkspace": true, + "location": "node_modules/workspace-a", + "name": "workspace-a", + "path": "{CWD}/test/arborist/tap-testdir-build-ideal-tree-workspaces-should-allow-cyclic-peer-dependencies-between-workspaces-and-packages-from-a-repository/node_modules/workspace-a", + "realpath": "{CWD}/test/arborist/tap-testdir-build-ideal-tree-workspaces-should-allow-cyclic-peer-dependencies-between-workspaces-and-packages-from-a-repository/workspace-a", + "resolved": "file:../workspace-a", + "target": ArboristNode { + "location": "workspace-a", + }, + "version": "1.0.0", + }, + }, + "edgesOut": Map { + "workspace-a" => EdgeOut { + "name": "workspace-a", + "spec": "file:{CWD}/test/arborist/tap-testdir-build-ideal-tree-workspaces-should-allow-cyclic-peer-dependencies-between-workspaces-and-packages-from-a-repository/workspace-a", + "to": "node_modules/workspace-a", + "type": "workspace", + }, + }, + "fsChildren": Set { + ArboristNode { + "edgesOut": Map { + "foo" => EdgeOut { + "name": "foo", + "spec": ">=1.0.0", + "to": "node_modules/foo", + "type": "prod", + }, + }, + "isWorkspace": true, + "location": "workspace-a", + "name": "workspace-a", + "path": "{CWD}/test/arborist/tap-testdir-build-ideal-tree-workspaces-should-allow-cyclic-peer-dependencies-between-workspaces-and-packages-from-a-repository/workspace-a", + "version": "1.0.0", + }, + }, + "isProjectRoot": true, + "location": "", + "name": "tap-testdir-build-ideal-tree-workspaces-should-allow-cyclic-peer-dependencies-between-workspaces-and-packages-from-a-repository", + "packageName": "root", + "path": "{CWD}/test/arborist/tap-testdir-build-ideal-tree-workspaces-should-allow-cyclic-peer-dependencies-between-workspaces-and-packages-from-a-repository", + "workspaces": Map { + "workspace-a" => "workspace-a", + }, +} +` + exports[`test/arborist/build-ideal-tree.js TAP workspaces should ignore nested node_modules folders > expect resolving Promise 1`] = ` ArboristNode { "children": Map { diff --git a/workspaces/arborist/test/arborist/build-ideal-tree.js b/workspaces/arborist/test/arborist/build-ideal-tree.js index a473e663ed162..0e9264b849879 100644 --- a/workspaces/arborist/test/arborist/build-ideal-tree.js +++ b/workspaces/arborist/test/arborist/build-ideal-tree.js @@ -877,6 +877,52 @@ t.test('workspaces', t => { t.matchSnapshot(printTree(await tree)) }) + t.test('should allow cyclic peer dependencies between workspaces and packages from a repository', async t => { + generateNocks(t, { + foo: { + versions: ['1.0.0'], + peerDependencies: ['workspace-a'], + }, + }) + const path = t.testdir({ + 'package.json': JSON.stringify({ + name: 'root', + dependencies: { + 'workspace-a': '*', + }, + workspaces: ['workspace-a'], + }), + 'workspace-a': { + 'package.json': JSON.stringify({ + name: 'workspace-a', + version: '1.0.0', + dependencies: { + foo: '>=1.0.0', + }, + }), + }, + }) + + const arb = new Arborist({ + ...OPT, + path, + workspaces: ['workspace-a'], + }) + + const tree = arb.buildIdealTree({ + path, + add: [ + 'foo', + ], + }) + + // just assert that the buildIdealTree call resolves, if there's a + // problem here it will reject because of nock disabling requests + await t.resolves(tree) + + t.matchSnapshot(printTree(await tree)) + }) + t.test('workspace nodes are used instead of fetching manifests when they are valid', async t => { // turn off networking, this should never make a registry request nock.disableNetConnect()