diff --git a/cmd/tsgo/main.go b/cmd/tsgo/main.go index f40ed39bd8..595b220da3 100644 --- a/cmd/tsgo/main.go +++ b/cmd/tsgo/main.go @@ -331,8 +331,8 @@ func getFormatOpts(host ts.CompilerHost) *diagnosticwriter.FormattingOptions { return &diagnosticwriter.FormattingOptions{ NewLine: host.NewLine(), ComparePathsOptions: tspath.ComparePathsOptions{ - CurrentDirectory: host.GetCurrentDirectory(), - UseCaseSensitiveFileNames: host.FS().UseCaseSensitiveFileNames(), + CurrentDirectory: host.GetCurrentDirectory(), + CaseSensitivity: host.FS().CaseSensitivity(), }, } } diff --git a/internal/binder/binder_test.go b/internal/binder/binder_test.go index 8518509df3..878203d2c6 100644 --- a/internal/binder/binder_test.go +++ b/internal/binder/binder_test.go @@ -19,7 +19,7 @@ func BenchmarkBind(b *testing.B) { f.SkipIfNotExist(b) fileName := tspath.GetNormalizedAbsolutePath(f.Path(), "/") - path := tspath.ToPath(fileName, "/", osvfs.FS().UseCaseSensitiveFileNames()) + path := tspath.ToPath(fileName, "/", osvfs.FS().CaseSensitivity()) sourceText := f.ReadFile(b) sourceFiles := make([]*ast.SourceFile, b.N) diff --git a/internal/bundled/embed.go b/internal/bundled/embed.go index 82ba6f9634..e1d915605a 100644 --- a/internal/bundled/embed.go +++ b/internal/bundled/embed.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" ) @@ -37,8 +38,8 @@ func wrapFS(fs vfs.FS) vfs.FS { return &wrappedFS{fs: fs} } -func (vfs *wrappedFS) UseCaseSensitiveFileNames() bool { - return vfs.fs.UseCaseSensitiveFileNames() +func (vfs *wrappedFS) CaseSensitivity() tspath.CaseSensitivity { + return vfs.fs.CaseSensitivity() } func (vfs *wrappedFS) FileExists(path string) bool { diff --git a/internal/checker/checker_test.go b/internal/checker/checker_test.go index cbf97b5ee9..6a3e2504b4 100644 --- a/internal/checker/checker_test.go +++ b/internal/checker/checker_test.go @@ -28,7 +28,7 @@ foo.bar;` "compilerOptions": {} } `, - }, false /*useCaseSensitiveFileNames*/) + }, tspath.CaseInsensitive) fs = bundled.WrapFS(fs) cd := "/" diff --git a/internal/compiler/emitHost.go b/internal/compiler/emitHost.go index 01445eeb10..7c21baefe1 100644 --- a/internal/compiler/emitHost.go +++ b/internal/compiler/emitHost.go @@ -19,7 +19,7 @@ type WriteFileData struct { type EmitHost interface { Options() *core.CompilerOptions SourceFiles() []*ast.SourceFile - UseCaseSensitiveFileNames() bool + CaseSensitivity() tspath.CaseSensitivity GetCurrentDirectory() string CommonSourceDirectory() string IsEmitBlocked(file string) bool @@ -39,8 +39,8 @@ func (host *emitHost) Options() *core.CompilerOptions { return host.program.Opti func (host *emitHost) SourceFiles() []*ast.SourceFile { return host.program.SourceFiles() } func (host *emitHost) GetCurrentDirectory() string { return host.program.host.GetCurrentDirectory() } func (host *emitHost) CommonSourceDirectory() string { return host.program.CommonSourceDirectory() } -func (host *emitHost) UseCaseSensitiveFileNames() bool { - return host.program.host.FS().UseCaseSensitiveFileNames() +func (host *emitHost) CaseSensitivity() tspath.CaseSensitivity { + return host.program.host.FS().CaseSensitivity() } func (host *emitHost) IsEmitBlocked(file string) bool { diff --git a/internal/compiler/emitter.go b/internal/compiler/emitter.go index 2970a179bf..d486e598da 100644 --- a/internal/compiler/emitter.go +++ b/internal/compiler/emitter.go @@ -157,8 +157,8 @@ func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, s getSourceRoot(options), e.getSourceMapDirectory(options, jsFilePath, sourceFile), tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: e.host.UseCaseSensitiveFileNames(), - CurrentDirectory: e.host.GetCurrentDirectory(), + CaseSensitivity: e.host.CaseSensitivity(), + CurrentDirectory: e.host.GetCurrentDirectory(), }, ) } @@ -219,12 +219,12 @@ func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, s return !data.SkippedDtsWrite } -func getSourceFilePathInNewDir(fileName string, newDirPath string, currentDirectory string, commonSourceDirectory string, useCaseSensitiveFileNames bool) string { +func getSourceFilePathInNewDir(fileName string, newDirPath string, currentDirectory string, commonSourceDirectory string, caseSensitivity tspath.CaseSensitivity) string { sourceFilePath := tspath.GetNormalizedAbsolutePath(fileName, currentDirectory) commonSourceDirectory = tspath.EnsureTrailingDirectorySeparator(commonSourceDirectory) isSourceFileInCommonSourceDirectory := tspath.ContainsPath(commonSourceDirectory, sourceFilePath, tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: useCaseSensitiveFileNames, - CurrentDirectory: currentDirectory, + CaseSensitivity: caseSensitivity, + CurrentDirectory: currentDirectory, }) if isSourceFileInCommonSourceDirectory { sourceFilePath = sourceFilePath[len(commonSourceDirectory):] @@ -242,7 +242,7 @@ func getOwnEmitOutputFilePath(fileName string, host EmitHost, extension string) compilerOptions.OutDir, currentDirectory, host.CommonSourceDirectory(), - host.UseCaseSensitiveFileNames(), + host.CaseSensitivity(), )) } else { emitOutputFilePathWithoutExtension = tspath.RemoveFileExtension(fileName) @@ -286,7 +286,7 @@ func (e *emitter) getSourceMapDirectory(mapOptions *core.CompilerOptions, filePa sourceMapDir, e.host.GetCurrentDirectory(), e.host.CommonSourceDirectory(), - e.host.UseCaseSensitiveFileNames(), + e.host.CaseSensitivity(), )) } if tspath.GetRootLength(sourceMapDir) == 0 { @@ -317,7 +317,7 @@ func (e *emitter) getSourceMappingURL(mapOptions *core.CompilerOptions, sourceMa sourceMapDir, e.host.GetCurrentDirectory(), e.host.CommonSourceDirectory(), - e.host.UseCaseSensitiveFileNames(), + e.host.CaseSensitivity(), )) } if tspath.GetRootLength(sourceMapDir) == 0 { @@ -329,8 +329,8 @@ func (e *emitter) getSourceMappingURL(mapOptions *core.CompilerOptions, sourceMa tspath.CombinePaths(sourceMapDir, sourceMapFile), // this is where user expects to see sourceMap /*isAbsolutePathAnUrl*/ true, tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: e.host.UseCaseSensitiveFileNames(), - CurrentDirectory: e.host.GetCurrentDirectory(), + CaseSensitivity: e.host.CaseSensitivity(), + CurrentDirectory: e.host.GetCurrentDirectory(), }, ), ) @@ -362,8 +362,8 @@ func getOutputPathsFor(sourceFile *ast.SourceFile, host EmitHost, forceDtsEmit b // If json file emits to the same location skip writing it, if emitDeclarationOnly skip writing it isJsonEmittedToSameLocation := isJsonFile && tspath.ComparePaths(sourceFile.FileName(), ownOutputFilePath, tspath.ComparePathsOptions{ - CurrentDirectory: host.GetCurrentDirectory(), - UseCaseSensitiveFileNames: host.UseCaseSensitiveFileNames(), + CurrentDirectory: host.GetCurrentDirectory(), + CaseSensitivity: host.CaseSensitivity(), }) == 0 paths := &outputPaths{} if options.EmitDeclarationOnly != core.TSTrue && !isJsonEmittedToSameLocation { diff --git a/internal/compiler/fileloader.go b/internal/compiler/fileloader.go index b07bf50f41..0c6ee983c7 100644 --- a/internal/compiler/fileloader.go +++ b/internal/compiler/fileloader.go @@ -54,8 +54,8 @@ func processAllProgramFiles( resolver: resolver, defaultLibraryPath: tspath.GetNormalizedAbsolutePath(host.DefaultLibraryPath(), host.GetCurrentDirectory()), comparePathsOptions: tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: host.GetCurrentDirectory(), + CaseSensitivity: host.FS().CaseSensitivity(), + CurrentDirectory: host.GetCurrentDirectory(), }, wg: core.NewWorkGroup(programOptions.SingleThreaded), rootTasks: make([]*parseTask, 0, len(rootFiles)+len(libs)), @@ -266,7 +266,7 @@ func (p *fileLoader) loadSourceFileMetaData(path tspath.Path) *ast.SourceFileMet } func (p *fileLoader) parseSourceFile(fileName string) *ast.SourceFile { - path := tspath.ToPath(fileName, p.host.GetCurrentDirectory(), p.host.FS().UseCaseSensitiveFileNames()) + path := tspath.ToPath(fileName, p.host.GetCurrentDirectory(), p.host.FS().CaseSensitivity()) sourceFile := p.host.GetSourceFile(fileName, path, p.compilerOptions.GetEmitScriptTarget()) return sourceFile } diff --git a/internal/compiler/module/cache.go b/internal/compiler/module/cache.go index 0f576c5285..962ba7984c 100644 --- a/internal/compiler/module/cache.go +++ b/internal/compiler/module/cache.go @@ -26,26 +26,26 @@ type caches struct { func newCaches( currentDirectory string, - useCaseSensitiveFileNames bool, + caseSensitivity tspath.CaseSensitivity, options *core.CompilerOptions, ) caches { optionsToRedirectsKey := make(map[*core.CompilerOptions]string) getOriginalOrResolvedModuleFileName := func(result *ResolvedModule) tspath.Path { if result.OriginalPath != "" { - return tspath.ToPath(result.OriginalPath, currentDirectory, useCaseSensitiveFileNames) + return tspath.ToPath(result.OriginalPath, currentDirectory, caseSensitivity) } - return tspath.ToPath(result.ResolvedFileName, currentDirectory, useCaseSensitiveFileNames) + return tspath.ToPath(result.ResolvedFileName, currentDirectory, caseSensitivity) } getOriginalOrResolvedTypeReferenceFileName := func(result *ResolvedTypeReferenceDirective) tspath.Path { if result.OriginalPath != "" { - return tspath.ToPath(result.OriginalPath, currentDirectory, useCaseSensitiveFileNames) + return tspath.ToPath(result.OriginalPath, currentDirectory, caseSensitivity) } - return tspath.ToPath(result.ResolvedFileName, currentDirectory, useCaseSensitiveFileNames) + return tspath.ToPath(result.ResolvedFileName, currentDirectory, caseSensitivity) } return caches{ - moduleNameCache: newResolutionCache(currentDirectory, useCaseSensitiveFileNames, options, getOriginalOrResolvedModuleFileName, optionsToRedirectsKey, false /*isReadonly*/), - typeReferenceDirectiveCache: newResolutionCache(currentDirectory, useCaseSensitiveFileNames, options, getOriginalOrResolvedTypeReferenceFileName, optionsToRedirectsKey, false /*isReadonly*/), - packageJsonInfoCache: packagejson.NewInfoCache(currentDirectory, useCaseSensitiveFileNames), + moduleNameCache: newResolutionCache(currentDirectory, caseSensitivity, options, getOriginalOrResolvedModuleFileName, optionsToRedirectsKey, false /*isReadonly*/), + typeReferenceDirectiveCache: newResolutionCache(currentDirectory, caseSensitivity, options, getOriginalOrResolvedTypeReferenceFileName, optionsToRedirectsKey, false /*isReadonly*/), + packageJsonInfoCache: packagejson.NewInfoCache(currentDirectory, caseSensitivity), } } @@ -59,15 +59,15 @@ type resolutionCache[T comparable] struct { func newResolutionCache[T comparable]( currentDirectory string, - useCaseSensitiveFileNames bool, + caseSensitivity tspath.CaseSensitivity, options *core.CompilerOptions, getResolvedFileName func(result T) tspath.Path, optionsToRedirectsKey map[*core.CompilerOptions]string, isReadonly bool, ) *resolutionCache[T] { return &resolutionCache[T]{ - perDirectoryResolutionCache: newPerDirectoryResolutionCache[T](currentDirectory, useCaseSensitiveFileNames, options, optionsToRedirectsKey), - nonRelativeNameResolutionCache: newNonRelativeNameResolutionCache(currentDirectory, useCaseSensitiveFileNames, options, getResolvedFileName, optionsToRedirectsKey), + perDirectoryResolutionCache: newPerDirectoryResolutionCache[T](currentDirectory, caseSensitivity, options, optionsToRedirectsKey), + nonRelativeNameResolutionCache: newNonRelativeNameResolutionCache(currentDirectory, caseSensitivity, options, getResolvedFileName, optionsToRedirectsKey), isReadonly: isReadonly, } } @@ -101,31 +101,31 @@ func (c *resolutionCache[T]) appendLookupLocations(resolved T, failedLookupLocat } type perDirectoryResolutionCache[T any] struct { - mu sync.RWMutex - currentDirectory string - useCaseSensitiveFileNames bool - options *core.CompilerOptions - directoryToModuleNameMap *cacheWithRedirects[tspath.Path, ModeAwareCache[T]] + mu sync.RWMutex + currentDirectory string + caseSensitivity tspath.CaseSensitivity + options *core.CompilerOptions + directoryToModuleNameMap *cacheWithRedirects[tspath.Path, ModeAwareCache[T]] } func newPerDirectoryResolutionCache[T any]( currentDirectory string, - useCaseSensitiveFileNames bool, + caseSensitivity tspath.CaseSensitivity, options *core.CompilerOptions, optionsToRedirectsKey map[*core.CompilerOptions]string, ) perDirectoryResolutionCache[T] { return perDirectoryResolutionCache[T]{ - currentDirectory: currentDirectory, - useCaseSensitiveFileNames: useCaseSensitiveFileNames, - options: options, - directoryToModuleNameMap: newCacheWithRedirects[tspath.Path, ModeAwareCache[T]](options, optionsToRedirectsKey), + currentDirectory: currentDirectory, + caseSensitivity: caseSensitivity, + options: options, + directoryToModuleNameMap: newCacheWithRedirects[tspath.Path, ModeAwareCache[T]](options, optionsToRedirectsKey), } } func (c *perDirectoryResolutionCache[T]) getFromDirectoryCache(nameAndMode ModeAwareCacheKey, directory string, redirectedReference *ResolvedProjectReference) (T, bool) { c.mu.RLock() defer c.mu.RUnlock() - result, ok := c.directoryToModuleNameMap.getMapOfCacheRedirects(redirectedReference)[tspath.ToPath(directory, c.currentDirectory, c.useCaseSensitiveFileNames)][nameAndMode] + result, ok := c.directoryToModuleNameMap.getMapOfCacheRedirects(redirectedReference)[tspath.ToPath(directory, c.currentDirectory, c.caseSensitivity)][nameAndMode] return result, ok } @@ -139,7 +139,7 @@ func (c *perDirectoryResolutionCache[T]) setInDirectoryCache(nameAndMode ModeAwa func (c *perDirectoryResolutionCache[T]) getOrCreateCacheForDirectory(directory string, redirectedReference *ResolvedProjectReference) ModeAwareCache[T] { c.mu.RLock() cache := c.directoryToModuleNameMap.getOrCreateMapOfCacheRedirects(redirectedReference) - key := tspath.ToPath(directory, c.currentDirectory, c.useCaseSensitiveFileNames) + key := tspath.ToPath(directory, c.currentDirectory, c.caseSensitivity) result, ok := cache[key] c.mu.RUnlock() if ok { @@ -154,27 +154,27 @@ func (c *perDirectoryResolutionCache[T]) getOrCreateCacheForDirectory(directory } type nonRelativeNameResolutionCache[T any] struct { - mu sync.RWMutex - currentDirectory string - useCaseSensitiveFileNames bool - options *core.CompilerOptions - getResolvedFileName func(result T) tspath.Path - moduleNameToDirectoryMap *cacheWithRedirects[ModeAwareCacheKey, *perNonRelativeNameCache[T]] + mu sync.RWMutex + currentDirectory string + caseSensitivity tspath.CaseSensitivity + options *core.CompilerOptions + getResolvedFileName func(result T) tspath.Path + moduleNameToDirectoryMap *cacheWithRedirects[ModeAwareCacheKey, *perNonRelativeNameCache[T]] } func newNonRelativeNameResolutionCache[T any]( currentDirectory string, - useCaseSensitiveFileNames bool, + caseSensitivity tspath.CaseSensitivity, options *core.CompilerOptions, getResolvedFileName func(result T) tspath.Path, optionsToRedirectsKey map[*core.CompilerOptions]string, ) nonRelativeNameResolutionCache[T] { return nonRelativeNameResolutionCache[T]{ - currentDirectory: currentDirectory, - useCaseSensitiveFileNames: useCaseSensitiveFileNames, - options: options, - getResolvedFileName: getResolvedFileName, - moduleNameToDirectoryMap: newCacheWithRedirects[ModeAwareCacheKey, *perNonRelativeNameCache[T]](options, optionsToRedirectsKey), + currentDirectory: currentDirectory, + caseSensitivity: caseSensitivity, + options: options, + getResolvedFileName: getResolvedFileName, + moduleNameToDirectoryMap: newCacheWithRedirects[ModeAwareCacheKey, *perNonRelativeNameCache[T]](options, optionsToRedirectsKey), } } @@ -184,7 +184,7 @@ func (c *nonRelativeNameResolutionCache[T]) getFromNonRelativeNameCache(nameAndM } c.mu.RLock() defer c.mu.RUnlock() - return c.moduleNameToDirectoryMap.getMapOfCacheRedirects(redirectedReference)[nameAndMode].get(tspath.ToPath(directoryName, c.currentDirectory, c.useCaseSensitiveFileNames)) + return c.moduleNameToDirectoryMap.getMapOfCacheRedirects(redirectedReference)[nameAndMode].get(tspath.ToPath(directoryName, c.currentDirectory, c.caseSensitivity)) } func (c *nonRelativeNameResolutionCache[T]) setInNonRelativeNameCache(nameAndMode ModeAwareCacheKey, directoryName string, value T, redirectedReference *ResolvedProjectReference) { @@ -194,7 +194,7 @@ func (c *nonRelativeNameResolutionCache[T]) setInNonRelativeNameCache(nameAndMod cache := c.getOrCreateCacheForNonRelativeName(nameAndMode, redirectedReference) c.mu.Lock() defer c.mu.Unlock() - cache.set(tspath.ToPath(directoryName, c.currentDirectory, c.useCaseSensitiveFileNames), value, c.getResolvedFileName) + cache.set(tspath.ToPath(directoryName, c.currentDirectory, c.caseSensitivity), value, c.getResolvedFileName) } func (c *nonRelativeNameResolutionCache[T]) getOrCreateCacheForNonRelativeName(nameAndMode ModeAwareCacheKey, redirectedReference *ResolvedProjectReference) *perNonRelativeNameCache[T] { diff --git a/internal/compiler/module/resolver.go b/internal/compiler/module/resolver.go index 0ef1194f61..0e1e207c7d 100644 --- a/internal/compiler/module/resolver.go +++ b/internal/compiler/module/resolver.go @@ -124,7 +124,7 @@ func NewResolver( ) *Resolver { return &Resolver{ host: host, - caches: newCaches(host.GetCurrentDirectory(), host.FS().UseCaseSensitiveFileNames(), options), + caches: newCaches(host.GetCurrentDirectory(), host.FS().CaseSensitivity(), options), compilerOptions: options, } } @@ -1028,8 +1028,8 @@ func (r *resolutionState) createResolvedTypeReferenceDirective(resolved *resolve func (r *resolutionState) getOriginalAndResolvedFileName(fileName string) (string, string) { resolvedFileName := r.realPath(fileName) comparePathsOptions := tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: r.resolver.host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: r.resolver.host.GetCurrentDirectory(), + CaseSensitivity: r.resolver.host.FS().CaseSensitivity(), + CurrentDirectory: r.resolver.host.GetCurrentDirectory(), } if tspath.ComparePaths(fileName, resolvedFileName, comparePathsOptions) == 0 { // If the fileName and realpath are differing only in casing, prefer fileName @@ -1345,7 +1345,7 @@ func (r *resolutionState) loadNodeModuleFromDirectoryWorker(ext extensions, cand onlyRecordFailuresForPackageFile bool versionPaths packagejson.VersionPaths ) - if packageInfo.Exists() && tspath.ComparePaths(candidate, packageInfo.PackageDirectory, tspath.ComparePathsOptions{UseCaseSensitiveFileNames: r.resolver.host.FS().UseCaseSensitiveFileNames()}) == 0 { + if packageInfo.Exists() && tspath.ComparePaths(candidate, packageInfo.PackageDirectory, tspath.ComparePathsOptions{CaseSensitivity: r.resolver.host.FS().CaseSensitivity()}) == 0 { if file, ok := r.getPackageFile(ext, packageInfo); ok { packageFile = file onlyRecordFailuresForPackageFile = !r.resolver.host.FS().DirectoryExists(tspath.GetDirectoryPath(file)) diff --git a/internal/compiler/module/resolver_test.go b/internal/compiler/module/resolver_test.go index 1f87ee4830..a1bb643650 100644 --- a/internal/compiler/module/resolver_test.go +++ b/internal/compiler/module/resolver_test.go @@ -158,7 +158,7 @@ func newVFSModuleResolutionHost(files map[string]string, currentDirectory string currentDirectory = "/.src/" + currentDirectory } return &vfsModuleResolutionHost{ - fs: vfstest.FromMap(fs, true /*useCaseSensitiveFileNames*/), + fs: vfstest.FromMap(fs, tspath.CaseSensitive), currentDirectory: currentDirectory, } } diff --git a/internal/compiler/packagejson/cache.go b/internal/compiler/packagejson/cache.go index 6c93fc99c7..1eaffe749e 100644 --- a/internal/compiler/packagejson/cache.go +++ b/internal/compiler/packagejson/cache.go @@ -113,24 +113,24 @@ func (p *InfoCacheEntry) Exists() bool { } type InfoCache struct { - mu sync.RWMutex - IsReadonly bool - cache map[tspath.Path]InfoCacheEntry - currentDirectory string - useCaseSensitiveFileNames bool + mu sync.RWMutex + IsReadonly bool + cache map[tspath.Path]InfoCacheEntry + currentDirectory string + caseSensitivity tspath.CaseSensitivity } -func NewInfoCache(currentDirectory string, useCaseSensitiveFileNames bool) *InfoCache { +func NewInfoCache(currentDirectory string, caseSensitivity tspath.CaseSensitivity) *InfoCache { return &InfoCache{ - currentDirectory: currentDirectory, - useCaseSensitiveFileNames: useCaseSensitiveFileNames, + currentDirectory: currentDirectory, + caseSensitivity: caseSensitivity, } } func (p *InfoCache) Get(packageJsonPath string) *InfoCacheEntry { p.mu.RLock() defer p.mu.RUnlock() - key := tspath.ToPath(packageJsonPath, p.currentDirectory, p.useCaseSensitiveFileNames) + key := tspath.ToPath(packageJsonPath, p.currentDirectory, p.caseSensitivity) entry, ok := p.cache[key] if !ok { return nil @@ -141,7 +141,7 @@ func (p *InfoCache) Get(packageJsonPath string) *InfoCacheEntry { func (p *InfoCache) Set(packageJsonPath string, info *InfoCacheEntry) { p.mu.Lock() defer p.mu.Unlock() - key := tspath.ToPath(packageJsonPath, p.currentDirectory, p.useCaseSensitiveFileNames) + key := tspath.ToPath(packageJsonPath, p.currentDirectory, p.caseSensitivity) if p.cache == nil { p.cache = make(map[tspath.Path]InfoCacheEntry) } diff --git a/internal/compiler/program.go b/internal/compiler/program.go index fdc48596c0..71d615fb3e 100644 --- a/internal/compiler/program.go +++ b/internal/compiler/program.go @@ -96,7 +96,7 @@ func NewProgram(options ProgramOptions) *Program { if !ok { panic("config file not found") } - configFilePath := tspath.ToPath(p.configFileName, p.host.GetCurrentDirectory(), p.host.FS().UseCaseSensitiveFileNames()) + configFilePath := tspath.ToPath(p.configFileName, p.host.GetCurrentDirectory(), p.host.FS().CaseSensitivity()) parsedConfig := parser.ParseJSONText(p.configFileName, configFilePath, jsonText) if len(parsedConfig.Diagnostics()) > 0 { p.configFileParsingDiagnostics = append(p.configFileParsingDiagnostics, parsedConfig.Diagnostics()...) @@ -259,7 +259,7 @@ func (p *Program) GetResolvedModule(file *ast.SourceFile, moduleReference string } func (p *Program) findSourceFile(candidate string, reason FileIncludeReason) *ast.SourceFile { - path := tspath.ToPath(candidate, p.host.GetCurrentDirectory(), p.host.FS().UseCaseSensitiveFileNames()) + path := tspath.ToPath(candidate, p.host.GetCurrentDirectory(), p.host.FS().CaseSensitivity()) return p.filesByPath[path] } @@ -496,7 +496,7 @@ func (p *Program) CommonSourceDirectory() string { p.compilerOptions, files, p.host.GetCurrentDirectory(), - p.host.FS().UseCaseSensitiveFileNames(), + p.host.FS().CaseSensitivity(), ) }) return p.commonSourceDirectory @@ -506,7 +506,7 @@ func (p *Program) GetCompilerOptions() *core.CompilerOptions { return p.compilerOptions } -func computeCommonSourceDirectoryOfFilenames(fileNames []string, currentDirectory string, useCaseSensitiveFileNames bool) string { +func computeCommonSourceDirectoryOfFilenames(fileNames []string, currentDirectory string, caseSensitivity tspath.CaseSensitivity) string { var commonPathComponents []string for _, sourceFile := range fileNames { // Each file contributes into common source file path @@ -523,7 +523,7 @@ func computeCommonSourceDirectoryOfFilenames(fileNames []string, currentDirector n := min(len(commonPathComponents), len(sourcePathComponents)) for i := range n { - if tspath.GetCanonicalFileName(commonPathComponents[i], useCaseSensitiveFileNames) != tspath.GetCanonicalFileName(sourcePathComponents[i], useCaseSensitiveFileNames) { + if tspath.GetCanonicalFileName(commonPathComponents[i], caseSensitivity) != tspath.GetCanonicalFileName(sourcePathComponents[i], caseSensitivity) { if i == 0 { // Failed to find any common path component return "" @@ -549,12 +549,12 @@ func computeCommonSourceDirectoryOfFilenames(fileNames []string, currentDirector return tspath.GetPathFromPathComponents(commonPathComponents) } -func getCommonSourceDirectory(options *core.CompilerOptions, files []string, currentDirectory string, useCaseSensitiveFileNames bool) string { +func getCommonSourceDirectory(options *core.CompilerOptions, files []string, currentDirectory string, caseSensitivity tspath.CaseSensitivity) string { var commonSourceDirectory string // !!! If a rootDir is specified use it as the commonSourceDirectory // !!! Project compilations never infer their root from the input source paths - commonSourceDirectory = computeCommonSourceDirectoryOfFilenames(files, currentDirectory, useCaseSensitiveFileNames) + commonSourceDirectory = computeCommonSourceDirectoryOfFilenames(files, currentDirectory, caseSensitivity) if len(commonSourceDirectory) > 0 { // Make sure directory path ends with directory separator so this string can directly @@ -645,7 +645,7 @@ func (p *Program) Emit(options EmitOptions) *EmitResult { } func (p *Program) GetSourceFile(filename string) *ast.SourceFile { - path := tspath.ToPath(filename, p.host.GetCurrentDirectory(), p.host.FS().UseCaseSensitiveFileNames()) + path := tspath.ToPath(filename, p.host.GetCurrentDirectory(), p.host.FS().CaseSensitivity()) return p.GetSourceFileByPath(path) } diff --git a/internal/compiler/program_test.go b/internal/compiler/program_test.go index 0379028c37..7664355e17 100644 --- a/internal/compiler/program_test.go +++ b/internal/compiler/program_test.go @@ -221,7 +221,7 @@ func TestProgram(t *testing.T) { t.Run(testCase.testName, func(t *testing.T) { t.Parallel() libPrefix := bundled.LibPath() + "/" - fs := vfstest.FromMap[any](nil, false /*useCaseSensitiveFileNames*/) + fs := vfstest.FromMap[any](nil, tspath.CaseInsensitive) fs = bundled.WrapFS(fs) for _, testFile := range testCase.files { @@ -256,7 +256,7 @@ func BenchmarkNewProgram(b *testing.B) { for _, testCase := range programTestCases { b.Run(testCase.testName, func(b *testing.B) { - fs := vfstest.FromMap[any](nil, false /*useCaseSensitiveFileNames*/) + fs := vfstest.FromMap[any](nil, tspath.CaseInsensitive) fs = bundled.WrapFS(fs) for _, testFile := range testCase.files { diff --git a/internal/execute/outputs.go b/internal/execute/outputs.go index 07bfac520a..30d4d00b7a 100644 --- a/internal/execute/outputs.go +++ b/internal/execute/outputs.go @@ -19,8 +19,8 @@ func getFormatOptsOfSys(sys System) *diagnosticwriter.FormattingOptions { return &diagnosticwriter.FormattingOptions{ NewLine: "\n", ComparePathsOptions: tspath.ComparePathsOptions{ - CurrentDirectory: sys.GetCurrentDirectory(), - UseCaseSensitiveFileNames: sys.FS().UseCaseSensitiveFileNames(), + CurrentDirectory: sys.GetCurrentDirectory(), + CaseSensitivity: sys.FS().CaseSensitivity(), }, } } diff --git a/internal/execute/testsys_test.go b/internal/execute/testsys_test.go index b512b23866..919a046a06 100644 --- a/internal/execute/testsys_test.go +++ b/internal/execute/testsys_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/microsoft/typescript-go/internal/bundled" + "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" "github.com/microsoft/typescript-go/internal/vfs/vfstest" ) @@ -22,7 +23,7 @@ func newTestSys(fileOrFolderList FileMap, cwd string, args ...string) *testSys { cwd = "/home/src/workspaces/project" } return &testSys{ - fs: bundled.WrapFS(vfstest.FromMap(fileOrFolderList, true /*useCaseSensitiveFileNames*/)), + fs: bundled.WrapFS(vfstest.FromMap(fileOrFolderList, tspath.CaseSensitive)), defaultLibraryPath: bundled.LibPath(), cwd: cwd, files: slices.Collect(maps.Keys(fileOrFolderList)), diff --git a/internal/execute/tsc.go b/internal/execute/tsc.go index be7d06f48d..c52ad2b8c9 100644 --- a/internal/execute/tsc.go +++ b/internal/execute/tsc.go @@ -159,7 +159,7 @@ func getParsedCommandLineOfConfigFile(configFileName string, options *core.Compi } cwd := sys.GetCurrentDirectory() - tsConfigSourceFile := tsoptions.NewTsconfigSourceFileFromFilePath(configFileName, tspath.ToPath(configFileName, cwd, sys.FS().UseCaseSensitiveFileNames()), configFileText) + tsConfigSourceFile := tsoptions.NewTsconfigSourceFileFromFilePath(configFileName, tspath.ToPath(configFileName, cwd, sys.FS().CaseSensitivity()), configFileText) // tsConfigSourceFile.resolvedPath = tsConfigSourceFile.FileName() // tsConfigSourceFile.originalFileName = tsConfigSourceFile.FileName() return tsoptions.ParseJsonSourceFileConfigFileContent( diff --git a/internal/execute/verifytsc_test.go b/internal/execute/verifytsc_test.go index bee7353071..a960d3263d 100644 --- a/internal/execute/verifytsc_test.go +++ b/internal/execute/verifytsc_test.go @@ -9,6 +9,7 @@ import ( "github.com/microsoft/typescript-go/internal/execute" "github.com/microsoft/typescript-go/internal/testutil/baseline" + "github.com/microsoft/typescript-go/internal/tspath" ) type testTscEdit struct { @@ -75,7 +76,7 @@ func (test *tscInput) startBaseline() *strings.Builder { "\ncurrentDirectory::", test.sys.GetCurrentDirectory(), "\nuseCaseSensitiveFileNames::", - test.sys.FS().UseCaseSensitiveFileNames(), + test.sys.FS().CaseSensitivity() == tspath.CaseSensitive, "\nInput::", ) fmt.Fprint(s, strings.Join(test.commandLineArgs, " "), "\n") diff --git a/internal/parser/parser_test.go b/internal/parser/parser_test.go index ae426ced43..d932bf97b3 100644 --- a/internal/parser/parser_test.go +++ b/internal/parser/parser_test.go @@ -32,7 +32,7 @@ func BenchmarkParse(b *testing.B) { f.SkipIfNotExist(b) fileName := tspath.GetNormalizedAbsolutePath(f.Path(), "/") - path := tspath.ToPath(fileName, "/", osvfs.FS().UseCaseSensitiveFileNames()) + path := tspath.ToPath(fileName, "/", osvfs.FS().CaseSensitivity()) sourceText := f.ReadFile(b) for _, jsdoc := range jsdocModes { @@ -75,7 +75,7 @@ func TestParseTypeScriptRepo(t *testing.T) { assert.NilError(t, err) fileName := tspath.GetNormalizedAbsolutePath(f.path, repo.TypeScriptSubmodulePath) - path := tspath.ToPath(f.path, repo.TypeScriptSubmodulePath, osvfs.FS().UseCaseSensitiveFileNames()) + path := tspath.ToPath(f.path, repo.TypeScriptSubmodulePath, osvfs.FS().CaseSensitivity()) var sourceFile *ast.SourceFile diff --git a/internal/project/project.go b/internal/project/project.go index 14bd41d354..26a7a9153a 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -292,7 +292,7 @@ func (p *Project) isOrphan() bool { } func (p *Project) toPath(fileName string) tspath.Path { - return tspath.ToPath(fileName, p.GetCurrentDirectory(), p.FS().UseCaseSensitiveFileNames()) + return tspath.ToPath(fileName, p.GetCurrentDirectory(), p.FS().CaseSensitivity()) } func (p *Project) isRoot(info *ScriptInfo) bool { diff --git a/internal/project/service.go b/internal/project/service.go index d2ecaa2617..020b289cf2 100644 --- a/internal/project/service.go +++ b/internal/project/service.go @@ -58,21 +58,21 @@ type Service struct { } func NewService(host ServiceHost, options ServiceOptions) *Service { - options.Logger.Info(fmt.Sprintf("currentDirectory:: %s useCaseSensitiveFileNames:: %t", host.GetCurrentDirectory(), host.FS().UseCaseSensitiveFileNames())) + options.Logger.Info(fmt.Sprintf("currentDirectory:: %s useCaseSensitiveFileNames:: %t", host.GetCurrentDirectory(), host.FS().CaseSensitivity() == tspath.CaseSensitive)) options.Logger.Info("libs Location:: " + options.DefaultLibraryPath) return &Service{ host: host, options: options, comparePathsOptions: tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: host.GetCurrentDirectory(), + CaseSensitivity: host.FS().CaseSensitivity(), + CurrentDirectory: host.GetCurrentDirectory(), }, configuredProjects: make(map[tspath.Path]*Project), documentRegistry: newDocumentRegistry(tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: host.GetCurrentDirectory(), + CaseSensitivity: host.FS().CaseSensitivity(), + CurrentDirectory: host.GetCurrentDirectory(), }), scriptInfos: make(map[tspath.Path]*ScriptInfo), openFiles: make(map[tspath.Path]string), @@ -522,7 +522,7 @@ func (s *Service) createInferredProject(currentDirectory string, projectRootPath } func (s *Service) toPath(fileName string) tspath.Path { - return tspath.ToPath(fileName, s.host.GetCurrentDirectory(), s.host.FS().UseCaseSensitiveFileNames()) + return tspath.ToPath(fileName, s.host.GetCurrentDirectory(), s.host.FS().CaseSensitivity()) } func (s *Service) loadConfiguredProject(project *Project) { diff --git a/internal/project/service_test.go b/internal/project/service_test.go index a0164f746a..5b1c45e4a1 100644 --- a/internal/project/service_test.go +++ b/internal/project/service_test.go @@ -12,6 +12,7 @@ import ( "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/ls" "github.com/microsoft/typescript-go/internal/project" + "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" "github.com/microsoft/typescript-go/internal/vfs/vfstest" "gotest.tools/v3/assert" @@ -237,7 +238,7 @@ type projectServiceHost struct { } func newProjectServiceHost(files map[string]string) *projectServiceHost { - fs := bundled.WrapFS(vfstest.FromMap(files, false /*useCaseSensitiveFileNames*/)) + fs := bundled.WrapFS(vfstest.FromMap(files, tspath.CaseInsensitive)) host := &projectServiceHost{ fs: fs, defaultLibraryPath: bundled.LibPath(), @@ -274,7 +275,7 @@ func (p *projectServiceHost) NewLine() string { } func (p *projectServiceHost) replaceFS(files map[string]string) { - p.fs = bundled.WrapFS(vfstest.FromMap(files, false /*useCaseSensitiveFileNames*/)) + p.fs = bundled.WrapFS(vfstest.FromMap(files, tspath.CaseInsensitive)) } var _ project.ServiceHost = (*projectServiceHost)(nil) diff --git a/internal/testrunner/test_case_parser.go b/internal/testrunner/test_case_parser.go index fe05c4ca10..15ade0001f 100644 --- a/internal/testrunner/test_case_parser.go +++ b/internal/testrunner/test_case_parser.go @@ -130,7 +130,7 @@ func makeUnitsFromTest(code string, fileName string) testCaseContent { for i, data := range testUnits { if harnessutil.GetConfigNameFromFileName(data.name) != "" { configFileName := tspath.GetNormalizedAbsolutePath(data.name, currentDirectory) - path := tspath.ToPath(data.name, parseConfigHost.GetCurrentDirectory(), parseConfigHost.Vfs.UseCaseSensitiveFileNames()) + path := tspath.ToPath(data.name, parseConfigHost.GetCurrentDirectory(), parseConfigHost.Vfs.CaseSensitivity()) configJson := parser.ParseJSONText(configFileName, path, data.content) tsConfigSourceFile := &tsoptions.TsConfigSourceFile{ SourceFile: configJson, diff --git a/internal/testutil/harnessutil/harnessutil.go b/internal/testutil/harnessutil/harnessutil.go index e80f345bb5..4829f765a8 100644 --- a/internal/testutil/harnessutil/harnessutil.go +++ b/internal/testutil/harnessutil/harnessutil.go @@ -53,24 +53,24 @@ type NamedTestConfiguration struct { } type HarnessOptions struct { - AllowNonTsExtensions bool - UseCaseSensitiveFileNames bool - BaselineFile string - IncludeBuiltFile string - FileName string - LibFiles []string - NoErrorTruncation bool - SuppressOutputPathCheck bool - NoImplicitReferences bool - CurrentDirectory string - Symlink string - Link string - NoTypesAndSymbols bool - FullEmitPaths bool - NoCheck bool - ReportDiagnostics bool - CaptureSuggestions bool - TypescriptVersion string + AllowNonTsExtensions bool + CaseSensitivity tspath.CaseSensitivity + BaselineFile string + IncludeBuiltFile string + FileName string + LibFiles []string + NoErrorTruncation bool + SuppressOutputPathCheck bool + NoImplicitReferences bool + CurrentDirectory string + Symlink string + Link string + NoTypesAndSymbols bool + FullEmitPaths bool + NoCheck bool + ReportDiagnostics bool + CaptureSuggestions bool + TypescriptVersion string } func CompileFiles( @@ -94,7 +94,7 @@ func CompileFiles( compilerOptions.SkipDefaultLibCheck = core.TSTrue } compilerOptions.NoErrorTruncation = core.TSTrue - harnessOptions := HarnessOptions{UseCaseSensitiveFileNames: true, CurrentDirectory: currentDirectory} + harnessOptions := HarnessOptions{CaseSensitivity: tspath.CaseSensitive, CurrentDirectory: currentDirectory} // Parse harness and compiler options from the test configuration if testConfig != nil { @@ -196,7 +196,7 @@ func CompileFilesEx( maps.Copy(testfs, testLibFolderMap()) } - fs := vfstest.FromMap(testfs, harnessOptions.UseCaseSensitiveFileNames) + fs := vfstest.FromMap(testfs, harnessOptions.CaseSensitivity) fs = bundled.WrapFS(fs) fs = NewOutputRecorderFS(fs) @@ -348,7 +348,11 @@ func parseHarnessOption(t *testing.T, key string, value any, options *HarnessOpt case "allowNonTsExtensions": options.AllowNonTsExtensions = value.(bool) case "useCaseSensitiveFileNames": - options.UseCaseSensitiveFileNames = value.(bool) + if useCaseSensitiveFileNames := value.(bool); useCaseSensitiveFileNames { + options.CaseSensitivity = tspath.CaseSensitive + } else { + options.CaseSensitivity = tspath.CaseInsensitive + } case "baselineFile": options.BaselineFile = value.(string) case "includeBuiltFile": @@ -679,8 +683,8 @@ func (c *CompilationResult) getOutputPath(path string, ext string) string { common := c.Program.CommonSourceDirectory() if common != "" { path = tspath.GetRelativePathFromDirectory(common, path, tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: c.Program.Host().FS().UseCaseSensitiveFileNames(), - CurrentDirectory: c.Program.Host().GetCurrentDirectory(), + CaseSensitivity: c.Program.Host().FS().CaseSensitivity(), + CurrentDirectory: c.Program.Host().GetCurrentDirectory(), }) path = tspath.CombinePaths(tspath.ResolvePath(c.Program.Host().GetCurrentDirectory(), c.Options.OutDir), path) } diff --git a/internal/testutil/tsbaseline/util.go b/internal/testutil/tsbaseline/util.go index 61c7cefe45..2883179b0e 100644 --- a/internal/testutil/tsbaseline/util.go +++ b/internal/testutil/tsbaseline/util.go @@ -66,6 +66,6 @@ func sanitizeTestFilePath(name string) string { path := testPathCharacters.ReplaceAllString(name, "_") path = tspath.NormalizeSlashes(path) path = testPathDotDot.ReplaceAllString(path, "__dotdot/") - path = string(tspath.ToPath(path, "", false /*useCaseSensitiveFileNames*/)) + path = string(tspath.ToPath(path, "", tspath.CaseInsensitive)) return strings.TrimPrefix(path, "/") } diff --git a/internal/tsoptions/tsconfigparsing.go b/internal/tsoptions/tsconfigparsing.go index 4ad41502e3..c45f39cbe8 100644 --- a/internal/tsoptions/tsconfigparsing.go +++ b/internal/tsoptions/tsconfigparsing.go @@ -818,7 +818,7 @@ func getExtendedConfig( extendedConfigCache map[tspath.Path]*ExtendedConfigCacheEntry, result *extendsResult, ) (*parsedTsconfig, []*ast.Diagnostic) { - path := tspath.ToPath(extendedConfigPath, host.GetCurrentDirectory(), host.FS().UseCaseSensitiveFileNames()) + path := tspath.ToPath(extendedConfigPath, host.GetCurrentDirectory(), host.FS().CaseSensitivity()) var extendedResult *TsConfigSourceFile var extendedConfig *parsedTsconfig var errors []*ast.Diagnostic @@ -918,8 +918,8 @@ func parseConfig( } else { if relativeDifference == "" { t := tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: host.FS().UseCaseSensitiveFileNames(), - CurrentDirectory: host.GetCurrentDirectory(), + CaseSensitivity: host.FS().CaseSensitivity(), + CurrentDirectory: host.GetCurrentDirectory(), } relativeDifference = tspath.ConvertToRelativePath(basePath, t) } @@ -1432,7 +1432,7 @@ func getFileNamesFromConfigSpecs( ) []string { extraFileExtensions = []fileExtensionInfo{} basePath = tspath.NormalizePath(basePath) - keyMappper := func(value string) string { return tspath.GetCanonicalFileName(value, host.UseCaseSensitiveFileNames()) } + keyMappper := func(value string) string { return tspath.GetCanonicalFileName(value, host.CaseSensitivity()) } // Literal file names (provided via the "files" array in tsconfig.json) are stored in a // file map with a possibly case insensitive key. We use this map later when when including // wildcard paths. @@ -1469,7 +1469,7 @@ func getFileNamesFromConfigSpecs( includeFilePatterns := core.Map(getRegularExpressionsForWildcards(includes, basePath, "files"), func(pattern string) string { return fmt.Sprintf("^%s$", pattern) }) if includeFilePatterns != nil { jsonOnlyIncludeRegexes = core.Map(includeFilePatterns, func(pattern string) *regexp2.Regexp { - return getRegexFromPattern(pattern, host.UseCaseSensitiveFileNames()) + return getRegexFromPattern(pattern, host.CaseSensitivity()) }) } else { jsonOnlyIncludeRegexes = nil diff --git a/internal/tsoptions/tsconfigparsing_test.go b/internal/tsoptions/tsconfigparsing_test.go index 9eeeef505b..7d6daee6a1 100644 --- a/internal/tsoptions/tsconfigparsing_test.go +++ b/internal/tsoptions/tsconfigparsing_test.go @@ -141,8 +141,8 @@ func TestParseConfigFileTextToJson(t *testing.T) { diagnosticwriter.FormatDiagnosticsWithColorAndContext(&baselineContent, errors, &diagnosticwriter.FormattingOptions{ NewLine: "\n", ComparePathsOptions: tspath.ComparePathsOptions{ - CurrentDirectory: "/", - UseCaseSensitiveFileNames: true, + CurrentDirectory: "/", + CaseSensitivity: tspath.CaseSensitive, }, }) baselineContent.WriteString("\n") @@ -554,7 +554,7 @@ func TestParseJsonConfigFileContent(t *testing.T) { t.Parallel() baselineParseConfigWith(t, rec.title+" with json api.js", rec.noSubmoduleBaseline, rec.input, func(config testConfig, host tsoptions.ParseConfigHost, basePath string) *tsoptions.ParsedCommandLine { configFileName := tspath.GetNormalizedAbsolutePath(config.configFileName, basePath) - path := tspath.ToPath(config.configFileName, basePath, host.FS().UseCaseSensitiveFileNames()) + path := tspath.ToPath(config.configFileName, basePath, host.FS().CaseSensitivity()) parsed, _ := tsoptions.ParseConfigFileTextToJson(configFileName, path, config.jsonText) return tsoptions.ParseJsonConfigFileContent( parsed, @@ -579,7 +579,7 @@ func TestParseJsonSourceFileConfigFileContent(t *testing.T) { t.Parallel() baselineParseConfigWith(t, rec.title+" with jsonSourceFile api.js", rec.noSubmoduleBaseline, rec.input, func(config testConfig, host tsoptions.ParseConfigHost, basePath string) *tsoptions.ParsedCommandLine { configFileName := tspath.GetNormalizedAbsolutePath(config.configFileName, basePath) - path := tspath.ToPath(config.configFileName, basePath, host.FS().UseCaseSensitiveFileNames()) + path := tspath.ToPath(config.configFileName, basePath, host.FS().CaseSensitivity()) parsed := parser.ParseJSONText(configFileName, path, config.jsonText) tsConfigSourceFile := &tsoptions.TsConfigSourceFile{ SourceFile: parsed, @@ -636,8 +636,8 @@ func baselineParseConfigWith(t *testing.T, baselineFileName string, noSubmoduleB diagnosticwriter.FormatDiagnosticsWithColorAndContext(&baselineContent, parsedConfigFileContent.Errors, &diagnosticwriter.FormattingOptions{ NewLine: "\r\n", ComparePathsOptions: tspath.ComparePathsOptions{ - CurrentDirectory: basePath, - UseCaseSensitiveFileNames: true, + CurrentDirectory: basePath, + CaseSensitivity: tspath.CaseSensitive, }, }) baselineContent.WriteString("\n") @@ -697,7 +697,7 @@ func TestParseSrcCompiler(t *testing.T) { jsonText, ok := fs.ReadFile(tsconfigFileName) assert.Assert(t, ok) - tsconfigPath := tspath.ToPath(tsconfigFileName, compilerDir, fs.UseCaseSensitiveFileNames()) + tsconfigPath := tspath.ToPath(tsconfigFileName, compilerDir, fs.CaseSensitivity()) parsed := parser.ParseJSONText(tsconfigFileName, tsconfigPath, jsonText) if len(parsed.Diagnostics()) > 0 { @@ -763,8 +763,8 @@ func TestParseSrcCompiler(t *testing.T) { } relativePaths = append(relativePaths, tspath.ConvertToRelativePath(fileName, tspath.ComparePathsOptions{ - CurrentDirectory: compilerDir, - UseCaseSensitiveFileNames: fs.UseCaseSensitiveFileNames(), + CurrentDirectory: compilerDir, + CaseSensitivity: fs.CaseSensitivity(), })) } @@ -862,7 +862,7 @@ func BenchmarkParseSrcCompiler(b *testing.B) { jsonText, ok := fs.ReadFile(tsconfigFileName) assert.Assert(b, ok) - tsconfigPath := tspath.ToPath(tsconfigFileName, compilerDir, fs.UseCaseSensitiveFileNames()) + tsconfigPath := tspath.ToPath(tsconfigFileName, compilerDir, fs.CaseSensitivity()) parsed := parser.ParseJSONText(tsconfigFileName, tsconfigPath, jsonText) b.ReportAllocs() diff --git a/internal/tsoptions/tsoptionstest/vfsparseconfighost.go b/internal/tsoptions/tsoptionstest/vfsparseconfighost.go index 0770f02c8f..546d4ad3a1 100644 --- a/internal/tsoptions/tsoptionstest/vfsparseconfighost.go +++ b/internal/tsoptions/tsoptionstest/vfsparseconfighost.go @@ -35,7 +35,7 @@ func (h *VfsParseConfigHost) GetCurrentDirectory() string { func NewVFSParseConfigHost(files map[string]string, currentDirectory string) *VfsParseConfigHost { return &VfsParseConfigHost{ - Vfs: vfstest.FromMap(files, true /*useCaseSensitiveFileNames*/), + Vfs: vfstest.FromMap(files, tspath.CaseSensitive), CurrentDirectory: currentDirectory, } } diff --git a/internal/tsoptions/utilities.go b/internal/tsoptions/utilities.go index b5889636ff..663f120f43 100644 --- a/internal/tsoptions/utilities.go +++ b/internal/tsoptions/utilities.go @@ -240,7 +240,7 @@ func getIncludeBasePath(absolute string) string { } // getBasePaths computes the unique non-wildcard base paths amongst the provided include patterns. -func getBasePaths(path string, includes []string, useCaseSensitiveFileNames bool) []string { +func getBasePaths(path string, includes []string, caseSensitivity tspath.CaseSensitivity) []string { // Storage for our results in the form of literal paths (e.g. the paths as written by the user). basePaths := []string{path} @@ -261,7 +261,7 @@ func getBasePaths(path string, includes []string, useCaseSensitiveFileNames bool } // Sort the offsets array using either the literal or canonical path representations. - stringComparer := stringutil.GetStringComparer(!useCaseSensitiveFileNames) + stringComparer := stringutil.GetStringComparer(caseSensitivity == tspath.CaseInsensitive) sort.SliceStable(includeBasePaths, func(i, j int) bool { return stringComparer(includeBasePaths[i], includeBasePaths[j]) < 0 }) @@ -270,7 +270,7 @@ func getBasePaths(path string, includes []string, useCaseSensitiveFileNames bool // subpath of an existing base path for _, includeBasePath := range includeBasePaths { if core.Every(basePaths, func(basepath string) bool { - return !tspath.ContainsPath(basepath, includeBasePath, tspath.ComparePathsOptions{CurrentDirectory: path, UseCaseSensitiveFileNames: !useCaseSensitiveFileNames}) + return !tspath.ContainsPath(basepath, includeBasePath, tspath.ComparePathsOptions{CurrentDirectory: path, CaseSensitivity: caseSensitivity.Invert()}) }) { basePaths = append(basePaths, includeBasePath) } @@ -282,7 +282,7 @@ func getBasePaths(path string, includes []string, useCaseSensitiveFileNames bool // getFileMatcherPatterns generates file matching patterns based on the provided path, // includes, excludes, and other parameters. path is the directory of the tsconfig.json file. -func getFileMatcherPatterns(path string, excludes []string, includes []string, useCaseSensitiveFileNames bool, currentDirectory string) FileMatcherPatterns { +func getFileMatcherPatterns(path string, excludes []string, includes []string, caseSensitivity tspath.CaseSensitivity, currentDirectory string) FileMatcherPatterns { path = tspath.NormalizePath(path) currentDirectory = tspath.NormalizePath(currentDirectory) absolutePath := tspath.CombinePaths(currentDirectory, path) @@ -292,7 +292,7 @@ func getFileMatcherPatterns(path string, excludes []string, includes []string, u includeFilePattern: getRegularExpressionForWildcard(includes, absolutePath, "files"), includeDirectoryPattern: getRegularExpressionForWildcard(includes, absolutePath, "directories"), excludePattern: getRegularExpressionForWildcard(excludes, absolutePath, "exclude"), - basePaths: getBasePaths(path, includes, useCaseSensitiveFileNames), + basePaths: getBasePaths(path, includes, caseSensitivity), } } @@ -306,9 +306,9 @@ var ( regexp2Cache = make(map[regexp2CacheKey]*regexp2.Regexp) ) -func getRegexFromPattern(pattern string, useCaseSensitiveFileNames bool) *regexp2.Regexp { +func getRegexFromPattern(pattern string, caseSensitivity tspath.CaseSensitivity) *regexp2.Regexp { flags := regexp2.ECMAScript - if !useCaseSensitiveFileNames { + if caseSensitivity == tspath.CaseInsensitive { flags |= regexp2.IgnoreCase } opts := regexp2.RegexOptions(flags) @@ -345,14 +345,14 @@ func getRegexFromPattern(pattern string, useCaseSensitiveFileNames bool) *regexp } type visitor struct { - includeFileRegexes []*regexp2.Regexp - excludeRegex *regexp2.Regexp - includeDirectoryRegex *regexp2.Regexp - extensions []string - useCaseSensitiveFileNames bool - host vfs.FS - visited core.Set[string] - results [][]string + includeFileRegexes []*regexp2.Regexp + excludeRegex *regexp2.Regexp + includeDirectoryRegex *regexp2.Regexp + extensions []string + caseSensitivity tspath.CaseSensitivity + host vfs.FS + visited core.Set[string] + results [][]string } func (v *visitor) visitDirectory( @@ -360,7 +360,7 @@ func (v *visitor) visitDirectory( absolutePath string, depth *int, ) { - canonicalPath := tspath.GetCanonicalFileName(absolutePath, v.useCaseSensitiveFileNames) + canonicalPath := tspath.GetCanonicalFileName(absolutePath, v.caseSensitivity) if v.visited.Has(canonicalPath) { return } @@ -406,22 +406,22 @@ func (v *visitor) visitDirectory( } // path is the directory of the tsconfig.json -func matchFiles(path string, extensions []string, excludes []string, includes []string, useCaseSensitiveFileNames bool, currentDirectory string, depth *int, host vfs.FS) []string { +func matchFiles(path string, extensions []string, excludes []string, includes []string, caseSensitivity tspath.CaseSensitivity, currentDirectory string, depth *int, host vfs.FS) []string { path = tspath.NormalizePath(path) currentDirectory = tspath.NormalizePath(currentDirectory) - patterns := getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames, currentDirectory) + patterns := getFileMatcherPatterns(path, excludes, includes, caseSensitivity, currentDirectory) var includeFileRegexes []*regexp2.Regexp if patterns.includeFilePatterns != nil { - includeFileRegexes = core.Map(patterns.includeFilePatterns, func(pattern string) *regexp2.Regexp { return getRegexFromPattern(pattern, useCaseSensitiveFileNames) }) + includeFileRegexes = core.Map(patterns.includeFilePatterns, func(pattern string) *regexp2.Regexp { return getRegexFromPattern(pattern, caseSensitivity) }) } var includeDirectoryRegex *regexp2.Regexp if patterns.includeDirectoryPattern != "" { - includeDirectoryRegex = getRegexFromPattern(patterns.includeDirectoryPattern, useCaseSensitiveFileNames) + includeDirectoryRegex = getRegexFromPattern(patterns.includeDirectoryPattern, caseSensitivity) } var excludeRegex *regexp2.Regexp if patterns.excludePattern != "" { - excludeRegex = getRegexFromPattern(patterns.excludePattern, useCaseSensitiveFileNames) + excludeRegex = getRegexFromPattern(patterns.excludePattern, caseSensitivity) } // Associate an array of results with each include regex. This keeps results in order of the "include" order. @@ -437,13 +437,13 @@ func matchFiles(path string, extensions []string, excludes []string, includes [] results = [][]string{{}} } v := visitor{ - useCaseSensitiveFileNames: useCaseSensitiveFileNames, - host: host, - includeFileRegexes: includeFileRegexes, - excludeRegex: excludeRegex, - includeDirectoryRegex: includeDirectoryRegex, - extensions: extensions, - results: results, + caseSensitivity: caseSensitivity, + host: host, + includeFileRegexes: includeFileRegexes, + excludeRegex: excludeRegex, + includeDirectoryRegex: includeDirectoryRegex, + extensions: extensions, + results: results, } for _, basePath := range patterns.basePaths { v.visitDirectory(basePath, tspath.CombinePaths(currentDirectory, basePath), depth) @@ -453,5 +453,5 @@ func matchFiles(path string, extensions []string, excludes []string, includes [] } func readDirectory(host vfs.FS, currentDir string, path string, extensions []string, excludes []string, includes []string, depth *int) []string { - return matchFiles(path, extensions, excludes, includes, host.UseCaseSensitiveFileNames(), currentDir, depth, host) + return matchFiles(path, extensions, excludes, includes, host.CaseSensitivity(), currentDir, depth, host) } diff --git a/internal/tspath/casesensitivity_string.go b/internal/tspath/casesensitivity_string.go new file mode 100644 index 0000000000..6bac82d3be --- /dev/null +++ b/internal/tspath/casesensitivity_string.go @@ -0,0 +1,24 @@ +// Code generated by "stringer -type=CaseSensitivity"; DO NOT EDIT. + +package tspath + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[CaseInsensitive-0] + _ = x[CaseSensitive-1] +} + +const _CaseSensitivity_name = "CaseInsensitiveCaseSensitive" + +var _CaseSensitivity_index = [...]uint8{0, 15, 28} + +func (i CaseSensitivity) String() string { + if i >= CaseSensitivity(len(_CaseSensitivity_index)-1) { + return "CaseSensitivity(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _CaseSensitivity_name[_CaseSensitivity_index[i]:_CaseSensitivity_index[i+1]] +} diff --git a/internal/tspath/path.go b/internal/tspath/path.go index 9b5b41492b..f6f68a502f 100644 --- a/internal/tspath/path.go +++ b/internal/tspath/path.go @@ -10,6 +10,22 @@ import ( type Path string +//go:generate go run golang.org/x/tools/cmd/stringer -type=CaseSensitivity + +type CaseSensitivity uint8 + +const ( + CaseInsensitive CaseSensitivity = iota + CaseSensitive +) + +func (c CaseSensitivity) Invert() CaseSensitivity { + if c == CaseSensitive { + return CaseInsensitive + } + return CaseSensitive +} + // Internally, we represent paths as strings with '/' as the directory separator. // When we make system calls (eg: LanguageServiceHost.getDirectory()), // we expect the host to correctly handle paths in our specified format. @@ -493,8 +509,8 @@ func NormalizePath(path string) string { return normalized } -func GetCanonicalFileName(fileName string, useCaseSensitiveFileNames bool) string { - if useCaseSensitiveFileNames { +func GetCanonicalFileName(fileName string, caseSensitivity CaseSensitivity) string { + if caseSensitivity == CaseSensitive { return fileName } return ToFileNameLowerCase(fileName) @@ -529,14 +545,14 @@ func ToFileNameLowerCase(fileName string) string { }, fileName) } -func ToPath(fileName string, basePath string, useCaseSensitiveFileNames bool) Path { +func ToPath(fileName string, basePath string, caseSensitivity CaseSensitivity) Path { var nonCanonicalizedPath string if IsRootedDiskPath(fileName) { nonCanonicalizedPath = NormalizePath(fileName) } else { nonCanonicalizedPath = GetNormalizedAbsolutePath(fileName, basePath) } - return Path(GetCanonicalFileName(nonCanonicalizedPath, useCaseSensitiveFileNames)) + return Path(GetCanonicalFileName(nonCanonicalizedPath, caseSensitivity)) } func RemoveTrailingDirectorySeparator(path string) string { @@ -768,16 +784,16 @@ func IsExternalModuleNameRelative(moduleName string) bool { } type ComparePathsOptions struct { - UseCaseSensitiveFileNames bool - CurrentDirectory string + CaseSensitivity CaseSensitivity + CurrentDirectory string } func (o ComparePathsOptions) GetComparer() func(a, b string) int { - return stringutil.GetStringComparer(!o.UseCaseSensitiveFileNames) + return stringutil.GetStringComparer(o.CaseSensitivity == CaseInsensitive) } func (o ComparePathsOptions) getEqualityComparer() func(a, b string) bool { - return stringutil.GetStringEqualityComparer(!o.UseCaseSensitiveFileNames) + return stringutil.GetStringEqualityComparer(o.CaseSensitivity == CaseInsensitive) } func ComparePaths(a string, b string, options ComparePathsOptions) int { @@ -826,11 +842,11 @@ func ComparePaths(a string, b string, options ComparePathsOptions) int { } func ComparePathsCaseSensitive(a string, b string, currentDirectory string) int { - return ComparePaths(a, b, ComparePathsOptions{UseCaseSensitiveFileNames: true, CurrentDirectory: currentDirectory}) + return ComparePaths(a, b, ComparePathsOptions{CaseSensitivity: CaseSensitive, CurrentDirectory: currentDirectory}) } func ComparePathsCaseInsensitive(a string, b string, currentDirectory string) int { - return ComparePaths(a, b, ComparePathsOptions{UseCaseSensitiveFileNames: false, CurrentDirectory: currentDirectory}) + return ComparePaths(a, b, ComparePathsOptions{CaseSensitivity: CaseInsensitive, CurrentDirectory: currentDirectory}) } func ContainsPath(parent string, child string, options ComparePathsOptions) bool { diff --git a/internal/tspath/path_test.go b/internal/tspath/path_test.go index 07683b8825..0e44f62448 100644 --- a/internal/tspath/path_test.go +++ b/internal/tspath/path_test.go @@ -566,9 +566,9 @@ func FuzzToFileNameLowerCase(f *testing.F) { func TestToPath(t *testing.T) { t.Parallel() - assert.Equal(t, string(ToPath("file.ext", "path/to", false /*useCaseSensitiveFileNames*/)), "path/to/file.ext") - assert.Equal(t, string(ToPath("file.ext", "/path/to", true /*useCaseSensitiveFileNames*/)), "/path/to/file.ext") - assert.Equal(t, string(ToPath("/path/to/../file.ext", "path/to", true /*useCaseSensitiveFileNames*/)), "/path/file.ext") + assert.Equal(t, string(ToPath("file.ext", "path/to", CaseInsensitive)), "path/to/file.ext") + assert.Equal(t, string(ToPath("file.ext", "/path/to", CaseSensitive)), "/path/to/file.ext") + assert.Equal(t, string(ToPath("/path/to/../file.ext", "path/to", CaseSensitive)), "/path/file.ext") } var relativePathSegmentRegExp = regexp.MustCompile(`//|(?:^|/)\.\.?(?:$|/)`) diff --git a/internal/vfs/cachedvfs/cachedvfs.go b/internal/vfs/cachedvfs/cachedvfs.go index 132c67bfaf..31e8c916d3 100644 --- a/internal/vfs/cachedvfs/cachedvfs.go +++ b/internal/vfs/cachedvfs/cachedvfs.go @@ -2,6 +2,7 @@ package cachedvfs import ( "github.com/microsoft/typescript-go/internal/collections" + "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" ) @@ -82,8 +83,8 @@ func (fsys *FS) Stat(path string) vfs.FileInfo { return ret } -func (fsys *FS) UseCaseSensitiveFileNames() bool { - return fsys.fs.UseCaseSensitiveFileNames() +func (fsys *FS) CaseSensitivity() tspath.CaseSensitivity { + return fsys.fs.CaseSensitivity() } func (fsys *FS) WalkDir(root string, walkFn vfs.WalkDirFunc) error { diff --git a/internal/vfs/cachedvfs/cachedvfs_test.go b/internal/vfs/cachedvfs/cachedvfs_test.go index f74a42c870..e89bc67c13 100644 --- a/internal/vfs/cachedvfs/cachedvfs_test.go +++ b/internal/vfs/cachedvfs/cachedvfs_test.go @@ -3,6 +3,7 @@ package cachedvfs_test import ( "testing" + "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" "github.com/microsoft/typescript-go/internal/vfs/cachedvfs" "github.com/microsoft/typescript-go/internal/vfs/vfsmock" @@ -13,7 +14,7 @@ import ( func createMockFS() *vfsmock.FSMock { return vfsmock.Wrap(vfstest.FromMap(map[string]string{ "/some/path/file.txt": "hello world", - }, true)) + }, tspath.CaseSensitive)) } func TestDirectoryExists(t *testing.T) { @@ -133,21 +134,21 @@ func TestReadFile(t *testing.T) { assert.Equal(t, 3, len(underlying.ReadFileCalls())) } -func TestUseCaseSensitiveFileNames(t *testing.T) { +func TestCaseSensitivity(t *testing.T) { t.Parallel() underlying := createMockFS() cached := cachedvfs.From(underlying) - cached.UseCaseSensitiveFileNames() - assert.Equal(t, 1, len(underlying.UseCaseSensitiveFileNamesCalls())) + cached.CaseSensitivity() + assert.Equal(t, 1, len(underlying.CaseSensitivityCalls())) - cached.UseCaseSensitiveFileNames() - assert.Equal(t, 2, len(underlying.UseCaseSensitiveFileNamesCalls())) + cached.CaseSensitivity() + assert.Equal(t, 2, len(underlying.CaseSensitivityCalls())) cached.ClearCache() - cached.UseCaseSensitiveFileNames() - assert.Equal(t, 3, len(underlying.UseCaseSensitiveFileNamesCalls())) + cached.CaseSensitivity() + assert.Equal(t, 3, len(underlying.CaseSensitivityCalls())) } func TestWalkDir(t *testing.T) { diff --git a/internal/vfs/iovfs/iofs.go b/internal/vfs/iovfs/iofs.go index 8b4fd5777d..ad887a4156 100644 --- a/internal/vfs/iovfs/iofs.go +++ b/internal/vfs/iovfs/iofs.go @@ -33,7 +33,7 @@ type WritableFS interface { // // From does not actually handle case-insensitivity; ensure the passed in [fs.FS] // respects case-insensitive file names if needed. Consider using [vfstest.FromMap] for testing. -func From(fsys fs.FS, useCaseSensitiveFileNames bool) vfs.FS { +func From(fsys fs.FS, caseSensitivity tspath.CaseSensitivity) vfs.FS { var realpath func(path string) (string, error) if fsys, ok := fsys.(RealpathFS); ok { realpath = func(path string) (string, error) { @@ -102,28 +102,28 @@ func From(fsys fs.FS, useCaseSensitiveFileNames bool) vfs.FS { return sub }, }, - useCaseSensitiveFileNames: useCaseSensitiveFileNames, - realpath: realpath, - writeFile: writeFile, - mkdirAll: mkdirAll, - remove: remove, + caseSensitivity: caseSensitivity, + realpath: realpath, + writeFile: writeFile, + mkdirAll: mkdirAll, + remove: remove, } } type ioFS struct { common internal.Common - useCaseSensitiveFileNames bool - realpath func(path string) (string, error) - writeFile func(path string, content string, writeByteOrderMark bool) error - mkdirAll func(path string) error - remove func(path string) error + caseSensitivity tspath.CaseSensitivity + realpath func(path string) (string, error) + writeFile func(path string, content string, writeByteOrderMark bool) error + mkdirAll func(path string) error + remove func(path string) error } var _ vfs.FS = (*ioFS)(nil) -func (vfs *ioFS) UseCaseSensitiveFileNames() bool { - return vfs.useCaseSensitiveFileNames +func (vfs *ioFS) CaseSensitivity() tspath.CaseSensitivity { + return vfs.caseSensitivity } func (vfs *ioFS) DirectoryExists(path string) bool { diff --git a/internal/vfs/iovfs/iofs_test.go b/internal/vfs/iovfs/iofs_test.go index cb096478b5..ea07a668df 100644 --- a/internal/vfs/iovfs/iofs_test.go +++ b/internal/vfs/iovfs/iofs_test.go @@ -6,6 +6,7 @@ import ( "testing/fstest" "github.com/microsoft/typescript-go/internal/testutil" + "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" "github.com/microsoft/typescript-go/internal/vfs/iovfs" "gotest.tools/v3/assert" @@ -29,7 +30,7 @@ func TestIOFS(t *testing.T) { }, } - fs := iovfs.From(testfs, true) + fs := iovfs.From(testfs, tspath.CaseSensitive) t.Run("ReadFile", func(t *testing.T) { t.Parallel() @@ -126,9 +127,9 @@ func TestIOFS(t *testing.T) { assert.Equal(t, realpath, "/foo.ts") }) - t.Run("UseCaseSensitiveFileNames", func(t *testing.T) { + t.Run("CaseSensitivity", func(t *testing.T) { t.Parallel() - assert.Assert(t, fs.UseCaseSensitiveFileNames()) + assert.Equal(t, fs.CaseSensitivity(), tspath.CaseSensitive) }) } diff --git a/internal/vfs/osvfs/os.go b/internal/vfs/osvfs/os.go index 47439b59d1..3301341a0b 100644 --- a/internal/vfs/osvfs/os.go +++ b/internal/vfs/osvfs/os.go @@ -30,15 +30,15 @@ type osFS struct { } // We do this right at startup to minimize the chance that executable gets moved or deleted. -var isFileSystemCaseSensitive = func() bool { +var osCaseSensitivity = func() tspath.CaseSensitivity { // win32/win64 are case insensitive platforms if runtime.GOOS == "windows" { - return false + return tspath.CaseInsensitive } if runtime.GOARCH == "wasm" { // !!! Who knows; this depends on the host implementation. - return true + return tspath.CaseSensitive } // As a proxy for case-insensitivity, we check if the current executable exists under a different case. @@ -53,11 +53,11 @@ var isFileSystemCaseSensitive = func() bool { swapped := swapCase(exe) if _, err := os.Stat(swapped); err != nil { if os.IsNotExist(err) { - return true + return tspath.CaseSensitive } panic(fmt.Sprintf("vfs: failed to stat %q: %v", swapped, err)) } - return false + return tspath.CaseInsensitive }() // Convert all lowercase chars to uppercase, and vice-versa @@ -72,8 +72,8 @@ func swapCase(str string) string { }, str) } -func (vfs *osFS) UseCaseSensitiveFileNames() bool { - return isFileSystemCaseSensitive +func (vfs *osFS) CaseSensitivity() tspath.CaseSensitivity { + return osCaseSensitivity } var readSema = make(chan struct{}, 128) diff --git a/internal/vfs/osvfs/os_test.go b/internal/vfs/osvfs/os_test.go index 7f9b7e2ed4..236991312e 100644 --- a/internal/vfs/osvfs/os_test.go +++ b/internal/vfs/osvfs/os_test.go @@ -51,17 +51,17 @@ func TestOS(t *testing.T) { assert.Equal(t, realpath, expected) }) - t.Run("UseCaseSensitiveFileNames", func(t *testing.T) { + t.Run("CaseSensitivity", func(t *testing.T) { t.Parallel() // Just check that it works. - fs.UseCaseSensitiveFileNames() + fs.CaseSensitivity() switch runtime.GOOS { case "windows": - assert.Assert(t, !fs.UseCaseSensitiveFileNames()) + assert.Equal(t, fs.CaseSensitivity(), tspath.CaseInsensitive) case "linux": - assert.Assert(t, fs.UseCaseSensitiveFileNames()) + assert.Equal(t, fs.CaseSensitivity(), tspath.CaseSensitive) } }) } diff --git a/internal/vfs/vfs.go b/internal/vfs/vfs.go index 5bf8d03655..17fd37e22c 100644 --- a/internal/vfs/vfs.go +++ b/internal/vfs/vfs.go @@ -2,14 +2,15 @@ package vfs import ( "io/fs" + + "github.com/microsoft/typescript-go/internal/tspath" ) //go:generate go tool github.com/matryer/moq -fmt goimports -out vfsmock/mock_generated.go -pkg vfsmock . FS // FS is a file system abstraction. type FS interface { - // UseCaseSensitiveFileNames returns true if the file system is case-sensitive. - UseCaseSensitiveFileNames() bool + CaseSensitivity() tspath.CaseSensitivity // FileExists returns true if the file exists. FileExists(path string) bool diff --git a/internal/vfs/vfsmock/mock_generated.go b/internal/vfs/vfsmock/mock_generated.go index 5f5dcbdedb..a111fc44af 100644 --- a/internal/vfs/vfsmock/mock_generated.go +++ b/internal/vfs/vfsmock/mock_generated.go @@ -6,6 +6,7 @@ package vfsmock import ( "sync" + "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" ) @@ -19,6 +20,9 @@ var _ vfs.FS = &FSMock{} // // // make and configure a mocked vfs.FS // mockedFS := &FSMock{ +// CaseSensitivityFunc: func() tspath.CaseSensitivity { +// panic("mock out the CaseSensitivity method") +// }, // DirectoryExistsFunc: func(path string) bool { // panic("mock out the DirectoryExists method") // }, @@ -40,9 +44,6 @@ var _ vfs.FS = &FSMock{} // StatFunc: func(path string) vfs.FileInfo { // panic("mock out the Stat method") // }, -// UseCaseSensitiveFileNamesFunc: func() bool { -// panic("mock out the UseCaseSensitiveFileNames method") -// }, // WalkDirFunc: func(root string, walkFn vfs.WalkDirFunc) error { // panic("mock out the WalkDir method") // }, @@ -56,6 +57,9 @@ var _ vfs.FS = &FSMock{} // // } type FSMock struct { + // CaseSensitivityFunc mocks the CaseSensitivity method. + CaseSensitivityFunc func() tspath.CaseSensitivity + // DirectoryExistsFunc mocks the DirectoryExists method. DirectoryExistsFunc func(path string) bool @@ -77,9 +81,6 @@ type FSMock struct { // StatFunc mocks the Stat method. StatFunc func(path string) vfs.FileInfo - // UseCaseSensitiveFileNamesFunc mocks the UseCaseSensitiveFileNames method. - UseCaseSensitiveFileNamesFunc func() bool - // WalkDirFunc mocks the WalkDir method. WalkDirFunc func(root string, walkFn vfs.WalkDirFunc) error @@ -88,6 +89,9 @@ type FSMock struct { // calls tracks calls to the methods. calls struct { + // CaseSensitivity holds details about calls to the CaseSensitivity method. + CaseSensitivity []struct { + } // DirectoryExists holds details about calls to the DirectoryExists method. DirectoryExists []struct { // Path is the path argument value. @@ -123,9 +127,6 @@ type FSMock struct { // Path is the path argument value. Path string } - // UseCaseSensitiveFileNames holds details about calls to the UseCaseSensitiveFileNames method. - UseCaseSensitiveFileNames []struct { - } // WalkDir holds details about calls to the WalkDir method. WalkDir []struct { // Root is the root argument value. @@ -143,16 +144,43 @@ type FSMock struct { WriteByteOrderMark bool } } - lockDirectoryExists sync.RWMutex - lockFileExists sync.RWMutex - lockGetAccessibleEntries sync.RWMutex - lockReadFile sync.RWMutex - lockRealpath sync.RWMutex - lockRemove sync.RWMutex - lockStat sync.RWMutex - lockUseCaseSensitiveFileNames sync.RWMutex - lockWalkDir sync.RWMutex - lockWriteFile sync.RWMutex + lockCaseSensitivity sync.RWMutex + lockDirectoryExists sync.RWMutex + lockFileExists sync.RWMutex + lockGetAccessibleEntries sync.RWMutex + lockReadFile sync.RWMutex + lockRealpath sync.RWMutex + lockRemove sync.RWMutex + lockStat sync.RWMutex + lockWalkDir sync.RWMutex + lockWriteFile sync.RWMutex +} + +// CaseSensitivity calls CaseSensitivityFunc. +func (mock *FSMock) CaseSensitivity() tspath.CaseSensitivity { + if mock.CaseSensitivityFunc == nil { + panic("FSMock.CaseSensitivityFunc: method is nil but FS.CaseSensitivity was just called") + } + callInfo := struct { + }{} + mock.lockCaseSensitivity.Lock() + mock.calls.CaseSensitivity = append(mock.calls.CaseSensitivity, callInfo) + mock.lockCaseSensitivity.Unlock() + return mock.CaseSensitivityFunc() +} + +// CaseSensitivityCalls gets all the calls that were made to CaseSensitivity. +// Check the length with: +// +// len(mockedFS.CaseSensitivityCalls()) +func (mock *FSMock) CaseSensitivityCalls() []struct { +} { + var calls []struct { + } + mock.lockCaseSensitivity.RLock() + calls = mock.calls.CaseSensitivity + mock.lockCaseSensitivity.RUnlock() + return calls } // DirectoryExists calls DirectoryExistsFunc. @@ -379,33 +407,6 @@ func (mock *FSMock) StatCalls() []struct { return calls } -// UseCaseSensitiveFileNames calls UseCaseSensitiveFileNamesFunc. -func (mock *FSMock) UseCaseSensitiveFileNames() bool { - if mock.UseCaseSensitiveFileNamesFunc == nil { - panic("FSMock.UseCaseSensitiveFileNamesFunc: method is nil but FS.UseCaseSensitiveFileNames was just called") - } - callInfo := struct { - }{} - mock.lockUseCaseSensitiveFileNames.Lock() - mock.calls.UseCaseSensitiveFileNames = append(mock.calls.UseCaseSensitiveFileNames, callInfo) - mock.lockUseCaseSensitiveFileNames.Unlock() - return mock.UseCaseSensitiveFileNamesFunc() -} - -// UseCaseSensitiveFileNamesCalls gets all the calls that were made to UseCaseSensitiveFileNames. -// Check the length with: -// -// len(mockedFS.UseCaseSensitiveFileNamesCalls()) -func (mock *FSMock) UseCaseSensitiveFileNamesCalls() []struct { -} { - var calls []struct { - } - mock.lockUseCaseSensitiveFileNames.RLock() - calls = mock.calls.UseCaseSensitiveFileNames - mock.lockUseCaseSensitiveFileNames.RUnlock() - return calls -} - // WalkDir calls WalkDirFunc. func (mock *FSMock) WalkDir(root string, walkFn vfs.WalkDirFunc) error { if mock.WalkDirFunc == nil { diff --git a/internal/vfs/vfsmock/wrapper.go b/internal/vfs/vfsmock/wrapper.go index 02ba3acd2f..33ab52fdb6 100644 --- a/internal/vfs/vfsmock/wrapper.go +++ b/internal/vfs/vfsmock/wrapper.go @@ -5,15 +5,15 @@ import "github.com/microsoft/typescript-go/internal/vfs" // Wrap wraps a vfs.FS and returns a FSMock which calls it. func Wrap(fs vfs.FS) *FSMock { return &FSMock{ - DirectoryExistsFunc: fs.DirectoryExists, - FileExistsFunc: fs.FileExists, - GetAccessibleEntriesFunc: fs.GetAccessibleEntries, - ReadFileFunc: fs.ReadFile, - RealpathFunc: fs.Realpath, - RemoveFunc: fs.Remove, - StatFunc: fs.Stat, - UseCaseSensitiveFileNamesFunc: fs.UseCaseSensitiveFileNames, - WalkDirFunc: fs.WalkDir, - WriteFileFunc: fs.WriteFile, + DirectoryExistsFunc: fs.DirectoryExists, + FileExistsFunc: fs.FileExists, + GetAccessibleEntriesFunc: fs.GetAccessibleEntries, + ReadFileFunc: fs.ReadFile, + RealpathFunc: fs.Realpath, + RemoveFunc: fs.Remove, + StatFunc: fs.Stat, + CaseSensitivityFunc: fs.CaseSensitivity, + WalkDirFunc: fs.WalkDir, + WriteFileFunc: fs.WriteFile, } } diff --git a/internal/vfs/vfsmock/wrapper_test.go b/internal/vfs/vfsmock/wrapper_test.go index 17623ae10a..60a2d57d12 100644 --- a/internal/vfs/vfsmock/wrapper_test.go +++ b/internal/vfs/vfsmock/wrapper_test.go @@ -4,6 +4,7 @@ import ( "reflect" "testing" + "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs/vfstest" "gotest.tools/v3/assert" ) @@ -11,7 +12,7 @@ import ( func TestWrap(t *testing.T) { t.Parallel() - wrapper := Wrap(vfstest.FromMap(map[string]string{}, true)) + wrapper := Wrap(vfstest.FromMap(map[string]string{}, tspath.CaseSensitive)) wrapperValue := reflect.ValueOf(wrapper).Elem() wrapperType := wrapperValue.Type() diff --git a/internal/vfs/vfstest/vfstest.go b/internal/vfs/vfstest/vfstest.go index 861a8c4212..14065eb763 100644 --- a/internal/vfs/vfstest/vfstest.go +++ b/internal/vfs/vfstest/vfstest.go @@ -25,7 +25,7 @@ type mapFS struct { // keys in m are canonicalPaths m fstest.MapFS - useCaseSensitiveFileNames bool + caseSensitivity tspath.CaseSensitivity symlinks map[canonicalPath]canonicalPath } @@ -46,7 +46,7 @@ type sys struct { // The paths must be normalized absolute paths according to the tspath package, // without trailing directory separators. // The paths must be all POSIX-style or all Windows-style, but not both. -func FromMap[File any](m map[string]File, useCaseSensitiveFileNames bool) vfs.FS { +func FromMap[File any](m map[string]File, caseSensitivity tspath.CaseSensitivity) vfs.FS { posix := false windows := false @@ -100,13 +100,17 @@ func FromMap[File any](m map[string]File, useCaseSensitiveFileNames bool) vfs.FS panic("mixed posix and windows paths") } - return iovfs.From(convertMapFS(mfs, useCaseSensitiveFileNames), useCaseSensitiveFileNames) + if posix && windows { + panic("mixed posix and windows paths") + } + + return iovfs.From(convertMapFS(mfs, caseSensitivity), caseSensitivity) } -func convertMapFS(input fstest.MapFS, useCaseSensitiveFileNames bool) *mapFS { +func convertMapFS(input fstest.MapFS, caseSensitivity tspath.CaseSensitivity) *mapFS { m := &mapFS{ - m: make(fstest.MapFS, len(input)), - useCaseSensitiveFileNames: useCaseSensitiveFileNames, + m: make(fstest.MapFS, len(input)), + caseSensitivity: caseSensitivity, } // Verify that the input is well-formed. @@ -163,7 +167,7 @@ func comparePathsByParts(a, b string) int { type canonicalPath string func (m *mapFS) getCanonicalPath(p string) canonicalPath { - return canonicalPath(tspath.GetCanonicalFileName(p, m.useCaseSensitiveFileNames)) + return canonicalPath(tspath.GetCanonicalFileName(p, m.caseSensitivity)) } func (m *mapFS) open(p canonicalPath) (fs.File, error) { diff --git a/internal/vfs/vfstest/vfstest_test.go b/internal/vfs/vfstest/vfstest_test.go index a0c0da026a..b067588101 100644 --- a/internal/vfs/vfstest/vfstest_test.go +++ b/internal/vfs/vfstest/vfstest_test.go @@ -12,6 +12,7 @@ import ( "unicode/utf16" "github.com/microsoft/typescript-go/internal/testutil" + "github.com/microsoft/typescript-go/internal/tspath" "github.com/microsoft/typescript-go/internal/vfs" "gotest.tools/v3/assert" ) @@ -34,7 +35,7 @@ func TestInsensitive(t *testing.T) { Data: contents, Sys: 1234, }, - }, false /*useCaseSensitiveFileNames*/) + }, tspath.CaseInsensitive) sensitive, err := fs.ReadFile(vfs, "foo/bar/baz") assert.NilError(t, err) @@ -97,7 +98,7 @@ func TestInsensitiveUpper(t *testing.T) { Data: contents, Sys: 1234, }, - }, false /*useCaseSensitiveFileNames*/) + }, tspath.CaseInsensitive) sensitive, err := fs.ReadFile(vfs, "foo/bar/baz") assert.NilError(t, err) @@ -142,7 +143,7 @@ func TestSensitive(t *testing.T) { Data: contents, Sys: 1234, }, - }, true /*useCaseSensitiveFileNames*/) + }, tspath.CaseSensitive) sensitive, err := fs.ReadFile(vfs, "foo/bar/baz") assert.NilError(t, err) @@ -170,7 +171,7 @@ func TestSensitiveDuplicatePath(t *testing.T) { } testutil.AssertPanics(t, func() { - convertMapFS(testfs, false /*useCaseSensitiveFileNames*/) + convertMapFS(testfs, tspath.CaseInsensitive) }, `duplicate path: "Foo" and "foo" have the same canonical path`) } @@ -186,7 +187,7 @@ func TestInsensitiveDuplicatePath(t *testing.T) { }, } - convertMapFS(testfs, true /*useCaseSensitiveFileNames*/) + convertMapFS(testfs, tspath.CaseSensitive) } func dirEntriesToNames(entries []fs.DirEntry) []string { @@ -200,7 +201,7 @@ func dirEntriesToNames(entries []fs.DirEntry) []string { func TestWritableFS(t *testing.T) { t.Parallel() - fs := FromMap[any](nil, false) + fs := FromMap[any](nil, tspath.CaseInsensitive) err := fs.WriteFile("/foo/bar/baz", "hello, world", false) assert.NilError(t, err) @@ -222,7 +223,7 @@ func TestWritableFS(t *testing.T) { func TestWritableFSDelete(t *testing.T) { t.Parallel() - fs := FromMap[any](nil, false) + fs := FromMap[any](nil, tspath.CaseInsensitive) _ = fs.WriteFile("/foo/bar/file.ts", "remove", false) assert.Assert(t, fs.FileExists("/foo/bar/file.ts")) @@ -251,7 +252,7 @@ func TestWritableFSDelete(t *testing.T) { func TestStress(t *testing.T) { t.Parallel() - fs := FromMap[any](nil, false) + fs := FromMap[any](nil, tspath.CaseInsensitive) ops := []func(){ func() { _ = fs.WriteFile("/foo/bar/baz.txt", "hello, world", false) }, @@ -303,7 +304,7 @@ func TestParentDirFile(t *testing.T) { } testutil.AssertPanics(t, func() { - convertMapFS(testfs, false /*useCaseSensitiveFileNames*/) + convertMapFS(testfs, tspath.CaseInsensitive) }, `failed to create intermediate directories for "foo/oops": mkdir "foo": path exists but is not a directory`) } @@ -319,7 +320,7 @@ func TestFromMap(t *testing.T) { "/mapfile": &fstest.MapFile{ Data: []byte("hello, world"), }, - }, false) + }, tspath.CaseInsensitive) content, ok := fs.ReadFile("/string") assert.Assert(t, ok) @@ -343,7 +344,7 @@ func TestFromMap(t *testing.T) { "e:/mapfile": &fstest.MapFile{ Data: []byte("hello, world"), }, - }, false) + }, tspath.CaseInsensitive) content, ok := fs.ReadFile("c:/string") assert.Assert(t, ok) @@ -365,7 +366,7 @@ func TestFromMap(t *testing.T) { FromMap(map[string]any{ "/string": "hello, world", "c:/bytes": []byte("hello, world"), - }, false) + }, tspath.CaseInsensitive) }, `mixed posix and windows paths`) }) @@ -375,7 +376,7 @@ func TestFromMap(t *testing.T) { testutil.AssertPanics(t, func() { FromMap(map[string]any{ "string": "hello, world", - }, false) + }, tspath.CaseInsensitive) }, `non-rooted path "string"`) }) @@ -385,7 +386,7 @@ func TestFromMap(t *testing.T) { testutil.AssertPanics(t, func() { FromMap(map[string]any{ "/string/": "hello, world", - }, false) + }, tspath.CaseInsensitive) }, `non-normalized path "/string/"`) }) @@ -395,7 +396,7 @@ func TestFromMap(t *testing.T) { testutil.AssertPanics(t, func() { FromMap(map[string]any{ "/string/../foo": "hello, world", - }, false) + }, tspath.CaseInsensitive) }, `non-normalized path "/string/../foo"`) }) @@ -405,7 +406,7 @@ func TestFromMap(t *testing.T) { testutil.AssertPanics(t, func() { FromMap(map[string]any{ "/string": 1234, - }, false) + }, tspath.CaseInsensitive) }, `invalid file type int`) }) } @@ -418,7 +419,7 @@ func TestVFSTestMapFS(t *testing.T) { "/dir1/file1.ts": "export const foo = 42;", "/dir1/file2.ts": "export const foo = 42;", "/dir2/file1.ts": "export const foo = 42;", - }, false /*useCaseSensitiveFileNames*/) + }, tspath.CaseInsensitive) t.Run("ReadFile", func(t *testing.T) { t.Parallel() @@ -445,10 +446,10 @@ func TestVFSTestMapFS(t *testing.T) { assert.Equal(t, realpath, "/does/not/exist.ts") }) - t.Run("UseCaseSensitiveFileNames", func(t *testing.T) { + t.Run("CaseSensitivity", func(t *testing.T) { t.Parallel() - assert.Assert(t, !fs.UseCaseSensitiveFileNames()) + assert.Equal(t, fs.CaseSensitivity(), tspath.CaseInsensitive) }) } @@ -460,7 +461,7 @@ func TestVFSTestMapFSWindows(t *testing.T) { "c:/dir1/file1.ts": "export const foo = 42;", "c:/dir1/file2.ts": "export const foo = 42;", "c:/dir2/file1.ts": "export const foo = 42;", - }, false) + }, tspath.CaseInsensitive) t.Run("ReadFile", func(t *testing.T) { t.Parallel() @@ -522,7 +523,7 @@ func TestBOM(t *testing.T) { fs := FromMap(map[string][]byte{ "/foo.ts": buf, - }, true) + }, tspath.CaseSensitive) content, ok := fs.ReadFile("/foo.ts") assert.Assert(t, ok) @@ -535,7 +536,7 @@ func TestBOM(t *testing.T) { fs := FromMap(map[string][]byte{ "/foo.ts": []byte("\xEF\xBB\xBF" + expected), - }, true) + }, tspath.CaseSensitive) content, ok := fs.ReadFile("/foo.ts") assert.Assert(t, ok) @@ -555,7 +556,7 @@ func TestSymlink(t *testing.T) { "/b": Symlink("/c"), "/c": Symlink("/d"), "/d/existing.ts": "this is existing.ts", - }, false) + }, tspath.CaseInsensitive) t.Run("ReadFile", func(t *testing.T) { t.Parallel() @@ -617,7 +618,7 @@ func TestWritableFSSymlink(t *testing.T) { "/b": Symlink("/c"), "/c": Symlink("/d"), "/d/existing.ts": "hello, world", - }, false) + }, tspath.CaseInsensitive) err := fs.WriteFile("/some/dirlink/file.ts", "hello, world", false) assert.NilError(t, err) @@ -677,7 +678,7 @@ func TestWritableFSSymlinkChain(t *testing.T) { "/b": Symlink("/c"), "/c": Symlink("/d"), "/d/existing.ts": "hello, world", - }, false) + }, tspath.CaseInsensitive) err := fs.WriteFile("/a/foo/bar/new.ts", "this is new.ts", false) assert.NilError(t, err) @@ -700,7 +701,7 @@ func TestWritableFSSymlinkChainNotDir(t *testing.T) { "/b": Symlink("/c"), "/c": Symlink("/d"), "/d": "hello, world", - }, false) + }, tspath.CaseInsensitive) err := fs.WriteFile("/a/foo/bar/new.ts", "this is new.ts", false) assert.Error(t, err, `mkdir "d": path exists but is not a directory`) @@ -718,7 +719,7 @@ func TestWritableFSSymlinkDelete(t *testing.T) { "/b": Symlink("/c"), "/c": Symlink("/d"), "/d/existing.ts": "hello, world", - }, false) + }, tspath.CaseInsensitive) err := fs.Remove("/a") assert.NilError(t, err)