From 80405a378639a4d8e50237d4f3cde0b5e5a3b2bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guru=20=F0=9F=A6=84?= Date: Thu, 15 Oct 2026 10:54:52 +0000 Subject: [PATCH 1/2] Avoid probing declaration outputs during source redirects Use project-reference mappings before probing declaration files and directories on disk. Cache symlinked file mappings even when declarations already exist, so realpath does not access redirected outputs. Retain filesystem fallback when a referenced source is missing. When a referenced project's declaration output is itself a symlink to another location and the source exists, the program now uses the source, as it already does for unbuilt references. Previously the symlink target's declaration file was loaded. Check ancestor links before scanning the symlink cache, and test emptiness without counting every link. This keeps external declaration lookups from scaling with unrelated workspace links. Cover built, unbuilt, source-missing and missing cases, outputs that are symlinks, and packages whose types entry names a declaration directory, using a recording filesystem and the program file lists. --- .../compiler/projectreferencedtsfakinghost.go | 41 ++++-- .../projectreferencedtsfakinghost_test.go | 128 ++++++++++++++++++ 2 files changed, 161 insertions(+), 8 deletions(-) create mode 100644 tsc/internal/compiler/projectreferencedtsfakinghost_test.go diff --git a/tsc/internal/compiler/projectreferencedtsfakinghost.go b/tsc/internal/compiler/projectreferencedtsfakinghost.go index 9eacfa3e64318..3ee0997466fcf 100644 --- a/tsc/internal/compiler/projectreferencedtsfakinghost.go +++ b/tsc/internal/compiler/projectreferencedtsfakinghost.go @@ -55,14 +55,11 @@ func (fs *projectReferenceDtsFakingVfs) CaseSensitivity() tspath.CaseSensitivity // FileExists implements vfs.FS. func (fs *projectReferenceDtsFakingVfs) FileExists(path tspath.RootedFilePath) bool { - if fs.host.FS().FileExists(path) { + // Prefer referenced sources so resolution does not probe their declaration outputs. + if path.IsDeclarationFile() && fs.fileExistsUsingSource(path) { return true } - if !path.IsDeclarationFile() { - return false - } - // Project references go to source file instead of .d.ts file - return fs.fileExistsUsingSource(path) + return fs.host.FS().FileExists(path) } // ReadFile implements vfs.FS. @@ -93,11 +90,14 @@ func (fs *projectReferenceDtsFakingVfs) Chtimes(path tspath.RootedPath, aTime ti // DirectoryExists implements vfs.FS. func (fs *projectReferenceDtsFakingVfs) DirectoryExists(path tspath.RootedDirectoryPath) bool { + if fs.directoryExistsUsingSource(path) { + return true + } if fs.host.FS().DirectoryExists(path) { fs.handleDirectoryCouldBeSymlink(path) return true } - return fs.directoryExistsUsingSource(path) + return false } // GetAccessibleEntries implements vfs.FS. @@ -201,7 +201,12 @@ func (fs *projectReferenceDtsFakingVfs) fileOrDirectoryExistsUsingSource( fs.handleDirectoryCouldBeSymlink(packageRoot) } knownDirectoryLinks := fs.knownSymlinks.Directories() - if knownDirectoryLinks.Size() == 0 { + hasDirectoryLinks := false + knownDirectoryLinks.Range(func(tspath.PathKey, *symlinks.KnownDirectoryLink) bool { + hasDirectoryLinks = true + return false + }) + if !hasDirectoryLinks { return false } if onFileExists != nil { @@ -211,6 +216,26 @@ func (fs *projectReferenceDtsFakingVfs) fileOrDirectoryExistsUsingSource( } } + // Avoid scanning unrelated links for files or directories in external packages. + canMap := false + for directory := fileOrDirectory.Directory(); ; { + if link, ok := knownDirectoryLinks.Load(fs.pathKey(directory.AsPath())); ok && link != nil { + realFile, ok := link.ResolveFilePath(tspath.RootedFilePathFromPath(fileOrDirectory), fs.CaseSensitivity()) + if ok && existsUsingSource(tspath.RootedPath(realFile)).IsTrue() { + canMap = true + break + } + } + parent := directory.AsPath().Directory() + if parent == directory { + break + } + directory = parent + } + if !canMap { + return false + } + // If it contains node_modules check if its one of the symlinked path we know of var exists bool knownDirectoryLinks.Range(func(directoryPath tspath.PathKey, knownDirectoryLink *symlinks.KnownDirectoryLink) bool { diff --git a/tsc/internal/compiler/projectreferencedtsfakinghost_test.go b/tsc/internal/compiler/projectreferencedtsfakinghost_test.go new file mode 100644 index 0000000000000..f95ceb599b61a --- /dev/null +++ b/tsc/internal/compiler/projectreferencedtsfakinghost_test.go @@ -0,0 +1,128 @@ +package compiler_test + +import ( + "slices" + "strings" + "sync" + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/compiler" + "github.com/microsoft/TypeScript/tsc/internal/core" + "github.com/microsoft/TypeScript/tsc/internal/tsoptions" + "github.com/microsoft/TypeScript/tsc/internal/tspath" + "github.com/microsoft/TypeScript/tsc/internal/vfs" + "github.com/microsoft/TypeScript/tsc/internal/vfs/vfstest" + "gotest.tools/v3/assert" +) + +type projectReferenceAccessFS struct { + vfs.FS + mu sync.Mutex + outputAccesses []string +} + +func (fs *projectReferenceAccessFS) record(operation, path string) { + if strings.HasSuffix(path, "/lib/index.d.ts") || strings.HasSuffix(path, "/lib") { + fs.mu.Lock() + defer fs.mu.Unlock() + fs.outputAccesses = append(fs.outputAccesses, operation+" "+path) + } +} + +func (fs *projectReferenceAccessFS) FileExists(path tspath.RootedFilePath) bool { + fs.record("FileExists", path.AsString()) + return fs.FS.FileExists(path) +} + +func (fs *projectReferenceAccessFS) DirectoryExists(path tspath.RootedDirectoryPath) bool { + fs.record("DirectoryExists", path.AsString()) + return fs.FS.DirectoryExists(path) +} + +func (fs *projectReferenceAccessFS) Realpath(path tspath.RootedPath) tspath.RootedPath { + fs.record("Realpath", path.AsString()) + return fs.FS.Realpath(path) +} + +func TestProjectReferenceSourceRedirectOutputAccesses(t *testing.T) { + t.Parallel() + for _, tc := range []struct { + name string + sourceExists bool + outputExists bool + directoryTypes bool + outputLink string + }{ + {"built", true, true, false, ""}, + {"unbuilt", true, false, false, ""}, + {"source-missing", false, true, false, ""}, + {"missing", false, false, false, ""}, + {"built-directory", true, true, true, ""}, + {"unbuilt-directory", true, false, true, ""}, + {"source-missing-directory", false, true, true, ""}, + {"missing-directory", false, false, true, ""}, + // Outputs that link elsewhere still map to the referenced source. + {"built-linked-file", true, true, false, "file"}, + {"built-linked-directory", true, true, true, "directory"}, + } { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + files := map[string]any{ + "/workspace/app/tsconfig.json": `{"compilerOptions":{"noLib":true,"module":"commonjs","moduleResolution":"node"},"files":["index.ts"],"references":[{"path":"../dependency"}]}`, + "/workspace/app/index.ts": `import { value } from "dependency"; import "external"; export { value };`, + "/workspace/app/node_modules/dependency": vfstest.Symlink("/workspace/dependency"), + "/workspace/app/node_modules/external/index.d.ts": `export {};`, + "/workspace/dependency/package.json": `{"name":"dependency","types":"lib/index.d.ts"}`, + "/workspace/dependency/tsconfig.json": `{"compilerOptions":{"composite":true,"rootDir":"src","outDir":"lib"},"files":["src/index.ts"]}`, + } + if tc.directoryTypes { + files["/workspace/dependency/package.json"] = `{"name":"dependency","types":"lib"}` + } + if tc.sourceExists { + files["/workspace/dependency/src/index.ts"] = `export const value = 1;` + } + if tc.outputExists { + switch tc.outputLink { + case "file": + files["/cache/index.d.ts"] = `export declare const value: number;` + files["/workspace/dependency/lib/index.d.ts"] = vfstest.Symlink("/cache/index.d.ts") + case "directory": + files["/cache/lib/index.d.ts"] = `export declare const value: number;` + files["/workspace/dependency/lib"] = vfstest.Symlink("/cache/lib") + default: + files["/workspace/dependency/lib/index.d.ts"] = `export declare const value: number;` + } + } + fs := &projectReferenceAccessFS{FS: vfstest.FromMap(files, tspath.CaseSensitive)} + host := compiler.NewCompilerHost(fs, "", nil, nil, nil) + config, diagnostics := tsoptions.GetParsedCommandLineOfConfigFile("/workspace/app/tsconfig.json", nil, nil, fs, nil) + assert.Equal(t, len(diagnostics), 0) + program := compiler.NewProgram(compiler.ProgramOptions{ + Host: host, + Config: config, + UseSourceOfProjectReference: true, + SingleThreaded: core.TSTrue, + }) + actualFiles := []string{} + for _, file := range program.GetSourceFiles() { + actualFiles = append(actualFiles, file.FileName().AsString()) + } + slices.Sort(actualFiles) + expectedFiles := []string{"/workspace/app/index.ts", "/workspace/app/node_modules/external/index.d.ts"} + if tc.sourceExists { + expectedFiles = append(expectedFiles, "/workspace/dependency/src/index.ts") + } + assert.DeepEqual(t, actualFiles, expectedFiles) + main := program.GetSourceFile("/workspace/app/index.ts") + resolved := program.GetResolvedModuleFromModuleSpecifier(main, main.Imports()[0]) + assert.Equal(t, resolved.IsResolved(), tc.sourceExists || tc.outputExists) + t.Logf("declaration output accesses: %v", fs.outputAccesses) + if tc.sourceExists { + assert.Equal(t, len(fs.outputAccesses), 0) + } else { + // Missing sources must not suppress the real output existence check. + assert.Assert(t, slices.Contains(fs.outputAccesses, "FileExists /workspace/app/node_modules/dependency/lib/index.d.ts")) + } + }) + } +} From 1c1683c7f861c93c808c1cca6deacca540d806e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guru=20=F0=9F=A6=84?= Date: Sat, 17 Oct 2026 01:58:37 +0530 Subject: [PATCH 2/2] Map through the nearest linked ancestor only The ancestor walk already finds a link whose target exists in a referenced project's sources. Use that result instead of scanning every known link again, so each lookup no longer grows with the number of links. When several ancestors are links, the nearest one is used; the scan picked any of them. --- .../compiler/projectreferencedtsfakinghost.go | 42 ++++++------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/tsc/internal/compiler/projectreferencedtsfakinghost.go b/tsc/internal/compiler/projectreferencedtsfakinghost.go index 3ee0997466fcf..a074a6a603cc2 100644 --- a/tsc/internal/compiler/projectreferencedtsfakinghost.go +++ b/tsc/internal/compiler/projectreferencedtsfakinghost.go @@ -216,45 +216,27 @@ func (fs *projectReferenceDtsFakingVfs) fileOrDirectoryExistsUsingSource( } } - // Avoid scanning unrelated links for files or directories in external packages. - canMap := false + // Map through the nearest linked ancestor. Only the path's own ancestors + // are checked, so the cost does not grow with the number of known links. for directory := fileOrDirectory.Directory(); ; { if link, ok := knownDirectoryLinks.Load(fs.pathKey(directory.AsPath())); ok && link != nil { - realFile, ok := link.ResolveFilePath(tspath.RootedFilePathFromPath(fileOrDirectory), fs.CaseSensitivity()) - if ok && existsUsingSource(tspath.RootedPath(realFile)).IsTrue() { - canMap = true - break + realFileOrDirectory, ok := link.ResolveFilePath(tspath.RootedFilePathFromPath(fileOrDirectory), fs.CaseSensitivity()) + if !ok { + panic("canonical symlink path did not match its presentation path") + } + if existsUsingSource(tspath.RootedPath(realFileOrDirectory)).IsTrue() { + if onFileExists != nil { + onFileExists(realFileOrDirectory) + } + return true } } parent := directory.AsPath().Directory() if parent == directory { - break + return false } directory = parent } - if !canMap { - return false - } - - // If it contains node_modules check if its one of the symlinked path we know of - var exists bool - knownDirectoryLinks.Range(func(directoryPath tspath.PathKey, knownDirectoryLink *symlinks.KnownDirectoryLink) bool { - if directoryPath == fileOrDirectoryPath || !directoryPath.ContainsPath(fileOrDirectoryPath) { - return true - } - realFileOrDirectory, ok := knownDirectoryLink.ResolveFilePath(tspath.RootedFilePathFromPath(fileOrDirectory), fs.CaseSensitivity()) - if !ok { - panic("canonical symlink path did not match its presentation path") - } - if exists = existsUsingSource(tspath.RootedPath(realFileOrDirectory)).IsTrue(); exists { - if onFileExists != nil { - onFileExists(realFileOrDirectory) - } - return false - } - return true - }) - return exists } func (fs *projectReferenceDtsFakingVfs) fileExistsIfProjectReferenceDts(file tspath.RootedFilePath) core.Tristate {