remove unused packages

This commit is contained in:
Egor Aristov 2025-10-15 17:26:56 +03:00
parent b1f5661baf
commit 2ba4cf4f1e
Signed by: egor3f
GPG Key ID: 40482A264AAEC85F
16 changed files with 0 additions and 5025 deletions

View File

@ -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() {}

View File

@ -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)
}

View File

@ -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()
}

View File

@ -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))
}
}

View File

@ -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
}

View File

@ -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}
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -1,2 +0,0 @@
// Package compiler implements the TypeScript compiler.
package compiler

View File

@ -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
}

File diff suppressed because it is too large Load Diff

View File

@ -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: "/// <reference path='c:/dev/src2/a/5.ts' />\n/// <reference path='c:/dev/src2/a/10.ts' />"},
{fileName: "c:/dev/src2/a/5.ts", contents: "/// <reference path='4.ts' />"},
{fileName: "c:/dev/src2/a/4.ts", contents: "/// <reference path='b/3.ts' />"},
{fileName: "c:/dev/src2/a/b/3.ts", contents: "/// <reference path='2.ts' />"},
{fileName: "c:/dev/src2/a/b/2.ts", contents: "/// <reference path='c/1.ts' />"},
{fileName: "c:/dev/src2/a/b/c/1.ts", contents: "console.log('hello');"},
{fileName: "c:/dev/src2/a/10.ts", contents: "/// <reference path='b/c/d/9.ts' />"},
{fileName: "c:/dev/src2/a/b/c/d/9.ts", contents: "/// <reference path='e/8.ts' />"},
{fileName: "c:/dev/src2/a/b/c/d/e/8.ts", contents: "/// <reference path='7.ts' />"},
{fileName: "c:/dev/src2/a/b/c/d/e/7.ts", contents: "/// <reference path='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: "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)
}
})
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}