vendor tsgo
This commit is contained in:
115
tools/tsgo/internal/compiler/projectreferenceparser.go
Normal file
115
tools/tsgo/internal/compiler/projectreferenceparser.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"maps"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/collections"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/tracing"
|
||||
"github.com/microsoft/typescript-go/internal/tsoptions"
|
||||
"github.com/microsoft/typescript-go/internal/tspath"
|
||||
)
|
||||
|
||||
type projectReferenceParseTask struct {
|
||||
configName string
|
||||
resolved *tsoptions.ParsedCommandLine
|
||||
subTasks []*projectReferenceParseTask
|
||||
}
|
||||
|
||||
func (t *projectReferenceParseTask) parse(projectReferenceParser *projectReferenceParser) {
|
||||
loader := projectReferenceParser.loader
|
||||
if tr := loader.opts.Tracing; tr != nil {
|
||||
defer tr.Push(tracing.PhaseParse, "parseJsonSourceFileConfigFileContent", map[string]any{"path": t.configName}, false)()
|
||||
}
|
||||
t.resolved = loader.opts.Host.GetResolvedProjectReference(t.configName, 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
|
||||
}
|
||||
p.loader.projectReferenceFileMapper.configToProjectReference[path] = task.resolved
|
||||
if task.resolved != nil && p.loader.projectReferenceFileMapper.opts.Config.ConfigFile != task.resolved.ConfigFile {
|
||||
// Map current task's files first, before recursing into subtasks.
|
||||
// This matches TypeScript's behavior where child project references
|
||||
// overwrite parent entries when a file belongs to multiple projects.
|
||||
maps.Copy(p.loader.projectReferenceFileMapper.sourceToProjectReference, task.resolved.SourceToProjectReference())
|
||||
maps.Copy(p.loader.projectReferenceFileMapper.outputDtsToProjectReference, task.resolved.OutputDtsToProjectReference())
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
referencesInConfig := p.initMapperWorker(task.subTasks, seen)
|
||||
p.loader.projectReferenceFileMapper.referencesInConfigFile[path] = referencesInConfig
|
||||
}
|
||||
return results
|
||||
}
|
||||
Reference in New Issue
Block a user