From 83eb43cb808d0f35d1d930aece59e0ec164a48fd Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Thu, 1 May 2025 11:21:32 -0700 Subject: [PATCH] fix(arborist): Add better error message when lockfile is malformed --- .../arborist/lib/arborist/load-virtual.js | 13 ++++++ .../arborist/test/arborist/load-virtual.js | 41 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/workspaces/arborist/lib/arborist/load-virtual.js b/workspaces/arborist/lib/arborist/load-virtual.js index 96cd18302e994..19621f01b21b5 100644 --- a/workspaces/arborist/lib/arborist/load-virtual.js +++ b/workspaces/arborist/lib/arborist/load-virtual.js @@ -200,6 +200,19 @@ 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) + + if (!target) { + const err = new Error( +`Missing target in lock file: "${targetLoc}" is referenced by "${location}" but does not exist. +To fix: +1. rm -rf node_modules +2. rm package-lock.json +3. npm install` + ) + err.code = 'EMISSINGTARGET' + throw err + } + 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..85c3e4ac2ad2a 100644 --- a/workspaces/arborist/test/arborist/load-virtual.js +++ b/workspaces/arborist/test/arborist/load-virtual.js @@ -245,3 +245,44 @@ t.test('do not bundle the entire universe', async t => { 'yaml', ].sort()) }) + +t.test('error when link target is missing', async t => { + const path = t.testdir({ + 'package.json': JSON.stringify({ + name: 'root', + workspaces: ['packages/*'], + }), + 'package-lock.json': JSON.stringify({ + name: 'root', + lockfileVersion: 3, + packages: { + '': { + workspaces: ['packages/*'], + }, + // This is the problematic entry - a link with no corresponding target + 'node_modules/@my-scope/my-package': { + resolved: 'packages/some-folder/my-package', + link: true, + }, + // Missing entry for 'packages/some-folder/my-package' + }, + }), + packages: { + 'some-folder': { + 'my-package': { + 'package.json': JSON.stringify({ + name: '@my-scope/my-package', + version: '1.0.0', + }), + }, + }, + }, + }) + + const arb = new Arborist({ path }) + + await t.rejects(arb.loadVirtual(), { + code: 'EMISSINGTARGET', + message: /Missing target in lock file:.*but does not exist/, + }) +})