Repository navigation
Conversation
…ter case On Windows, junctions keep the target spelling they were created with, so links can name one directory as D:\pkg and d:\pkg. loadActual cached link targets by the exact realpath string but placed nodes by a case-insensitive relative location, so a second spelling built a duplicate target that evicted the first and left its links without a target. Adding the next link then read null.package in set root. Reuse the node already at the target's inventory location before falling back to the realpath cache. Refs: npm#9079 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On Windows, links can spell the same target directory differently, most commonly with a different drive-letter case (
D:\work\pkgandd:\work\pkg). Junctions keep the spelling they were created with, andfs.realpath/readlinkreturn it unchanged.loadActualcaches link targets by the exact realpath string, but places nodes in the inventory byrelpath(root.realpath, path), andpath.relativeis case-insensitive on Windows. So when a second spelling shows up:new Link()builds a second target node for the same directory;target === null;inventory.query('realpath', ...)inset root, and readsnull.package.That is the
TypeError: Cannot read properties of null (reading 'package')atset root/set parent/new Link/#newLinkreported in #9079. Depending on the orderPromise.allloads the children, the load either throws or finishes with detached, targetless links.Reproduction (npm 12.2.0, Windows 11)
Calling
new Arborist({ path: 'app' }).loadActual({ forceActual: true })40 times in one process gave:aandchavetarget === nullbhastarget === nullTypeError: Cannot read properties of null (reading 'package')atset root/set parent/new Node/new LinklinksIn.size === 3)Six fresh processes of five loads each threw the same TypeError in two of them before the change; which outcome you get depends on the order the children finish loading.
I can't confirm that this spelling difference is how the reporter's tree got there: the issue describes nested
file:dependencies and suspects load ordering, and this PR does not change ordering. It fixes one mechanism that produces exactly that TypeError, so I've referenced the issue instead of closing it.One related case is left as it was: a workspace that is first reached through a link spelled differently from the workspace path still gets a second node when the workspace loop loads it. npm spells the workspace links it creates from the root path, so that needs a hand-made link. I kept this change small, and can extend it if you'd like it covered.
Change
#newLinkfirst reuses the node already at the target's inventory location, and only then falls back to the realpath cache. The link takes that node's path spelling, so no duplicate target is created and nothing is evicted.Tests
test/arborist/load-actual.js: three junctions with mixed drive-letter case must share one target that is in the tree and has all threelinksIn. It fails without the change (links have a target, thenCannot read properties of null (reading 'root')) and passes with it.node . test -w @npmcli/arboriston Windows 11 with Node 24: 58 of 59 test files pass and coverage stays at 100%. The one failure,linked strategy exposes store node_modules via NODE_PATH for lifecycle scriptsintest/arborist/reify.js(spawn C:\WINDOWS\system32\cmd.exe ENOENT), fails the same way on unmodifiedlateston this machine, so it is environmental and unrelated.eslinton both changed files: clean.Only Windows was run locally. On other platforms the new test is skipped, and the changed lookup gives the same result as the cache did before.
Refs #9079
AI assistance: the investigation, the change and the checks above were done with Claude Code (an AI coding agent). The results quoted are from real runs on a Windows 11 machine.
🤖 Generated with Claude Code