diff --git a/workspaces/arborist/lib/arborist/load-virtual.js b/workspaces/arborist/lib/arborist/load-virtual.js index 07c986853913e..baedcb412cf25 100644 --- a/workspaces/arborist/lib/arborist/load-virtual.js +++ b/workspaces/arborist/lib/arborist/load-virtual.js @@ -200,6 +200,12 @@ module.exports = cls => class VirtualLoader extends cls { const targetPath = resolve(this.path, meta.resolved) const targetLoc = relpath(this.path, targetPath) const target = nodes.get(targetLoc) + // Skip loading the target if it doesn't exist + // This can happen if the edge to it has error MISSING + // For example, where a workspace has a broken link dependency + if (!target) { + continue + } const link = this.#loadLink(location, targetLoc, target, meta) nodes.set(location, link) nodes.set(targetLoc, link.target) diff --git a/workspaces/arborist/test/arborist/load-virtual.js b/workspaces/arborist/test/arborist/load-virtual.js index 4540d969d71a9..fb8996765bf94 100644 --- a/workspaces/arborist/test/arborist/load-virtual.js +++ b/workspaces/arborist/test/arborist/load-virtual.js @@ -245,3 +245,37 @@ t.test('do not bundle the entire universe', async t => { 'yaml', ].sort()) }) + +t.test('load-virtual does not fail if a link target does not exist', async t => { + // Create a mock Shrinkwrap with a link to a non-existent target + const mockPath = t.testdir({ + 'package.json': JSON.stringify({ + name: 'test-package', + version: '1.0.0', + }), + 'package-lock.json': JSON.stringify({ + name: 'test-package', + version: '1.0.0', + lockfileVersion: 2, + requires: true, + packages: { + '': { + name: 'test-package', + version: '1.0.0', + dependencies: { + 'missing-pkg': 'file:./missing-pkg', + }, + }, + 'node_modules/missing-pkg': { + link: true, + resolved: './missing-pkg', + }, + }, + }), + }) + + // This should not throw an error because the #resolveLinks method + // should skip loading the target if it doesn't exist + const tree = await loadVirtual(mockPath) + t.ok(tree, 'tree loaded successfully despite missing target') +})