vendor tsgo
This commit is contained in:
154
tools/tsgo/internal/vfs/cachedvfs/cachedvfs.go
Normal file
154
tools/tsgo/internal/vfs/cachedvfs/cachedvfs.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package cachedvfs
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/collections"
|
||||
"github.com/microsoft/typescript-go/internal/vfs"
|
||||
)
|
||||
|
||||
type FS struct {
|
||||
fs vfs.FS
|
||||
enabled atomic.Bool
|
||||
|
||||
directoryExistsCache collections.SyncMap[string, bool]
|
||||
fileExistsCache collections.SyncMap[string, bool]
|
||||
getAccessibleEntriesCache collections.SyncMap[string, vfs.Entries]
|
||||
realpathCache collections.SyncMap[string, string]
|
||||
statCache collections.SyncMap[string, vfs.FileInfo]
|
||||
}
|
||||
|
||||
var _ vfs.FS = (*FS)(nil)
|
||||
|
||||
func From(fs vfs.FS) *FS {
|
||||
fsys := &FS{fs: fs}
|
||||
fsys.enabled.Store(true)
|
||||
return fsys
|
||||
}
|
||||
|
||||
func (fsys *FS) DisableAndClearCache() {
|
||||
if fsys.enabled.CompareAndSwap(true, false) {
|
||||
fsys.ClearCache()
|
||||
}
|
||||
}
|
||||
|
||||
func (fsys *FS) Enable() {
|
||||
fsys.enabled.Store(true)
|
||||
}
|
||||
|
||||
func (fsys *FS) ClearCache() {
|
||||
fsys.directoryExistsCache.Clear()
|
||||
fsys.fileExistsCache.Clear()
|
||||
fsys.getAccessibleEntriesCache.Clear()
|
||||
fsys.realpathCache.Clear()
|
||||
fsys.statCache.Clear()
|
||||
}
|
||||
|
||||
func (fsys *FS) DirectoryExists(path string) bool {
|
||||
if fsys.enabled.Load() {
|
||||
if ret, ok := fsys.directoryExistsCache.Load(path); ok {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
ret := fsys.fs.DirectoryExists(path)
|
||||
|
||||
if fsys.enabled.Load() {
|
||||
fsys.directoryExistsCache.Store(path, ret)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (fsys *FS) FileExists(path string) bool {
|
||||
if fsys.enabled.Load() {
|
||||
if ret, ok := fsys.fileExistsCache.Load(path); ok {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
ret := fsys.fs.FileExists(path)
|
||||
|
||||
if fsys.enabled.Load() {
|
||||
fsys.fileExistsCache.Store(path, ret)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (fsys *FS) GetAccessibleEntries(path string) vfs.Entries {
|
||||
if fsys.enabled.Load() {
|
||||
if ret, ok := fsys.getAccessibleEntriesCache.Load(path); ok {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
ret := fsys.fs.GetAccessibleEntries(path)
|
||||
|
||||
if fsys.enabled.Load() {
|
||||
fsys.getAccessibleEntriesCache.Store(path, ret)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (fsys *FS) ReadFile(path string) (contents string, ok bool) {
|
||||
return fsys.fs.ReadFile(path)
|
||||
}
|
||||
|
||||
func (fsys *FS) Realpath(path string) string {
|
||||
if fsys.enabled.Load() {
|
||||
if ret, ok := fsys.realpathCache.Load(path); ok {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
ret := fsys.fs.Realpath(path)
|
||||
|
||||
if fsys.enabled.Load() {
|
||||
fsys.realpathCache.Store(path, ret)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (fsys *FS) Remove(path string) error {
|
||||
return fsys.fs.Remove(path)
|
||||
}
|
||||
|
||||
func (fsys *FS) Chtimes(path string, aTime time.Time, mTime time.Time) error {
|
||||
return fsys.fs.Chtimes(path, aTime, mTime)
|
||||
}
|
||||
|
||||
func (fsys *FS) Stat(path string) vfs.FileInfo {
|
||||
if fsys.enabled.Load() {
|
||||
if ret, ok := fsys.statCache.Load(path); ok {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
ret := fsys.fs.Stat(path)
|
||||
|
||||
if fsys.enabled.Load() {
|
||||
fsys.statCache.Store(path, ret)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (fsys *FS) UseCaseSensitiveFileNames() bool {
|
||||
return fsys.fs.UseCaseSensitiveFileNames()
|
||||
}
|
||||
|
||||
func (fsys *FS) WalkDir(root string, walkFn vfs.WalkDirFunc) error {
|
||||
return fsys.fs.WalkDir(root, walkFn)
|
||||
}
|
||||
|
||||
func (fsys *FS) WriteFile(path string, data string) error {
|
||||
return fsys.fs.WriteFile(path, data)
|
||||
}
|
||||
|
||||
func (fsys *FS) AppendFile(path string, data string) error {
|
||||
return fsys.fs.AppendFile(path, data)
|
||||
}
|
||||
350
tools/tsgo/internal/vfs/cachedvfs/cachedvfs_test.go
Normal file
350
tools/tsgo/internal/vfs/cachedvfs/cachedvfs_test.go
Normal file
@@ -0,0 +1,350 @@
|
||||
package cachedvfs_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/vfs"
|
||||
"github.com/microsoft/typescript-go/internal/vfs/cachedvfs"
|
||||
"github.com/microsoft/typescript-go/internal/vfs/vfsmock"
|
||||
"github.com/microsoft/typescript-go/internal/vfs/vfstest"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func createMockFS() *vfsmock.FSMock {
|
||||
return vfsmock.Wrap(vfstest.FromMap(map[string]string{
|
||||
"/some/path/file.txt": "hello world",
|
||||
}, true))
|
||||
}
|
||||
|
||||
func TestDirectoryExists(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
underlying := createMockFS()
|
||||
cached := cachedvfs.From(underlying)
|
||||
|
||||
cached.DirectoryExists("/some/path")
|
||||
assert.Equal(t, 1, len(underlying.DirectoryExistsCalls()))
|
||||
|
||||
cached.DirectoryExists("/some/path")
|
||||
assert.Equal(t, 1, len(underlying.DirectoryExistsCalls()))
|
||||
|
||||
cached.ClearCache()
|
||||
cached.DirectoryExists("/some/path")
|
||||
assert.Equal(t, 2, len(underlying.DirectoryExistsCalls()))
|
||||
|
||||
cached.DirectoryExists("/other/path")
|
||||
assert.Equal(t, 3, len(underlying.DirectoryExistsCalls()))
|
||||
|
||||
cached.DisableAndClearCache()
|
||||
cached.DirectoryExists("/some/path")
|
||||
assert.Equal(t, 4, len(underlying.DirectoryExistsCalls()))
|
||||
|
||||
cached.DirectoryExists("/some/path")
|
||||
assert.Equal(t, 5, len(underlying.DirectoryExistsCalls()))
|
||||
|
||||
cached.Enable()
|
||||
cached.DirectoryExists("/some/path")
|
||||
assert.Equal(t, 6, len(underlying.DirectoryExistsCalls()))
|
||||
|
||||
cached.DirectoryExists("/some/path")
|
||||
assert.Equal(t, 6, len(underlying.DirectoryExistsCalls()))
|
||||
}
|
||||
|
||||
func TestFileExists(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
underlying := createMockFS()
|
||||
cached := cachedvfs.From(underlying)
|
||||
|
||||
cached.FileExists("/some/path/file.txt")
|
||||
assert.Equal(t, 1, len(underlying.FileExistsCalls()))
|
||||
|
||||
cached.FileExists("/some/path/file.txt")
|
||||
assert.Equal(t, 1, len(underlying.FileExistsCalls()))
|
||||
|
||||
cached.ClearCache()
|
||||
cached.FileExists("/some/path/file.txt")
|
||||
assert.Equal(t, 2, len(underlying.FileExistsCalls()))
|
||||
|
||||
cached.FileExists("/other/path/file.txt")
|
||||
assert.Equal(t, 3, len(underlying.FileExistsCalls()))
|
||||
|
||||
cached.DisableAndClearCache()
|
||||
cached.FileExists("/some/path/file.txt")
|
||||
assert.Equal(t, 4, len(underlying.FileExistsCalls()))
|
||||
|
||||
cached.FileExists("/some/path/file.txt")
|
||||
assert.Equal(t, 5, len(underlying.FileExistsCalls()))
|
||||
|
||||
cached.Enable()
|
||||
cached.FileExists("/some/path/file.txt")
|
||||
assert.Equal(t, 6, len(underlying.FileExistsCalls()))
|
||||
|
||||
cached.FileExists("/some/path/file.txt")
|
||||
assert.Equal(t, 6, len(underlying.FileExistsCalls()))
|
||||
}
|
||||
|
||||
func TestGetAccessibleEntries(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
underlying := createMockFS()
|
||||
cached := cachedvfs.From(underlying)
|
||||
|
||||
cached.GetAccessibleEntries("/some/path")
|
||||
assert.Equal(t, 1, len(underlying.GetAccessibleEntriesCalls()))
|
||||
|
||||
cached.GetAccessibleEntries("/some/path")
|
||||
assert.Equal(t, 1, len(underlying.GetAccessibleEntriesCalls()))
|
||||
|
||||
cached.ClearCache()
|
||||
cached.GetAccessibleEntries("/some/path")
|
||||
assert.Equal(t, 2, len(underlying.GetAccessibleEntriesCalls()))
|
||||
|
||||
cached.GetAccessibleEntries("/other/path")
|
||||
assert.Equal(t, 3, len(underlying.GetAccessibleEntriesCalls()))
|
||||
|
||||
cached.DisableAndClearCache()
|
||||
cached.GetAccessibleEntries("/some/path")
|
||||
assert.Equal(t, 4, len(underlying.GetAccessibleEntriesCalls()))
|
||||
|
||||
cached.GetAccessibleEntries("/some/path")
|
||||
assert.Equal(t, 5, len(underlying.GetAccessibleEntriesCalls()))
|
||||
|
||||
cached.Enable()
|
||||
cached.GetAccessibleEntries("/some/path")
|
||||
assert.Equal(t, 6, len(underlying.GetAccessibleEntriesCalls()))
|
||||
|
||||
cached.GetAccessibleEntries("/some/path")
|
||||
assert.Equal(t, 6, len(underlying.GetAccessibleEntriesCalls()))
|
||||
}
|
||||
|
||||
func TestRealpath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
underlying := createMockFS()
|
||||
cached := cachedvfs.From(underlying)
|
||||
|
||||
cached.Realpath("/some/path")
|
||||
assert.Equal(t, 1, len(underlying.RealpathCalls()))
|
||||
|
||||
cached.Realpath("/some/path")
|
||||
assert.Equal(t, 1, len(underlying.RealpathCalls()))
|
||||
|
||||
cached.ClearCache()
|
||||
cached.Realpath("/some/path")
|
||||
assert.Equal(t, 2, len(underlying.RealpathCalls()))
|
||||
|
||||
cached.Realpath("/other/path")
|
||||
assert.Equal(t, 3, len(underlying.RealpathCalls()))
|
||||
|
||||
cached.DisableAndClearCache()
|
||||
cached.Realpath("/some/path")
|
||||
assert.Equal(t, 4, len(underlying.RealpathCalls()))
|
||||
|
||||
cached.Realpath("/some/path")
|
||||
assert.Equal(t, 5, len(underlying.RealpathCalls()))
|
||||
|
||||
cached.Enable()
|
||||
cached.Realpath("/some/path")
|
||||
assert.Equal(t, 6, len(underlying.RealpathCalls()))
|
||||
|
||||
cached.Realpath("/some/path")
|
||||
assert.Equal(t, 6, len(underlying.RealpathCalls()))
|
||||
}
|
||||
|
||||
func TestStat(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
underlying := createMockFS()
|
||||
cached := cachedvfs.From(underlying)
|
||||
|
||||
cached.Stat("/some/path")
|
||||
assert.Equal(t, 1, len(underlying.StatCalls()))
|
||||
|
||||
cached.Stat("/some/path")
|
||||
assert.Equal(t, 1, len(underlying.StatCalls()))
|
||||
|
||||
cached.ClearCache()
|
||||
cached.Stat("/some/path")
|
||||
assert.Equal(t, 2, len(underlying.StatCalls()))
|
||||
|
||||
cached.Stat("/other/path")
|
||||
assert.Equal(t, 3, len(underlying.StatCalls()))
|
||||
|
||||
cached.DisableAndClearCache()
|
||||
cached.Stat("/some/path")
|
||||
assert.Equal(t, 4, len(underlying.StatCalls()))
|
||||
|
||||
cached.Stat("/some/path")
|
||||
assert.Equal(t, 5, len(underlying.StatCalls()))
|
||||
|
||||
cached.Enable()
|
||||
cached.Stat("/some/path")
|
||||
assert.Equal(t, 6, len(underlying.StatCalls()))
|
||||
|
||||
cached.Stat("/some/path")
|
||||
assert.Equal(t, 6, len(underlying.StatCalls()))
|
||||
}
|
||||
|
||||
func TestReadFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
underlying := createMockFS()
|
||||
cached := cachedvfs.From(underlying)
|
||||
|
||||
cached.ReadFile("/some/path/file.txt")
|
||||
assert.Equal(t, 1, len(underlying.ReadFileCalls()))
|
||||
|
||||
cached.ReadFile("/some/path/file.txt")
|
||||
assert.Equal(t, 2, len(underlying.ReadFileCalls()))
|
||||
|
||||
cached.ClearCache()
|
||||
cached.ReadFile("/some/path/file.txt")
|
||||
assert.Equal(t, 3, len(underlying.ReadFileCalls()))
|
||||
|
||||
cached.DisableAndClearCache()
|
||||
cached.ReadFile("/some/path/file.txt")
|
||||
assert.Equal(t, 4, len(underlying.ReadFileCalls()))
|
||||
|
||||
cached.ReadFile("/some/path/file.txt")
|
||||
assert.Equal(t, 5, len(underlying.ReadFileCalls()))
|
||||
|
||||
cached.Enable()
|
||||
cached.ReadFile("/some/path/file.txt")
|
||||
assert.Equal(t, 6, len(underlying.ReadFileCalls()))
|
||||
|
||||
cached.ReadFile("/some/path/file.txt")
|
||||
assert.Equal(t, 7, len(underlying.ReadFileCalls()))
|
||||
}
|
||||
|
||||
func TestUseCaseSensitiveFileNames(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
underlying := createMockFS()
|
||||
cached := cachedvfs.From(underlying)
|
||||
|
||||
cached.UseCaseSensitiveFileNames()
|
||||
assert.Equal(t, 1, len(underlying.UseCaseSensitiveFileNamesCalls()))
|
||||
|
||||
cached.UseCaseSensitiveFileNames()
|
||||
assert.Equal(t, 2, len(underlying.UseCaseSensitiveFileNamesCalls()))
|
||||
|
||||
cached.ClearCache()
|
||||
cached.UseCaseSensitiveFileNames()
|
||||
assert.Equal(t, 3, len(underlying.UseCaseSensitiveFileNamesCalls()))
|
||||
|
||||
cached.DisableAndClearCache()
|
||||
cached.UseCaseSensitiveFileNames()
|
||||
assert.Equal(t, 4, len(underlying.UseCaseSensitiveFileNamesCalls()))
|
||||
|
||||
cached.UseCaseSensitiveFileNames()
|
||||
assert.Equal(t, 5, len(underlying.UseCaseSensitiveFileNamesCalls()))
|
||||
|
||||
cached.Enable()
|
||||
cached.UseCaseSensitiveFileNames()
|
||||
assert.Equal(t, 6, len(underlying.UseCaseSensitiveFileNamesCalls()))
|
||||
|
||||
cached.UseCaseSensitiveFileNames()
|
||||
assert.Equal(t, 7, len(underlying.UseCaseSensitiveFileNamesCalls()))
|
||||
}
|
||||
|
||||
func TestWalkDir(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
underlying := createMockFS()
|
||||
cached := cachedvfs.From(underlying)
|
||||
|
||||
walkFn := vfs.WalkDirFunc(func(path string, info vfs.DirEntry, err error) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
_ = cached.WalkDir("/some/path", walkFn)
|
||||
assert.Equal(t, 1, len(underlying.WalkDirCalls()))
|
||||
|
||||
_ = cached.WalkDir("/some/path", walkFn)
|
||||
assert.Equal(t, 2, len(underlying.WalkDirCalls()))
|
||||
|
||||
cached.ClearCache()
|
||||
_ = cached.WalkDir("/some/path", walkFn)
|
||||
assert.Equal(t, 3, len(underlying.WalkDirCalls()))
|
||||
|
||||
cached.DisableAndClearCache()
|
||||
_ = cached.WalkDir("/some/path", walkFn)
|
||||
assert.Equal(t, 4, len(underlying.WalkDirCalls()))
|
||||
|
||||
_ = cached.WalkDir("/some/path", walkFn)
|
||||
assert.Equal(t, 5, len(underlying.WalkDirCalls()))
|
||||
|
||||
cached.Enable()
|
||||
_ = cached.WalkDir("/some/path", walkFn)
|
||||
assert.Equal(t, 6, len(underlying.WalkDirCalls()))
|
||||
|
||||
_ = cached.WalkDir("/some/path", walkFn)
|
||||
assert.Equal(t, 7, len(underlying.WalkDirCalls()))
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
underlying := createMockFS()
|
||||
cached := cachedvfs.From(underlying)
|
||||
|
||||
_ = cached.Remove("/some/path/file.txt")
|
||||
assert.Equal(t, 1, len(underlying.RemoveCalls()))
|
||||
|
||||
_ = cached.Remove("/some/path/file.txt")
|
||||
assert.Equal(t, 2, len(underlying.RemoveCalls()))
|
||||
|
||||
cached.ClearCache()
|
||||
_ = cached.Remove("/some/path/file.txt")
|
||||
assert.Equal(t, 3, len(underlying.RemoveCalls()))
|
||||
|
||||
cached.DisableAndClearCache()
|
||||
_ = cached.Remove("/some/path/file.txt")
|
||||
assert.Equal(t, 4, len(underlying.RemoveCalls()))
|
||||
|
||||
_ = cached.Remove("/some/path/file.txt")
|
||||
assert.Equal(t, 5, len(underlying.RemoveCalls()))
|
||||
|
||||
cached.Enable()
|
||||
_ = cached.Remove("/some/path/file.txt")
|
||||
assert.Equal(t, 6, len(underlying.RemoveCalls()))
|
||||
|
||||
_ = cached.Remove("/some/path/file.txt")
|
||||
assert.Equal(t, 7, len(underlying.RemoveCalls()))
|
||||
}
|
||||
|
||||
func TestWriteFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
underlying := createMockFS()
|
||||
cached := cachedvfs.From(underlying)
|
||||
|
||||
_ = cached.WriteFile("/some/path/file.txt", "new content")
|
||||
assert.Equal(t, 1, len(underlying.WriteFileCalls()))
|
||||
|
||||
_ = cached.WriteFile("/some/path/file.txt", "another content")
|
||||
assert.Equal(t, 2, len(underlying.WriteFileCalls()))
|
||||
|
||||
cached.ClearCache()
|
||||
_ = cached.WriteFile("/some/path/file.txt", "third content")
|
||||
assert.Equal(t, 3, len(underlying.WriteFileCalls()))
|
||||
|
||||
call := underlying.WriteFileCalls()[2]
|
||||
assert.Equal(t, "/some/path/file.txt", call.Path)
|
||||
assert.Equal(t, "third content", call.Data)
|
||||
|
||||
cached.DisableAndClearCache()
|
||||
_ = cached.WriteFile("/some/path/file.txt", "fourth content")
|
||||
assert.Equal(t, 4, len(underlying.WriteFileCalls()))
|
||||
|
||||
_ = cached.WriteFile("/some/path/file.txt", "fifth content")
|
||||
assert.Equal(t, 5, len(underlying.WriteFileCalls()))
|
||||
|
||||
cached.Enable()
|
||||
_ = cached.WriteFile("/some/path/file.txt", "sixth content")
|
||||
assert.Equal(t, 6, len(underlying.WriteFileCalls()))
|
||||
|
||||
_ = cached.WriteFile("/some/path/file.txt", "seventh content")
|
||||
assert.Equal(t, 7, len(underlying.WriteFileCalls()))
|
||||
}
|
||||
Reference in New Issue
Block a user