Skip to content

Resolve symlinked project-reference directory (index.ts) subpaths to source#4374

Open
sverrejoh wants to merge 1 commit into
microsoft:mainfrom
sverrejoh:fix-projectref-symlink-dir-subpath
Open

Resolve symlinked project-reference directory (index.ts) subpaths to source#4374
sverrejoh wants to merge 1 commit into
microsoft:mainfrom
sverrejoh:fix-projectref-symlink-dir-subpath

Conversation

@sverrejoh

@sverrejoh sverrejoh commented Jun 19, 2026

Copy link
Copy Markdown
Member

Fixes #4373. This is a 6.0 → 7.0 regression: TypeScript 6.0.3 resolves the same project from source; the native compiler reports TS2307 (details + runnable repro in the issue).

The source-of-project-reference redirect failed to resolve a directory subpath import (e.g. b/lib/File where the source is b/src/File/index.ts) when the referenced package is reached through a symlinked node_modules entry and its lib/ is unbuilt.

Cause

The dts faking host resolves symlinked paths through knownSymlinks, which is populated lazily by handleDirectoryCouldBeSymlink — and only when an existing directory is probed. For a directory subpath the resolver DirectoryExists-probes the unbuilt subpath (node_modules/b/lib/File) before it ever directory-probes the package root (node_modules/b), so the package symlink isn't in knownSymlinks yet, the lookup misses, and the cached negative sticks. (File subpaths happen to probe the package root first, which is why the existing symlink tests pass.)

Fix

On a node_modules miss in fileOrDirectoryExistsUsingSource, discover and register the package symlink on demand: walk up to the nearest existing ancestor directory and reuse the existing handleDirectoryCouldBeSymlink. The existing symlink-aware fallback then resolves the subpath to source. It only runs on a miss, so built/normal resolution is unaffected.

Test

Added internal/project TestProjectReferencesProgram / "references through symlink to a directory subpath (index file)", which fails without the change (indexFile is nil) and passes with it. internal/project and internal/compiler suites are green.

AI assistance

This change was authored with AI assistance (Claude Code). I investigated the bug, directed and reviewed the work, understand the change, and will shepherd it through review and respond to feedback myself.

…source

The source-of-project-reference redirect failed for a directory subpath import
(e.g. b/lib/File, source b/src/File/index.ts) when the referenced package is
reached through a symlinked node_modules entry and its lib/ is unbuilt.

knownSymlinks is populated lazily by handleDirectoryCouldBeSymlink only when an
existing directory is probed. For a directory subpath the resolver
DirectoryExists-probes the unbuilt subpath before the package root, so the
symlink isn't registered yet and the (cached) lookup misses.

Discover and register the package symlink on demand on a node_modules miss, by
walking up to the nearest existing ancestor and reusing
handleDirectoryCouldBeSymlink; the existing symlink-aware fallback then resolves
to source. Runs only on a miss, so built/normal resolution is unaffected.

Fixes microsoft#4373
Copilot AI review requested due to automatic review settings June 19, 2026 09:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a path-resolution edge case in the project-reference “use source instead of .d.ts outputs” redirect when the referenced package is reached via a symlinked node_modules entry and the resolver probes an unbuilt directory subpath (e.g. b/lib/Fileb/src/File/index.ts) before ever probing the package root, causing a cached negative lookup.

Changes:

  • Teach the project-reference d.ts faking VFS to opportunistically discover/register a node_modules package symlink by walking up to the nearest existing ancestor directory on a miss, so subsequent symlink-aware fallback can map to source.
  • Add a regression test covering a directory-subpath (index.ts) import through a symlinked referenced project.
Show a summary per file
File Description
internal/compiler/projectreferencedtsfakinghost.go On a miss, walks up to an existing ancestor under node_modules and calls existing symlink registration logic so directory-subpath redirects can resolve to source.
internal/project/projectreferencesprogram_test.go Adds a regression test for resolving a symlinked project reference via a directory subpath that maps to src/.../index.ts when outputs are unbuilt.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 0

// records it in knownSymlinks. This handles the case where the resolver probes a
// path inside an unbuilt package before directory-probing the package root, so
// handleDirectoryCouldBeSymlink has not fired for it yet.
func (fs *projectReferenceDtsFakingVfs) registerNodeModulesSymlinkFromAncestor(fileOrDirectory string) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems very special case-y. Surely any dir we resolve into could be under be a symlink? How are we not working with realpaths already?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Project-reference redirect fails for directory (index.ts) subpaths of symlinked packages

3 participants