2025-10-15 10:12:44 +03:00

745 lines
29 KiB
Go

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
}