From 2ba4cf4f1e4f30696078d54f949e13910b920454 Mon Sep 17 00:00:00 2001 From: Egor Aristov Date: Wed, 15 Oct 2025 17:26:56 +0300 Subject: [PATCH] remove unused packages --- kitcom/internal/tsgo/compiler/checkerpool.go | 90 - kitcom/internal/tsgo/compiler/emitHost.go | 128 -- kitcom/internal/tsgo/compiler/emitter.go | 482 ----- kitcom/internal/tsgo/compiler/fileInclude.go | 341 ---- kitcom/internal/tsgo/compiler/fileloader.go | 744 ------- kitcom/internal/tsgo/compiler/filesparser.go | 280 --- kitcom/internal/tsgo/compiler/host.go | 88 - .../tsgo/compiler/includeprocessor.go | 144 -- .../internal/tsgo/compiler/knownsymlinks.go | 53 - kitcom/internal/tsgo/compiler/pkg.go | 2 - .../tsgo/compiler/processingDiagnostic.go | 134 -- kitcom/internal/tsgo/compiler/program.go | 1722 ----------------- kitcom/internal/tsgo/compiler/program_test.go | 314 --- .../compiler/projectreferencedtsfakinghost.go | 226 --- .../compiler/projectreferencefilemapper.go | 166 -- .../tsgo/compiler/projectreferenceparser.go | 111 -- 16 files changed, 5025 deletions(-) delete mode 100644 kitcom/internal/tsgo/compiler/checkerpool.go delete mode 100644 kitcom/internal/tsgo/compiler/emitHost.go delete mode 100644 kitcom/internal/tsgo/compiler/emitter.go delete mode 100644 kitcom/internal/tsgo/compiler/fileInclude.go delete mode 100644 kitcom/internal/tsgo/compiler/fileloader.go delete mode 100644 kitcom/internal/tsgo/compiler/filesparser.go delete mode 100644 kitcom/internal/tsgo/compiler/host.go delete mode 100644 kitcom/internal/tsgo/compiler/includeprocessor.go delete mode 100644 kitcom/internal/tsgo/compiler/knownsymlinks.go delete mode 100644 kitcom/internal/tsgo/compiler/pkg.go delete mode 100644 kitcom/internal/tsgo/compiler/processingDiagnostic.go delete mode 100644 kitcom/internal/tsgo/compiler/program.go delete mode 100644 kitcom/internal/tsgo/compiler/program_test.go delete mode 100644 kitcom/internal/tsgo/compiler/projectreferencedtsfakinghost.go delete mode 100644 kitcom/internal/tsgo/compiler/projectreferencefilemapper.go delete mode 100644 kitcom/internal/tsgo/compiler/projectreferenceparser.go diff --git a/kitcom/internal/tsgo/compiler/checkerpool.go b/kitcom/internal/tsgo/compiler/checkerpool.go deleted file mode 100644 index d697239..0000000 --- a/kitcom/internal/tsgo/compiler/checkerpool.go +++ /dev/null @@ -1,90 +0,0 @@ -package compiler - -import ( - "context" - "iter" - "slices" - "sync" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/checker" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" -) - -type CheckerPool interface { - GetChecker(ctx context.Context) (*checker.Checker, func()) - GetCheckerForFile(ctx context.Context, file *ast.SourceFile) (*checker.Checker, func()) - GetAllCheckers(ctx context.Context) ([]*checker.Checker, func()) - Files(checker *checker.Checker) iter.Seq[*ast.SourceFile] -} - -type checkerPool struct { - checkerCount int - program *Program - - createCheckersOnce sync.Once - checkers []*checker.Checker - fileAssociations map[*ast.SourceFile]*checker.Checker -} - -var _ CheckerPool = (*checkerPool)(nil) - -func newCheckerPool(checkerCount int, program *Program) *checkerPool { - pool := &checkerPool{ - program: program, - checkerCount: checkerCount, - checkers: make([]*checker.Checker, checkerCount), - } - - return pool -} - -func (p *checkerPool) GetCheckerForFile(ctx context.Context, file *ast.SourceFile) (*checker.Checker, func()) { - p.createCheckers() - checker := p.fileAssociations[file] - return checker, noop -} - -func (p *checkerPool) GetChecker(ctx context.Context) (*checker.Checker, func()) { - p.createCheckers() - checker := p.checkers[0] - return checker, noop -} - -func (p *checkerPool) createCheckers() { - p.createCheckersOnce.Do(func() { - wg := core.NewWorkGroup(p.program.SingleThreaded()) - for i := range p.checkerCount { - wg.Queue(func() { - p.checkers[i] = checker.NewChecker(p.program) - }) - } - - wg.RunAndWait() - - p.fileAssociations = make(map[*ast.SourceFile]*checker.Checker, len(p.program.files)) - for i, file := range p.program.files { - p.fileAssociations[file] = p.checkers[i%p.checkerCount] - } - }) -} - -func (p *checkerPool) GetAllCheckers(ctx context.Context) ([]*checker.Checker, func()) { - p.createCheckers() - return p.checkers, noop -} - -func (p *checkerPool) Files(checker *checker.Checker) iter.Seq[*ast.SourceFile] { - checkerIndex := slices.Index(p.checkers, checker) - return func(yield func(*ast.SourceFile) bool) { - for i, file := range p.program.files { - if i%p.checkerCount == checkerIndex { - if !yield(file) { - return - } - } - } - } -} - -func noop() {} diff --git a/kitcom/internal/tsgo/compiler/emitHost.go b/kitcom/internal/tsgo/compiler/emitHost.go deleted file mode 100644 index 4ecca3d..0000000 --- a/kitcom/internal/tsgo/compiler/emitHost.go +++ /dev/null @@ -1,128 +0,0 @@ -package compiler - -import ( - "context" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/module" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/outputpaths" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/declarations" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -// NOTE: EmitHost operations must be thread-safe -type EmitHost interface { - printer.EmitHost - declarations.DeclarationEmitHost - Options() *core.CompilerOptions - SourceFiles() []*ast.SourceFile - UseCaseSensitiveFileNames() bool - GetCurrentDirectory() string - CommonSourceDirectory() string - IsEmitBlocked(file string) bool -} - -var _ EmitHost = (*emitHost)(nil) - -// NOTE: emitHost operations must be thread-safe -type emitHost struct { - program *Program - emitResolver printer.EmitResolver -} - -func newEmitHost(ctx context.Context, program *Program, file *ast.SourceFile) (*emitHost, func()) { - checker, done := program.GetTypeCheckerForFile(ctx, file) - return &emitHost{ - program: program, - emitResolver: checker.GetEmitResolver(), - }, done -} - -func (host *emitHost) GetModeForUsageLocation(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) core.ResolutionMode { - return host.program.GetModeForUsageLocation(file, moduleSpecifier) -} - -func (host *emitHost) GetResolvedModuleFromModuleSpecifier(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) *module.ResolvedModule { - return host.program.GetResolvedModuleFromModuleSpecifier(file, moduleSpecifier) -} - -func (host *emitHost) GetDefaultResolutionModeForFile(file ast.HasFileName) core.ResolutionMode { - return host.program.GetDefaultResolutionModeForFile(file) -} - -func (host *emitHost) GetEmitModuleFormatOfFile(file ast.HasFileName) core.ModuleKind { - return host.program.GetEmitModuleFormatOfFile(file) -} - -func (host *emitHost) FileExists(path string) bool { - return host.program.FileExists(path) -} - -func (host *emitHost) GetGlobalTypingsCacheLocation() string { - return host.program.GetGlobalTypingsCacheLocation() -} - -func (host *emitHost) GetNearestAncestorDirectoryWithPackageJson(dirname string) string { - return host.program.GetNearestAncestorDirectoryWithPackageJson(dirname) -} - -func (host *emitHost) GetPackageJsonInfo(pkgJsonPath string) modulespecifiers.PackageJsonInfo { - return host.program.GetPackageJsonInfo(pkgJsonPath) -} - -func (host *emitHost) GetSourceOfProjectReferenceIfOutputIncluded(file ast.HasFileName) string { - return host.program.GetSourceOfProjectReferenceIfOutputIncluded(file) -} - -func (host *emitHost) GetProjectReferenceFromSource(path tspath.Path) *tsoptions.SourceOutputAndProjectReference { - return host.program.GetProjectReferenceFromSource(path) -} - -func (host *emitHost) GetRedirectTargets(path tspath.Path) []string { - return host.program.GetRedirectTargets(path) -} - -func (host *emitHost) GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags { - return host.GetEmitResolver().GetEffectiveDeclarationFlags(node, flags) -} - -func (host *emitHost) GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) declarations.OutputPaths { - // TODO: cache - return outputpaths.GetOutputPathsFor(file, host.Options(), host, forceDtsPaths) -} - -func (host *emitHost) GetResolutionModeOverride(node *ast.Node) core.ResolutionMode { - return host.GetEmitResolver().GetResolutionModeOverride(node) -} - -func (host *emitHost) GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile { - return host.program.GetSourceFileFromReference(origin, ref) -} - -func (host *emitHost) Options() *core.CompilerOptions { return host.program.Options() } -func (host *emitHost) SourceFiles() []*ast.SourceFile { return host.program.SourceFiles() } -func (host *emitHost) GetCurrentDirectory() string { return host.program.GetCurrentDirectory() } -func (host *emitHost) CommonSourceDirectory() string { return host.program.CommonSourceDirectory() } -func (host *emitHost) UseCaseSensitiveFileNames() bool { - return host.program.UseCaseSensitiveFileNames() -} - -func (host *emitHost) IsEmitBlocked(file string) bool { - return host.program.IsEmitBlocked(file) -} - -func (host *emitHost) WriteFile(fileName string, text string, writeByteOrderMark bool) error { - return host.program.Host().FS().WriteFile(fileName, text, writeByteOrderMark) -} - -func (host *emitHost) GetEmitResolver() printer.EmitResolver { - return host.emitResolver -} - -func (host *emitHost) IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool { - return host.program.IsSourceFileFromExternalLibrary(file) -} diff --git a/kitcom/internal/tsgo/compiler/emitter.go b/kitcom/internal/tsgo/compiler/emitter.go deleted file mode 100644 index 8b3f1e2..0000000 --- a/kitcom/internal/tsgo/compiler/emitter.go +++ /dev/null @@ -1,482 +0,0 @@ -package compiler - -import ( - "encoding/base64" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/outputpaths" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/sourcemap" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/declarations" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/estransforms" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/inliners" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/jsxtransforms" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/moduletransforms" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/tstransforms" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type EmitOnly byte - -const ( - EmitAll EmitOnly = iota - EmitOnlyJs - EmitOnlyDts - EmitOnlyForcedDts -) - -type emitter struct { - host EmitHost - emitOnly EmitOnly - emitterDiagnostics ast.DiagnosticsCollection - writer printer.EmitTextWriter - paths *outputpaths.OutputPaths - sourceFile *ast.SourceFile - emitResult EmitResult - writeFile func(fileName string, text string, writeByteOrderMark bool, data *WriteFileData) error -} - -func (e *emitter) emit() { - // !!! tracing - e.emitJSFile(e.sourceFile, e.paths.JsFilePath(), e.paths.SourceMapFilePath()) - e.emitDeclarationFile(e.sourceFile, e.paths.DeclarationFilePath(), e.paths.DeclarationMapPath()) - e.emitResult.Diagnostics = e.emitterDiagnostics.GetDiagnostics() -} - -func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, declarationFilePath string, declarationMapPath string) []*declarations.DeclarationTransformer { - transform := declarations.NewDeclarationTransformer(e.host, emitContext, e.host.Options(), declarationFilePath, declarationMapPath) - return []*declarations.DeclarationTransformer{transform} -} - -func getModuleTransformer(opts *transformers.TransformOptions) *transformers.Transformer { - switch opts.CompilerOptions.GetEmitModuleKind() { - case core.ModuleKindPreserve: - // `ESModuleTransformer` contains logic for preserving CJS input syntax in `--module preserve` - return moduletransforms.NewESModuleTransformer(opts) - - case core.ModuleKindESNext, - core.ModuleKindES2022, - core.ModuleKindES2020, - core.ModuleKindES2015, - core.ModuleKindNode20, - core.ModuleKindNode18, - core.ModuleKindNode16, - core.ModuleKindNodeNext, - core.ModuleKindCommonJS: - return moduletransforms.NewImpliedModuleTransformer(opts) - - default: - return moduletransforms.NewCommonJSModuleTransformer(opts) - } -} - -func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHost, sourceFile *ast.SourceFile) []*transformers.Transformer { - var tx []*transformers.Transformer - options := host.Options() - - // JS files don't use reference calculations as they don't do import elision, no need to calculate it - importElisionEnabled := !options.VerbatimModuleSyntax.IsTrue() && !ast.IsInJSFile(sourceFile.AsNode()) - - var emitResolver printer.EmitResolver - var referenceResolver binder.ReferenceResolver - if importElisionEnabled || options.GetJSXTransformEnabled() || !options.GetIsolatedModules() { // full emit resolver is needed for import ellision and const enum inlining - emitResolver = host.GetEmitResolver() - emitResolver.MarkLinkedReferencesRecursively(sourceFile) - referenceResolver = emitResolver - } else { - referenceResolver = binder.NewReferenceResolver(options, binder.ReferenceResolverHooks{}) - } - - opts := transformers.TransformOptions{ - Context: emitContext, - CompilerOptions: options, - Resolver: referenceResolver, - EmitResolver: emitResolver, - GetEmitModuleFormatOfFile: host.GetEmitModuleFormatOfFile, - } - - // transform TypeScript syntax - { - // erase types - tx = append(tx, tstransforms.NewTypeEraserTransformer(&opts)) - - // elide imports - if importElisionEnabled { - tx = append(tx, tstransforms.NewImportElisionTransformer(&opts)) - } - - // transform `enum`, `namespace`, and parameter properties - tx = append(tx, tstransforms.NewRuntimeSyntaxTransformer(&opts)) - } - - // !!! transform legacy decorator syntax - if options.GetJSXTransformEnabled() { - tx = append(tx, jsxtransforms.NewJSXTransformer(&opts)) - } - - downleveler := estransforms.GetESTransformer(&opts) - if downleveler != nil { - tx = append(tx, downleveler) - } - - // transform module syntax - tx = append(tx, getModuleTransformer(&opts)) - - // inlining (formerly done via substitutions) - if !options.GetIsolatedModules() { - tx = append(tx, inliners.NewConstEnumInliningTransformer(&opts)) - } - return tx -} - -func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath string, sourceMapFilePath string) { - options := e.host.Options() - - if sourceFile == nil || e.emitOnly != EmitAll && e.emitOnly != EmitOnlyJs || len(jsFilePath) == 0 { - return - } - - if options.NoEmit == core.TSTrue || e.host.IsEmitBlocked(jsFilePath) { - e.emitResult.EmitSkipped = true - return - } - - emitContext, putEmitContext := printer.GetEmitContext() - defer putEmitContext() - - for _, transformer := range getScriptTransformers(emitContext, e.host, sourceFile) { - sourceFile = transformer.TransformSourceFile(sourceFile) - } - - printerOptions := printer.PrinterOptions{ - RemoveComments: options.RemoveComments.IsTrue(), - NewLine: options.NewLine, - NoEmitHelpers: options.NoEmitHelpers.IsTrue(), - SourceMap: options.SourceMap.IsTrue(), - InlineSourceMap: options.InlineSourceMap.IsTrue(), - InlineSources: options.InlineSources.IsTrue(), - // !!! - } - - // create a printer to print the nodes - printer := printer.NewPrinter(printerOptions, printer.PrintHandlers{ - // !!! - }, emitContext) - - e.printSourceFile(jsFilePath, sourceMapFilePath, sourceFile, printer, shouldEmitSourceMaps(options, sourceFile)) -} - -func (e *emitter) emitDeclarationFile(sourceFile *ast.SourceFile, declarationFilePath string, declarationMapPath string) { - options := e.host.Options() - - if sourceFile == nil || e.emitOnly == EmitOnlyJs || len(declarationFilePath) == 0 { - return - } - - if e.emitOnly != EmitOnlyForcedDts && (options.NoEmit == core.TSTrue || e.host.IsEmitBlocked(declarationFilePath)) { - e.emitResult.EmitSkipped = true - return - } - - var diags []*ast.Diagnostic - emitContext, putEmitContext := printer.GetEmitContext() - defer putEmitContext() - for _, transformer := range e.getDeclarationTransformers(emitContext, declarationFilePath, declarationMapPath) { - sourceFile = transformer.TransformSourceFile(sourceFile) - diags = append(diags, transformer.GetDiagnostics()...) - } - - // !!! strada skipped emit if there were diagnostics - - printerOptions := printer.PrinterOptions{ - RemoveComments: options.RemoveComments.IsTrue(), - OnlyPrintJSDocStyle: true, - NewLine: options.NewLine, - NoEmitHelpers: options.NoEmitHelpers.IsTrue(), - SourceMap: options.DeclarationMap.IsTrue(), - InlineSourceMap: options.InlineSourceMap.IsTrue(), - InlineSources: options.InlineSources.IsTrue(), - // !!! - } - - // create a printer to print the nodes - printer := printer.NewPrinter(printerOptions, printer.PrintHandlers{ - // !!! - }, emitContext) - - for _, elem := range diags { - // Add declaration transform diagnostics to emit diagnostics - e.emitterDiagnostics.Add(elem) - } - e.printSourceFile(declarationFilePath, declarationMapPath, sourceFile, printer, e.emitOnly != EmitOnlyForcedDts && shouldEmitDeclarationSourceMaps(options, sourceFile)) -} - -func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, sourceFile *ast.SourceFile, printer_ *printer.Printer, shouldEmitSourceMaps bool) { - // !!! sourceMapGenerator - options := e.host.Options() - var sourceMapGenerator *sourcemap.Generator - if shouldEmitSourceMaps { - sourceMapGenerator = sourcemap.NewGenerator( - tspath.GetBaseFileName(tspath.NormalizeSlashes(jsFilePath)), - getSourceRoot(options), - e.getSourceMapDirectory(options, jsFilePath, sourceFile), - tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: e.host.UseCaseSensitiveFileNames(), - CurrentDirectory: e.host.GetCurrentDirectory(), - }, - ) - } - - printer_.Write(sourceFile.AsNode(), sourceFile, e.writer, sourceMapGenerator) - - sourceMapUrlPos := -1 - if sourceMapGenerator != nil { - if options.SourceMap.IsTrue() || options.InlineSourceMap.IsTrue() || options.GetAreDeclarationMapsEnabled() { - e.emitResult.SourceMaps = append(e.emitResult.SourceMaps, &SourceMapEmitResult{ - InputSourceFileNames: sourceMapGenerator.Sources(), - SourceMap: sourceMapGenerator.RawSourceMap(), - GeneratedFile: jsFilePath, - }) - } - - sourceMappingURL := e.getSourceMappingURL( - options, - sourceMapGenerator, - jsFilePath, - sourceMapFilePath, - sourceFile, - ) - - if len(sourceMappingURL) > 0 { - if !e.writer.IsAtStartOfLine() { - e.writer.RawWrite(core.IfElse(options.NewLine == core.NewLineKindCRLF, "\r\n", "\n")) - } - sourceMapUrlPos = e.writer.GetTextPos() - e.writer.WriteComment("//# sourceMappingURL=" + sourceMappingURL) - } - - // Write the source map - if len(sourceMapFilePath) > 0 { - sourceMap := sourceMapGenerator.String() - err := e.writeText(sourceMapFilePath, sourceMap, false /*writeByteOrderMark*/, nil) - if err != nil { - e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, jsFilePath, err.Error())) - } else { - e.emitResult.EmittedFiles = append(e.emitResult.EmittedFiles, sourceMapFilePath) - } - } - } else { - e.writer.WriteLine() - } - - // Write the output file - text := e.writer.String() - data := &WriteFileData{ - SourceMapUrlPos: sourceMapUrlPos, - Diagnostics: e.emitterDiagnostics.GetDiagnostics(), - } - err := e.writeText(jsFilePath, text, options.EmitBOM.IsTrue(), data) - skippedDtsWrite := data.SkippedDtsWrite - if err != nil { - e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, jsFilePath, err.Error())) - } else if !skippedDtsWrite { - e.emitResult.EmittedFiles = append(e.emitResult.EmittedFiles, jsFilePath) - } - - // Reset state - e.writer.Clear() -} - -func (e *emitter) writeText(fileName string, text string, writeByteOrderMark bool, data *WriteFileData) error { - if e.writeFile != nil { - return e.writeFile(fileName, text, writeByteOrderMark, data) - } - return e.host.WriteFile(fileName, text, writeByteOrderMark) -} - -func shouldEmitSourceMaps(mapOptions *core.CompilerOptions, sourceFile *ast.SourceFile) bool { - return (mapOptions.SourceMap.IsTrue() || mapOptions.InlineSourceMap.IsTrue()) && - !tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionJson) -} - -func shouldEmitDeclarationSourceMaps(mapOptions *core.CompilerOptions, sourceFile *ast.SourceFile) bool { - return mapOptions.DeclarationMap.IsTrue() && - !tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionJson) -} - -func getSourceRoot(mapOptions *core.CompilerOptions) string { - // Normalize source root and make sure it has trailing "/" so that it can be used to combine paths with the - // relative paths of the sources list in the sourcemap - sourceRoot := tspath.NormalizeSlashes(mapOptions.SourceRoot) - if len(sourceRoot) > 0 { - sourceRoot = tspath.EnsureTrailingDirectorySeparator(sourceRoot) - } - return sourceRoot -} - -func (e *emitter) getSourceMapDirectory(mapOptions *core.CompilerOptions, filePath string, sourceFile *ast.SourceFile) string { - if len(mapOptions.SourceRoot) > 0 { - return e.host.CommonSourceDirectory() - } - if len(mapOptions.MapRoot) > 0 { - sourceMapDir := tspath.NormalizeSlashes(mapOptions.MapRoot) - if sourceFile != nil { - // For modules or multiple emit files the mapRoot will have directory structure like the sources - // So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map - sourceMapDir = tspath.GetDirectoryPath(outputpaths.GetSourceFilePathInNewDir( - sourceFile.FileName(), - sourceMapDir, - e.host.GetCurrentDirectory(), - e.host.CommonSourceDirectory(), - e.host.UseCaseSensitiveFileNames(), - )) - } - if tspath.GetRootLength(sourceMapDir) == 0 { - // The relative paths are relative to the common directory - sourceMapDir = tspath.CombinePaths(e.host.CommonSourceDirectory(), sourceMapDir) - } - return sourceMapDir - } - return tspath.GetDirectoryPath(tspath.NormalizePath(filePath)) -} - -func (e *emitter) getSourceMappingURL(mapOptions *core.CompilerOptions, sourceMapGenerator *sourcemap.Generator, filePath string, sourceMapFilePath string, sourceFile *ast.SourceFile) string { - if mapOptions.InlineSourceMap.IsTrue() { - // Encode the sourceMap into the sourceMap url - sourceMapText := sourceMapGenerator.String() - base64SourceMapText := base64.StdEncoding.EncodeToString([]byte(sourceMapText)) - return "data:application/json;base64," + base64SourceMapText - } - - sourceMapFile := tspath.GetBaseFileName(tspath.NormalizeSlashes(sourceMapFilePath)) - if len(mapOptions.MapRoot) > 0 { - sourceMapDir := tspath.NormalizeSlashes(mapOptions.MapRoot) - if sourceFile != nil { - // For modules or multiple emit files the mapRoot will have directory structure like the sources - // So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map - sourceMapDir = tspath.GetDirectoryPath(outputpaths.GetSourceFilePathInNewDir( - sourceFile.FileName(), - sourceMapDir, - e.host.GetCurrentDirectory(), - e.host.CommonSourceDirectory(), - e.host.UseCaseSensitiveFileNames(), - )) - } - if tspath.GetRootLength(sourceMapDir) == 0 { - // The relative paths are relative to the common directory - sourceMapDir = tspath.CombinePaths(e.host.CommonSourceDirectory(), sourceMapDir) - return stringutil.EncodeURI( - tspath.GetRelativePathToDirectoryOrUrl( - tspath.GetDirectoryPath(tspath.NormalizePath(filePath)), // get the relative sourceMapDir path based on jsFilePath - tspath.CombinePaths(sourceMapDir, sourceMapFile), // this is where user expects to see sourceMap - /*isAbsolutePathAnUrl*/ true, - tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: e.host.UseCaseSensitiveFileNames(), - CurrentDirectory: e.host.GetCurrentDirectory(), - }, - ), - ) - } else { - return stringutil.EncodeURI(tspath.CombinePaths(sourceMapDir, sourceMapFile)) - } - } - return stringutil.EncodeURI(sourceMapFile) -} - -type SourceFileMayBeEmittedHost interface { - Options() *core.CompilerOptions - GetProjectReferenceFromSource(path tspath.Path) *tsoptions.SourceOutputAndProjectReference - IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool - GetCurrentDirectory() string - UseCaseSensitiveFileNames() bool - SourceFiles() []*ast.SourceFile -} - -func sourceFileMayBeEmitted(sourceFile *ast.SourceFile, host SourceFileMayBeEmittedHost, forceDtsEmit bool) bool { - // TODO: move this to outputpaths? - - options := host.Options() - // Js files are emitted only if option is enabled - if options.NoEmitForJsFiles.IsTrue() && ast.IsSourceFileJS(sourceFile) { - return false - } - - // Declaration files are not emitted - if sourceFile.IsDeclarationFile { - return false - } - - // Source file from node_modules are not emitted - if host.IsSourceFileFromExternalLibrary(sourceFile) { - return false - } - - // forcing dts emit => file needs to be emitted - if forceDtsEmit { - return true - } - - // Check other conditions for file emit - // Source files from referenced projects are not emitted - if host.GetProjectReferenceFromSource(sourceFile.Path()) != nil { - return false - } - - // Any non json file should be emitted - if !ast.IsJsonSourceFile(sourceFile) { - return true - } - - // Json file is not emitted if outDir is not specified - if options.OutDir == "" { - return false - } - - // Otherwise if rootDir or composite config file, we know common sourceDir and can check if file would be emitted in same location - if options.RootDir != "" || (options.Composite.IsTrue() && options.ConfigFilePath != "") { - commonDir := tspath.GetNormalizedAbsolutePath(outputpaths.GetCommonSourceDirectory(options, func() []string { return nil }, host.GetCurrentDirectory(), host.UseCaseSensitiveFileNames()), host.GetCurrentDirectory()) - outputPath := outputpaths.GetSourceFilePathInNewDirWorker(sourceFile.FileName(), options.OutDir, host.GetCurrentDirectory(), commonDir, host.UseCaseSensitiveFileNames()) - if tspath.ComparePaths(sourceFile.FileName(), outputPath, tspath.ComparePathsOptions{ - UseCaseSensitiveFileNames: host.UseCaseSensitiveFileNames(), - CurrentDirectory: host.GetCurrentDirectory(), - }) == 0 { - return false - } - } - - return true -} - -func getSourceFilesToEmit(host SourceFileMayBeEmittedHost, targetSourceFile *ast.SourceFile, forceDtsEmit bool) []*ast.SourceFile { - var sourceFiles []*ast.SourceFile - if targetSourceFile != nil { - sourceFiles = []*ast.SourceFile{targetSourceFile} - } else { - sourceFiles = host.SourceFiles() - } - return core.Filter(sourceFiles, func(sourceFile *ast.SourceFile) bool { - return sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit) - }) -} - -func isSourceFileNotJson(file *ast.SourceFile) bool { - return !ast.IsJsonSourceFile(file) -} - -func getDeclarationDiagnostics(host EmitHost, file *ast.SourceFile) []*ast.Diagnostic { - // TODO: use p.getSourceFilesToEmit cache - fullFiles := core.Filter(getSourceFilesToEmit(host, file, false), isSourceFileNotJson) - if !core.Some(fullFiles, func(f *ast.SourceFile) bool { return f == file }) { - return []*ast.Diagnostic{} - } - options := host.Options() - transform := declarations.NewDeclarationTransformer(host, nil, options, "", "") - transform.TransformSourceFile(file) - return transform.GetDiagnostics() -} diff --git a/kitcom/internal/tsgo/compiler/fileInclude.go b/kitcom/internal/tsgo/compiler/fileInclude.go deleted file mode 100644 index 4aaf31f..0000000 --- a/kitcom/internal/tsgo/compiler/fileInclude.go +++ /dev/null @@ -1,341 +0,0 @@ -package compiler - -import ( - "fmt" - "sync" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/module" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type fileIncludeKind int - -const ( - // References from file - fileIncludeKindImport = iota - fileIncludeKindReferenceFile - fileIncludeKindTypeReferenceDirective - fileIncludeKindLibReferenceDirective - - fileIncludeKindRootFile - fileIncludeKindSourceFromProjectReference - fileIncludeKindOutputFromProjectReference - fileIncludeKindLibFile - fileIncludeKindAutomaticTypeDirectiveFile -) - -type fileIncludeReason struct { - kind fileIncludeKind - data any - - // Uses relative file name - relativeFileNameDiag *ast.Diagnostic - relativeFileNameDiagOnce sync.Once - - // Uses file name as is - diag *ast.Diagnostic - diagOnce sync.Once -} - -type referencedFileData struct { - file tspath.Path - index int - synthetic *ast.Node -} - -type referenceFileLocation struct { - file *ast.SourceFile - node *ast.Node - ref *ast.FileReference - packageId module.PackageId - isSynthetic bool -} - -func (r *referenceFileLocation) text() string { - if r.node != nil { - if !ast.NodeIsSynthesized(r.node) { - return r.file.Text()[scanner.SkipTrivia(r.file.Text(), r.node.Loc.Pos()):r.node.End()] - } else { - return fmt.Sprintf(`"%s"`, r.node.Text()) - } - } else { - return r.file.Text()[r.ref.Pos():r.ref.End()] - } -} - -func (r *referenceFileLocation) diagnosticAt(message *diagnostics.Message, args ...any) *ast.Diagnostic { - if r.node != nil { - return tsoptions.CreateDiagnosticForNodeInSourceFile(r.file, r.node, message, args...) - } else { - return ast.NewDiagnostic(r.file, r.ref.TextRange, message, args...) - } -} - -type automaticTypeDirectiveFileData struct { - typeReference string - packageId module.PackageId -} - -func (r *fileIncludeReason) asIndex() int { - return r.data.(int) -} - -func (r *fileIncludeReason) asLibFileIndex() (int, bool) { - index, ok := r.data.(int) - return index, ok -} - -func (r *fileIncludeReason) isReferencedFile() bool { - return r != nil && r.kind <= fileIncludeKindLibReferenceDirective -} - -func (r *fileIncludeReason) asReferencedFileData() *referencedFileData { - return r.data.(*referencedFileData) -} - -func (r *fileIncludeReason) asAutomaticTypeDirectiveFileData() *automaticTypeDirectiveFileData { - return r.data.(*automaticTypeDirectiveFileData) -} - -func (r *fileIncludeReason) getReferencedLocation(program *Program) *referenceFileLocation { - ref := r.asReferencedFileData() - file := program.GetSourceFileByPath(ref.file) - switch r.kind { - case fileIncludeKindImport: - var specifier *ast.Node - var isSynthetic bool - if ref.synthetic != nil { - specifier = ref.synthetic - isSynthetic = true - } else if ref.index < len(file.Imports()) { - specifier = file.Imports()[ref.index] - } else { - augIndex := len(file.Imports()) - for _, imp := range file.ModuleAugmentations { - if imp.Kind == ast.KindStringLiteral { - if augIndex == ref.index { - specifier = imp - break - } - augIndex++ - } - } - } - resolution := program.GetResolvedModuleFromModuleSpecifier(file, specifier) - return &referenceFileLocation{ - file: file, - node: specifier, - packageId: resolution.PackageId, - isSynthetic: isSynthetic, - } - case fileIncludeKindReferenceFile: - return &referenceFileLocation{ - file: file, - ref: file.ReferencedFiles[ref.index], - } - case fileIncludeKindTypeReferenceDirective: - return &referenceFileLocation{ - file: file, - ref: file.TypeReferenceDirectives[ref.index], - } - case fileIncludeKindLibReferenceDirective: - return &referenceFileLocation{ - file: file, - ref: file.LibReferenceDirectives[ref.index], - } - default: - panic(fmt.Sprintf("unknown reason: %v", r.kind)) - } -} - -func (r *fileIncludeReason) toDiagnostic(program *Program, relativeFileName bool) *ast.Diagnostic { - if relativeFileName { - r.relativeFileNameDiagOnce.Do(func() { - r.relativeFileNameDiag = r.computeDiagnostic(program, func(fileName string) string { - return tspath.GetRelativePathFromDirectory(program.GetCurrentDirectory(), fileName, program.comparePathsOptions) - }) - }) - return r.relativeFileNameDiag - } else { - r.diagOnce.Do(func() { - r.diag = r.computeDiagnostic(program, func(fileName string) string { return fileName }) - }) - return r.diag - } -} - -func (r *fileIncludeReason) computeDiagnostic(program *Program, toFileName func(string) string) *ast.Diagnostic { - if r.isReferencedFile() { - return r.computeReferenceFileDiagnostic(program, toFileName) - } - switch r.kind { - case fileIncludeKindRootFile: - if program.opts.Config.ConfigFile != nil { - config := program.opts.Config - fileName := tspath.GetNormalizedAbsolutePath(config.FileNames()[r.asIndex()], program.GetCurrentDirectory()) - if matchedFileSpec := config.GetMatchedFileSpec(fileName); matchedFileSpec != "" { - return ast.NewCompilerDiagnostic(diagnostics.Part_of_files_list_in_tsconfig_json, matchedFileSpec, toFileName(fileName)) - } else if matchedIncludeSpec, isDefaultIncludeSpec := config.GetMatchedIncludeSpec(fileName); matchedIncludeSpec != "" { - if isDefaultIncludeSpec { - return ast.NewCompilerDiagnostic(diagnostics.Matched_by_default_include_pattern_Asterisk_Asterisk_Slash_Asterisk) - } else { - return ast.NewCompilerDiagnostic(diagnostics.Matched_by_include_pattern_0_in_1, matchedIncludeSpec, toFileName(config.ConfigName())) - } - } else { - return ast.NewCompilerDiagnostic(diagnostics.Root_file_specified_for_compilation) - } - } else { - return ast.NewCompilerDiagnostic(diagnostics.Root_file_specified_for_compilation) - } - case fileIncludeKindSourceFromProjectReference, - fileIncludeKindOutputFromProjectReference: - diag := core.IfElse( - r.kind == fileIncludeKindOutputFromProjectReference, - diagnostics.Output_from_referenced_project_0_included_because_module_is_specified_as_none, - diagnostics.Source_from_referenced_project_0_included_because_module_is_specified_as_none, - ) - referencedResolvedRef := program.projectReferenceFileMapper.getResolvedProjectReferences()[r.asIndex()] - return ast.NewCompilerDiagnostic(diag, toFileName(referencedResolvedRef.ConfigName())) - case fileIncludeKindAutomaticTypeDirectiveFile: - data := r.asAutomaticTypeDirectiveFileData() - if program.Options().Types != nil { - if data.packageId.Name != "" { - return ast.NewCompilerDiagnostic(diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions_with_packageId_1, data.typeReference, data.packageId.String()) - } else { - return ast.NewCompilerDiagnostic(diagnostics.Entry_point_of_type_library_0_specified_in_compilerOptions, data.typeReference) - } - } else { - if data.packageId.Name != "" { - return ast.NewCompilerDiagnostic(diagnostics.Entry_point_for_implicit_type_library_0_with_packageId_1, data.typeReference, data.packageId.String()) - } else { - return ast.NewCompilerDiagnostic(diagnostics.Entry_point_for_implicit_type_library_0, data.typeReference) - } - } - case fileIncludeKindLibFile: - if index, ok := r.asLibFileIndex(); ok { - return ast.NewCompilerDiagnostic(diagnostics.Library_0_specified_in_compilerOptions, program.Options().Lib[index]) - } else if target := program.Options().GetEmitScriptTarget().String(); target != "" { - return ast.NewCompilerDiagnostic(diagnostics.Default_library_for_target_0, target) - } else { - return ast.NewCompilerDiagnostic(diagnostics.Default_library) - } - default: - panic(fmt.Sprintf("unknown reason: %v", r.kind)) - } -} - -func (r *fileIncludeReason) computeReferenceFileDiagnostic(program *Program, toFileName func(string) string) *ast.Diagnostic { - referenceLocation := program.includeProcessor.getReferenceLocation(r, program) - referenceText := referenceLocation.text() - switch r.kind { - case fileIncludeKindImport: - if !referenceLocation.isSynthetic { - if referenceLocation.packageId.Name != "" { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) - } else { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1, referenceText, toFileName(referenceLocation.file.FileName())) - } - } else if specifier, ok := program.importHelpersImportSpecifiers[referenceLocation.file.Path()]; ok && specifier == referenceLocation.node { - if referenceLocation.packageId.Name != "" { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_importHelpers_as_specified_in_compilerOptions, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) - } else { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_to_import_importHelpers_as_specified_in_compilerOptions, referenceText, toFileName(referenceLocation.file.FileName())) - } - } else { - if referenceLocation.packageId.Name != "" { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_with_packageId_2_to_import_jsx_and_jsxs_factory_functions, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) - } else { - return ast.NewCompilerDiagnostic(diagnostics.Imported_via_0_from_file_1_to_import_jsx_and_jsxs_factory_functions, referenceText, toFileName(referenceLocation.file.FileName())) - } - } - case fileIncludeKindReferenceFile: - return ast.NewCompilerDiagnostic(diagnostics.Referenced_via_0_from_file_1, referenceText, toFileName(referenceLocation.file.FileName())) - case fileIncludeKindTypeReferenceDirective: - if referenceLocation.packageId.Name != "" { - return ast.NewCompilerDiagnostic(diagnostics.Type_library_referenced_via_0_from_file_1_with_packageId_2, referenceText, toFileName(referenceLocation.file.FileName()), referenceLocation.packageId.String()) - } else { - return ast.NewCompilerDiagnostic(diagnostics.Type_library_referenced_via_0_from_file_1, referenceText, toFileName(referenceLocation.file.FileName())) - } - case fileIncludeKindLibReferenceDirective: - return ast.NewCompilerDiagnostic(diagnostics.Library_referenced_via_0_from_file_1, referenceText, toFileName(referenceLocation.file.FileName())) - default: - panic(fmt.Sprintf("unknown reason: %v", r.kind)) - } -} - -func (r *fileIncludeReason) toRelatedInfo(program *Program) *ast.Diagnostic { - if r.isReferencedFile() { - return r.computeReferenceFileRelatedInfo(program) - } - if program.opts.Config.ConfigFile == nil { - return nil - } - config := program.opts.Config - switch r.kind { - case fileIncludeKindRootFile: - fileName := tspath.GetNormalizedAbsolutePath(config.FileNames()[r.asIndex()], program.GetCurrentDirectory()) - if matchedFileSpec := config.GetMatchedFileSpec(fileName); matchedFileSpec != "" { - if filesNode := tsoptions.GetTsConfigPropArrayElementValue(config.ConfigFile.SourceFile, "files", matchedFileSpec); filesNode != nil { - return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, filesNode.AsNode(), diagnostics.File_is_matched_by_files_list_specified_here) - } - } else if matchedIncludeSpec, isDefaultIncludeSpec := config.GetMatchedIncludeSpec(fileName); matchedIncludeSpec != "" && !isDefaultIncludeSpec { - if includeNode := tsoptions.GetTsConfigPropArrayElementValue(config.ConfigFile.SourceFile, "include", matchedIncludeSpec); includeNode != nil { - return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, includeNode.AsNode(), diagnostics.File_is_matched_by_include_pattern_specified_here) - } - } - case fileIncludeKindSourceFromProjectReference, - fileIncludeKindOutputFromProjectReference: - return tsoptions.CreateDiagnosticAtReferenceSyntax( - config, - r.asIndex(), - core.IfElse( - r.kind == fileIncludeKindOutputFromProjectReference, - diagnostics.File_is_output_from_referenced_project_specified_here, - diagnostics.File_is_source_from_referenced_project_specified_here, - )) - case fileIncludeKindAutomaticTypeDirectiveFile: - if program.Options().Types != nil { - data := r.asAutomaticTypeDirectiveFileData() - if typesSyntax := tsoptions.GetOptionsSyntaxByArrayElementValue(program.includeProcessor.getCompilerOptionsObjectLiteralSyntax(program), "types", data.typeReference); typesSyntax != nil { - return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, typesSyntax.AsNode(), diagnostics.File_is_entry_point_of_type_library_specified_here) - } - } - case fileIncludeKindLibFile: - if index, ok := r.asLibFileIndex(); ok { - if libSyntax := tsoptions.GetOptionsSyntaxByArrayElementValue(program.includeProcessor.getCompilerOptionsObjectLiteralSyntax(program), "lib", program.Options().Lib[index]); libSyntax != nil { - return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, libSyntax.AsNode(), diagnostics.File_is_library_specified_here) - } - } else if target := program.Options().GetEmitScriptTarget().String(); target != "" { - if targetValueSyntax := tsoptions.ForEachPropertyAssignment(program.includeProcessor.getCompilerOptionsObjectLiteralSyntax(program), "target", tsoptions.GetCallbackForFindingPropertyAssignmentByValue(target)); targetValueSyntax != nil { - return tsoptions.CreateDiagnosticForNodeInSourceFile(config.ConfigFile.SourceFile, targetValueSyntax.AsNode(), diagnostics.File_is_default_library_for_target_specified_here) - } - } - default: - panic(fmt.Sprintf("unknown reason: %v", r.kind)) - } - return nil -} - -func (r *fileIncludeReason) computeReferenceFileRelatedInfo(program *Program) *ast.Diagnostic { - referenceLocation := program.includeProcessor.getReferenceLocation(r, program) - if referenceLocation.isSynthetic { - return nil - } - switch r.kind { - case fileIncludeKindImport: - return referenceLocation.diagnosticAt(diagnostics.File_is_included_via_import_here) - case fileIncludeKindReferenceFile: - return referenceLocation.diagnosticAt(diagnostics.File_is_included_via_reference_here) - case fileIncludeKindTypeReferenceDirective: - return referenceLocation.diagnosticAt(diagnostics.File_is_included_via_type_library_reference_here) - case fileIncludeKindLibReferenceDirective: - return referenceLocation.diagnosticAt(diagnostics.File_is_included_via_library_reference_here) - default: - panic(fmt.Sprintf("unknown reason: %v", r.kind)) - } -} diff --git a/kitcom/internal/tsgo/compiler/fileloader.go b/kitcom/internal/tsgo/compiler/fileloader.go deleted file mode 100644 index 2fc9cf0..0000000 --- a/kitcom/internal/tsgo/compiler/fileloader.go +++ /dev/null @@ -1,744 +0,0 @@ -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 -} diff --git a/kitcom/internal/tsgo/compiler/filesparser.go b/kitcom/internal/tsgo/compiler/filesparser.go deleted file mode 100644 index e6b2c73..0000000 --- a/kitcom/internal/tsgo/compiler/filesparser.go +++ /dev/null @@ -1,280 +0,0 @@ -package compiler - -import ( - "math" - "sync" - - "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 parseTask struct { - normalizedFilePath string - path tspath.Path - file *ast.SourceFile - libFile *LibFile - redirectedParseTask *parseTask - subTasks []*parseTask - loaded bool - isForAutomaticTypeDirective bool - includeReason *fileIncludeReason - - metadata ast.SourceFileMetaData - resolutionsInFile module.ModeAwareCache[*module.ResolvedModule] - resolutionsTrace []string - typeResolutionsInFile module.ModeAwareCache[*module.ResolvedTypeReferenceDirective] - typeResolutionsTrace []string - resolutionDiagnostics []*ast.Diagnostic - importHelpersImportSpecifier *ast.Node - jsxRuntimeImportSpecifier *jsxRuntimeImportSpecifier - increaseDepth bool - elideOnDepth bool - - // Track if this file is from an external library (node_modules) - // This mirrors the TypeScript currentNodeModulesDepth > 0 check - fromExternalLibrary bool - - loadedTask *parseTask - allIncludeReasons []*fileIncludeReason -} - -func (t *parseTask) FileName() string { - return t.normalizedFilePath -} - -func (t *parseTask) Path() tspath.Path { - return t.path -} - -func (t *parseTask) isRoot() bool { - // Intentionally not checking t.includeReason != nil to ensure we can catch cases for missing include reason - return !t.isForAutomaticTypeDirective && (t.includeReason.kind == fileIncludeKindRootFile || t.includeReason.kind == fileIncludeKindLibFile) -} - -func (t *parseTask) load(loader *fileLoader) { - t.loaded = true - t.path = loader.toPath(t.normalizedFilePath) - if t.isForAutomaticTypeDirective { - t.loadAutomaticTypeDirectives(loader) - return - } - redirect := loader.projectReferenceFileMapper.getParseFileRedirect(t) - if redirect != "" { - t.redirect(loader, redirect) - return - } - - loader.totalFileCount.Add(1) - if t.libFile != nil { - loader.libFileCount.Add(1) - } - - t.metadata = loader.loadSourceFileMetaData(t.normalizedFilePath) - file := loader.parseSourceFile(t) - if file == nil { - return - } - - t.file = file - t.subTasks = make([]*parseTask, 0, len(file.ReferencedFiles)+len(file.Imports())+len(file.ModuleAugmentations)) - - for index, ref := range file.ReferencedFiles { - resolvedPath := loader.resolveTripleslashPathReference(ref.FileName, file.FileName(), index) - t.addSubTask(resolvedPath, nil) - } - - compilerOptions := loader.opts.Config.CompilerOptions() - loader.resolveTypeReferenceDirectives(t) - - if compilerOptions.NoLib != core.TSTrue { - for index, lib := range file.LibReferenceDirectives { - includeReason := &fileIncludeReason{ - kind: fileIncludeKindLibReferenceDirective, - data: &referencedFileData{ - file: t.path, - index: index, - }, - } - if name, ok := tsoptions.GetLibFileName(lib.FileName); ok { - libFile := loader.pathForLibFile(name) - t.addSubTask(resolvedRef{ - fileName: libFile.path, - includeReason: includeReason, - }, libFile) - } else { - loader.includeProcessor.addProcessingDiagnostic(&processingDiagnostic{ - kind: processingDiagnosticKindUnknownReference, - data: includeReason, - }) - } - } - } - - loader.resolveImportsAndModuleAugmentations(t) -} - -func (t *parseTask) redirect(loader *fileLoader, fileName string) { - t.redirectedParseTask = &parseTask{ - normalizedFilePath: tspath.NormalizePath(fileName), - libFile: t.libFile, - fromExternalLibrary: t.fromExternalLibrary, - includeReason: t.includeReason, - } - // increaseDepth and elideOnDepth are not copied to redirects, otherwise their depth would be double counted. - t.subTasks = []*parseTask{t.redirectedParseTask} -} - -func (t *parseTask) loadAutomaticTypeDirectives(loader *fileLoader) { - toParseTypeRefs, typeResolutionsInFile, typeResolutionsTrace := loader.resolveAutomaticTypeDirectives(t.normalizedFilePath) - t.typeResolutionsInFile = typeResolutionsInFile - t.typeResolutionsTrace = typeResolutionsTrace - for _, typeResolution := range toParseTypeRefs { - t.addSubTask(typeResolution, nil) - } -} - -type resolvedRef struct { - fileName string - increaseDepth bool - elideOnDepth bool - isFromExternalLibrary bool - includeReason *fileIncludeReason -} - -func (t *parseTask) addSubTask(ref resolvedRef, libFile *LibFile) { - normalizedFilePath := tspath.NormalizePath(ref.fileName) - subTask := &parseTask{ - normalizedFilePath: normalizedFilePath, - libFile: libFile, - increaseDepth: ref.increaseDepth, - elideOnDepth: ref.elideOnDepth, - fromExternalLibrary: ref.isFromExternalLibrary, - includeReason: ref.includeReason, - } - t.subTasks = append(t.subTasks, subTask) -} - -type filesParser struct { - wg core.WorkGroup - tasksByFileName collections.SyncMap[string, *queuedParseTask] - maxDepth int -} - -type queuedParseTask struct { - task *parseTask - mu sync.Mutex - lowestDepth int - fromExternalLibrary bool -} - -func (w *filesParser) parse(loader *fileLoader, tasks []*parseTask) { - w.start(loader, tasks, 0, false) - w.wg.RunAndWait() -} - -func (w *filesParser) start(loader *fileLoader, tasks []*parseTask, depth int, isFromExternalLibrary bool) { - for i, task := range tasks { - taskIsFromExternalLibrary := isFromExternalLibrary || task.fromExternalLibrary - newTask := &queuedParseTask{task: task, lowestDepth: math.MaxInt} - loadedTask, loaded := w.tasksByFileName.LoadOrStore(task.FileName(), newTask) - task = loadedTask.task - if loaded { - tasks[i].loadedTask = task - // Add in the loaded task's external-ness. - taskIsFromExternalLibrary = taskIsFromExternalLibrary || task.fromExternalLibrary - } - - w.wg.Queue(func() { - loadedTask.mu.Lock() - defer loadedTask.mu.Unlock() - - startSubtasks := false - - currentDepth := depth - if task.increaseDepth { - currentDepth++ - } - if currentDepth < loadedTask.lowestDepth { - // If we're seeing this task at a lower depth than before, - // reprocess its subtasks to ensure they are loaded. - loadedTask.lowestDepth = currentDepth - startSubtasks = true - } - - if !task.isRoot() && taskIsFromExternalLibrary && !loadedTask.fromExternalLibrary { - // If we're seeing this task now as an external library, - // reprocess its subtasks to ensure they are also marked as external. - loadedTask.fromExternalLibrary = true - startSubtasks = true - } - - if task.elideOnDepth && currentDepth > w.maxDepth { - return - } - - if !task.loaded { - task.load(loader) - } - - if startSubtasks { - w.start(loader, task.subTasks, loadedTask.lowestDepth, loadedTask.fromExternalLibrary) - } - }) - } -} - -func (w *filesParser) collect(loader *fileLoader, tasks []*parseTask, iterate func(*parseTask)) { - // Mark all tasks we saw as external after the fact. - w.tasksByFileName.Range(func(key string, value *queuedParseTask) bool { - if value.fromExternalLibrary { - value.task.fromExternalLibrary = true - } - return true - }) - w.collectWorker(loader, tasks, iterate, collections.Set[*parseTask]{}) -} - -func (w *filesParser) collectWorker(loader *fileLoader, tasks []*parseTask, iterate func(*parseTask), seen collections.Set[*parseTask]) { - for _, task := range tasks { - // Exclude automatic type directive tasks from include reason processing, - // as these are internal implementation details and should not contribute - // to the reasons for including files. - if task.redirectedParseTask == nil && !task.isForAutomaticTypeDirective { - includeReason := task.includeReason - if task.loadedTask != nil { - task = task.loadedTask - } - w.addIncludeReason(loader, task, includeReason) - } - // ensure we only walk each task once - if !task.loaded || !seen.AddIfAbsent(task) { - continue - } - for _, trace := range task.typeResolutionsTrace { - loader.opts.Host.Trace(trace) - } - for _, trace := range task.resolutionsTrace { - loader.opts.Host.Trace(trace) - } - if subTasks := task.subTasks; len(subTasks) > 0 { - w.collectWorker(loader, subTasks, iterate, seen) - } - iterate(task) - } -} - -func (w *filesParser) addIncludeReason(loader *fileLoader, task *parseTask, reason *fileIncludeReason) { - if task.redirectedParseTask != nil { - w.addIncludeReason(loader, task.redirectedParseTask, reason) - } else if task.loaded { - if existing, ok := loader.includeProcessor.fileIncludeReasons[task.path]; ok { - loader.includeProcessor.fileIncludeReasons[task.path] = append(existing, reason) - } else { - loader.includeProcessor.fileIncludeReasons[task.path] = []*fileIncludeReason{reason} - } - } -} diff --git a/kitcom/internal/tsgo/compiler/host.go b/kitcom/internal/tsgo/compiler/host.go deleted file mode 100644 index d32df1f..0000000 --- a/kitcom/internal/tsgo/compiler/host.go +++ /dev/null @@ -1,88 +0,0 @@ -package compiler - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/cachedvfs" -) - -type CompilerHost interface { - FS() vfs.FS - DefaultLibraryPath() string - GetCurrentDirectory() string - Trace(msg string) - GetSourceFile(opts ast.SourceFileParseOptions) *ast.SourceFile - GetResolvedProjectReference(fileName string, path tspath.Path) *tsoptions.ParsedCommandLine -} - -var _ CompilerHost = (*compilerHost)(nil) - -type compilerHost struct { - currentDirectory string - fs vfs.FS - defaultLibraryPath string - extendedConfigCache tsoptions.ExtendedConfigCache - trace func(msg string) -} - -func NewCachedFSCompilerHost( - currentDirectory string, - fs vfs.FS, - defaultLibraryPath string, - extendedConfigCache tsoptions.ExtendedConfigCache, - trace func(msg string), -) CompilerHost { - return NewCompilerHost(currentDirectory, cachedvfs.From(fs), defaultLibraryPath, extendedConfigCache, trace) -} - -func NewCompilerHost( - currentDirectory string, - fs vfs.FS, - defaultLibraryPath string, - extendedConfigCache tsoptions.ExtendedConfigCache, - trace func(msg string), -) CompilerHost { - if trace == nil { - trace = func(msg string) {} - } - return &compilerHost{ - currentDirectory: currentDirectory, - fs: fs, - defaultLibraryPath: defaultLibraryPath, - extendedConfigCache: extendedConfigCache, - trace: trace, - } -} - -func (h *compilerHost) FS() vfs.FS { - return h.fs -} - -func (h *compilerHost) DefaultLibraryPath() string { - return h.defaultLibraryPath -} - -func (h *compilerHost) GetCurrentDirectory() string { - return h.currentDirectory -} - -func (h *compilerHost) Trace(msg string) { - h.trace(msg) -} - -func (h *compilerHost) GetSourceFile(opts ast.SourceFileParseOptions) *ast.SourceFile { - text, ok := h.FS().ReadFile(opts.FileName) - if !ok { - return nil - } - return parser.ParseSourceFile(opts, text, core.GetScriptKindFromFileName(opts.FileName)) -} - -func (h *compilerHost) GetResolvedProjectReference(fileName string, path tspath.Path) *tsoptions.ParsedCommandLine { - commandLine, _ := tsoptions.GetParsedCommandLineOfConfigFilePath(fileName, path, nil, h, h.extendedConfigCache) - return commandLine -} diff --git a/kitcom/internal/tsgo/compiler/includeprocessor.go b/kitcom/internal/tsgo/compiler/includeprocessor.go deleted file mode 100644 index 6367108..0000000 --- a/kitcom/internal/tsgo/compiler/includeprocessor.go +++ /dev/null @@ -1,144 +0,0 @@ -package compiler - -import ( - "sync" - - "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/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type includeProcessor struct { - fileIncludeReasons map[tspath.Path][]*fileIncludeReason - processingDiagnostics []*processingDiagnostic - - reasonToReferenceLocation collections.SyncMap[*fileIncludeReason, *referenceFileLocation] - includeReasonToRelatedInfo collections.SyncMap[*fileIncludeReason, *ast.Diagnostic] - redirectAndFileFormat collections.SyncMap[tspath.Path, []*ast.Diagnostic] - computedDiagnostics *ast.DiagnosticsCollection - computedDiagnosticsOnce sync.Once - compilerOptionsSyntax *ast.ObjectLiteralExpression - compilerOptionsSyntaxOnce sync.Once -} - -func updateFileIncludeProcessor(p *Program) { - p.includeProcessor = &includeProcessor{ - fileIncludeReasons: p.includeProcessor.fileIncludeReasons, - processingDiagnostics: p.includeProcessor.processingDiagnostics, - } -} - -func (i *includeProcessor) getDiagnostics(p *Program) *ast.DiagnosticsCollection { - i.computedDiagnosticsOnce.Do(func() { - i.computedDiagnostics = &ast.DiagnosticsCollection{} - for _, d := range i.processingDiagnostics { - i.computedDiagnostics.Add(d.toDiagnostic(p)) - } - for _, resolutions := range p.resolvedModules { - for _, resolvedModule := range resolutions { - for _, diag := range resolvedModule.ResolutionDiagnostics { - i.computedDiagnostics.Add(diag) - } - } - } - for _, typeResolutions := range p.typeResolutionsInFile { - for _, resolvedTypeRef := range typeResolutions { - for _, diag := range resolvedTypeRef.ResolutionDiagnostics { - i.computedDiagnostics.Add(diag) - } - } - } - }) - return i.computedDiagnostics -} - -func (i *includeProcessor) addProcessingDiagnostic(d ...*processingDiagnostic) { - i.processingDiagnostics = append(i.processingDiagnostics, d...) -} - -func (i *includeProcessor) getReferenceLocation(r *fileIncludeReason, program *Program) *referenceFileLocation { - if existing, ok := i.reasonToReferenceLocation.Load(r); ok { - return existing - } - - loc, _ := i.reasonToReferenceLocation.LoadOrStore(r, r.getReferencedLocation(program)) - return loc -} - -func (i *includeProcessor) getCompilerOptionsObjectLiteralSyntax(program *Program) *ast.ObjectLiteralExpression { - i.compilerOptionsSyntaxOnce.Do(func() { - configFile := program.opts.Config.ConfigFile - if configFile != nil { - if compilerOptionsProperty := tsoptions.ForEachTsConfigPropArray(configFile.SourceFile, "compilerOptions", core.Identity); compilerOptionsProperty != nil && - compilerOptionsProperty.Initializer != nil && - ast.IsObjectLiteralExpression(compilerOptionsProperty.Initializer) { - i.compilerOptionsSyntax = compilerOptionsProperty.Initializer.AsObjectLiteralExpression() - } - } else { - i.compilerOptionsSyntax = nil - } - }) - return i.compilerOptionsSyntax -} - -func (i *includeProcessor) getRelatedInfo(r *fileIncludeReason, program *Program) *ast.Diagnostic { - if existing, ok := i.includeReasonToRelatedInfo.Load(r); ok { - return existing - } - - relatedInfo, _ := i.includeReasonToRelatedInfo.LoadOrStore(r, r.toRelatedInfo(program)) - return relatedInfo -} - -func (i *includeProcessor) explainRedirectAndImpliedFormat( - program *Program, - file *ast.SourceFile, - toFileName func(fileName string) string, -) []*ast.Diagnostic { - if existing, ok := i.redirectAndFileFormat.Load(file.Path()); ok { - return existing - } - var result []*ast.Diagnostic - if source := program.GetSourceOfProjectReferenceIfOutputIncluded(file); source != file.FileName() { - result = append(result, ast.NewCompilerDiagnostic( - diagnostics.File_is_output_of_project_reference_source_0, - toFileName(source), - )) - } - // !!! redirects - // if (file.redirectInfo) { - // (result ??= []).push(chainDiagnosticMessages( - // /*details*/ undefined, - // Diagnostics.File_redirects_to_file_0, - // toFileName(file.redirectInfo.redirectTarget, fileNameConvertor), - // )); - // } - if ast.IsExternalOrCommonJSModule(file) { - metaData := program.GetSourceFileMetaData(file.Path()) - switch program.GetImpliedNodeFormatForEmit(file) { - case core.ModuleKindESNext: - if metaData.PackageJsonType == "module" { - result = append(result, ast.NewCompilerDiagnostic( - diagnostics.File_is_ECMAScript_module_because_0_has_field_type_with_value_module, - toFileName(metaData.PackageJsonDirectory+"/package.json"), - )) - } - case core.ModuleKindCommonJS: - if metaData.PackageJsonType != "" { - result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_0_has_field_type_whose_value_is_not_module, toFileName(metaData.PackageJsonDirectory+"/package.json"))) - } else if metaData.PackageJsonDirectory != "" { - if metaData.PackageJsonType == "" { - result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_0_does_not_have_field_type, toFileName(metaData.PackageJsonDirectory+"/package.json"))) - } - } else { - result = append(result, ast.NewCompilerDiagnostic(diagnostics.File_is_CommonJS_module_because_package_json_was_not_found)) - } - } - } - - result, _ = i.redirectAndFileFormat.LoadOrStore(file.Path(), result) - return result -} diff --git a/kitcom/internal/tsgo/compiler/knownsymlinks.go b/kitcom/internal/tsgo/compiler/knownsymlinks.go deleted file mode 100644 index 9960a80..0000000 --- a/kitcom/internal/tsgo/compiler/knownsymlinks.go +++ /dev/null @@ -1,53 +0,0 @@ -package compiler - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type knownDirectoryLink struct { - /** - * Matches the casing returned by `realpath`. Used to compute the `realpath` of children. - * Always has trailing directory separator - */ - Real string - /** - * toPath(real). Stored to avoid repeated recomputation. - * Always has trailing directory separator - */ - RealPath tspath.Path -} - -type knownSymlinks struct { - directories collections.SyncMap[tspath.Path, *knownDirectoryLink] - files collections.SyncMap[tspath.Path, string] -} - -/** Gets a map from symlink to realpath. Keys have trailing directory separators. */ -func (cache *knownSymlinks) Directories() *collections.SyncMap[tspath.Path, *knownDirectoryLink] { - return &cache.directories -} - -/** Gets a map from symlink to realpath */ -func (cache *knownSymlinks) Files() *collections.SyncMap[tspath.Path, string] { - return &cache.files -} - -// all callers should check !containsIgnoredPath(symlinkPath) -func (cache *knownSymlinks) SetDirectory(symlink string, symlinkPath tspath.Path, realDirectory *knownDirectoryLink) { - // Large, interconnected dependency graphs in pnpm will have a huge number of symlinks - // where both the realpath and the symlink path are inside node_modules/.pnpm. Since - // this path is never a candidate for a module specifier, we can ignore it entirely. - - // !!! - // if realDirectory != nil { - // if _, ok := cache.directories.Load(symlinkPath); !ok { - // cache.directoriesByRealpath.Add(realDirectory.RealPath, symlink) - // } - // } - cache.directories.Store(symlinkPath, realDirectory) -} - -func (cache *knownSymlinks) SetFile(symlinkPath tspath.Path, realpath string) { - cache.files.Store(symlinkPath, realpath) -} diff --git a/kitcom/internal/tsgo/compiler/pkg.go b/kitcom/internal/tsgo/compiler/pkg.go deleted file mode 100644 index aa86763..0000000 --- a/kitcom/internal/tsgo/compiler/pkg.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package compiler implements the TypeScript compiler. -package compiler diff --git a/kitcom/internal/tsgo/compiler/processingDiagnostic.go b/kitcom/internal/tsgo/compiler/processingDiagnostic.go deleted file mode 100644 index 325ddcd..0000000 --- a/kitcom/internal/tsgo/compiler/processingDiagnostic.go +++ /dev/null @@ -1,134 +0,0 @@ -package compiler - -import ( - "strings" - - "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/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type processingDiagnosticKind int - -const ( - processingDiagnosticKindUnknownReference processingDiagnosticKind = iota - processingDiagnosticKindExplainingFileInclude -) - -type processingDiagnostic struct { - kind processingDiagnosticKind - data any -} - -func (d *processingDiagnostic) asFileIncludeReason() *fileIncludeReason { - return d.data.(*fileIncludeReason) -} - -type includeExplainingDiagnostic struct { - file tspath.Path - diagnosticReason *fileIncludeReason - message *diagnostics.Message - args []any -} - -func (d *processingDiagnostic) asIncludeExplainingDiagnostic() *includeExplainingDiagnostic { - return d.data.(*includeExplainingDiagnostic) -} - -func (d *processingDiagnostic) toDiagnostic(program *Program) *ast.Diagnostic { - switch d.kind { - case processingDiagnosticKindUnknownReference: - ref := d.asFileIncludeReason() - loc := ref.getReferencedLocation(program) - switch ref.kind { - case fileIncludeKindTypeReferenceDirective: - return loc.diagnosticAt(diagnostics.Cannot_find_type_definition_file_for_0, loc.ref.FileName) - case fileIncludeKindLibReferenceDirective: - libName := tspath.ToFileNameLowerCase(loc.ref.FileName) - unqualifiedLibName := strings.TrimSuffix(strings.TrimPrefix(libName, "lib."), ".d.ts") - suggestion := core.GetSpellingSuggestion(unqualifiedLibName, tsoptions.Libs, core.Identity) - return loc.diagnosticAt(core.IfElse( - suggestion != "", - diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1, - diagnostics.Cannot_find_lib_definition_for_0, - ), libName, suggestion) - default: - panic("unknown include kind") - } - case processingDiagnosticKindExplainingFileInclude: - return d.createDiagnosticExplainingFile(program) - default: - panic("unknown processingDiagnosticKind") - } -} - -func (d *processingDiagnostic) createDiagnosticExplainingFile(program *Program) *ast.Diagnostic { - diag := d.asIncludeExplainingDiagnostic() - var includeDetails []*ast.Diagnostic - var relatedInfo []*ast.Diagnostic - var redirectInfo []*ast.Diagnostic - var preferredLocation *fileIncludeReason - var seenReasons collections.Set[*fileIncludeReason] - if diag.diagnosticReason.isReferencedFile() && !program.includeProcessor.getReferenceLocation(diag.diagnosticReason, program).isSynthetic { - preferredLocation = diag.diagnosticReason - } - - processRelatedInfo := func(includeReason *fileIncludeReason) { - if preferredLocation == nil && includeReason.isReferencedFile() && !program.includeProcessor.getReferenceLocation(includeReason, program).isSynthetic { - preferredLocation = includeReason - } else { - info := program.includeProcessor.getRelatedInfo(includeReason, program) - if info != nil { - relatedInfo = append(relatedInfo, info) - } - } - } - processInclude := func(includeReason *fileIncludeReason) { - if !seenReasons.AddIfAbsent(includeReason) { - return - } - includeDetails = append(includeDetails, includeReason.toDiagnostic(program, false)) - processRelatedInfo(includeReason) - } - - // !!! todo sheetal caching - - if diag.file != "" { - reasons := program.includeProcessor.fileIncludeReasons[diag.file] - includeDetails = make([]*ast.Diagnostic, 0, len(reasons)) - for _, reason := range reasons { - processInclude(reason) - } - redirectInfo = program.includeProcessor.explainRedirectAndImpliedFormat(program, program.GetSourceFileByPath(diag.file), func(fileName string) string { return fileName }) - } - if diag.diagnosticReason != nil { - processInclude(diag.diagnosticReason) - } - var chain []*ast.Diagnostic - if includeDetails != nil && (preferredLocation == nil || seenReasons.Len() != 1) { - fileReason := ast.NewCompilerDiagnostic(diagnostics.The_file_is_in_the_program_because_Colon) - fileReason.SetMessageChain(includeDetails) - chain = []*ast.Diagnostic{fileReason} - } - if redirectInfo != nil { - chain = append(chain, redirectInfo...) - } - - var result *ast.Diagnostic - if preferredLocation != nil { - result = program.includeProcessor.getReferenceLocation(preferredLocation, program).diagnosticAt(diag.message, diag.args...) - } - if result == nil { - result = ast.NewCompilerDiagnostic(diag.message, diag.args...) - } - if chain != nil { - result.SetMessageChain(chain) - } - if relatedInfo != nil { - result.SetRelatedInfo(relatedInfo) - } - return result -} diff --git a/kitcom/internal/tsgo/compiler/program.go b/kitcom/internal/tsgo/compiler/program.go deleted file mode 100644 index 06bf28d..0000000 --- a/kitcom/internal/tsgo/compiler/program.go +++ /dev/null @@ -1,1722 +0,0 @@ -package compiler - -import ( - "context" - "fmt" - "io" - "maps" - "slices" - "strings" - "sync" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/checker" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/module" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/outputpaths" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/sourcemap" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "github.com/go-json-experiment/json" -) - -type ProgramOptions struct { - Host CompilerHost - Config *tsoptions.ParsedCommandLine - UseSourceOfProjectReference bool - SingleThreaded core.Tristate - CreateCheckerPool func(*Program) CheckerPool - TypingsLocation string - ProjectName string - JSDocParsingMode ast.JSDocParsingMode -} - -func (p *ProgramOptions) canUseProjectReferenceSource() bool { - return p.UseSourceOfProjectReference && !p.Config.CompilerOptions().DisableSourceOfProjectReferenceRedirect.IsTrue() -} - -type Program struct { - opts ProgramOptions - checkerPool CheckerPool - - comparePathsOptions tspath.ComparePathsOptions - - processedFiles - - usesUriStyleNodeCoreModules core.Tristate - - commonSourceDirectory string - commonSourceDirectoryOnce sync.Once - - declarationDiagnosticCache collections.SyncMap[*ast.SourceFile, []*ast.Diagnostic] - - programDiagnostics []*ast.Diagnostic - hasEmitBlockingDiagnostics collections.Set[tspath.Path] - - sourceFilesToEmitOnce sync.Once - sourceFilesToEmit []*ast.SourceFile - - // Cached unresolved imports for ATA - unresolvedImportsOnce sync.Once - unresolvedImports *collections.Set[string] -} - -// FileExists implements checker.Program. -func (p *Program) FileExists(path string) bool { - return p.Host().FS().FileExists(path) -} - -// GetCurrentDirectory implements checker.Program. -func (p *Program) GetCurrentDirectory() string { - return p.Host().GetCurrentDirectory() -} - -// GetGlobalTypingsCacheLocation implements checker.Program. -func (p *Program) GetGlobalTypingsCacheLocation() string { - return "" // !!! see src/tsserver/nodeServer.ts for strada's node-specific implementation -} - -// GetNearestAncestorDirectoryWithPackageJson implements checker.Program. -func (p *Program) GetNearestAncestorDirectoryWithPackageJson(dirname string) string { - scoped := p.resolver.GetPackageScopeForPath(dirname) - if scoped != nil && scoped.Exists() { - return scoped.PackageDirectory - } - return "" -} - -// GetPackageJsonInfo implements checker.Program. -func (p *Program) GetPackageJsonInfo(pkgJsonPath string) modulespecifiers.PackageJsonInfo { - scoped := p.resolver.GetPackageScopeForPath(pkgJsonPath) - if scoped != nil && scoped.Exists() && scoped.PackageDirectory == tspath.GetDirectoryPath(pkgJsonPath) { - return scoped - } - return nil -} - -// GetRedirectTargets implements checker.Program. -func (p *Program) GetRedirectTargets(path tspath.Path) []string { - return nil // !!! TODO: project references support -} - -// gets the original file that was included in program -// this returns original source file name when including output of project reference -// otherwise same name -// Equivalent to originalFileName on SourceFile in Strada -func (p *Program) GetSourceOfProjectReferenceIfOutputIncluded(file ast.HasFileName) string { - if source, ok := p.outputFileToProjectReferenceSource[file.Path()]; ok { - return source - } - return file.FileName() -} - -// GetProjectReferenceFromSource implements checker.Program. -func (p *Program) GetProjectReferenceFromSource(path tspath.Path) *tsoptions.SourceOutputAndProjectReference { - return p.projectReferenceFileMapper.getProjectReferenceFromSource(path) -} - -// IsSourceFromProjectReference implements checker.Program. -func (p *Program) IsSourceFromProjectReference(path tspath.Path) bool { - return p.projectReferenceFileMapper.isSourceFromProjectReference(path) -} - -func (p *Program) GetProjectReferenceFromOutputDts(path tspath.Path) *tsoptions.SourceOutputAndProjectReference { - return p.projectReferenceFileMapper.getProjectReferenceFromOutputDts(path) -} - -func (p *Program) GetResolvedProjectReferenceFor(path tspath.Path) (*tsoptions.ParsedCommandLine, bool) { - return p.projectReferenceFileMapper.getResolvedReferenceFor(path) -} - -func (p *Program) GetRedirectForResolution(file ast.HasFileName) *tsoptions.ParsedCommandLine { - redirect, _ := p.projectReferenceFileMapper.getRedirectForResolution(file) - return redirect -} - -func (p *Program) GetParseFileRedirect(fileName string) string { - return p.projectReferenceFileMapper.getParseFileRedirect(ast.NewHasFileName(fileName, p.toPath(fileName))) -} - -func (p *Program) ForEachResolvedProjectReference( - fn func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int), -) { - p.projectReferenceFileMapper.forEachResolvedProjectReference(fn) -} - -// UseCaseSensitiveFileNames implements checker.Program. -func (p *Program) UseCaseSensitiveFileNames() bool { - return p.Host().FS().UseCaseSensitiveFileNames() -} - -func (p *Program) UsesUriStyleNodeCoreModules() bool { - return p.usesUriStyleNodeCoreModules.IsTrue() -} - -var _ checker.Program = (*Program)(nil) - -/** This should have similar behavior to 'processSourceFile' without diagnostics or mutation. */ -func (p *Program) GetSourceFileFromReference(origin *ast.SourceFile, ref *ast.FileReference) *ast.SourceFile { - // TODO: The module loader in corsa is fairly different than strada, it should probably be able to expose this functionality at some point, - // rather than redoing the logic approximately here, since most of the related logic now lives in module.Resolver - // Still, without the failed lookup reporting that only the loader does, this isn't terribly complicated - - fileName := tspath.ResolvePath(tspath.GetDirectoryPath(origin.FileName()), ref.FileName) - supportedExtensionsBase := tsoptions.GetSupportedExtensions(p.Options(), nil /*extraFileExtensions*/) - supportedExtensions := tsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(p.Options(), supportedExtensionsBase) - allowNonTsExtensions := p.Options().AllowNonTsExtensions.IsTrue() - if tspath.HasExtension(fileName) { - if !allowNonTsExtensions { - canonicalFileName := tspath.GetCanonicalFileName(fileName, p.UseCaseSensitiveFileNames()) - supported := false - for _, group := range supportedExtensions { - if tspath.FileExtensionIsOneOf(canonicalFileName, group) { - supported = true - break - } - } - if !supported { - return nil // unsupported extensions are forced to fail - } - } - - return p.GetSourceFile(fileName) - } - if allowNonTsExtensions { - extensionless := p.GetSourceFile(fileName) - if extensionless != nil { - return extensionless - } - } - - // Only try adding extensions from the first supported group (which should be .ts/.tsx/.d.ts) - for _, ext := range supportedExtensions[0] { - result := p.GetSourceFile(fileName + ext) - if result != nil { - return result - } - } - return nil -} - -func NewProgram(opts ProgramOptions) *Program { - p := &Program{opts: opts} - p.initCheckerPool() - p.processedFiles = processAllProgramFiles(p.opts, p.SingleThreaded()) - p.verifyCompilerOptions() - return p -} - -// Return an updated program for which it is known that only the file with the given path has changed. -// In addition to a new program, return a boolean indicating whether the data of the old program was reused. -func (p *Program) UpdateProgram(changedFilePath tspath.Path, newHost CompilerHost) (*Program, bool) { - oldFile := p.filesByPath[changedFilePath] - newOpts := p.opts - newOpts.Host = newHost - newFile := newHost.GetSourceFile(oldFile.ParseOptions()) - if !canReplaceFileInProgram(oldFile, newFile) { - return NewProgram(newOpts), false - } - // TODO: reverify compiler options when config has changed? - result := &Program{ - opts: newOpts, - comparePathsOptions: p.comparePathsOptions, - processedFiles: p.processedFiles, - usesUriStyleNodeCoreModules: p.usesUriStyleNodeCoreModules, - programDiagnostics: p.programDiagnostics, - hasEmitBlockingDiagnostics: p.hasEmitBlockingDiagnostics, - unresolvedImports: p.unresolvedImports, - } - result.initCheckerPool() - index := core.FindIndex(result.files, func(file *ast.SourceFile) bool { return file.Path() == newFile.Path() }) - result.files = slices.Clone(result.files) - result.files[index] = newFile - result.filesByPath = maps.Clone(result.filesByPath) - result.filesByPath[newFile.Path()] = newFile - updateFileIncludeProcessor(result) - return result, true -} - -func (p *Program) initCheckerPool() { - if p.opts.CreateCheckerPool != nil { - p.checkerPool = p.opts.CreateCheckerPool(p) - } else { - p.checkerPool = newCheckerPool(core.IfElse(p.SingleThreaded(), 1, 4), p) - } -} - -func canReplaceFileInProgram(file1 *ast.SourceFile, file2 *ast.SourceFile) bool { - return file2 != nil && - file1.ParseOptions() == file2.ParseOptions() && - file1.UsesUriStyleNodeCoreModules == file2.UsesUriStyleNodeCoreModules && - slices.EqualFunc(file1.Imports(), file2.Imports(), equalModuleSpecifiers) && - slices.EqualFunc(file1.ModuleAugmentations, file2.ModuleAugmentations, equalModuleAugmentationNames) && - slices.Equal(file1.AmbientModuleNames, file2.AmbientModuleNames) && - slices.EqualFunc(file1.ReferencedFiles, file2.ReferencedFiles, equalFileReferences) && - slices.EqualFunc(file1.TypeReferenceDirectives, file2.TypeReferenceDirectives, equalFileReferences) && - slices.EqualFunc(file1.LibReferenceDirectives, file2.LibReferenceDirectives, equalFileReferences) && - equalCheckJSDirectives(file1.CheckJsDirective, file2.CheckJsDirective) -} - -func equalModuleSpecifiers(n1 *ast.Node, n2 *ast.Node) bool { - return n1.Kind == n2.Kind && (!ast.IsStringLiteral(n1) || n1.Text() == n2.Text()) -} - -func equalModuleAugmentationNames(n1 *ast.Node, n2 *ast.Node) bool { - return n1.Kind == n2.Kind && n1.Text() == n2.Text() -} - -func equalFileReferences(f1 *ast.FileReference, f2 *ast.FileReference) bool { - return f1.FileName == f2.FileName && f1.ResolutionMode == f2.ResolutionMode && f1.Preserve == f2.Preserve -} - -func equalCheckJSDirectives(d1 *ast.CheckJsDirective, d2 *ast.CheckJsDirective) bool { - return d1 == nil && d2 == nil || d1 != nil && d2 != nil && d1.Enabled == d2.Enabled -} - -func (p *Program) SourceFiles() []*ast.SourceFile { return p.files } -func (p *Program) Options() *core.CompilerOptions { return p.opts.Config.CompilerOptions() } -func (p *Program) CommandLine() *tsoptions.ParsedCommandLine { return p.opts.Config } -func (p *Program) Host() CompilerHost { return p.opts.Host } -func (p *Program) GetConfigFileParsingDiagnostics() []*ast.Diagnostic { - return slices.Clip(p.opts.Config.GetConfigFileParsingDiagnostics()) -} - -// GetUnresolvedImports returns the unresolved imports for this program. -// The result is cached and computed only once. -func (p *Program) GetUnresolvedImports() *collections.Set[string] { - p.unresolvedImportsOnce.Do(func() { - if p.unresolvedImports == nil { - p.unresolvedImports = p.extractUnresolvedImports() - } - }) - - return p.unresolvedImports -} - -func (p *Program) extractUnresolvedImports() *collections.Set[string] { - unresolvedSet := &collections.Set[string]{} - - for _, sourceFile := range p.files { - unresolvedImports := p.extractUnresolvedImportsFromSourceFile(sourceFile) - for _, imp := range unresolvedImports { - unresolvedSet.Add(imp) - } - } - - return unresolvedSet -} - -func (p *Program) extractUnresolvedImportsFromSourceFile(file *ast.SourceFile) []string { - var unresolvedImports []string - - resolvedModules := p.resolvedModules[file.Path()] - for cacheKey, resolution := range resolvedModules { - resolved := resolution.IsResolved() - if (!resolved || !tspath.ExtensionIsOneOf(resolution.Extension, tspath.SupportedTSExtensionsWithJsonFlat)) && - !tspath.IsExternalModuleNameRelative(cacheKey.Name) { - unresolvedImports = append(unresolvedImports, cacheKey.Name) - } - } - - return unresolvedImports -} - -func (p *Program) SingleThreaded() bool { - return p.opts.SingleThreaded.DefaultIfUnknown(p.Options().SingleThreaded).IsTrue() -} - -func (p *Program) BindSourceFiles() { - wg := core.NewWorkGroup(p.SingleThreaded()) - for _, file := range p.files { - if !file.IsBound() { - wg.Queue(func() { - binder.BindSourceFile(file) - }) - } - } - wg.RunAndWait() -} - -func (p *Program) CheckSourceFiles(ctx context.Context, files []*ast.SourceFile) { - wg := core.NewWorkGroup(p.SingleThreaded()) - checkers, done := p.checkerPool.GetAllCheckers(ctx) - defer done() - for _, checker := range checkers { - wg.Queue(func() { - for file := range p.checkerPool.Files(checker) { - if files == nil || slices.Contains(files, file) { - checker.CheckSourceFile(ctx, file) - } - } - }) - } - wg.RunAndWait() -} - -// Return the type checker associated with the program. -func (p *Program) GetTypeChecker(ctx context.Context) (*checker.Checker, func()) { - return p.checkerPool.GetChecker(ctx) -} - -func (p *Program) GetTypeCheckers(ctx context.Context) ([]*checker.Checker, func()) { - return p.checkerPool.GetAllCheckers(ctx) -} - -// Return a checker for the given file. We may have multiple checkers in concurrent scenarios and this -// method returns the checker that was tasked with checking the file. Note that it isn't possible to mix -// types obtained from different checkers, so only non-type data (such as diagnostics or string -// representations of types) should be obtained from checkers returned by this method. -func (p *Program) GetTypeCheckerForFile(ctx context.Context, file *ast.SourceFile) (*checker.Checker, func()) { - return p.checkerPool.GetCheckerForFile(ctx, file) -} - -func (p *Program) GetResolvedModule(file ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule { - if resolutions, ok := p.resolvedModules[file.Path()]; ok { - if resolved, ok := resolutions[module.ModeAwareCacheKey{Name: moduleReference, Mode: mode}]; ok { - return resolved - } - } - return nil -} - -func (p *Program) GetResolvedModuleFromModuleSpecifier(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) *module.ResolvedModule { - if !ast.IsStringLiteralLike(moduleSpecifier) { - panic("moduleSpecifier must be a StringLiteralLike") - } - mode := p.GetModeForUsageLocation(file, moduleSpecifier) - return p.GetResolvedModule(file, moduleSpecifier.Text(), mode) -} - -func (p *Program) GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule] { - return p.resolvedModules -} - -func (p *Program) GetSyntacticDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - return p.getDiagnosticsHelper(ctx, sourceFile, false /*ensureBound*/, false /*ensureChecked*/, p.getSyntacticDiagnosticsForFile) -} - -func (p *Program) GetBindDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - return p.getDiagnosticsHelper(ctx, sourceFile, true /*ensureBound*/, false /*ensureChecked*/, p.getBindDiagnosticsForFile) -} - -func (p *Program) GetSemanticDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - return p.getDiagnosticsHelper(ctx, sourceFile, true /*ensureBound*/, true /*ensureChecked*/, p.getSemanticDiagnosticsForFile) -} - -func (p *Program) GetSemanticDiagnosticsNoFilter(ctx context.Context, sourceFiles []*ast.SourceFile) map[*ast.SourceFile][]*ast.Diagnostic { - p.BindSourceFiles() - p.CheckSourceFiles(ctx, sourceFiles) - if ctx.Err() != nil { - return nil - } - result := make(map[*ast.SourceFile][]*ast.Diagnostic, len(sourceFiles)) - for _, file := range sourceFiles { - result[file] = SortAndDeduplicateDiagnostics(p.getSemanticDiagnosticsForFileNotFilter(ctx, file)) - } - return result -} - -func (p *Program) GetSuggestionDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - return p.getDiagnosticsHelper(ctx, sourceFile, true /*ensureBound*/, true /*ensureChecked*/, p.getSuggestionDiagnosticsForFile) -} - -func (p *Program) GetProgramDiagnostics() []*ast.Diagnostic { - return SortAndDeduplicateDiagnostics(slices.Concat( - p.programDiagnostics, - p.includeProcessor.getDiagnostics(p).GetGlobalDiagnostics())) -} - -func (p *Program) GetIncludeProcessorDiagnostics(sourceFile *ast.SourceFile) []*ast.Diagnostic { - if checker.SkipTypeChecking(sourceFile, p.Options(), p, false) { - return nil - } - filtered, _ := p.getDiagnosticsWithPrecedingDirectives(sourceFile, p.includeProcessor.getDiagnostics(p).GetDiagnosticsForFile(sourceFile.FileName())) - return filtered -} - -func (p *Program) getSourceFilesToEmit(targetSourceFile *ast.SourceFile, forceDtsEmit bool) []*ast.SourceFile { - if targetSourceFile == nil && !forceDtsEmit { - p.sourceFilesToEmitOnce.Do(func() { - p.sourceFilesToEmit = getSourceFilesToEmit(p, nil, false) - }) - return p.sourceFilesToEmit - } - return getSourceFilesToEmit(p, targetSourceFile, forceDtsEmit) -} - -func (p *Program) verifyCompilerOptions() { - options := p.Options() - - sourceFile := core.Memoize(func() *ast.SourceFile { - configFile := p.opts.Config.ConfigFile - if configFile == nil { - return nil - } - return configFile.SourceFile - }) - - configFilePath := core.Memoize(func() string { - file := sourceFile() - if file != nil { - return file.FileName() - } - return "" - }) - - getCompilerOptionsPropertySyntax := core.Memoize(func() *ast.PropertyAssignment { - return tsoptions.ForEachTsConfigPropArray(sourceFile(), "compilerOptions", core.Identity) - }) - - getCompilerOptionsObjectLiteralSyntax := core.Memoize(func() *ast.ObjectLiteralExpression { - compilerOptionsProperty := getCompilerOptionsPropertySyntax() - if compilerOptionsProperty != nil && - compilerOptionsProperty.Initializer != nil && - ast.IsObjectLiteralExpression(compilerOptionsProperty.Initializer) { - return compilerOptionsProperty.Initializer.AsObjectLiteralExpression() - } - return nil - }) - - createOptionDiagnosticInObjectLiteralSyntax := func(objectLiteral *ast.ObjectLiteralExpression, onKey bool, key1 string, key2 string, message *diagnostics.Message, args ...any) *ast.Diagnostic { - diag := tsoptions.ForEachPropertyAssignment(objectLiteral, key1, func(property *ast.PropertyAssignment) *ast.Diagnostic { - return tsoptions.CreateDiagnosticForNodeInSourceFile(sourceFile(), core.IfElse(onKey, property.Name(), property.Initializer), message, args...) - }, key2) - if diag != nil { - p.programDiagnostics = append(p.programDiagnostics, diag) - } - return diag - } - - createCompilerOptionsDiagnostic := func(message *diagnostics.Message, args ...any) *ast.Diagnostic { - compilerOptionsProperty := getCompilerOptionsPropertySyntax() - var diag *ast.Diagnostic - if compilerOptionsProperty != nil { - diag = tsoptions.CreateDiagnosticForNodeInSourceFile(sourceFile(), compilerOptionsProperty.Name(), message, args...) - } else { - diag = ast.NewCompilerDiagnostic(message, args...) - } - p.programDiagnostics = append(p.programDiagnostics, diag) - return diag - } - - createDiagnosticForOption := func(onKey bool, option1 string, option2 string, message *diagnostics.Message, args ...any) *ast.Diagnostic { - diag := createOptionDiagnosticInObjectLiteralSyntax(getCompilerOptionsObjectLiteralSyntax(), onKey, option1, option2, message, args...) - if diag == nil { - diag = createCompilerOptionsDiagnostic(message, args...) - } - return diag - } - - createDiagnosticForOptionName := func(message *diagnostics.Message, option1 string, option2 string, args ...any) { - newArgs := make([]any, 0, len(args)+2) - newArgs = append(newArgs, option1, option2) - newArgs = append(newArgs, args...) - createDiagnosticForOption(true /*onKey*/, option1, option2, message, newArgs...) - } - - createOptionValueDiagnostic := func(option1 string, message *diagnostics.Message, args ...any) { - createDiagnosticForOption(false /*onKey*/, option1, "", message, args...) - } - - createRemovedOptionDiagnostic := func(name string, value string, useInstead string) { - var message *diagnostics.Message - var args []any - if value == "" { - message = diagnostics.Option_0_has_been_removed_Please_remove_it_from_your_configuration - args = []any{name} - } else { - message = diagnostics.Option_0_1_has_been_removed_Please_remove_it_from_your_configuration - args = []any{name, value} - } - - diag := createDiagnosticForOption(value == "", name, "", message, args...) - if useInstead != "" { - diag.AddMessageChain(ast.NewCompilerDiagnostic(diagnostics.Use_0_instead, useInstead)) - } - } - - // Removed in TS7 - - if options.BaseUrl != "" { - // BaseUrl will have been turned absolute by this point. - var useInstead string - if configFilePath() != "" { - relative := tspath.GetRelativePathFromFile(configFilePath(), options.BaseUrl, p.comparePathsOptions) - if !(strings.HasPrefix(relative, "./") || strings.HasPrefix(relative, "../")) { - relative = "./" + relative - } - suggestion := tspath.CombinePaths(relative, "*") - useInstead = fmt.Sprintf(`"paths": {"*": [%s]}`, core.Must(json.Marshal(suggestion))) - } - createRemovedOptionDiagnostic("baseUrl", "", useInstead) - } - - if options.OutFile != "" { - createRemovedOptionDiagnostic("outFile", "", "") - } - - // if options.Target == core.ScriptTargetES3 { - // createRemovedOptionDiagnostic("target", "ES3", "") - // } - // if options.Target == core.ScriptTargetES5 { - // createRemovedOptionDiagnostic("target", "ES5", "") - // } - - if options.Module == core.ModuleKindAMD { - createRemovedOptionDiagnostic("module", "AMD", "") - } - if options.Module == core.ModuleKindSystem { - createRemovedOptionDiagnostic("module", "System", "") - } - if options.Module == core.ModuleKindUMD { - createRemovedOptionDiagnostic("module", "UMD", "") - } - - if options.StrictPropertyInitialization.IsTrue() && !options.GetStrictOptionValue(options.StrictNullChecks) { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "strictPropertyInitialization", "strictNullChecks") - } - if options.ExactOptionalPropertyTypes.IsTrue() && !options.GetStrictOptionValue(options.StrictNullChecks) { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "exactOptionalPropertyTypes", "strictNullChecks") - } - - if options.IsolatedDeclarations.IsTrue() { - if options.GetAllowJS() { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "isolatedDeclarations") - } - if !options.GetEmitDeclarations() { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "isolatedDeclarations", "declaration", "composite") - } - } - - if options.InlineSourceMap.IsTrue() { - if options.SourceMap.IsTrue() { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceMap", "inlineSourceMap") - } - if options.MapRoot != "" { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_with_option_1, "mapRoot", "inlineSourceMap") - } - } - - if options.Composite.IsTrue() { - if options.Declaration.IsFalse() { - createDiagnosticForOptionName(diagnostics.Composite_projects_may_not_disable_declaration_emit, "declaration", "") - } - if options.Incremental.IsFalse() { - createDiagnosticForOptionName(diagnostics.Composite_projects_may_not_disable_incremental_compilation, "declaration", "") - } - } - - p.verifyProjectReferences() - - if options.Composite.IsTrue() { - var rootPaths collections.Set[tspath.Path] - for _, fileName := range p.opts.Config.FileNames() { - rootPaths.Add(p.toPath(fileName)) - } - - for _, file := range p.files { - if sourceFileMayBeEmitted(file, p, false) && !rootPaths.Has(file.Path()) { - p.includeProcessor.addProcessingDiagnostic(&processingDiagnostic{ - kind: processingDiagnosticKindExplainingFileInclude, - data: &includeExplainingDiagnostic{ - file: file.Path(), - message: diagnostics.File_0_is_not_listed_within_the_file_list_of_project_1_Projects_must_list_all_files_or_use_an_include_pattern, - args: []any{file.FileName(), configFilePath()}, - }, - }) - } - } - } - - forEachOptionPathsSyntax := func(callback func(*ast.PropertyAssignment) *ast.Diagnostic) *ast.Diagnostic { - return tsoptions.ForEachPropertyAssignment(getCompilerOptionsObjectLiteralSyntax(), "paths", callback) - } - - createDiagnosticForOptionPaths := func(onKey bool, key string, message *diagnostics.Message, args ...any) *ast.Diagnostic { - diag := forEachOptionPathsSyntax(func(pathProp *ast.PropertyAssignment) *ast.Diagnostic { - if ast.IsObjectLiteralExpression(pathProp.Initializer) { - return createOptionDiagnosticInObjectLiteralSyntax(pathProp.Initializer.AsObjectLiteralExpression(), onKey, key, "", message, args...) - } - return nil - }) - if diag == nil { - diag = createCompilerOptionsDiagnostic(message, args...) - } - return diag - } - - createDiagnosticForOptionPathKeyValue := func(key string, valueIndex int, message *diagnostics.Message, args ...any) *ast.Diagnostic { - diag := forEachOptionPathsSyntax(func(pathProp *ast.PropertyAssignment) *ast.Diagnostic { - if ast.IsObjectLiteralExpression(pathProp.Initializer) { - return tsoptions.ForEachPropertyAssignment(pathProp.Initializer.AsObjectLiteralExpression(), key, func(keyProps *ast.PropertyAssignment) *ast.Diagnostic { - initializer := keyProps.Initializer - if ast.IsArrayLiteralExpression(initializer) { - elements := initializer.AsArrayLiteralExpression().Elements - if elements != nil && len(elements.Nodes) > valueIndex { - diag := tsoptions.CreateDiagnosticForNodeInSourceFile(sourceFile(), elements.Nodes[valueIndex], message, args...) - p.programDiagnostics = append(p.programDiagnostics, diag) - return diag - } - } - return nil - }) - } - return nil - }) - if diag == nil { - diag = createCompilerOptionsDiagnostic(message, args...) - } - return diag - } - - for key, value := range options.Paths.Entries() { - // !!! This code does not handle cases where where the path mappings have the wrong types, - // as that information is mostly lost during the parsing process. - if !hasZeroOrOneAsteriskCharacter(key) { - createDiagnosticForOptionPaths(true /*onKey*/, key, diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, key) - } - if value == nil { - createDiagnosticForOptionPaths(false /*onKey*/, key, diagnostics.Substitutions_for_pattern_0_should_be_an_array, key) - } else if len(value) == 0 { - createDiagnosticForOptionPaths(false /*onKey*/, key, diagnostics.Substitutions_for_pattern_0_shouldn_t_be_an_empty_array, key) - } - for i, subst := range value { - if !hasZeroOrOneAsteriskCharacter(subst) { - createDiagnosticForOptionPathKeyValue(key, i, diagnostics.Substitution_0_in_pattern_1_can_have_at_most_one_Asterisk_character, subst, key) - } - if !tspath.PathIsRelative(subst) && !tspath.PathIsAbsolute(subst) { - createDiagnosticForOptionPathKeyValue(key, i, diagnostics.Non_relative_paths_are_not_allowed_Did_you_forget_a_leading_Slash) - } - } - } - - if options.SourceMap.IsFalseOrUnknown() && options.InlineSourceMap.IsFalseOrUnknown() { - if options.InlineSources.IsTrue() { - createDiagnosticForOptionName(diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources", "") - } - if options.SourceRoot != "" { - createDiagnosticForOptionName(diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot", "") - } - } - - if options.MapRoot != "" && !(options.SourceMap.IsTrue() || options.DeclarationMap.IsTrue()) { - // Error to specify --mapRoot without --sourcemap - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "mapRoot", "sourceMap", "declarationMap") - } - - if options.DeclarationDir != "" { - if !options.GetEmitDeclarations() { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationDir", "declaration", "composite") - } - } - - if options.DeclarationMap.IsTrue() && !options.GetEmitDeclarations() { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "declarationMap", "declaration", "composite") - } - - if options.Lib != nil && options.NoLib.IsTrue() { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_with_option_1, "lib", "noLib") - } - - languageVersion := options.GetEmitScriptTarget() - - firstNonAmbientExternalModuleSourceFile := core.Find(p.files, func(f *ast.SourceFile) bool { return ast.IsExternalModule(f) && !f.IsDeclarationFile }) - if options.IsolatedModules.IsTrue() || options.VerbatimModuleSyntax.IsTrue() { - if options.Module == core.ModuleKindNone && languageVersion < core.ScriptTargetES2015 && options.IsolatedModules.IsTrue() { - // !!! - // createDiagnosticForOptionName(diagnostics.Option_isolatedModules_can_only_be_used_when_either_option_module_is_provided_or_option_target_is_ES2015_or_higher, "isolatedModules", "target") - } - - if options.PreserveConstEnums.IsFalse() { - createDiagnosticForOptionName(diagnostics.Option_preserveConstEnums_cannot_be_disabled_when_0_is_enabled, core.IfElse(options.VerbatimModuleSyntax.IsTrue(), "verbatimModuleSyntax", "isolatedModules"), "preserveConstEnums") - } - } else if firstNonAmbientExternalModuleSourceFile != nil && languageVersion < core.ScriptTargetES2015 && options.Module == core.ModuleKindNone { - // !!! - } - - if options.OutDir != "" || - options.RootDir != "" || - options.SourceRoot != "" || - options.MapRoot != "" || - (options.GetEmitDeclarations() && options.DeclarationDir != "") { - // !!! sheetal checkSourceFilesBelongToPath - for root Dir and configFile - explaining why file is in the program - dir := p.CommonSourceDirectory() - if options.OutDir != "" && dir == "" && core.Some(p.files, func(f *ast.SourceFile) bool { return tspath.GetRootLength(f.FileName()) > 1 }) { - createDiagnosticForOptionName(diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files, "outDir", "") - } - } - - if options.CheckJs.IsTrue() && !options.GetAllowJS() { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "checkJs", "allowJs") - } - - if options.EmitDeclarationOnly.IsTrue() { - if !options.GetEmitDeclarations() { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1_or_option_2, "emitDeclarationOnly", "declaration", "composite") - } - } - - // !!! emitDecoratorMetadata - - if options.JsxFactory != "" { - if options.ReactNamespace != "" { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_with_option_1, "reactNamespace", "jsxFactory") - } - if options.Jsx == core.JsxEmitReactJSX || options.Jsx == core.JsxEmitReactJSXDev { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFactory", tsoptions.InverseJsxOptionMap.GetOrZero(options.Jsx)) - } - if parser.ParseIsolatedEntityName(options.JsxFactory) == nil { - createOptionValueDiagnostic("jsxFactory", diagnostics.Invalid_value_for_jsxFactory_0_is_not_a_valid_identifier_or_qualified_name, options.JsxFactory) - } - } else if options.ReactNamespace != "" && !scanner.IsIdentifierText(options.ReactNamespace, core.LanguageVariantStandard) { - createOptionValueDiagnostic("reactNamespace", diagnostics.Invalid_value_for_reactNamespace_0_is_not_a_valid_identifier, options.ReactNamespace) - } - - if options.JsxFragmentFactory != "" { - if options.JsxFactory == "" { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "jsxFragmentFactory", "jsxFactory") - } - if options.Jsx == core.JsxEmitReactJSX || options.Jsx == core.JsxEmitReactJSXDev { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxFragmentFactory", tsoptions.InverseJsxOptionMap.GetOrZero(options.Jsx)) - } - if parser.ParseIsolatedEntityName(options.JsxFragmentFactory) == nil { - createOptionValueDiagnostic("jsxFragmentFactory", diagnostics.Invalid_value_for_jsxFragmentFactory_0_is_not_a_valid_identifier_or_qualified_name, options.JsxFragmentFactory) - } - } - - if options.ReactNamespace != "" { - if options.Jsx == core.JsxEmitReactJSX || options.Jsx == core.JsxEmitReactJSXDev { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "reactNamespace", tsoptions.InverseJsxOptionMap.GetOrZero(options.Jsx)) - } - } - - if options.JsxImportSource != "" { - if options.Jsx == core.JsxEmitReact { - createDiagnosticForOptionName(diagnostics.Option_0_cannot_be_specified_when_option_jsx_is_1, "jsxImportSource", tsoptions.InverseJsxOptionMap.GetOrZero(options.Jsx)) - } - } - - moduleKind := options.GetEmitModuleKind() - - if options.AllowImportingTsExtensions.IsTrue() && !(options.NoEmit.IsTrue() || options.EmitDeclarationOnly.IsTrue() || options.RewriteRelativeImportExtensions.IsTrue()) { - createOptionValueDiagnostic("allowImportingTsExtensions", diagnostics.Option_allowImportingTsExtensions_can_only_be_used_when_either_noEmit_or_emitDeclarationOnly_is_set) - } - - moduleResolution := options.GetModuleResolutionKind() - if options.ResolvePackageJsonExports.IsTrue() && !moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution) { - createDiagnosticForOptionName(diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "resolvePackageJsonExports", "") - } - if options.ResolvePackageJsonImports.IsTrue() && !moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution) { - createDiagnosticForOptionName(diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "resolvePackageJsonImports", "") - } - if options.CustomConditions != nil && !moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution) { - createDiagnosticForOptionName(diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "customConditions", "") - } - - // !!! Reenable once we don't map old moduleResolution kinds to bundler. - // if moduleResolution == core.ModuleResolutionKindBundler && !emitModuleKindIsNonNodeESM(moduleKind) && moduleKind != core.ModuleKindPreserve { - // createOptionValueDiagnostic("moduleResolution", diagnostics.Option_0_can_only_be_used_when_module_is_set_to_preserve_or_to_es2015_or_later, "bundler") - // } - - if core.ModuleKindNode16 <= moduleKind && moduleKind <= core.ModuleKindNodeNext && - !(core.ModuleResolutionKindNode16 <= moduleResolution && moduleResolution <= core.ModuleResolutionKindNodeNext) { - moduleKindName := moduleKind.String() - var moduleResolutionName string - if v, ok := core.ModuleKindToModuleResolutionKind[moduleKind]; ok { - moduleResolutionName = v.String() - } else { - moduleResolutionName = "Node16" - } - createOptionValueDiagnostic("moduleResolution", diagnostics.Option_moduleResolution_must_be_set_to_0_or_left_unspecified_when_option_module_is_set_to_1, moduleResolutionName, moduleKindName) - } else if core.ModuleResolutionKindNode16 <= moduleResolution && moduleResolution <= core.ModuleResolutionKindNodeNext && - !(core.ModuleKindNode16 <= moduleKind && moduleKind <= core.ModuleKindNodeNext) { - moduleResolutionName := moduleResolution.String() - createOptionValueDiagnostic("module", diagnostics.Option_module_must_be_set_to_0_when_option_moduleResolution_is_set_to_1, moduleResolutionName, moduleResolutionName) - } - - // !!! The below needs filesByName, which is not equivalent to p.filesByPath. - - // If the emit is enabled make sure that every output file is unique and not overwriting any of the input files - if !options.NoEmit.IsTrue() && !options.SuppressOutputPathCheck.IsTrue() { - var emitFilesSeen collections.Set[string] - - // Verify that all the emit files are unique and don't overwrite input files - verifyEmitFilePath := func(emitFileName string) { - if emitFileName != "" { - emitFilePath := p.toPath(emitFileName) - // Report error if the output overwrites input file - if _, ok := p.filesByPath[emitFilePath]; ok { - diag := ast.NewCompilerDiagnostic(diagnostics.Cannot_write_file_0_because_it_would_overwrite_input_file, emitFileName) - if configFilePath() == "" { - // The program is from either an inferred project or an external project - diag.AddMessageChain(ast.NewCompilerDiagnostic(diagnostics.Adding_a_tsconfig_json_file_will_help_organize_projects_that_contain_both_TypeScript_and_JavaScript_files_Learn_more_at_https_Colon_Slash_Slashaka_ms_Slashtsconfig)) - } - p.blockEmittingOfFile(emitFileName, diag) - } - - var emitFileKey string - if !p.Host().FS().UseCaseSensitiveFileNames() { - emitFileKey = tspath.ToFileNameLowerCase(string(emitFilePath)) - } else { - emitFileKey = string(emitFilePath) - } - - // Report error if multiple files write into same file - if emitFilesSeen.Has(emitFileKey) { - // Already seen the same emit file - report error - p.blockEmittingOfFile(emitFileName, ast.NewCompilerDiagnostic(diagnostics.Cannot_write_file_0_because_it_would_be_overwritten_by_multiple_input_files, emitFileName)) - } else { - emitFilesSeen.Add(emitFileKey) - } - } - } - - outputpaths.ForEachEmittedFile(p, options, func(emitFileNames *outputpaths.OutputPaths, sourceFile *ast.SourceFile) bool { - verifyEmitFilePath(emitFileNames.JsFilePath()) - verifyEmitFilePath(emitFileNames.SourceMapFilePath()) - verifyEmitFilePath(emitFileNames.DeclarationFilePath()) - verifyEmitFilePath(emitFileNames.DeclarationMapPath()) - return false - }, p.getSourceFilesToEmit(nil, false), false) - verifyEmitFilePath(p.opts.Config.GetBuildInfoFileName()) - } -} - -func (p *Program) blockEmittingOfFile(emitFileName string, diag *ast.Diagnostic) { - p.hasEmitBlockingDiagnostics.Add(p.toPath(emitFileName)) - p.programDiagnostics = append(p.programDiagnostics, diag) -} - -func (p *Program) IsEmitBlocked(emitFileName string) bool { - return p.hasEmitBlockingDiagnostics.Has(p.toPath(emitFileName)) -} - -func (p *Program) verifyProjectReferences() { - buildInfoFileName := core.IfElse(!p.Options().SuppressOutputPathCheck.IsTrue(), p.opts.Config.GetBuildInfoFileName(), "") - createDiagnosticForReference := func(config *tsoptions.ParsedCommandLine, index int, message *diagnostics.Message, args ...any) { - diag := tsoptions.CreateDiagnosticAtReferenceSyntax(config, index, message, args...) - if diag == nil { - diag = ast.NewCompilerDiagnostic(message, args...) - } - p.programDiagnostics = append(p.programDiagnostics, diag) - } - - p.ForEachResolvedProjectReference(func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int) { - ref := parent.ProjectReferences()[index] - // !!! Deprecated in 5.0 and removed since 5.5 - // verifyRemovedProjectReference(ref, parent, index); - if config == nil { - createDiagnosticForReference(parent, index, diagnostics.File_0_not_found, ref.Path) - return - } - refOptions := config.CompilerOptions() - if !refOptions.Composite.IsTrue() || refOptions.NoEmit.IsTrue() { - if len(parent.FileNames()) > 0 { - if !refOptions.Composite.IsTrue() { - createDiagnosticForReference(parent, index, diagnostics.Referenced_project_0_must_have_setting_composite_Colon_true, ref.Path) - } - if refOptions.NoEmit.IsTrue() { - createDiagnosticForReference(parent, index, diagnostics.Referenced_project_0_may_not_disable_emit, ref.Path) - } - } - } - if buildInfoFileName != "" && buildInfoFileName == config.GetBuildInfoFileName() { - createDiagnosticForReference(parent, index, diagnostics.Cannot_write_file_0_because_it_will_overwrite_tsbuildinfo_file_generated_by_referenced_project_1, buildInfoFileName, ref.Path) - p.hasEmitBlockingDiagnostics.Add(p.toPath(buildInfoFileName)) - } - }) -} - -func hasZeroOrOneAsteriskCharacter(str string) bool { - seenAsterisk := false - for _, ch := range str { - if ch == '*' { - if !seenAsterisk { - seenAsterisk = true - } else { - // have already seen asterisk - return false - } - } - } - return true -} - -func moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution core.ModuleResolutionKind) bool { - return moduleResolution >= core.ModuleResolutionKindNode16 && moduleResolution <= core.ModuleResolutionKindNodeNext || - moduleResolution == core.ModuleResolutionKindBundler -} - -func emitModuleKindIsNonNodeESM(moduleKind core.ModuleKind) bool { - return moduleKind >= core.ModuleKindES2015 && moduleKind <= core.ModuleKindESNext -} - -func (p *Program) GetGlobalDiagnostics(ctx context.Context) []*ast.Diagnostic { - if len(p.files) == 0 { - return nil - } - - var globalDiagnostics []*ast.Diagnostic - checkers, done := p.checkerPool.GetAllCheckers(ctx) - defer done() - for _, checker := range checkers { - globalDiagnostics = append(globalDiagnostics, checker.GetGlobalDiagnostics()...) - } - - return SortAndDeduplicateDiagnostics(globalDiagnostics) -} - -func (p *Program) GetDeclarationDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - return p.getDiagnosticsHelper(ctx, sourceFile, true /*ensureBound*/, true /*ensureChecked*/, p.getDeclarationDiagnosticsForFile) -} - -func (p *Program) GetOptionsDiagnostics(ctx context.Context) []*ast.Diagnostic { - return SortAndDeduplicateDiagnostics(append(p.GetGlobalDiagnostics(ctx), p.getOptionsDiagnosticsOfConfigFile()...)) -} - -func (p *Program) getOptionsDiagnosticsOfConfigFile() []*ast.Diagnostic { - // todo update p.configParsingDiagnostics when updateAndGetProgramDiagnostics is implemented - if p.Options() == nil || p.Options().ConfigFilePath == "" { - return nil - } - return p.GetConfigFileParsingDiagnostics() // TODO: actually call getDiagnosticsHelper on config path -} - -func (p *Program) getSyntacticDiagnosticsForFile(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - return core.Concatenate(sourceFile.Diagnostics(), sourceFile.JSDiagnostics()) -} - -func (p *Program) getBindDiagnosticsForFile(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - // TODO: restore this; tsgo's main depends on this function binding all files for timing. - // if checker.SkipTypeChecking(sourceFile, p.compilerOptions) { - // return nil - // } - - return sourceFile.BindDiagnostics() -} - -func FilterNoEmitSemanticDiagnostics(diagnostics []*ast.Diagnostic, options *core.CompilerOptions) []*ast.Diagnostic { - if !options.NoEmit.IsTrue() { - return diagnostics - } - return core.Filter(diagnostics, func(d *ast.Diagnostic) bool { - return !d.SkippedOnNoEmit() - }) -} - -func (p *Program) getSemanticDiagnosticsForFile(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - return slices.Concat( - FilterNoEmitSemanticDiagnostics(p.getSemanticDiagnosticsForFileNotFilter(ctx, sourceFile), p.Options()), - p.GetIncludeProcessorDiagnostics(sourceFile), - ) -} - -func (p *Program) getSemanticDiagnosticsForFileNotFilter(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - compilerOptions := p.Options() - if checker.SkipTypeChecking(sourceFile, compilerOptions, p, false) { - return nil - } - - var fileChecker *checker.Checker - var done func() - if sourceFile != nil { - fileChecker, done = p.checkerPool.GetCheckerForFile(ctx, sourceFile) - defer done() - } - diags := slices.Clip(sourceFile.BindDiagnostics()) - checkers, closeCheckers := p.checkerPool.GetAllCheckers(ctx) - defer closeCheckers() - - // Ask for diags from all checkers; checking one file may add diagnostics to other files. - // These are deduplicated later. - for _, checker := range checkers { - if sourceFile == nil || checker == fileChecker { - diags = append(diags, checker.GetDiagnostics(ctx, sourceFile)...) - } else { - diags = append(diags, checker.GetDiagnosticsWithoutCheck(sourceFile)...) - } - } - if ctx.Err() != nil { - return nil - } - - // !!! This should be rewritten to work like getBindAndCheckDiagnosticsForFileNoCache. - - isPlainJS := ast.IsPlainJSFile(sourceFile, compilerOptions.CheckJs) - if isPlainJS { - return core.Filter(diags, func(d *ast.Diagnostic) bool { - return plainJSErrors.Has(d.Code()) - }) - } - - filtered, directivesByLine := p.getDiagnosticsWithPrecedingDirectives(sourceFile, diags) - for _, directive := range directivesByLine { - // Above we changed all used directive kinds to @ts-ignore, so any @ts-expect-error directives that - // remain are unused and thus errors. - if directive.Kind == ast.CommentDirectiveKindExpectError { - filtered = append(filtered, ast.NewDiagnostic(sourceFile, directive.Loc, diagnostics.Unused_ts_expect_error_directive)) - } - } - return filtered -} - -func (p *Program) getDiagnosticsWithPrecedingDirectives(sourceFile *ast.SourceFile, diags []*ast.Diagnostic) ([]*ast.Diagnostic, map[int]ast.CommentDirective) { - if len(sourceFile.CommentDirectives) == 0 { - return diags, nil - } - // Build map of directives by line number - directivesByLine := make(map[int]ast.CommentDirective) - for _, directive := range sourceFile.CommentDirectives { - line, _ := scanner.GetECMALineAndCharacterOfPosition(sourceFile, directive.Loc.Pos()) - directivesByLine[line] = directive - } - lineStarts := scanner.GetECMALineStarts(sourceFile) - filtered := make([]*ast.Diagnostic, 0, len(diags)) - for _, diagnostic := range diags { - ignoreDiagnostic := false - for line := scanner.ComputeLineOfPosition(lineStarts, diagnostic.Pos()) - 1; line >= 0; line-- { - // If line contains a @ts-ignore or @ts-expect-error directive, ignore this diagnostic and change - // the directive kind to @ts-ignore to indicate it was used. - if directive, ok := directivesByLine[line]; ok { - ignoreDiagnostic = true - directive.Kind = ast.CommentDirectiveKindIgnore - directivesByLine[line] = directive - break - } - // Stop searching backwards when we encounter a line that isn't blank or a comment. - if !isCommentOrBlankLine(sourceFile.Text(), int(lineStarts[line])) { - break - } - } - if !ignoreDiagnostic { - filtered = append(filtered, diagnostic) - } - } - return filtered, directivesByLine -} - -func (p *Program) getDeclarationDiagnosticsForFile(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - if sourceFile.IsDeclarationFile { - return []*ast.Diagnostic{} - } - - if cached, ok := p.declarationDiagnosticCache.Load(sourceFile); ok { - return cached - } - - host, done := newEmitHost(ctx, p, sourceFile) - defer done() - diagnostics := getDeclarationDiagnostics(host, sourceFile) - diagnostics, _ = p.declarationDiagnosticCache.LoadOrStore(sourceFile, diagnostics) - return diagnostics -} - -func (p *Program) getSuggestionDiagnosticsForFile(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic { - if checker.SkipTypeChecking(sourceFile, p.Options(), p, false) { - return nil - } - - var fileChecker *checker.Checker - var done func() - if sourceFile != nil { - fileChecker, done = p.checkerPool.GetCheckerForFile(ctx, sourceFile) - defer done() - } - - diags := slices.Clip(sourceFile.BindSuggestionDiagnostics) - - checkers, closeCheckers := p.checkerPool.GetAllCheckers(ctx) - defer closeCheckers() - - // Ask for diags from all checkers; checking one file may add diagnostics to other files. - // These are deduplicated later. - for _, checker := range checkers { - if sourceFile == nil || checker == fileChecker { - diags = append(diags, checker.GetSuggestionDiagnostics(ctx, sourceFile)...) - } else { - // !!! is there any case where suggestion diagnostics are produced in other checkers? - } - } - if ctx.Err() != nil { - return nil - } - - return diags -} - -func isCommentOrBlankLine(text string, pos int) bool { - for pos < len(text) && (text[pos] == ' ' || text[pos] == '\t') { - pos++ - } - return pos == len(text) || - pos < len(text) && (text[pos] == '\r' || text[pos] == '\n') || - pos+1 < len(text) && text[pos] == '/' && text[pos+1] == '/' -} - -func SortAndDeduplicateDiagnostics(diagnostics []*ast.Diagnostic) []*ast.Diagnostic { - diagnostics = slices.Clone(diagnostics) - slices.SortFunc(diagnostics, ast.CompareDiagnostics) - return compactAndMergeRelatedInfos(diagnostics) -} - -// Remove duplicate diagnostics and, for sequences of diagnostics that differ only by related information, -// create a single diagnostic with sorted and deduplicated related information. -func compactAndMergeRelatedInfos(diagnostics []*ast.Diagnostic) []*ast.Diagnostic { - if len(diagnostics) < 2 { - return diagnostics - } - i := 0 - j := 0 - for i < len(diagnostics) { - d := diagnostics[i] - n := 1 - for i+n < len(diagnostics) && ast.EqualDiagnosticsNoRelatedInfo(d, diagnostics[i+n]) { - n++ - } - if n > 1 { - var relatedInfos []*ast.Diagnostic - for k := range n { - relatedInfos = append(relatedInfos, diagnostics[i+k].RelatedInformation()...) - } - if relatedInfos != nil { - slices.SortFunc(relatedInfos, ast.CompareDiagnostics) - relatedInfos = slices.CompactFunc(relatedInfos, ast.EqualDiagnostics) - d = d.Clone().SetRelatedInfo(relatedInfos) - } - } - diagnostics[j] = d - i += n - j++ - } - clear(diagnostics[j:]) - return diagnostics[:j] -} - -func (p *Program) getDiagnosticsHelper(ctx context.Context, sourceFile *ast.SourceFile, ensureBound bool, ensureChecked bool, getDiagnostics func(context.Context, *ast.SourceFile) []*ast.Diagnostic) []*ast.Diagnostic { - if sourceFile != nil { - if ensureBound { - binder.BindSourceFile(sourceFile) - } - return SortAndDeduplicateDiagnostics(getDiagnostics(ctx, sourceFile)) - } - if ensureBound { - p.BindSourceFiles() - } - if ensureChecked { - p.CheckSourceFiles(ctx, nil) - if ctx.Err() != nil { - return nil - } - } - var result []*ast.Diagnostic - for _, file := range p.files { - result = append(result, getDiagnostics(ctx, file)...) - } - return SortAndDeduplicateDiagnostics(result) -} - -func (p *Program) LineCount() int { - var count int - for _, file := range p.files { - count += len(file.ECMALineMap()) - } - return count -} - -func (p *Program) IdentifierCount() int { - var count int - for _, file := range p.files { - count += file.IdentifierCount - } - return count -} - -func (p *Program) SymbolCount() int { - var count int - for _, file := range p.files { - count += file.SymbolCount - } - checkers, done := p.checkerPool.GetAllCheckers(context.Background()) - defer done() - for _, checker := range checkers { - count += int(checker.SymbolCount) - } - return count -} - -func (p *Program) TypeCount() int { - var count int - checkers, done := p.checkerPool.GetAllCheckers(context.Background()) - defer done() - for _, checker := range checkers { - count += int(checker.TypeCount) - } - return count -} - -func (p *Program) InstantiationCount() int { - var count int - checkers, done := p.checkerPool.GetAllCheckers(context.Background()) - defer done() - for _, checker := range checkers { - count += int(checker.TotalInstantiationCount) - } - return count -} - -func (p *Program) GetSourceFileMetaData(path tspath.Path) ast.SourceFileMetaData { - return p.sourceFileMetaDatas[path] -} - -func (p *Program) GetEmitModuleFormatOfFile(sourceFile ast.HasFileName) core.ModuleKind { - return ast.GetEmitModuleFormatOfFileWorker(sourceFile.FileName(), p.projectReferenceFileMapper.getCompilerOptionsForFile(sourceFile), p.GetSourceFileMetaData(sourceFile.Path())) -} - -func (p *Program) GetEmitSyntaxForUsageLocation(sourceFile ast.HasFileName, location *ast.StringLiteralLike) core.ResolutionMode { - return getEmitSyntaxForUsageLocationWorker(sourceFile.FileName(), p.sourceFileMetaDatas[sourceFile.Path()], location, p.projectReferenceFileMapper.getCompilerOptionsForFile(sourceFile)) -} - -func (p *Program) GetImpliedNodeFormatForEmit(sourceFile ast.HasFileName) core.ResolutionMode { - return ast.GetImpliedNodeFormatForEmitWorker(sourceFile.FileName(), p.projectReferenceFileMapper.getCompilerOptionsForFile(sourceFile).GetEmitModuleKind(), p.GetSourceFileMetaData(sourceFile.Path())) -} - -func (p *Program) GetModeForUsageLocation(sourceFile ast.HasFileName, location *ast.StringLiteralLike) core.ResolutionMode { - return getModeForUsageLocation(sourceFile.FileName(), p.sourceFileMetaDatas[sourceFile.Path()], location, p.projectReferenceFileMapper.getCompilerOptionsForFile(sourceFile)) -} - -func (p *Program) GetDefaultResolutionModeForFile(sourceFile ast.HasFileName) core.ResolutionMode { - return getDefaultResolutionModeForFile(sourceFile.FileName(), p.sourceFileMetaDatas[sourceFile.Path()], p.projectReferenceFileMapper.getCompilerOptionsForFile(sourceFile)) -} - -func (p *Program) IsSourceFileDefaultLibrary(path tspath.Path) bool { - _, ok := p.libFiles[path] - return ok -} - -func (p *Program) GetDefaultLibFile(path tspath.Path) *LibFile { - if libFile, ok := p.libFiles[path]; ok { - return libFile - } - return nil -} - -func (p *Program) CommonSourceDirectory() string { - p.commonSourceDirectoryOnce.Do(func() { - p.commonSourceDirectory = outputpaths.GetCommonSourceDirectory( - p.Options(), - func() []string { - var files []string - for _, file := range p.files { - if sourceFileMayBeEmitted(file, p, false /*forceDtsEmit*/) { - files = append(files, file.FileName()) - } - } - return files - }, - p.GetCurrentDirectory(), - p.UseCaseSensitiveFileNames(), - ) - }) - return p.commonSourceDirectory -} - -type WriteFileData struct { - SourceMapUrlPos int - BuildInfo any - Diagnostics []*ast.Diagnostic - SkippedDtsWrite bool -} - -type WriteFile func(fileName string, text string, writeByteOrderMark bool, data *WriteFileData) error - -type EmitOptions struct { - TargetSourceFile *ast.SourceFile // Single file to emit. If `nil`, emits all files - EmitOnly EmitOnly - WriteFile WriteFile -} - -type EmitResult struct { - EmitSkipped bool - Diagnostics []*ast.Diagnostic // Contains declaration emit diagnostics - EmittedFiles []string // Array of files the compiler wrote to disk - SourceMaps []*SourceMapEmitResult // Array of sourceMapData if compiler emitted sourcemaps -} - -type SourceMapEmitResult struct { - InputSourceFileNames []string // Input source file (which one can use on program to get the file), 1:1 mapping with the sourceMap.sources list - SourceMap *sourcemap.RawSourceMap - GeneratedFile string -} - -func (p *Program) Emit(ctx context.Context, options EmitOptions) *EmitResult { - // !!! performance measurement - p.BindSourceFiles() - if options.EmitOnly != EmitOnlyForcedDts { - result := HandleNoEmitOnError( - ctx, - p, - options.TargetSourceFile, - ) - if result != nil || ctx.Err() != nil { - return result - } - } - - writerPool := &sync.Pool{ - New: func() any { - return printer.NewTextWriter(p.Options().NewLine.GetNewLineCharacter()) - }, - } - wg := core.NewWorkGroup(p.SingleThreaded()) - var emitters []*emitter - sourceFiles := p.getSourceFilesToEmit(options.TargetSourceFile, options.EmitOnly == EmitOnlyForcedDts) - - for _, sourceFile := range sourceFiles { - emitter := &emitter{ - writer: nil, - sourceFile: sourceFile, - emitOnly: options.EmitOnly, - writeFile: options.WriteFile, - } - emitters = append(emitters, emitter) - wg.Queue(func() { - host, done := newEmitHost(ctx, p, sourceFile) - defer done() - emitter.host = host - - // take an unused writer - writer := writerPool.Get().(printer.EmitTextWriter) - writer.Clear() - - // attach writer and perform emit - emitter.writer = writer - emitter.paths = outputpaths.GetOutputPathsFor(sourceFile, host.Options(), host, options.EmitOnly == EmitOnlyForcedDts) - emitter.emit() - emitter.writer = nil - - // put the writer back in the pool - writerPool.Put(writer) - }) - } - - // wait for emit to complete - wg.RunAndWait() - - // collect results from emit, preserving input order - return CombineEmitResults(core.Map(emitters, func(e *emitter) *EmitResult { - return &e.emitResult - })) -} - -func CombineEmitResults(results []*EmitResult) *EmitResult { - result := &EmitResult{} - for _, emitResult := range results { - if emitResult == nil { - continue // Skip nil results - } - if emitResult.EmitSkipped { - result.EmitSkipped = true - } - result.Diagnostics = append(result.Diagnostics, emitResult.Diagnostics...) - result.EmittedFiles = append(result.EmittedFiles, emitResult.EmittedFiles...) - if emitResult.SourceMaps != nil { - result.SourceMaps = append(result.SourceMaps, emitResult.SourceMaps...) - } - } - return result -} - -type ProgramLike interface { - Options() *core.CompilerOptions - GetSourceFiles() []*ast.SourceFile - GetConfigFileParsingDiagnostics() []*ast.Diagnostic - GetSyntacticDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic - GetBindDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic - GetOptionsDiagnostics(ctx context.Context) []*ast.Diagnostic - GetProgramDiagnostics() []*ast.Diagnostic - GetGlobalDiagnostics(ctx context.Context) []*ast.Diagnostic - GetSemanticDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic - GetDeclarationDiagnostics(ctx context.Context, file *ast.SourceFile) []*ast.Diagnostic - Emit(ctx context.Context, options EmitOptions) *EmitResult -} - -func HandleNoEmitOnError(ctx context.Context, program ProgramLike, file *ast.SourceFile) *EmitResult { - if !program.Options().NoEmitOnError.IsTrue() { - return nil // No emit on error is not set, so we can proceed with emitting - } - - diagnostics := GetDiagnosticsOfAnyProgram( - ctx, - program, - file, - true, - program.GetBindDiagnostics, - program.GetSemanticDiagnostics, - ) - if len(diagnostics) == 0 { - return nil // No diagnostics, so we can proceed with emitting - } - return &EmitResult{ - Diagnostics: diagnostics, - EmitSkipped: true, - } -} - -func GetDiagnosticsOfAnyProgram( - ctx context.Context, - program ProgramLike, - file *ast.SourceFile, - skipNoEmitCheckForDtsDiagnostics bool, - getBindDiagnostics func(context.Context, *ast.SourceFile) []*ast.Diagnostic, - getSemanticDiagnostics func(context.Context, *ast.SourceFile) []*ast.Diagnostic, -) []*ast.Diagnostic { - allDiagnostics := slices.Clip(program.GetConfigFileParsingDiagnostics()) - configFileParsingDiagnosticsLength := len(allDiagnostics) - - allDiagnostics = append(allDiagnostics, program.GetSyntacticDiagnostics(ctx, file)...) - allDiagnostics = append(allDiagnostics, program.GetProgramDiagnostics()...) - - if len(allDiagnostics) == configFileParsingDiagnosticsLength { - // Options diagnostics include global diagnostics (even though we collect them separately), - // and global diagnostics create checkers, which then bind all of the files. Do this binding - // early so we can track the time. - getBindDiagnostics(ctx, file) - - allDiagnostics = append(allDiagnostics, program.GetOptionsDiagnostics(ctx)...) - - if program.Options().ListFilesOnly.IsFalseOrUnknown() { - allDiagnostics = append(allDiagnostics, program.GetGlobalDiagnostics(ctx)...) - - if len(allDiagnostics) == configFileParsingDiagnosticsLength { - allDiagnostics = append(allDiagnostics, getSemanticDiagnostics(ctx, file)...) - } - - if (skipNoEmitCheckForDtsDiagnostics || program.Options().NoEmit.IsTrue()) && program.Options().GetEmitDeclarations() && len(allDiagnostics) == configFileParsingDiagnosticsLength { - allDiagnostics = append(allDiagnostics, program.GetDeclarationDiagnostics(ctx, file)...) - } - } - } - return allDiagnostics -} - -func (p *Program) toPath(filename string) tspath.Path { - return tspath.ToPath(filename, p.GetCurrentDirectory(), p.UseCaseSensitiveFileNames()) -} - -func (p *Program) GetSourceFile(filename string) *ast.SourceFile { - path := p.toPath(filename) - return p.GetSourceFileByPath(path) -} - -func (p *Program) GetSourceFileForResolvedModule(fileName string) *ast.SourceFile { - file := p.GetSourceFile(fileName) - if file == nil { - filename := p.GetParseFileRedirect(fileName) - if filename != "" { - return p.GetSourceFile(filename) - } - } - return file -} - -func (p *Program) GetSourceFileByPath(path tspath.Path) *ast.SourceFile { - return p.filesByPath[path] -} - -func (p *Program) HasSameFileNames(other *Program) bool { - return maps.EqualFunc(p.filesByPath, other.filesByPath, func(a, b *ast.SourceFile) bool { - // checks for casing differences on case-insensitive file systems - return a.FileName() == b.FileName() - }) -} - -func (p *Program) GetSourceFiles() []*ast.SourceFile { - return p.files -} - -// Testing only -func (p *Program) GetIncludeReasons() map[tspath.Path][]*fileIncludeReason { - return p.includeProcessor.fileIncludeReasons -} - -// Testing only -func (p *Program) IsMissingPath(path tspath.Path) bool { - return slices.ContainsFunc(p.missingFiles, func(missingPath string) bool { - return p.toPath(missingPath) == path - }) -} - -func (p *Program) ExplainFiles(w io.Writer) { - toRelativeFileName := func(fileName string) string { - return tspath.GetRelativePathFromDirectory(p.GetCurrentDirectory(), fileName, p.comparePathsOptions) - } - for _, file := range p.GetSourceFiles() { - fmt.Fprintln(w, toRelativeFileName(file.FileName())) - for _, reason := range p.includeProcessor.fileIncludeReasons[file.Path()] { - fmt.Fprintln(w, " ", reason.toDiagnostic(p, true).Message()) - } - for _, diag := range p.includeProcessor.explainRedirectAndImpliedFormat(p, file, toRelativeFileName) { - fmt.Fprintln(w, " ", diag.Message()) - } - } -} - -func (p *Program) GetLibFileFromReference(ref *ast.FileReference) *ast.SourceFile { - path, ok := tsoptions.GetLibFileName(ref.FileName) - if !ok { - return nil - } - if sourceFile, ok := p.filesByPath[tspath.Path(path)]; ok { - return sourceFile - } - return nil -} - -func (p *Program) GetResolvedTypeReferenceDirectiveFromTypeReferenceDirective(typeRef *ast.FileReference, sourceFile *ast.SourceFile) *module.ResolvedTypeReferenceDirective { - if resolutions, ok := p.typeResolutionsInFile[sourceFile.Path()]; ok { - if resolved, ok := resolutions[module.ModeAwareCacheKey{Name: typeRef.FileName, Mode: p.getModeForTypeReferenceDirectiveInFile(typeRef, sourceFile)}]; ok { - return resolved - } - } - return nil -} - -func (p *Program) GetResolvedTypeReferenceDirectives() map[tspath.Path]module.ModeAwareCache[*module.ResolvedTypeReferenceDirective] { - return p.typeResolutionsInFile -} - -func (p *Program) getModeForTypeReferenceDirectiveInFile(ref *ast.FileReference, sourceFile *ast.SourceFile) core.ResolutionMode { - if ref.ResolutionMode != core.ResolutionModeNone { - return ref.ResolutionMode - } - return p.GetDefaultResolutionModeForFile(sourceFile) -} - -func (p *Program) IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool { - return p.sourceFilesFoundSearchingNodeModules.Has(file.Path()) -} - -// UnsupportedExtensions returns a list of all present "unsupported" extensions, -// e.g. extensions that are not yet supported by the port. -func (p *Program) UnsupportedExtensions() []string { - return p.unsupportedExtensions -} - -func (p *Program) GetJSXRuntimeImportSpecifier(path tspath.Path) (moduleReference string, specifier *ast.Node) { - if result := p.jsxRuntimeImportSpecifiers[path]; result != nil { - return result.moduleReference, result.specifier - } - return "", nil -} - -func (p *Program) GetImportHelpersImportSpecifier(path tspath.Path) *ast.Node { - return p.importHelpersImportSpecifiers[path] -} - -func (p *Program) SourceFileMayBeEmitted(sourceFile *ast.SourceFile, forceDtsEmit bool) bool { - return sourceFileMayBeEmitted(sourceFile, p, forceDtsEmit) -} - -var plainJSErrors = collections.NewSetFromItems( - // binder errors - diagnostics.Cannot_redeclare_block_scoped_variable_0.Code(), - diagnostics.A_module_cannot_have_multiple_default_exports.Code(), - diagnostics.Another_export_default_is_here.Code(), - diagnostics.The_first_export_default_is_here.Code(), - diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module.Code(), - diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Modules_are_automatically_in_strict_mode.Code(), - diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here.Code(), - diagnostics.X_constructor_is_a_reserved_word.Code(), - diagnostics.X_delete_cannot_be_called_on_an_identifier_in_strict_mode.Code(), - diagnostics.Code_contained_in_a_class_is_evaluated_in_JavaScript_s_strict_mode_which_does_not_allow_this_use_of_0_For_more_information_see_https_Colon_Slash_Slashdeveloper_mozilla_org_Slashen_US_Slashdocs_SlashWeb_SlashJavaScript_SlashReference_SlashStrict_mode.Code(), - diagnostics.Invalid_use_of_0_Modules_are_automatically_in_strict_mode.Code(), - diagnostics.Invalid_use_of_0_in_strict_mode.Code(), - diagnostics.A_label_is_not_allowed_here.Code(), - diagnostics.X_with_statements_are_not_allowed_in_strict_mode.Code(), - // grammar errors - diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement.Code(), - diagnostics.A_break_statement_can_only_jump_to_a_label_of_an_enclosing_statement.Code(), - diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name.Code(), - diagnostics.A_class_member_cannot_have_the_0_keyword.Code(), - diagnostics.A_comma_expression_is_not_allowed_in_a_computed_property_name.Code(), - diagnostics.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement.Code(), - diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement.Code(), - diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement.Code(), - diagnostics.A_default_export_must_be_at_the_top_level_of_a_file_or_module_declaration.Code(), - diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context.Code(), - diagnostics.A_destructuring_declaration_must_have_an_initializer.Code(), - diagnostics.A_get_accessor_cannot_have_parameters.Code(), - diagnostics.A_rest_element_cannot_contain_a_binding_pattern.Code(), - diagnostics.A_rest_element_cannot_have_a_property_name.Code(), - diagnostics.A_rest_element_cannot_have_an_initializer.Code(), - diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern.Code(), - diagnostics.A_rest_parameter_cannot_have_an_initializer.Code(), - diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list.Code(), - diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma.Code(), - diagnostics.A_return_statement_cannot_be_used_inside_a_class_static_block.Code(), - diagnostics.A_set_accessor_cannot_have_rest_parameter.Code(), - diagnostics.A_set_accessor_must_have_exactly_one_parameter.Code(), - diagnostics.An_export_declaration_can_only_be_used_at_the_top_level_of_a_module.Code(), - diagnostics.An_export_declaration_cannot_have_modifiers.Code(), - diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_module.Code(), - diagnostics.An_import_declaration_cannot_have_modifiers.Code(), - diagnostics.An_object_member_cannot_be_declared_optional.Code(), - diagnostics.Argument_of_dynamic_import_cannot_be_spread_element.Code(), - diagnostics.Cannot_assign_to_private_method_0_Private_methods_are_not_writable.Code(), - diagnostics.Cannot_redeclare_identifier_0_in_catch_clause.Code(), - diagnostics.Catch_clause_variable_cannot_have_an_initializer.Code(), - diagnostics.Class_decorators_can_t_be_used_with_static_private_identifier_Consider_removing_the_experimental_decorator.Code(), - diagnostics.Classes_can_only_extend_a_single_class.Code(), - diagnostics.Classes_may_not_have_a_field_named_constructor.Code(), - diagnostics.Did_you_mean_to_use_a_Colon_An_can_only_follow_a_property_name_when_the_containing_object_literal_is_part_of_a_destructuring_pattern.Code(), - diagnostics.Duplicate_label_0.Code(), - diagnostics.Dynamic_imports_can_only_accept_a_module_specifier_and_an_optional_set_of_attributes_as_arguments.Code(), - diagnostics.X_for_await_loops_cannot_be_used_inside_a_class_static_block.Code(), - diagnostics.JSX_attributes_must_only_be_assigned_a_non_empty_expression.Code(), - diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name.Code(), - diagnostics.JSX_expressions_may_not_use_the_comma_operator_Did_you_mean_to_write_an_array.Code(), - diagnostics.JSX_property_access_expressions_cannot_include_JSX_namespace_names.Code(), - diagnostics.Jump_target_cannot_cross_function_boundary.Code(), - diagnostics.Line_terminator_not_permitted_before_arrow.Code(), - diagnostics.Modifiers_cannot_appear_here.Code(), - diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement.Code(), - diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_of_statement.Code(), - diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies.Code(), - diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression.Code(), - diagnostics.Property_0_is_not_accessible_outside_class_1_because_it_has_a_private_identifier.Code(), - diagnostics.Tagged_template_expressions_are_not_permitted_in_an_optional_chain.Code(), - diagnostics.The_left_hand_side_of_a_for_of_statement_may_not_be_async.Code(), - diagnostics.The_variable_declaration_of_a_for_in_statement_cannot_have_an_initializer.Code(), - diagnostics.The_variable_declaration_of_a_for_of_statement_cannot_have_an_initializer.Code(), - diagnostics.Trailing_comma_not_allowed.Code(), - diagnostics.Variable_declaration_list_cannot_be_empty.Code(), - diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses.Code(), - diagnostics.X_0_expected.Code(), - diagnostics.X_0_is_not_a_valid_meta_property_for_keyword_1_Did_you_mean_2.Code(), - diagnostics.X_0_list_cannot_be_empty.Code(), - diagnostics.X_0_modifier_already_seen.Code(), - diagnostics.X_0_modifier_cannot_appear_on_a_constructor_declaration.Code(), - diagnostics.X_0_modifier_cannot_appear_on_a_module_or_namespace_element.Code(), - diagnostics.X_0_modifier_cannot_appear_on_a_parameter.Code(), - diagnostics.X_0_modifier_cannot_appear_on_class_elements_of_this_kind.Code(), - diagnostics.X_0_modifier_cannot_be_used_here.Code(), - diagnostics.X_0_modifier_must_precede_1_modifier.Code(), - diagnostics.X_0_declarations_can_only_be_declared_inside_a_block.Code(), - diagnostics.X_0_declarations_must_be_initialized.Code(), - diagnostics.X_extends_clause_already_seen.Code(), - diagnostics.X_let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations.Code(), - diagnostics.Class_constructor_may_not_be_a_generator.Code(), - diagnostics.Class_constructor_may_not_be_an_accessor.Code(), - diagnostics.X_await_expressions_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules.Code(), - diagnostics.X_await_using_statements_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules.Code(), - diagnostics.Private_field_0_must_be_declared_in_an_enclosing_class.Code(), - // Type errors - diagnostics.This_condition_will_always_return_0_since_JavaScript_compares_objects_by_reference_not_value.Code(), -) diff --git a/kitcom/internal/tsgo/compiler/program_test.go b/kitcom/internal/tsgo/compiler/program_test.go deleted file mode 100644 index eba35f2..0000000 --- a/kitcom/internal/tsgo/compiler/program_test.go +++ /dev/null @@ -1,314 +0,0 @@ -package compiler_test - -import ( - "path/filepath" - "slices" - "strings" - "testing" - - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/bundled" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/compiler" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/repo" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/osvfs" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/vfstest" - "gotest.tools/v3/assert" -) - -type testFile struct { - fileName string - contents string -} - -type programTest struct { - testName string - files []testFile - expectedFiles []string - target core.ScriptTarget -} - -var esnextLibs = []string{ - "lib.es5.d.ts", - "lib.es2015.d.ts", - "lib.es2016.d.ts", - "lib.es2017.d.ts", - "lib.es2018.d.ts", - "lib.es2019.d.ts", - "lib.es2020.d.ts", - "lib.es2021.d.ts", - "lib.es2022.d.ts", - "lib.es2023.d.ts", - "lib.es2024.d.ts", - "lib.esnext.d.ts", - "lib.dom.d.ts", - "lib.dom.iterable.d.ts", - "lib.dom.asynciterable.d.ts", - "lib.webworker.importscripts.d.ts", - "lib.scripthost.d.ts", - "lib.es2015.core.d.ts", - "lib.es2015.collection.d.ts", - "lib.es2015.generator.d.ts", - "lib.es2015.iterable.d.ts", - "lib.es2015.promise.d.ts", - "lib.es2015.proxy.d.ts", - "lib.es2015.reflect.d.ts", - "lib.es2015.symbol.d.ts", - "lib.es2015.symbol.wellknown.d.ts", - "lib.es2016.array.include.d.ts", - "lib.es2016.intl.d.ts", - "lib.es2017.arraybuffer.d.ts", - "lib.es2017.date.d.ts", - "lib.es2017.object.d.ts", - "lib.es2017.sharedmemory.d.ts", - "lib.es2017.string.d.ts", - "lib.es2017.intl.d.ts", - "lib.es2017.typedarrays.d.ts", - "lib.es2018.asyncgenerator.d.ts", - "lib.es2018.asynciterable.d.ts", - "lib.es2018.intl.d.ts", - "lib.es2018.promise.d.ts", - "lib.es2018.regexp.d.ts", - "lib.es2019.array.d.ts", - "lib.es2019.object.d.ts", - "lib.es2019.string.d.ts", - "lib.es2019.symbol.d.ts", - "lib.es2019.intl.d.ts", - "lib.es2020.bigint.d.ts", - "lib.es2020.date.d.ts", - "lib.es2020.promise.d.ts", - "lib.es2020.sharedmemory.d.ts", - "lib.es2020.string.d.ts", - "lib.es2020.symbol.wellknown.d.ts", - "lib.es2020.intl.d.ts", - "lib.es2020.number.d.ts", - "lib.es2021.promise.d.ts", - "lib.es2021.string.d.ts", - "lib.es2021.weakref.d.ts", - "lib.es2021.intl.d.ts", - "lib.es2022.array.d.ts", - "lib.es2022.error.d.ts", - "lib.es2022.intl.d.ts", - "lib.es2022.object.d.ts", - "lib.es2022.string.d.ts", - "lib.es2022.regexp.d.ts", - "lib.es2023.array.d.ts", - "lib.es2023.collection.d.ts", - "lib.es2023.intl.d.ts", - "lib.es2024.arraybuffer.d.ts", - "lib.es2024.collection.d.ts", - "lib.es2024.object.d.ts", - "lib.es2024.promise.d.ts", - "lib.es2024.regexp.d.ts", - "lib.es2024.sharedmemory.d.ts", - "lib.es2024.string.d.ts", - "lib.esnext.array.d.ts", - "lib.esnext.collection.d.ts", - "lib.esnext.intl.d.ts", - "lib.esnext.disposable.d.ts", - "lib.esnext.promise.d.ts", - "lib.esnext.decorators.d.ts", - "lib.esnext.iterator.d.ts", - "lib.esnext.float16.d.ts", - "lib.esnext.error.d.ts", - "lib.esnext.sharedmemory.d.ts", - "lib.decorators.d.ts", - "lib.decorators.legacy.d.ts", - "lib.esnext.full.d.ts", -} - -var programTestCases = []programTest{ - { - testName: "BasicFileOrdering", - files: []testFile{ - {fileName: "c:/dev/src/index.ts", contents: "/// \n/// "}, - {fileName: "c:/dev/src2/a/5.ts", contents: "/// "}, - {fileName: "c:/dev/src2/a/4.ts", contents: "/// "}, - {fileName: "c:/dev/src2/a/b/3.ts", contents: "/// "}, - {fileName: "c:/dev/src2/a/b/2.ts", contents: "/// "}, - {fileName: "c:/dev/src2/a/b/c/1.ts", contents: "console.log('hello');"}, - {fileName: "c:/dev/src2/a/10.ts", contents: "/// "}, - {fileName: "c:/dev/src2/a/b/c/d/9.ts", contents: "/// "}, - {fileName: "c:/dev/src2/a/b/c/d/e/8.ts", contents: "/// "}, - {fileName: "c:/dev/src2/a/b/c/d/e/7.ts", contents: "/// "}, - {fileName: "c:/dev/src2/a/b/c/d/e/f/6.ts", contents: "console.log('world!');"}, - }, - expectedFiles: slices.Concat(esnextLibs, - []string{ - "c:/dev/src2/a/b/c/1.ts", - "c:/dev/src2/a/b/2.ts", - "c:/dev/src2/a/b/3.ts", - "c:/dev/src2/a/4.ts", - "c:/dev/src2/a/5.ts", - "c:/dev/src2/a/b/c/d/e/f/6.ts", - "c:/dev/src2/a/b/c/d/e/7.ts", - "c:/dev/src2/a/b/c/d/e/8.ts", - "c:/dev/src2/a/b/c/d/9.ts", - "c:/dev/src2/a/10.ts", - "c:/dev/src/index.ts", - }), - target: core.ScriptTargetESNext, - }, - { - testName: "FileOrderingImports", - files: []testFile{ - {fileName: "c:/dev/src/index.ts", contents: "import * as five from '../src2/a/5.ts';\nimport * as ten from '../src2/a/10.ts';"}, - {fileName: "c:/dev/src2/a/5.ts", contents: "import * as four from './4.ts';"}, - {fileName: "c:/dev/src2/a/4.ts", contents: "import * as three from './b/3.ts';"}, - {fileName: "c:/dev/src2/a/b/3.ts", contents: "import * as two from './2.ts';"}, - {fileName: "c:/dev/src2/a/b/2.ts", contents: "import * as one from './c/1.ts';"}, - {fileName: "c:/dev/src2/a/b/c/1.ts", contents: "console.log('hello');"}, - {fileName: "c:/dev/src2/a/10.ts", contents: "import * as nine from './b/c/d/9.ts';"}, - {fileName: "c:/dev/src2/a/b/c/d/9.ts", contents: "import * as eight from './e/8.ts';"}, - {fileName: "c:/dev/src2/a/b/c/d/e/8.ts", contents: "import * as seven from './7.ts';"}, - {fileName: "c:/dev/src2/a/b/c/d/e/7.ts", contents: "import * as six from './f/6.ts';"}, - {fileName: "c:/dev/src2/a/b/c/d/e/f/6.ts", contents: "console.log('world!');"}, - }, - expectedFiles: slices.Concat(esnextLibs, - []string{ - "c:/dev/src2/a/b/c/1.ts", - "c:/dev/src2/a/b/2.ts", - "c:/dev/src2/a/b/3.ts", - "c:/dev/src2/a/4.ts", - "c:/dev/src2/a/5.ts", - "c:/dev/src2/a/b/c/d/e/f/6.ts", - "c:/dev/src2/a/b/c/d/e/7.ts", - "c:/dev/src2/a/b/c/d/e/8.ts", - "c:/dev/src2/a/b/c/d/9.ts", - "c:/dev/src2/a/10.ts", - "c:/dev/src/index.ts", - }), - target: core.ScriptTargetESNext, - }, - { - testName: "FileOrderingCycles", - files: []testFile{ - {fileName: "c:/dev/src/index.ts", contents: "import * as five from '../src2/a/5.ts';\nimport * as ten from '../src2/a/10.ts';"}, - {fileName: "c:/dev/src2/a/5.ts", contents: "import * as four from './4.ts';"}, - {fileName: "c:/dev/src2/a/4.ts", contents: "import * as three from './b/3.ts';"}, - {fileName: "c:/dev/src2/a/b/3.ts", contents: "import * as two from './2.ts';\nimport * as cycle from 'c:/dev/src/index.ts'; "}, - {fileName: "c:/dev/src2/a/b/2.ts", contents: "import * as one from './c/1.ts';"}, - {fileName: "c:/dev/src2/a/b/c/1.ts", contents: "console.log('hello');"}, - {fileName: "c:/dev/src2/a/10.ts", contents: "import * as nine from './b/c/d/9.ts';"}, - {fileName: "c:/dev/src2/a/b/c/d/9.ts", contents: "import * as eight from './e/8.ts';\nimport * as cycle from 'c:/dev/src/index.ts';"}, - {fileName: "c:/dev/src2/a/b/c/d/e/8.ts", contents: "import * as seven from './7.ts';"}, - {fileName: "c:/dev/src2/a/b/c/d/e/7.ts", contents: "import * as six from './f/6.ts';"}, - {fileName: "c:/dev/src2/a/b/c/d/e/f/6.ts", contents: "console.log('world!');"}, - }, - expectedFiles: slices.Concat(esnextLibs, - []string{ - "c:/dev/src2/a/b/c/1.ts", - "c:/dev/src2/a/b/2.ts", - "c:/dev/src2/a/b/3.ts", - "c:/dev/src2/a/4.ts", - "c:/dev/src2/a/5.ts", - "c:/dev/src2/a/b/c/d/e/f/6.ts", - "c:/dev/src2/a/b/c/d/e/7.ts", - "c:/dev/src2/a/b/c/d/e/8.ts", - "c:/dev/src2/a/b/c/d/9.ts", - "c:/dev/src2/a/10.ts", - "c:/dev/src/index.ts", - }), - target: core.ScriptTargetESNext, - }, -} - -func TestProgram(t *testing.T) { - t.Parallel() - - if !bundled.Embedded { - // Without embedding, we'd need to read all of the lib files out from disk into the MapFS. - // Just skip this for now. - t.Skip("bundled files are not embedded") - } - - for _, testCase := range programTestCases { - t.Run(testCase.testName, func(t *testing.T) { - t.Parallel() - libPrefix := bundled.LibPath() + "/" - fs := vfstest.FromMap[any](nil, false /*useCaseSensitiveFileNames*/) - fs = bundled.WrapFS(fs) - - for _, testFile := range testCase.files { - _ = fs.WriteFile(testFile.fileName, testFile.contents, false) - } - - opts := core.CompilerOptions{Target: testCase.target} - - program := compiler.NewProgram(compiler.ProgramOptions{ - Config: &tsoptions.ParsedCommandLine{ - ParsedConfig: &core.ParsedOptions{ - FileNames: []string{"c:/dev/src/index.ts"}, - CompilerOptions: &opts, - }, - }, - Host: compiler.NewCompilerHost("c:/dev/src", fs, bundled.LibPath(), nil, nil), - }) - - actualFiles := []string{} - for _, file := range program.GetSourceFiles() { - actualFiles = append(actualFiles, strings.TrimPrefix(file.FileName(), libPrefix)) - } - - assert.DeepEqual(t, testCase.expectedFiles, actualFiles) - }) - } -} - -func BenchmarkNewProgram(b *testing.B) { - if !bundled.Embedded { - // Without embedding, we'd need to read all of the lib files out from disk into the MapFS. - // Just skip this for now. - b.Skip("bundled files are not embedded") - } - - for _, testCase := range programTestCases { - b.Run(testCase.testName, func(b *testing.B) { - fs := vfstest.FromMap[any](nil, false /*useCaseSensitiveFileNames*/) - fs = bundled.WrapFS(fs) - - for _, testFile := range testCase.files { - _ = fs.WriteFile(testFile.fileName, testFile.contents, false) - } - - opts := core.CompilerOptions{Target: testCase.target} - programOpts := compiler.ProgramOptions{ - Config: &tsoptions.ParsedCommandLine{ - ParsedConfig: &core.ParsedOptions{ - FileNames: []string{"c:/dev/src/index.ts"}, - CompilerOptions: &opts, - }, - }, - Host: compiler.NewCompilerHost("c:/dev/src", fs, bundled.LibPath(), nil, nil), - } - - for b.Loop() { - compiler.NewProgram(programOpts) - } - }) - } - - b.Run("compiler", func(b *testing.B) { - repo.SkipIfNoTypeScriptSubmodule(b) - - rootPath := tspath.NormalizeSlashes(filepath.Join(repo.TypeScriptSubmodulePath, "src", "compiler")) - - fs := osvfs.FS() - fs = bundled.WrapFS(fs) - - host := compiler.NewCompilerHost(rootPath, fs, bundled.LibPath(), nil, nil) - - parsed, errors := tsoptions.GetParsedCommandLineOfConfigFile(tspath.CombinePaths(rootPath, "tsconfig.json"), nil, host, nil) - assert.Equal(b, len(errors), 0, "Expected no errors in parsed command line") - - opts := compiler.ProgramOptions{ - Config: parsed, - Host: host, - } - - for b.Loop() { - compiler.NewProgram(opts) - } - }) -} diff --git a/kitcom/internal/tsgo/compiler/projectreferencedtsfakinghost.go b/kitcom/internal/tsgo/compiler/projectreferencedtsfakinghost.go deleted file mode 100644 index a5ced2d..0000000 --- a/kitcom/internal/tsgo/compiler/projectreferencedtsfakinghost.go +++ /dev/null @@ -1,226 +0,0 @@ -package compiler - -import ( - "strings" - "time" - - "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/tspath" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/vfs/cachedvfs" -) - -type projectReferenceDtsFakingHost struct { - host CompilerHost - fs *cachedvfs.FS -} - -var _ module.ResolutionHost = (*projectReferenceDtsFakingHost)(nil) - -func newProjectReferenceDtsFakingHost(loader *fileLoader) module.ResolutionHost { - // Create a new host that will fake the dts files - host := &projectReferenceDtsFakingHost{ - host: loader.opts.Host, - fs: cachedvfs.From(&projectReferenceDtsFakingVfs{ - projectReferenceFileMapper: loader.projectReferenceFileMapper, - dtsDirectories: loader.dtsDirectories, - knownSymlinks: knownSymlinks{}, - }), - } - return host -} - -// FS implements module.ResolutionHost. -func (h *projectReferenceDtsFakingHost) FS() vfs.FS { - return h.fs -} - -// GetCurrentDirectory implements module.ResolutionHost. -func (h *projectReferenceDtsFakingHost) GetCurrentDirectory() string { - return h.host.GetCurrentDirectory() -} - -type projectReferenceDtsFakingVfs struct { - projectReferenceFileMapper *projectReferenceFileMapper - dtsDirectories collections.Set[tspath.Path] - knownSymlinks knownSymlinks -} - -var _ vfs.FS = (*projectReferenceDtsFakingVfs)(nil) - -// UseCaseSensitiveFileNames implements vfs.FS. -func (fs *projectReferenceDtsFakingVfs) UseCaseSensitiveFileNames() bool { - return fs.projectReferenceFileMapper.opts.Host.FS().UseCaseSensitiveFileNames() -} - -// FileExists implements vfs.FS. -func (fs *projectReferenceDtsFakingVfs) FileExists(path string) bool { - if fs.projectReferenceFileMapper.opts.Host.FS().FileExists(path) { - return true - } - if !tspath.IsDeclarationFileName(path) { - return false - } - // Project references go to source file instead of .d.ts file - return fs.fileOrDirectoryExistsUsingSource(path /*isFile*/, true) -} - -// ReadFile implements vfs.FS. -func (fs *projectReferenceDtsFakingVfs) ReadFile(path string) (contents string, ok bool) { - // Dont need to override as we cannot mimick read file - return fs.projectReferenceFileMapper.opts.Host.FS().ReadFile(path) -} - -// WriteFile implements vfs.FS. -func (fs *projectReferenceDtsFakingVfs) WriteFile(path string, data string, writeByteOrderMark bool) error { - panic("should not be called by resolver") -} - -// Remove implements vfs.FS. -func (fs *projectReferenceDtsFakingVfs) Remove(path string) error { - panic("should not be called by resolver") -} - -// Chtimes implements vfs.FS. -func (fs *projectReferenceDtsFakingVfs) Chtimes(path string, aTime time.Time, mTime time.Time) error { - panic("should not be called by resolver") -} - -// DirectoryExists implements vfs.FS. -func (fs *projectReferenceDtsFakingVfs) DirectoryExists(path string) bool { - if fs.projectReferenceFileMapper.opts.Host.FS().DirectoryExists(path) { - fs.handleDirectoryCouldBeSymlink(path) - return true - } - return fs.fileOrDirectoryExistsUsingSource(path /*isFile*/, false) -} - -// GetAccessibleEntries implements vfs.FS. -func (fs *projectReferenceDtsFakingVfs) GetAccessibleEntries(path string) vfs.Entries { - panic("should not be called by resolver") -} - -// Stat implements vfs.FS. -func (fs *projectReferenceDtsFakingVfs) Stat(path string) vfs.FileInfo { - panic("should not be called by resolver") -} - -// WalkDir implements vfs.FS. -func (fs *projectReferenceDtsFakingVfs) WalkDir(root string, walkFn vfs.WalkDirFunc) error { - panic("should not be called by resolver") -} - -// Realpath implements vfs.FS. -func (fs *projectReferenceDtsFakingVfs) Realpath(path string) string { - result, ok := fs.knownSymlinks.Files().Load(fs.toPath(path)) - if ok { - return result - } - return fs.projectReferenceFileMapper.opts.Host.FS().Realpath(path) -} - -func (fs *projectReferenceDtsFakingVfs) toPath(path string) tspath.Path { - return tspath.ToPath(path, fs.projectReferenceFileMapper.opts.Host.GetCurrentDirectory(), fs.UseCaseSensitiveFileNames()) -} - -func (fs *projectReferenceDtsFakingVfs) handleDirectoryCouldBeSymlink(directory string) { - if tspath.ContainsIgnoredPath(directory) { - return - } - - // Because we already watch node_modules, handle symlinks in there - if !strings.Contains(directory, "/node_modules/") { - return - } - - directoryPath := tspath.Path(tspath.EnsureTrailingDirectorySeparator(string(fs.toPath(directory)))) - if _, ok := fs.knownSymlinks.Directories().Load(directoryPath); ok { - return - } - - realDirectory := fs.Realpath(directory) - var realPath tspath.Path - if realDirectory == directory { - // not symlinked - return - } - if realPath = tspath.Path(tspath.EnsureTrailingDirectorySeparator(string(fs.toPath(realDirectory)))); realPath == directoryPath { - // not symlinked - return - } - fs.knownSymlinks.SetDirectory(directory, directoryPath, &knownDirectoryLink{ - Real: tspath.EnsureTrailingDirectorySeparator(realDirectory), - RealPath: realPath, - }) -} - -func (fs *projectReferenceDtsFakingVfs) fileOrDirectoryExistsUsingSource(fileOrDirectory string, isFile bool) bool { - fileOrDirectoryExistsUsingSource := core.IfElse(isFile, fs.fileExistsIfProjectReferenceDts, fs.directoryExistsIfProjectReferenceDeclDir) - // Check current directory or file - result := fileOrDirectoryExistsUsingSource(fileOrDirectory) - if result != core.TSUnknown { - return result == core.TSTrue - } - - knownDirectoryLinks := fs.knownSymlinks.Directories() - if knownDirectoryLinks.Size() == 0 { - return false - } - fileOrDirectoryPath := fs.toPath(fileOrDirectory) - if !strings.Contains(string(fileOrDirectoryPath), "/node_modules/") { - return false - } - if isFile { - _, ok := fs.knownSymlinks.Files().Load(fileOrDirectoryPath) - if ok { - return true - } - } - - // If it contains node_modules check if its one of the symlinked path we know of - var exists bool - knownDirectoryLinks.Range(func(directoryPath tspath.Path, knownDirectoryLink *knownDirectoryLink) bool { - relative, hasPrefix := strings.CutPrefix(string(fileOrDirectoryPath), string(directoryPath)) - if !hasPrefix { - return true - } - if exists = fileOrDirectoryExistsUsingSource(string(knownDirectoryLink.RealPath) + relative).IsTrue(); exists { - if isFile { - // Store the real path for the file - absolutePath := tspath.GetNormalizedAbsolutePath(fileOrDirectory, fs.projectReferenceFileMapper.opts.Host.GetCurrentDirectory()) - fs.knownSymlinks.SetFile( - fileOrDirectoryPath, - knownDirectoryLink.Real+absolutePath[len(directoryPath):], - ) - } - return false - } - return true - }) - return exists -} - -func (fs *projectReferenceDtsFakingVfs) fileExistsIfProjectReferenceDts(file string) core.Tristate { - source := fs.projectReferenceFileMapper.getProjectReferenceFromOutputDts(fs.toPath(file)) - if source != nil { - return core.IfElse(fs.projectReferenceFileMapper.opts.Host.FS().FileExists(source.Source), core.TSTrue, core.TSFalse) - } - return core.TSUnknown -} - -func (fs *projectReferenceDtsFakingVfs) directoryExistsIfProjectReferenceDeclDir(dir string) core.Tristate { - dirPath := fs.toPath(dir) - dirPathWithTrailingDirectorySeparator := dirPath + "/" - for declDirPath := range fs.dtsDirectories.Keys() { - if dirPath == declDirPath || - // Any parent directory of declaration dir - strings.HasPrefix(string(declDirPath), string(dirPathWithTrailingDirectorySeparator)) || - // Any directory inside declaration dir - strings.HasPrefix(string(dirPath), string(declDirPath)+"/") { - return core.TSTrue - } - } - return core.TSUnknown -} diff --git a/kitcom/internal/tsgo/compiler/projectreferencefilemapper.go b/kitcom/internal/tsgo/compiler/projectreferencefilemapper.go deleted file mode 100644 index 40a50df..0000000 --- a/kitcom/internal/tsgo/compiler/projectreferencefilemapper.go +++ /dev/null @@ -1,166 +0,0 @@ -package compiler - -import ( - "strings" - - "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 projectReferenceFileMapper struct { - opts ProgramOptions - host module.ResolutionHost - loader *fileLoader // Only present during populating the mapper and parsing, released after that - - configToProjectReference map[tspath.Path]*tsoptions.ParsedCommandLine // All the resolved references needed - referencesInConfigFile map[tspath.Path][]tspath.Path // Map of config file to its references - sourceToProjectReference map[tspath.Path]*tsoptions.SourceOutputAndProjectReference - outputDtsToProjectReference map[tspath.Path]*tsoptions.SourceOutputAndProjectReference - - // Store all the realpath from dts in node_modules to source file from project reference needed during parsing so it can be used later - realpathDtsToSource collections.SyncMap[tspath.Path, *tsoptions.SourceOutputAndProjectReference] -} - -func (mapper *projectReferenceFileMapper) getParseFileRedirect(file ast.HasFileName) string { - if mapper.opts.canUseProjectReferenceSource() { - // Map to source file from project reference - source := mapper.getProjectReferenceFromOutputDts(file.Path()) - if source == nil { - source = mapper.getSourceToDtsIfSymlink(file) - } - if source != nil { - return source.Source - } - } else { - // Map to dts file from project reference - output := mapper.getProjectReferenceFromSource(file.Path()) - if output != nil && output.OutputDts != "" { - return output.OutputDts - } - } - return "" -} - -func (mapper *projectReferenceFileMapper) getResolvedProjectReferences() []*tsoptions.ParsedCommandLine { - refs, ok := mapper.referencesInConfigFile[mapper.opts.Config.ConfigFile.SourceFile.Path()] - var result []*tsoptions.ParsedCommandLine - if ok { - result = make([]*tsoptions.ParsedCommandLine, 0, len(refs)) - for _, refPath := range refs { - refConfig, _ := mapper.configToProjectReference[refPath] - result = append(result, refConfig) - } - } - return result -} - -func (mapper *projectReferenceFileMapper) getProjectReferenceFromSource(path tspath.Path) *tsoptions.SourceOutputAndProjectReference { - return mapper.sourceToProjectReference[path] -} - -func (mapper *projectReferenceFileMapper) getProjectReferenceFromOutputDts(path tspath.Path) *tsoptions.SourceOutputAndProjectReference { - return mapper.outputDtsToProjectReference[path] -} - -func (mapper *projectReferenceFileMapper) isSourceFromProjectReference(path tspath.Path) bool { - return mapper.opts.canUseProjectReferenceSource() && mapper.getProjectReferenceFromSource(path) != nil -} - -func (mapper *projectReferenceFileMapper) getCompilerOptionsForFile(file ast.HasFileName) *core.CompilerOptions { - redirect := mapper.getRedirectParsedCommandLineForResolution(file) - return module.GetCompilerOptionsWithRedirect(mapper.opts.Config.CompilerOptions(), redirect) -} - -func (mapper *projectReferenceFileMapper) getRedirectParsedCommandLineForResolution(file ast.HasFileName) *tsoptions.ParsedCommandLine { - redirect, _ := mapper.getRedirectForResolution(file) - return redirect -} - -func (mapper *projectReferenceFileMapper) getRedirectForResolution(file ast.HasFileName) (*tsoptions.ParsedCommandLine, string) { - path := file.Path() - // Check if outputdts of source file from project reference - output := mapper.getProjectReferenceFromSource(path) - if output != nil { - return output.Resolved, output.Source - } - - // Source file from project reference - resultFromDts := mapper.getProjectReferenceFromOutputDts(path) - if resultFromDts != nil { - return resultFromDts.Resolved, resultFromDts.Source - } - - realpathDtsToSource := mapper.getSourceToDtsIfSymlink(file) - if realpathDtsToSource != nil { - return realpathDtsToSource.Resolved, realpathDtsToSource.Source - } - return nil, file.FileName() -} - -func (mapper *projectReferenceFileMapper) getResolvedReferenceFor(path tspath.Path) (*tsoptions.ParsedCommandLine, bool) { - config, ok := mapper.configToProjectReference[path] - return config, ok -} - -func (mapper *projectReferenceFileMapper) forEachResolvedProjectReference( - fn func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int), -) { - if mapper.opts.Config.ConfigFile == nil { - return - } - seenRef := collections.NewSetWithSizeHint[tspath.Path](len(mapper.referencesInConfigFile)) - seenRef.Add(mapper.opts.Config.ConfigFile.SourceFile.Path()) - refs := mapper.referencesInConfigFile[mapper.opts.Config.ConfigFile.SourceFile.Path()] - mapper.forEachResolvedReferenceWorker(refs, fn, mapper.opts.Config, seenRef) -} - -func (mapper *projectReferenceFileMapper) forEachResolvedReferenceWorker( - references []tspath.Path, - fn func(path tspath.Path, config *tsoptions.ParsedCommandLine, parent *tsoptions.ParsedCommandLine, index int), - parent *tsoptions.ParsedCommandLine, - seenRef *collections.Set[tspath.Path], -) { - for index, path := range references { - if !seenRef.AddIfAbsent(path) { - continue - } - config, _ := mapper.configToProjectReference[path] - fn(path, config, parent, index) - mapper.forEachResolvedReferenceWorker(mapper.referencesInConfigFile[path], fn, config, seenRef) - } -} - -func (mapper *projectReferenceFileMapper) getSourceToDtsIfSymlink(file ast.HasFileName) *tsoptions.SourceOutputAndProjectReference { - // If preserveSymlinks is true, module resolution wont jump the symlink - // but the resolved real path may be the .d.ts from project reference - // Note:: Currently we try the real path only if the - // file is from node_modules to avoid having to run real path on all file paths - path := file.Path() - realpathDtsToSource, ok := mapper.realpathDtsToSource.Load(path) - if ok { - return realpathDtsToSource - } - if mapper.loader != nil && mapper.opts.Config.CompilerOptions().PreserveSymlinks == core.TSTrue { - fileName := file.FileName() - if !strings.Contains(fileName, "/node_modules/") { - mapper.realpathDtsToSource.Store(path, nil) - } else { - realDeclarationPath := mapper.loader.toPath(mapper.host.FS().Realpath(fileName)) - if realDeclarationPath == path { - mapper.realpathDtsToSource.Store(path, nil) - } else { - realpathDtsToSource := mapper.getProjectReferenceFromOutputDts(realDeclarationPath) - if realpathDtsToSource != nil { - mapper.realpathDtsToSource.Store(path, realpathDtsToSource) - return realpathDtsToSource - } - mapper.realpathDtsToSource.Store(path, nil) - } - } - } - return nil -} diff --git a/kitcom/internal/tsgo/compiler/projectreferenceparser.go b/kitcom/internal/tsgo/compiler/projectreferenceparser.go deleted file mode 100644 index 98d43ed..0000000 --- a/kitcom/internal/tsgo/compiler/projectreferenceparser.go +++ /dev/null @@ -1,111 +0,0 @@ -package compiler - -import ( - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions" - "efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath" -) - -type projectReferenceParseTask struct { - configName string - resolved *tsoptions.ParsedCommandLine - subTasks []*projectReferenceParseTask -} - -func (t *projectReferenceParseTask) parse(projectReferenceParser *projectReferenceParser) { - t.resolved = projectReferenceParser.loader.opts.Host.GetResolvedProjectReference(t.configName, projectReferenceParser.loader.toPath(t.configName)) - if t.resolved == nil { - return - } - t.resolved.ParseInputOutputNames() - if subReferences := t.resolved.ResolvedProjectReferencePaths(); len(subReferences) > 0 { - t.subTasks = createProjectReferenceParseTasks(subReferences) - } -} - -func createProjectReferenceParseTasks(projectReferences []string) []*projectReferenceParseTask { - return core.Map(projectReferences, func(configName string) *projectReferenceParseTask { - return &projectReferenceParseTask{ - configName: configName, - } - }) -} - -type projectReferenceParser struct { - loader *fileLoader - wg core.WorkGroup - tasksByFileName collections.SyncMap[tspath.Path, *projectReferenceParseTask] -} - -func (p *projectReferenceParser) parse(tasks []*projectReferenceParseTask) { - p.loader.projectReferenceFileMapper.loader = p.loader - p.start(tasks) - p.wg.RunAndWait() - p.initMapper(tasks) -} - -func (p *projectReferenceParser) start(tasks []*projectReferenceParseTask) { - for i, task := range tasks { - path := p.loader.toPath(task.configName) - if loadedTask, loaded := p.tasksByFileName.LoadOrStore(path, task); loaded { - // dedup tasks to ensure correct file order, regardless of which task would be started first - tasks[i] = loadedTask - } else { - p.wg.Queue(func() { - task.parse(p) - p.start(task.subTasks) - }) - } - } -} - -func (p *projectReferenceParser) initMapper(tasks []*projectReferenceParseTask) { - totalReferences := p.tasksByFileName.Size() + 1 - p.loader.projectReferenceFileMapper.configToProjectReference = make(map[tspath.Path]*tsoptions.ParsedCommandLine, totalReferences) - p.loader.projectReferenceFileMapper.referencesInConfigFile = make(map[tspath.Path][]tspath.Path, totalReferences) - p.loader.projectReferenceFileMapper.sourceToProjectReference = make(map[tspath.Path]*tsoptions.SourceOutputAndProjectReference) - p.loader.projectReferenceFileMapper.outputDtsToProjectReference = make(map[tspath.Path]*tsoptions.SourceOutputAndProjectReference) - p.loader.projectReferenceFileMapper.referencesInConfigFile[p.loader.opts.Config.ConfigFile.SourceFile.Path()] = p.initMapperWorker(tasks, &collections.Set[*projectReferenceParseTask]{}) - if p.loader.projectReferenceFileMapper.opts.canUseProjectReferenceSource() && len(p.loader.projectReferenceFileMapper.outputDtsToProjectReference) != 0 { - p.loader.projectReferenceFileMapper.host = newProjectReferenceDtsFakingHost(p.loader) - } -} - -func (p *projectReferenceParser) initMapperWorker(tasks []*projectReferenceParseTask, seen *collections.Set[*projectReferenceParseTask]) []tspath.Path { - if len(tasks) == 0 { - return nil - } - results := make([]tspath.Path, 0, len(tasks)) - for _, task := range tasks { - path := p.loader.toPath(task.configName) - results = append(results, path) - // ensure we only walk each task once - if !seen.AddIfAbsent(task) { - continue - } - var referencesInConfig []tspath.Path - referencesInConfig = p.initMapperWorker(task.subTasks, seen) - p.loader.projectReferenceFileMapper.configToProjectReference[path] = task.resolved - p.loader.projectReferenceFileMapper.referencesInConfigFile[path] = referencesInConfig - if task.resolved == nil || p.loader.projectReferenceFileMapper.opts.Config.ConfigFile == task.resolved.ConfigFile { - continue - } - for key, value := range task.resolved.SourceToProjectReference() { - p.loader.projectReferenceFileMapper.sourceToProjectReference[key] = value - } - for key, value := range task.resolved.OutputDtsToProjectReference() { - p.loader.projectReferenceFileMapper.outputDtsToProjectReference[key] = value - } - if p.loader.projectReferenceFileMapper.opts.canUseProjectReferenceSource() { - declDir := task.resolved.CompilerOptions().DeclarationDir - if declDir == "" { - declDir = task.resolved.CompilerOptions().OutDir - } - if declDir != "" { - p.loader.dtsDirectories.Add(p.loader.toPath(declDir)) - } - } - } - return results -}