135 lines
5.7 KiB
Go
135 lines
5.7 KiB
Go
package symlinks
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/microsoft/typescript-go/internal/ast"
|
|
"github.com/microsoft/typescript-go/internal/collections"
|
|
"github.com/microsoft/typescript-go/internal/core"
|
|
"github.com/microsoft/typescript-go/internal/module"
|
|
"github.com/microsoft/typescript-go/internal/tspath"
|
|
)
|
|
|
|
type KnownDirectoryLink struct {
|
|
// Matches the casing returned by `realpath`. Used to compute the `realpath` of children.
|
|
// Always has trailing directory separator
|
|
Real string
|
|
// toPath(real). Stored to avoid repeated recomputation.
|
|
// Always has trailing directory separator
|
|
RealPath tspath.Path
|
|
}
|
|
|
|
type KnownSymlinks struct {
|
|
directories collections.SyncMap[tspath.Path, *KnownDirectoryLink]
|
|
directoriesByRealpath collections.SyncMap[tspath.Path, *collections.SyncSet[string]]
|
|
files collections.SyncMap[tspath.Path, string]
|
|
filesByRealpath collections.SyncMap[tspath.Path, *collections.SyncSet[string]]
|
|
cwd string
|
|
useCaseSensitiveFileNames bool
|
|
}
|
|
|
|
func (cache *KnownSymlinks) HasDirectory(symlinkPath tspath.Path) bool {
|
|
_, ok := cache.directories.Load(symlinkPath.EnsureTrailingDirectorySeparator())
|
|
return ok
|
|
}
|
|
|
|
// Gets a map from symlink to realpath. Keys have trailing directory separators.
|
|
func (cache *KnownSymlinks) Directories() *collections.SyncMap[tspath.Path, *KnownDirectoryLink] {
|
|
return &cache.directories
|
|
}
|
|
|
|
func (cache *KnownSymlinks) DirectoriesByRealpath() *collections.SyncMap[tspath.Path, *collections.SyncSet[string]] {
|
|
return &cache.directoriesByRealpath
|
|
}
|
|
|
|
// Gets a map from symlink to realpath
|
|
func (cache *KnownSymlinks) Files() *collections.SyncMap[tspath.Path, string] {
|
|
return &cache.files
|
|
}
|
|
|
|
// Gets a map from realpath to symlinks
|
|
func (cache *KnownSymlinks) FilesByRealpath() *collections.SyncMap[tspath.Path, *collections.SyncSet[string]] {
|
|
return &cache.filesByRealpath
|
|
}
|
|
|
|
func (cache *KnownSymlinks) SetDirectory(symlink string, symlinkPath tspath.Path, realDirectory *KnownDirectoryLink) {
|
|
if realDirectory != nil {
|
|
if _, ok := cache.directories.Load(symlinkPath); !ok {
|
|
set, _ := cache.directoriesByRealpath.LoadOrStore(realDirectory.RealPath, &collections.SyncSet[string]{})
|
|
set.Add(symlink)
|
|
}
|
|
}
|
|
cache.directories.Store(symlinkPath, realDirectory)
|
|
}
|
|
|
|
func (cache *KnownSymlinks) SetFile(symlink string, symlinkPath tspath.Path, realpath string) {
|
|
if _, ok := cache.files.Load(symlinkPath); !ok {
|
|
realpathPath := tspath.ToPath(realpath, cache.cwd, cache.useCaseSensitiveFileNames)
|
|
set, _ := cache.filesByRealpath.LoadOrStore(realpathPath, &collections.SyncSet[string]{})
|
|
set.Add(symlink)
|
|
}
|
|
cache.files.Store(symlinkPath, realpath)
|
|
}
|
|
|
|
func NewKnownSymlink(currentDirectory string, useCaseSensitiveFileNames bool) *KnownSymlinks {
|
|
return &KnownSymlinks{
|
|
cwd: currentDirectory,
|
|
useCaseSensitiveFileNames: useCaseSensitiveFileNames,
|
|
}
|
|
}
|
|
|
|
func (cache *KnownSymlinks) SetSymlinksFromResolutions(
|
|
forEachResolvedModule func(callback func(resolution *module.ResolvedModule, moduleName string, mode core.ResolutionMode, filePath tspath.Path), file *ast.SourceFile),
|
|
forEachResolvedTypeReferenceDirective func(callback func(resolution *module.ResolvedTypeReferenceDirective, moduleName string, mode core.ResolutionMode, filePath tspath.Path), file *ast.SourceFile),
|
|
) {
|
|
forEachResolvedModule(func(resolution *module.ResolvedModule, moduleName string, mode core.ResolutionMode, filePath tspath.Path) {
|
|
cache.ProcessResolution(resolution.OriginalPath, resolution.ResolvedFileName)
|
|
}, nil)
|
|
forEachResolvedTypeReferenceDirective(func(resolution *module.ResolvedTypeReferenceDirective, moduleName string, mode core.ResolutionMode, filePath tspath.Path) {
|
|
cache.ProcessResolution(resolution.OriginalPath, resolution.ResolvedFileName)
|
|
}, nil)
|
|
}
|
|
|
|
func (cache *KnownSymlinks) ProcessResolution(originalPath string, resolvedFileName string) {
|
|
if originalPath == "" || resolvedFileName == "" {
|
|
return
|
|
}
|
|
cache.SetFile(originalPath, tspath.ToPath(originalPath, cache.cwd, cache.useCaseSensitiveFileNames), resolvedFileName)
|
|
commonResolved, commonOriginal := cache.guessDirectorySymlink(resolvedFileName, originalPath, cache.cwd)
|
|
if commonResolved != "" && commonOriginal != "" {
|
|
symlinkPath := tspath.ToPath(commonOriginal, cache.cwd, cache.useCaseSensitiveFileNames)
|
|
if !tspath.ContainsIgnoredPath(string(symlinkPath)) {
|
|
cache.SetDirectory(
|
|
commonOriginal,
|
|
symlinkPath.EnsureTrailingDirectorySeparator(),
|
|
&KnownDirectoryLink{
|
|
Real: tspath.EnsureTrailingDirectorySeparator(commonResolved),
|
|
RealPath: tspath.ToPath(commonResolved, cache.cwd, cache.useCaseSensitiveFileNames).EnsureTrailingDirectorySeparator(),
|
|
},
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (cache *KnownSymlinks) guessDirectorySymlink(a string, b string, cwd string) (string, string) {
|
|
aParts := tspath.GetPathComponents(tspath.GetNormalizedAbsolutePath(a, cwd), "")
|
|
bParts := tspath.GetPathComponents(tspath.GetNormalizedAbsolutePath(b, cwd), "")
|
|
isDirectory := false
|
|
for len(aParts) >= 2 && len(bParts) >= 2 &&
|
|
!cache.isNodeModulesOrScopedPackageDirectory(aParts[len(aParts)-2]) &&
|
|
!cache.isNodeModulesOrScopedPackageDirectory(bParts[len(bParts)-2]) &&
|
|
tspath.GetCanonicalFileName(aParts[len(aParts)-1], cache.useCaseSensitiveFileNames) == tspath.GetCanonicalFileName(bParts[len(bParts)-1], cache.useCaseSensitiveFileNames) {
|
|
aParts = aParts[:len(aParts)-1]
|
|
bParts = bParts[:len(bParts)-1]
|
|
isDirectory = true
|
|
}
|
|
if isDirectory {
|
|
return tspath.GetPathFromPathComponents(aParts), tspath.GetPathFromPathComponents(bParts)
|
|
}
|
|
return "", ""
|
|
}
|
|
|
|
func (cache *KnownSymlinks) isNodeModulesOrScopedPackageDirectory(s string) bool {
|
|
return s != "" && (tspath.GetCanonicalFileName(s, cache.useCaseSensitiveFileNames) == "node_modules" || strings.HasPrefix(s, "@"))
|
|
}
|