package compiler import ( "cmp" "slices" "strings" "sync" "sync/atomic" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/module" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" ) type libResolution struct { libraryName string resolution *module.ResolvedModule trace []string } type LibFile struct { Name string path string Replaced bool } type fileLoader struct { opts ProgramOptions resolver *module.Resolver defaultLibraryPath string comparePathsOptions tspath.ComparePathsOptions supportedExtensions []string filesParser *filesParser rootTasks []*parseTask includeProcessor *includeProcessor totalFileCount atomic.Int32 libFileCount atomic.Int32 factoryMu sync.Mutex factory ast.NodeFactory projectReferenceFileMapper *projectReferenceFileMapper dtsDirectories collections.Set[tspath.Path] pathForLibFileCache collections.SyncMap[string, *LibFile] pathForLibFileResolutions collections.SyncMap[tspath.Path, *libResolution] } type processedFiles struct { resolver *module.Resolver files []*ast.SourceFile filesByPath map[tspath.Path]*ast.SourceFile projectReferenceFileMapper *projectReferenceFileMapper missingFiles []string resolvedModules map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule] typeResolutionsInFile map[tspath.Path]module.ModeAwareCache[*module.ResolvedTypeReferenceDirective] sourceFileMetaDatas map[tspath.Path]ast.SourceFileMetaData jsxRuntimeImportSpecifiers map[tspath.Path]*jsxRuntimeImportSpecifier importHelpersImportSpecifiers map[tspath.Path]*ast.Node libFiles map[tspath.Path]*LibFile // List of present unsupported extensions unsupportedExtensions []string sourceFilesFoundSearchingNodeModules collections.Set[tspath.Path] includeProcessor *includeProcessor // if file was included using source file and its output is actually part of program // this contains mapping from output to source file outputFileToProjectReferenceSource map[tspath.Path]string } type jsxRuntimeImportSpecifier struct { moduleReference string specifier *ast.Node } func processAllProgramFiles( opts ProgramOptions, singleThreaded bool, ) processedFiles { compilerOptions := opts.Config.CompilerOptions() rootFiles := opts.Config.FileNames() supportedExtensions := tsoptions.GetSupportedExtensions(compilerOptions, nil /*extraFileExtensions*/) var maxNodeModuleJsDepth int if p := opts.Config.CompilerOptions().MaxNodeModuleJsDepth; p != nil { maxNodeModuleJsDepth = *p } loader := fileLoader{ opts: opts, defaultLibraryPath: tspath.GetNormalizedAbsolutePath(opts.Host.DefaultLibraryPath(), opts.Host.GetCurrentDirectory()), comparePathsOptions: tspath.ComparePathsOptions{ UseCaseSensitiveFileNames: opts.Host.FS().UseCaseSensitiveFileNames(), CurrentDirectory: opts.Host.GetCurrentDirectory(), }, filesParser: &filesParser{ wg: core.NewWorkGroup(singleThreaded), maxDepth: maxNodeModuleJsDepth, }, rootTasks: make([]*parseTask, 0, len(rootFiles)+len(compilerOptions.Lib)), supportedExtensions: core.Flatten(tsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(compilerOptions, supportedExtensions)), includeProcessor: &includeProcessor{}, } loader.addProjectReferenceTasks(singleThreaded) loader.resolver = module.NewResolver(loader.projectReferenceFileMapper.host, compilerOptions, opts.TypingsLocation, opts.ProjectName) for index, rootFile := range rootFiles { loader.addRootTask(rootFile, nil, &fileIncludeReason{kind: fileIncludeKindRootFile, data: index}) } if len(rootFiles) > 0 && compilerOptions.NoLib.IsFalseOrUnknown() { if compilerOptions.Lib == nil { name := tsoptions.GetDefaultLibFileName(compilerOptions) libFile := loader.pathForLibFile(name) loader.addRootTask(libFile.path, libFile, &fileIncludeReason{kind: fileIncludeKindLibFile}) } else { for index, lib := range compilerOptions.Lib { if name, ok := tsoptions.GetLibFileName(lib); ok { libFile := loader.pathForLibFile(name) loader.addRootTask(libFile.path, libFile, &fileIncludeReason{kind: fileIncludeKindLibFile, data: index}) } // !!! error on unknown name } } } if len(rootFiles) > 0 { loader.addAutomaticTypeDirectiveTasks() } loader.filesParser.parse(&loader, loader.rootTasks) // Clear out loader and host to ensure its not used post program creation loader.projectReferenceFileMapper.loader = nil loader.projectReferenceFileMapper.host = nil totalFileCount := int(loader.totalFileCount.Load()) libFileCount := int(loader.libFileCount.Load()) var missingFiles []string files := make([]*ast.SourceFile, 0, totalFileCount-libFileCount) libFiles := make([]*ast.SourceFile, 0, totalFileCount) // totalFileCount here since we append files to it later to construct the final list filesByPath := make(map[tspath.Path]*ast.SourceFile, totalFileCount) loader.includeProcessor.fileIncludeReasons = make(map[tspath.Path][]*fileIncludeReason, totalFileCount) var outputFileToProjectReferenceSource map[tspath.Path]string if !opts.canUseProjectReferenceSource() { outputFileToProjectReferenceSource = make(map[tspath.Path]string, totalFileCount) } resolvedModules := make(map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule], totalFileCount+1) typeResolutionsInFile := make(map[tspath.Path]module.ModeAwareCache[*module.ResolvedTypeReferenceDirective], totalFileCount) sourceFileMetaDatas := make(map[tspath.Path]ast.SourceFileMetaData, totalFileCount) var jsxRuntimeImportSpecifiers map[tspath.Path]*jsxRuntimeImportSpecifier var importHelpersImportSpecifiers map[tspath.Path]*ast.Node var unsupportedExtensions []string var sourceFilesFoundSearchingNodeModules collections.Set[tspath.Path] libFilesMap := make(map[tspath.Path]*LibFile, libFileCount) loader.filesParser.collect(&loader, loader.rootTasks, func(task *parseTask) { if task.redirectedParseTask != nil { if !opts.canUseProjectReferenceSource() { outputFileToProjectReferenceSource[task.redirectedParseTask.path] = task.FileName() } return } if task.isForAutomaticTypeDirective { typeResolutionsInFile[task.path] = task.typeResolutionsInFile return } file := task.file path := task.path if file == nil { // !!! sheetal file preprocessing diagnostic explaining getSourceFileFromReferenceWorker missingFiles = append(missingFiles, task.normalizedFilePath) return } // !!! sheetal todo porting file case errors // if _, ok := filesByPath[path]; ok { // Check if it differs only in drive letters its ok to ignore that error: // const checkedAbsolutePath = getNormalizedAbsolutePathWithoutRoot(checkedName, currentDirectory); // const inputAbsolutePath = getNormalizedAbsolutePathWithoutRoot(fileName, currentDirectory); // if (checkedAbsolutePath !== inputAbsolutePath) { // reportFileNamesDifferOnlyInCasingError(fileName, file, reason); // } // } else if loader.comparePathsOptions.UseCaseSensitiveFileNames { // pathIgnoreCase := tspath.ToPath(file.FileName(), loader.comparePathsOptions.CurrentDirectory, false) // // for case-sensitsive file systems check if we've already seen some file with similar filename ignoring case // if _, ok := filesByNameIgnoreCase[pathIgnoreCase]; ok { // reportFileNamesDifferOnlyInCasingError(fileName, existingFile, reason); // } else { // filesByNameIgnoreCase[pathIgnoreCase] = file // } // } if task.libFile != nil { libFiles = append(libFiles, file) libFilesMap[path] = task.libFile } else { files = append(files, file) } filesByPath[path] = file resolvedModules[path] = task.resolutionsInFile typeResolutionsInFile[path] = task.typeResolutionsInFile sourceFileMetaDatas[path] = task.metadata if task.jsxRuntimeImportSpecifier != nil { if jsxRuntimeImportSpecifiers == nil { jsxRuntimeImportSpecifiers = make(map[tspath.Path]*jsxRuntimeImportSpecifier, totalFileCount) } jsxRuntimeImportSpecifiers[path] = task.jsxRuntimeImportSpecifier } if task.importHelpersImportSpecifier != nil { if importHelpersImportSpecifiers == nil { importHelpersImportSpecifiers = make(map[tspath.Path]*ast.Node, totalFileCount) } importHelpersImportSpecifiers[path] = task.importHelpersImportSpecifier } extension := tspath.TryGetExtensionFromPath(file.FileName()) if slices.Contains(tspath.SupportedJSExtensionsFlat, extension) { unsupportedExtensions = core.AppendIfUnique(unsupportedExtensions, extension) } if task.fromExternalLibrary { sourceFilesFoundSearchingNodeModules.Add(path) } }) loader.sortLibs(libFiles) allFiles := append(libFiles, files...) keys := slices.Collect(loader.pathForLibFileResolutions.Keys()) slices.Sort(keys) for _, key := range keys { value, _ := loader.pathForLibFileResolutions.Load(key) resolvedModules[key] = module.ModeAwareCache[*module.ResolvedModule]{ module.ModeAwareCacheKey{Name: value.libraryName, Mode: core.ModuleKindCommonJS}: value.resolution, } for _, trace := range value.trace { opts.Host.Trace(trace) } } return processedFiles{ resolver: loader.resolver, files: allFiles, filesByPath: filesByPath, projectReferenceFileMapper: loader.projectReferenceFileMapper, resolvedModules: resolvedModules, typeResolutionsInFile: typeResolutionsInFile, sourceFileMetaDatas: sourceFileMetaDatas, jsxRuntimeImportSpecifiers: jsxRuntimeImportSpecifiers, importHelpersImportSpecifiers: importHelpersImportSpecifiers, unsupportedExtensions: unsupportedExtensions, sourceFilesFoundSearchingNodeModules: sourceFilesFoundSearchingNodeModules, libFiles: libFilesMap, missingFiles: missingFiles, includeProcessor: loader.includeProcessor, outputFileToProjectReferenceSource: outputFileToProjectReferenceSource, } } func (p *fileLoader) toPath(file string) tspath.Path { return tspath.ToPath(file, p.opts.Host.GetCurrentDirectory(), p.opts.Host.FS().UseCaseSensitiveFileNames()) } func (p *fileLoader) addRootTask(fileName string, libFile *LibFile, includeReason *fileIncludeReason) { absPath := tspath.GetNormalizedAbsolutePath(fileName, p.opts.Host.GetCurrentDirectory()) if core.Tristate.IsTrue(p.opts.Config.CompilerOptions().AllowNonTsExtensions) || slices.Contains(p.supportedExtensions, tspath.TryGetExtensionFromPath(absPath)) { p.rootTasks = append(p.rootTasks, &parseTask{ normalizedFilePath: absPath, libFile: libFile, includeReason: includeReason, }) } } func (p *fileLoader) addAutomaticTypeDirectiveTasks() { var containingDirectory string compilerOptions := p.opts.Config.CompilerOptions() if compilerOptions.ConfigFilePath != "" { containingDirectory = tspath.GetDirectoryPath(compilerOptions.ConfigFilePath) } else { containingDirectory = p.opts.Host.GetCurrentDirectory() } containingFileName := tspath.CombinePaths(containingDirectory, module.InferredTypesContainingFile) p.rootTasks = append(p.rootTasks, &parseTask{ normalizedFilePath: containingFileName, isForAutomaticTypeDirective: true, }) } func (p *fileLoader) resolveAutomaticTypeDirectives(containingFileName string) ( toParse []resolvedRef, typeResolutionsInFile module.ModeAwareCache[*module.ResolvedTypeReferenceDirective], typeResolutionsTrace []string, ) { automaticTypeDirectiveNames := module.GetAutomaticTypeDirectiveNames(p.opts.Config.CompilerOptions(), p.opts.Host) if len(automaticTypeDirectiveNames) != 0 { toParse = make([]resolvedRef, 0, len(automaticTypeDirectiveNames)) typeResolutionsInFile = make(module.ModeAwareCache[*module.ResolvedTypeReferenceDirective], len(automaticTypeDirectiveNames)) for _, name := range automaticTypeDirectiveNames { resolutionMode := core.ModuleKindNodeNext resolved, trace := p.resolver.ResolveTypeReferenceDirective(name, containingFileName, resolutionMode, nil) typeResolutionsInFile[module.ModeAwareCacheKey{Name: name, Mode: resolutionMode}] = resolved typeResolutionsTrace = append(typeResolutionsTrace, trace...) if resolved.IsResolved() { toParse = append(toParse, resolvedRef{ fileName: resolved.ResolvedFileName, increaseDepth: resolved.IsExternalLibraryImport, elideOnDepth: false, includeReason: &fileIncludeReason{ kind: fileIncludeKindAutomaticTypeDirectiveFile, data: &automaticTypeDirectiveFileData{name, resolved.PackageId}, }, }) } } } return toParse, typeResolutionsInFile, typeResolutionsTrace } func (p *fileLoader) addProjectReferenceTasks(singleThreaded bool) { p.projectReferenceFileMapper = &projectReferenceFileMapper{ opts: p.opts, host: p.opts.Host, } projectReferences := p.opts.Config.ResolvedProjectReferencePaths() if len(projectReferences) == 0 { return } parser := &projectReferenceParser{ loader: p, wg: core.NewWorkGroup(singleThreaded), } rootTasks := createProjectReferenceParseTasks(projectReferences) parser.parse(rootTasks) // Add files from project references as root if the module kind is 'none'. // This ensures that files from project references are included in the root tasks // when no module system is specified, allowing including all files for global symbol merging // !!! sheetal Do we really need it? if len(p.opts.Config.FileNames()) != 0 { for index, resolved := range p.projectReferenceFileMapper.getResolvedProjectReferences() { if resolved == nil || resolved.CompilerOptions().GetEmitModuleKind() != core.ModuleKindNone { continue } if p.opts.canUseProjectReferenceSource() { for _, fileName := range resolved.FileNames() { p.rootTasks = append(p.rootTasks, &parseTask{ normalizedFilePath: fileName, includeReason: &fileIncludeReason{ kind: fileIncludeKindSourceFromProjectReference, data: index, }, }) } } else { for outputDts := range resolved.GetOutputDeclarationAndSourceFileNames() { if outputDts != "" { p.rootTasks = append(p.rootTasks, &parseTask{ normalizedFilePath: outputDts, includeReason: &fileIncludeReason{ kind: fileIncludeKindOutputFromProjectReference, data: index, }, }) } } } } } } func (p *fileLoader) sortLibs(libFiles []*ast.SourceFile) { slices.SortFunc(libFiles, func(f1 *ast.SourceFile, f2 *ast.SourceFile) int { return cmp.Compare(p.getDefaultLibFilePriority(f1), p.getDefaultLibFilePriority(f2)) }) } func (p *fileLoader) getDefaultLibFilePriority(a *ast.SourceFile) int { // defaultLibraryPath and a.FileName() are absolute and normalized; a prefix check should suffice. defaultLibraryPath := tspath.RemoveTrailingDirectorySeparator(p.defaultLibraryPath) aFileName := a.FileName() if strings.HasPrefix(aFileName, defaultLibraryPath) && len(aFileName) > len(defaultLibraryPath) && aFileName[len(defaultLibraryPath)] == tspath.DirectorySeparator { // avoid tspath.GetBaseFileName; we know these paths are already absolute and normalized. basename := aFileName[strings.LastIndexByte(aFileName, tspath.DirectorySeparator)+1:] if basename == "lib.d.ts" || basename == "lib.es6.d.ts" { return 0 } name := strings.TrimSuffix(strings.TrimPrefix(basename, "lib."), ".d.ts") index := slices.Index(tsoptions.Libs, name) if index != -1 { return index + 1 } } return len(tsoptions.Libs) + 2 } func (p *fileLoader) loadSourceFileMetaData(fileName string) ast.SourceFileMetaData { packageJsonScope := p.resolver.GetPackageJsonScopeIfApplicable(fileName) var packageJsonType, packageJsonDirectory string if packageJsonScope.Exists() { packageJsonDirectory = packageJsonScope.PackageDirectory if value, ok := packageJsonScope.Contents.Type.GetValue(); ok { packageJsonType = value } } impliedNodeFormat := ast.GetImpliedNodeFormatForFile(fileName, packageJsonType) return ast.SourceFileMetaData{ PackageJsonType: packageJsonType, PackageJsonDirectory: packageJsonDirectory, ImpliedNodeFormat: impliedNodeFormat, } } func (p *fileLoader) parseSourceFile(t *parseTask) *ast.SourceFile { path := p.toPath(t.normalizedFilePath) options := p.projectReferenceFileMapper.getCompilerOptionsForFile(t) sourceFile := p.opts.Host.GetSourceFile(ast.SourceFileParseOptions{ FileName: t.normalizedFilePath, Path: path, CompilerOptions: ast.GetSourceFileAffectingCompilerOptions(t.normalizedFilePath, options), ExternalModuleIndicatorOptions: ast.GetExternalModuleIndicatorOptions(t.normalizedFilePath, options, t.metadata), JSDocParsingMode: p.opts.JSDocParsingMode, }) return sourceFile } func (p *fileLoader) resolveTripleslashPathReference(moduleName string, containingFile string, index int) resolvedRef { basePath := tspath.GetDirectoryPath(containingFile) referencedFileName := moduleName if !tspath.IsRootedDiskPath(moduleName) { referencedFileName = tspath.CombinePaths(basePath, moduleName) } return resolvedRef{ fileName: tspath.NormalizePath(referencedFileName), includeReason: &fileIncludeReason{ kind: fileIncludeKindReferenceFile, data: &referencedFileData{ file: p.toPath(containingFile), index: index, }, }, } } func (p *fileLoader) resolveTypeReferenceDirectives(t *parseTask) { file := t.file if len(file.TypeReferenceDirectives) == 0 { return } meta := t.metadata typeResolutionsInFile := make(module.ModeAwareCache[*module.ResolvedTypeReferenceDirective], len(file.TypeReferenceDirectives)) var typeResolutionsTrace []string for index, ref := range file.TypeReferenceDirectives { redirect, fileName := p.projectReferenceFileMapper.getRedirectForResolution(file) resolutionMode := getModeForTypeReferenceDirectiveInFile(ref, file, meta, module.GetCompilerOptionsWithRedirect(p.opts.Config.CompilerOptions(), redirect)) resolved, trace := p.resolver.ResolveTypeReferenceDirective(ref.FileName, fileName, resolutionMode, redirect) typeResolutionsInFile[module.ModeAwareCacheKey{Name: ref.FileName, Mode: resolutionMode}] = resolved includeReason := &fileIncludeReason{ kind: fileIncludeKindTypeReferenceDirective, data: &referencedFileData{ file: t.path, index: index, }, } typeResolutionsTrace = append(typeResolutionsTrace, trace...) if resolved.IsResolved() { t.addSubTask(resolvedRef{ fileName: resolved.ResolvedFileName, increaseDepth: resolved.IsExternalLibraryImport, elideOnDepth: false, isFromExternalLibrary: resolved.IsExternalLibraryImport, includeReason: includeReason, }, nil) } else { p.includeProcessor.addProcessingDiagnostic(&processingDiagnostic{ kind: processingDiagnosticKindUnknownReference, data: includeReason, }) } } t.typeResolutionsInFile = typeResolutionsInFile t.typeResolutionsTrace = typeResolutionsTrace } const externalHelpersModuleNameText = "tslib" // TODO(jakebailey): dedupe func (p *fileLoader) resolveImportsAndModuleAugmentations(t *parseTask) { file := t.file meta := t.metadata moduleNames := make([]*ast.Node, 0, len(file.Imports())+len(file.ModuleAugmentations)+2) isJavaScriptFile := ast.IsSourceFileJS(file) isExternalModuleFile := ast.IsExternalModule(file) redirect, fileName := p.projectReferenceFileMapper.getRedirectForResolution(file) optionsForFile := module.GetCompilerOptionsWithRedirect(p.opts.Config.CompilerOptions(), redirect) if isJavaScriptFile || (!file.IsDeclarationFile && (optionsForFile.GetIsolatedModules() || isExternalModuleFile)) { if optionsForFile.ImportHelpers.IsTrue() { specifier := p.createSyntheticImport(externalHelpersModuleNameText, file) moduleNames = append(moduleNames, specifier) t.importHelpersImportSpecifier = specifier } jsxImport := ast.GetJSXRuntimeImport(ast.GetJSXImplicitImportBase(optionsForFile, file), optionsForFile) if jsxImport != "" { specifier := p.createSyntheticImport(jsxImport, file) moduleNames = append(moduleNames, specifier) t.jsxRuntimeImportSpecifier = &jsxRuntimeImportSpecifier{ moduleReference: jsxImport, specifier: specifier, } } } importsStart := len(moduleNames) moduleNames = append(moduleNames, file.Imports()...) for _, imp := range file.ModuleAugmentations { if imp.Kind == ast.KindStringLiteral { moduleNames = append(moduleNames, imp) } // Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`. } if len(moduleNames) != 0 { resolutionsInFile := make(module.ModeAwareCache[*module.ResolvedModule], len(moduleNames)) var resolutionsTrace []string for index, entry := range moduleNames { moduleName := entry.Text() if moduleName == "" { continue } mode := getModeForUsageLocation(file.FileName(), meta, entry, optionsForFile) resolvedModule, trace := p.resolver.ResolveModuleName(moduleName, fileName, mode, redirect) resolutionsInFile[module.ModeAwareCacheKey{Name: moduleName, Mode: mode}] = resolvedModule resolutionsTrace = append(resolutionsTrace, trace...) if !resolvedModule.IsResolved() { continue } resolvedFileName := resolvedModule.ResolvedFileName isFromNodeModulesSearch := resolvedModule.IsExternalLibraryImport // Don't treat redirected files as JS files. isJsFile := !tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedTSExtensionsWithJsonFlat) && p.projectReferenceFileMapper.getRedirectParsedCommandLineForResolution(ast.NewHasFileName(resolvedFileName, p.toPath(resolvedFileName))) == nil isJsFileFromNodeModules := isFromNodeModulesSearch && isJsFile && strings.Contains(resolvedFileName, "/node_modules/") // add file to program only if: // - resolution was successful // - noResolve is falsy // - module name comes from the list of imports // - it's not a top level JavaScript module that exceeded the search max importIndex := index - importsStart shouldAddFile := moduleName != "" && module.GetResolutionDiagnostic(optionsForFile, resolvedModule, file) == nil && !optionsForFile.NoResolve.IsTrue() && !(isJsFile && !optionsForFile.GetAllowJS()) && (importIndex < 0 || (importIndex < len(file.Imports()) && (ast.IsInJSFile(file.Imports()[importIndex]) || file.Imports()[importIndex].Flags&ast.NodeFlagsJSDoc == 0))) if shouldAddFile { t.addSubTask(resolvedRef{ fileName: resolvedFileName, increaseDepth: resolvedModule.IsExternalLibraryImport, elideOnDepth: isJsFileFromNodeModules, isFromExternalLibrary: resolvedModule.IsExternalLibraryImport, includeReason: &fileIncludeReason{ kind: fileIncludeKindImport, data: &referencedFileData{ file: t.path, index: importIndex, synthetic: core.IfElse(importIndex < 0, entry, nil), }, }, }, nil) } } t.resolutionsInFile = resolutionsInFile t.resolutionsTrace = resolutionsTrace } } func (p *fileLoader) createSyntheticImport(text string, file *ast.SourceFile) *ast.Node { p.factoryMu.Lock() defer p.factoryMu.Unlock() externalHelpersModuleReference := p.factory.NewStringLiteral(text) importDecl := p.factory.NewImportDeclaration(nil, nil, externalHelpersModuleReference, nil) // !!! addInternalEmitFlags(importDecl, InternalEmitFlags.NeverApplyImportHelper); externalHelpersModuleReference.Parent = importDecl importDecl.Parent = file.AsNode() // !!! externalHelpersModuleReference.Flags &^= ast.NodeFlagsSynthesized // !!! importDecl.Flags &^= ast.NodeFlagsSynthesized return externalHelpersModuleReference } func (p *fileLoader) pathForLibFile(name string) *LibFile { if cached, ok := p.pathForLibFileCache.Load(name); ok { return cached } path := tspath.CombinePaths(p.defaultLibraryPath, name) replaced := false if p.opts.Config.CompilerOptions().LibReplacement.IsTrue() && name != "lib.d.ts" { libraryName := getLibraryNameFromLibFileName(name) resolveFrom := getInferredLibraryNameResolveFrom(p.opts.Config.CompilerOptions(), p.opts.Host.GetCurrentDirectory(), name) resolution, trace := p.resolver.ResolveModuleName(libraryName, resolveFrom, core.ModuleKindCommonJS, nil) if resolution.IsResolved() { path = resolution.ResolvedFileName replaced = true } p.pathForLibFileResolutions.LoadOrStore(p.toPath(resolveFrom), &libResolution{ libraryName: libraryName, resolution: resolution, trace: trace, }) } libPath, _ := p.pathForLibFileCache.LoadOrStore(name, &LibFile{name, path, replaced}) return libPath } func getLibraryNameFromLibFileName(libFileName string) string { // Support resolving to lib.dom.d.ts -> @typescript/lib-dom, and // lib.dom.iterable.d.ts -> @typescript/lib-dom/iterable // lib.es2015.symbol.wellknown.d.ts -> @typescript/lib-es2015/symbol-wellknown components := strings.Split(libFileName, ".") var path string if len(components) > 1 { path = components[1] } i := 2 for i < len(components) && components[i] != "" && components[i] != "d" { path += core.IfElse(i == 2, "/", "-") + components[i] i++ } return "@typescript/lib-" + path } func getInferredLibraryNameResolveFrom(options *core.CompilerOptions, currentDirectory string, libFileName string) string { var containingDirectory string if options.ConfigFilePath != "" { containingDirectory = tspath.GetDirectoryPath(options.ConfigFilePath) } else { containingDirectory = currentDirectory } return tspath.CombinePaths(containingDirectory, "__lib_node_modules_lookup_"+libFileName+"__.ts") } func getModeForTypeReferenceDirectiveInFile(ref *ast.FileReference, file *ast.SourceFile, meta ast.SourceFileMetaData, options *core.CompilerOptions) core.ResolutionMode { if ref.ResolutionMode != core.ResolutionModeNone { return ref.ResolutionMode } else { return getDefaultResolutionModeForFile(file.FileName(), meta, options) } } func getDefaultResolutionModeForFile(fileName string, meta ast.SourceFileMetaData, options *core.CompilerOptions) core.ResolutionMode { if importSyntaxAffectsModuleResolution(options) { return ast.GetImpliedNodeFormatForEmitWorker(fileName, options.GetEmitModuleKind(), meta) } else { return core.ResolutionModeNone } } func getModeForUsageLocation(fileName string, meta ast.SourceFileMetaData, usage *ast.StringLiteralLike, options *core.CompilerOptions) core.ResolutionMode { if ast.IsImportDeclaration(usage.Parent) || usage.Parent.Kind == ast.KindJSImportDeclaration || ast.IsExportDeclaration(usage.Parent) || ast.IsJSDocImportTag(usage.Parent) { isTypeOnly := ast.IsExclusivelyTypeOnlyImportOrExport(usage.Parent) if isTypeOnly { var override core.ResolutionMode var ok bool switch usage.Parent.Kind { case ast.KindImportDeclaration, ast.KindJSImportDeclaration: override, ok = usage.Parent.AsImportDeclaration().Attributes.GetResolutionModeOverride() case ast.KindExportDeclaration: override, ok = usage.Parent.AsExportDeclaration().Attributes.GetResolutionModeOverride() case ast.KindJSDocImportTag: override, ok = usage.Parent.AsJSDocImportTag().Attributes.GetResolutionModeOverride() } if ok { return override } } } if ast.IsLiteralTypeNode(usage.Parent) && ast.IsImportTypeNode(usage.Parent.Parent) { if override, ok := usage.Parent.Parent.AsImportTypeNode().Attributes.GetResolutionModeOverride(); ok { return override } } if options != nil && importSyntaxAffectsModuleResolution(options) { return getEmitSyntaxForUsageLocationWorker(fileName, meta, usage, options) } return core.ResolutionModeNone } func importSyntaxAffectsModuleResolution(options *core.CompilerOptions) bool { moduleResolution := options.GetModuleResolutionKind() return core.ModuleResolutionKindNode16 <= moduleResolution && moduleResolution <= core.ModuleResolutionKindNodeNext || options.GetResolvePackageJsonExports() || options.GetResolvePackageJsonImports() } func getEmitSyntaxForUsageLocationWorker(fileName string, meta ast.SourceFileMetaData, usage *ast.Node, options *core.CompilerOptions) core.ResolutionMode { if ast.IsRequireCall(usage.Parent, false /*requireStringLiteralLikeArgument*/) || ast.IsExternalModuleReference(usage.Parent) && ast.IsImportEqualsDeclaration(usage.Parent.Parent) { return core.ModuleKindCommonJS } fileEmitMode := ast.GetEmitModuleFormatOfFileWorker(fileName, options, meta) if ast.IsImportCall(ast.WalkUpParenthesizedExpressions(usage.Parent)) { if ast.ShouldTransformImportCall(fileName, options, fileEmitMode) { return core.ModuleKindCommonJS } else { return core.ModuleKindESNext } } // If we're in --module preserve on an input file, we know that an import // is an import. But if this is a declaration file, we'd prefer to use the // impliedNodeFormat. Since we want things to be consistent between the two, // we need to issue errors when the user writes ESM syntax in a definitely-CJS // file, until/unless declaration emit can indicate a true ESM import. On the // other hand, writing CJS syntax in a definitely-ESM file is fine, since declaration // emit preserves the CJS syntax. if fileEmitMode == core.ModuleKindCommonJS { return core.ModuleKindCommonJS } else { if fileEmitMode.IsNonNodeESM() || fileEmitMode == core.ModuleKindPreserve { return core.ModuleKindESNext } } return core.ModuleKindNone }