227 lines
7.4 KiB
Go
227 lines
7.4 KiB
Go
package compiler
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/module"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/cachedvfs"
|
|
)
|
|
|
|
type projectReferenceDtsFakingHost struct {
|
|
host CompilerHost
|
|
fs *cachedvfs.FS
|
|
}
|
|
|
|
var _ module.ResolutionHost = (*projectReferenceDtsFakingHost)(nil)
|
|
|
|
func newProjectReferenceDtsFakingHost(loader *fileLoader) module.ResolutionHost {
|
|
// Create a new host that will fake the dts files
|
|
host := &projectReferenceDtsFakingHost{
|
|
host: loader.opts.Host,
|
|
fs: cachedvfs.From(&projectReferenceDtsFakingVfs{
|
|
projectReferenceFileMapper: loader.projectReferenceFileMapper,
|
|
dtsDirectories: loader.dtsDirectories,
|
|
knownSymlinks: knownSymlinks{},
|
|
}),
|
|
}
|
|
return host
|
|
}
|
|
|
|
// FS implements module.ResolutionHost.
|
|
func (h *projectReferenceDtsFakingHost) FS() vfs.FS {
|
|
return h.fs
|
|
}
|
|
|
|
// GetCurrentDirectory implements module.ResolutionHost.
|
|
func (h *projectReferenceDtsFakingHost) GetCurrentDirectory() string {
|
|
return h.host.GetCurrentDirectory()
|
|
}
|
|
|
|
type projectReferenceDtsFakingVfs struct {
|
|
projectReferenceFileMapper *projectReferenceFileMapper
|
|
dtsDirectories collections.Set[tspath.Path]
|
|
knownSymlinks knownSymlinks
|
|
}
|
|
|
|
var _ vfs.FS = (*projectReferenceDtsFakingVfs)(nil)
|
|
|
|
// UseCaseSensitiveFileNames implements vfs.FS.
|
|
func (fs *projectReferenceDtsFakingVfs) UseCaseSensitiveFileNames() bool {
|
|
return fs.projectReferenceFileMapper.opts.Host.FS().UseCaseSensitiveFileNames()
|
|
}
|
|
|
|
// FileExists implements vfs.FS.
|
|
func (fs *projectReferenceDtsFakingVfs) FileExists(path string) bool {
|
|
if fs.projectReferenceFileMapper.opts.Host.FS().FileExists(path) {
|
|
return true
|
|
}
|
|
if !tspath.IsDeclarationFileName(path) {
|
|
return false
|
|
}
|
|
// Project references go to source file instead of .d.ts file
|
|
return fs.fileOrDirectoryExistsUsingSource(path /*isFile*/, true)
|
|
}
|
|
|
|
// ReadFile implements vfs.FS.
|
|
func (fs *projectReferenceDtsFakingVfs) ReadFile(path string) (contents string, ok bool) {
|
|
// Dont need to override as we cannot mimick read file
|
|
return fs.projectReferenceFileMapper.opts.Host.FS().ReadFile(path)
|
|
}
|
|
|
|
// WriteFile implements vfs.FS.
|
|
func (fs *projectReferenceDtsFakingVfs) WriteFile(path string, data string, writeByteOrderMark bool) error {
|
|
panic("should not be called by resolver")
|
|
}
|
|
|
|
// Remove implements vfs.FS.
|
|
func (fs *projectReferenceDtsFakingVfs) Remove(path string) error {
|
|
panic("should not be called by resolver")
|
|
}
|
|
|
|
// Chtimes implements vfs.FS.
|
|
func (fs *projectReferenceDtsFakingVfs) Chtimes(path string, aTime time.Time, mTime time.Time) error {
|
|
panic("should not be called by resolver")
|
|
}
|
|
|
|
// DirectoryExists implements vfs.FS.
|
|
func (fs *projectReferenceDtsFakingVfs) DirectoryExists(path string) bool {
|
|
if fs.projectReferenceFileMapper.opts.Host.FS().DirectoryExists(path) {
|
|
fs.handleDirectoryCouldBeSymlink(path)
|
|
return true
|
|
}
|
|
return fs.fileOrDirectoryExistsUsingSource(path /*isFile*/, false)
|
|
}
|
|
|
|
// GetAccessibleEntries implements vfs.FS.
|
|
func (fs *projectReferenceDtsFakingVfs) GetAccessibleEntries(path string) vfs.Entries {
|
|
panic("should not be called by resolver")
|
|
}
|
|
|
|
// Stat implements vfs.FS.
|
|
func (fs *projectReferenceDtsFakingVfs) Stat(path string) vfs.FileInfo {
|
|
panic("should not be called by resolver")
|
|
}
|
|
|
|
// WalkDir implements vfs.FS.
|
|
func (fs *projectReferenceDtsFakingVfs) WalkDir(root string, walkFn vfs.WalkDirFunc) error {
|
|
panic("should not be called by resolver")
|
|
}
|
|
|
|
// Realpath implements vfs.FS.
|
|
func (fs *projectReferenceDtsFakingVfs) Realpath(path string) string {
|
|
result, ok := fs.knownSymlinks.Files().Load(fs.toPath(path))
|
|
if ok {
|
|
return result
|
|
}
|
|
return fs.projectReferenceFileMapper.opts.Host.FS().Realpath(path)
|
|
}
|
|
|
|
func (fs *projectReferenceDtsFakingVfs) toPath(path string) tspath.Path {
|
|
return tspath.ToPath(path, fs.projectReferenceFileMapper.opts.Host.GetCurrentDirectory(), fs.UseCaseSensitiveFileNames())
|
|
}
|
|
|
|
func (fs *projectReferenceDtsFakingVfs) handleDirectoryCouldBeSymlink(directory string) {
|
|
if tspath.ContainsIgnoredPath(directory) {
|
|
return
|
|
}
|
|
|
|
// Because we already watch node_modules, handle symlinks in there
|
|
if !strings.Contains(directory, "/node_modules/") {
|
|
return
|
|
}
|
|
|
|
directoryPath := tspath.Path(tspath.EnsureTrailingDirectorySeparator(string(fs.toPath(directory))))
|
|
if _, ok := fs.knownSymlinks.Directories().Load(directoryPath); ok {
|
|
return
|
|
}
|
|
|
|
realDirectory := fs.Realpath(directory)
|
|
var realPath tspath.Path
|
|
if realDirectory == directory {
|
|
// not symlinked
|
|
return
|
|
}
|
|
if realPath = tspath.Path(tspath.EnsureTrailingDirectorySeparator(string(fs.toPath(realDirectory)))); realPath == directoryPath {
|
|
// not symlinked
|
|
return
|
|
}
|
|
fs.knownSymlinks.SetDirectory(directory, directoryPath, &knownDirectoryLink{
|
|
Real: tspath.EnsureTrailingDirectorySeparator(realDirectory),
|
|
RealPath: realPath,
|
|
})
|
|
}
|
|
|
|
func (fs *projectReferenceDtsFakingVfs) fileOrDirectoryExistsUsingSource(fileOrDirectory string, isFile bool) bool {
|
|
fileOrDirectoryExistsUsingSource := core.IfElse(isFile, fs.fileExistsIfProjectReferenceDts, fs.directoryExistsIfProjectReferenceDeclDir)
|
|
// Check current directory or file
|
|
result := fileOrDirectoryExistsUsingSource(fileOrDirectory)
|
|
if result != core.TSUnknown {
|
|
return result == core.TSTrue
|
|
}
|
|
|
|
knownDirectoryLinks := fs.knownSymlinks.Directories()
|
|
if knownDirectoryLinks.Size() == 0 {
|
|
return false
|
|
}
|
|
fileOrDirectoryPath := fs.toPath(fileOrDirectory)
|
|
if !strings.Contains(string(fileOrDirectoryPath), "/node_modules/") {
|
|
return false
|
|
}
|
|
if isFile {
|
|
_, ok := fs.knownSymlinks.Files().Load(fileOrDirectoryPath)
|
|
if ok {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// If it contains node_modules check if its one of the symlinked path we know of
|
|
var exists bool
|
|
knownDirectoryLinks.Range(func(directoryPath tspath.Path, knownDirectoryLink *knownDirectoryLink) bool {
|
|
relative, hasPrefix := strings.CutPrefix(string(fileOrDirectoryPath), string(directoryPath))
|
|
if !hasPrefix {
|
|
return true
|
|
}
|
|
if exists = fileOrDirectoryExistsUsingSource(string(knownDirectoryLink.RealPath) + relative).IsTrue(); exists {
|
|
if isFile {
|
|
// Store the real path for the file
|
|
absolutePath := tspath.GetNormalizedAbsolutePath(fileOrDirectory, fs.projectReferenceFileMapper.opts.Host.GetCurrentDirectory())
|
|
fs.knownSymlinks.SetFile(
|
|
fileOrDirectoryPath,
|
|
knownDirectoryLink.Real+absolutePath[len(directoryPath):],
|
|
)
|
|
}
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
return exists
|
|
}
|
|
|
|
func (fs *projectReferenceDtsFakingVfs) fileExistsIfProjectReferenceDts(file string) core.Tristate {
|
|
source := fs.projectReferenceFileMapper.getProjectReferenceFromOutputDts(fs.toPath(file))
|
|
if source != nil {
|
|
return core.IfElse(fs.projectReferenceFileMapper.opts.Host.FS().FileExists(source.Source), core.TSTrue, core.TSFalse)
|
|
}
|
|
return core.TSUnknown
|
|
}
|
|
|
|
func (fs *projectReferenceDtsFakingVfs) directoryExistsIfProjectReferenceDeclDir(dir string) core.Tristate {
|
|
dirPath := fs.toPath(dir)
|
|
dirPathWithTrailingDirectorySeparator := dirPath + "/"
|
|
for declDirPath := range fs.dtsDirectories.Keys() {
|
|
if dirPath == declDirPath ||
|
|
// Any parent directory of declaration dir
|
|
strings.HasPrefix(string(declDirPath), string(dirPathWithTrailingDirectorySeparator)) ||
|
|
// Any directory inside declaration dir
|
|
strings.HasPrefix(string(dirPath), string(declDirPath)+"/") {
|
|
return core.TSTrue
|
|
}
|
|
}
|
|
return core.TSUnknown
|
|
}
|