745 lines
29 KiB
Go
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
|
|
}
|