@@ -19,12 +19,13 @@ import (
1919 "github.com/microsoft/typescript-go/internal/diagnostics"
2020 "github.com/microsoft/typescript-go/internal/locale"
2121 "github.com/microsoft/typescript-go/internal/module"
22- "github.com/microsoft/typescript-go/internal/modulespecifiers"
2322 "github.com/microsoft/typescript-go/internal/outputpaths"
23+ "github.com/microsoft/typescript-go/internal/packagejson"
2424 "github.com/microsoft/typescript-go/internal/parser"
2525 "github.com/microsoft/typescript-go/internal/printer"
2626 "github.com/microsoft/typescript-go/internal/scanner"
2727 "github.com/microsoft/typescript-go/internal/sourcemap"
28+ "github.com/microsoft/typescript-go/internal/symlinks"
2829 "github.com/microsoft/typescript-go/internal/tsoptions"
2930 "github.com/microsoft/typescript-go/internal/tspath"
3031)
@@ -68,6 +69,8 @@ type Program struct {
6869 // Cached unresolved imports for ATA
6970 unresolvedImportsOnce sync.Once
7071 unresolvedImports * collections.Set [string ]
72+ knownSymlinks * symlinks.KnownSymlinks
73+ knownSymlinksOnce sync.Once
7174
7275 // Used by workspace/symbol
7376 hasTSFileOnce sync.Once
@@ -99,7 +102,7 @@ func (p *Program) GetNearestAncestorDirectoryWithPackageJson(dirname string) str
99102}
100103
101104// GetPackageJsonInfo implements checker.Program.
102- func (p * Program ) GetPackageJsonInfo (pkgJsonPath string ) modulespecifiers. PackageJsonInfo {
105+ func (p * Program ) GetPackageJsonInfo (pkgJsonPath string ) * packagejson. InfoCacheEntry {
103106 scoped := p .resolver .GetPackageScopeForPath (pkgJsonPath )
104107 if scoped != nil && scoped .Exists () && scoped .PackageDirectory == tspath .GetDirectoryPath (pkgJsonPath ) {
105108 return scoped
@@ -247,6 +250,7 @@ func (p *Program) UpdateProgram(changedFilePath tspath.Path, newHost CompilerHos
247250 programDiagnostics : p .programDiagnostics ,
248251 hasEmitBlockingDiagnostics : p .hasEmitBlockingDiagnostics ,
249252 unresolvedImports : p .unresolvedImports ,
253+ knownSymlinks : p .knownSymlinks ,
250254 }
251255 result .initCheckerPool ()
252256 index := core .FindIndex (result .files , func (file * ast.SourceFile ) bool { return file .Path () == newFile .Path () })
@@ -255,6 +259,10 @@ func (p *Program) UpdateProgram(changedFilePath tspath.Path, newHost CompilerHos
255259 result .filesByPath = maps .Clone (result .filesByPath )
256260 result .filesByPath [newFile .Path ()] = newFile
257261 updateFileIncludeProcessor (result )
262+ result .knownSymlinks = symlinks .NewKnownSymlink (result .GetCurrentDirectory (), result .UseCaseSensitiveFileNames ())
263+ if len (result .resolvedModules ) > 0 || len (result .typeResolutionsInFile ) > 0 {
264+ result .knownSymlinks .SetSymlinksFromResolutions (result .ForEachResolvedModule , result .ForEachResolvedTypeReferenceDirective )
265+ }
258266 return result , true
259267}
260268
@@ -1597,6 +1605,86 @@ func (p *Program) HasTSFile() bool {
15971605 return p .hasTSFile
15981606}
15991607
1608+ func (p * Program ) GetSymlinkCache () * symlinks.KnownSymlinks {
1609+ p .knownSymlinksOnce .Do (func () {
1610+ if p .knownSymlinks == nil {
1611+ p .knownSymlinks = symlinks .NewKnownSymlink (p .GetCurrentDirectory (), p .UseCaseSensitiveFileNames ())
1612+
1613+ // Resolved modules store realpath information when they're resolved inside node_modules
1614+ if len (p .resolvedModules ) > 0 || len (p .typeResolutionsInFile ) > 0 {
1615+ p .knownSymlinks .SetSymlinksFromResolutions (p .ForEachResolvedModule , p .ForEachResolvedTypeReferenceDirective )
1616+ }
1617+
1618+ // Check other dependencies for symlinks
1619+ var seenPackageJsons collections.Set [tspath.Path ]
1620+ for filePath , meta := range p .sourceFileMetaDatas {
1621+ if meta .PackageJsonDirectory == "" ||
1622+ ! p .SourceFileMayBeEmitted (p .GetSourceFileByPath (filePath ), false ) ||
1623+ ! seenPackageJsons .AddIfAbsent (p .toPath (meta .PackageJsonDirectory )) {
1624+ continue
1625+ }
1626+ packageJsonName := tspath .CombinePaths (meta .PackageJsonDirectory , "package.json" )
1627+ info := p .GetPackageJsonInfo (packageJsonName )
1628+ if info .GetContents () == nil {
1629+ continue
1630+ }
1631+
1632+ for dep := range info .GetContents ().GetRuntimeDependencyNames ().Keys () {
1633+ // Skip work in common case: we already saved a symlink for this package directory
1634+ // in the node_modules adjacent to this package.json
1635+ possibleDirectoryPath := p .toPath (tspath .CombinePaths (meta .PackageJsonDirectory , "node_modules" , dep ))
1636+ if p .knownSymlinks .HasDirectory (possibleDirectoryPath ) {
1637+ continue
1638+ }
1639+ if ! strings .HasPrefix (dep , "@types" ) {
1640+ possibleTypesDirectoryPath := p .toPath (tspath .CombinePaths (meta .PackageJsonDirectory , "node_modules" , module .GetTypesPackageName (dep )))
1641+ if p .knownSymlinks .HasDirectory (possibleTypesDirectoryPath ) {
1642+ continue
1643+ }
1644+ }
1645+
1646+ if packageResolution := p .resolver .ResolvePackageDirectory (dep , packageJsonName , core .ResolutionModeCommonJS , nil ); packageResolution .IsResolved () {
1647+ p .knownSymlinks .ProcessResolution (
1648+ tspath .CombinePaths (packageResolution .OriginalPath , "package.json" ),
1649+ tspath .CombinePaths (packageResolution .ResolvedFileName , "package.json" ),
1650+ )
1651+ }
1652+ }
1653+ }
1654+ }
1655+ })
1656+ return p .knownSymlinks
1657+ }
1658+
1659+ func (p * Program ) ResolveModuleName (moduleName string , containingFile string , resolutionMode core.ResolutionMode ) * module.ResolvedModule {
1660+ resolved , _ := p .resolver .ResolveModuleName (moduleName , containingFile , resolutionMode , nil )
1661+ return resolved
1662+ }
1663+
1664+ func (p * Program ) ForEachResolvedModule (callback func (resolution * module.ResolvedModule , moduleName string , mode core.ResolutionMode , filePath tspath.Path ), file * ast.SourceFile ) {
1665+ forEachResolution (p .resolvedModules , callback , file )
1666+ }
1667+
1668+ func (p * Program ) ForEachResolvedTypeReferenceDirective (callback func (resolution * module.ResolvedTypeReferenceDirective , moduleName string , mode core.ResolutionMode , filePath tspath.Path ), file * ast.SourceFile ) {
1669+ forEachResolution (p .typeResolutionsInFile , callback , file )
1670+ }
1671+
1672+ func forEachResolution [T any ](resolutionCache map [tspath.Path ]module.ModeAwareCache [T ], callback func (resolution T , moduleName string , mode core.ResolutionMode , filePath tspath.Path ), file * ast.SourceFile ) {
1673+ if file != nil {
1674+ if resolutions , ok := resolutionCache [file .Path ()]; ok {
1675+ for key , resolution := range resolutions {
1676+ callback (resolution , key .Name , key .Mode , file .Path ())
1677+ }
1678+ }
1679+ } else {
1680+ for filePath , resolutions := range resolutionCache {
1681+ for key , resolution := range resolutions {
1682+ callback (resolution , key .Name , key .Mode , filePath )
1683+ }
1684+ }
1685+ }
1686+ }
1687+
16001688var plainJSErrors = collections .NewSetFromItems (
16011689 // binder errors
16021690 diagnostics .Cannot_redeclare_block_scoped_variable_0 .Code (),
0 commit comments