Skip to content

Commit 96e5669

Browse files
Simplify file parsing and project reference parsing (#1536)
Co-authored-by: Copilot <[email protected]>
1 parent 29441f1 commit 96e5669

File tree

6 files changed

+215
-262
lines changed

6 files changed

+215
-262
lines changed

internal/compiler/fileloader.go

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ type fileLoader struct {
2222
comparePathsOptions tspath.ComparePathsOptions
2323
supportedExtensions []string
2424

25-
parseTasks *fileLoaderWorker[*parseTask]
26-
projectReferenceParseTasks *fileLoaderWorker[*projectReferenceParseTask]
27-
rootTasks []*parseTask
25+
filesParser *filesParser
26+
rootTasks []*parseTask
2827

2928
totalFileCount atomic.Int32
3029
libFileCount atomic.Int32
@@ -80,17 +79,14 @@ func processAllProgramFiles(
8079
UseCaseSensitiveFileNames: opts.Host.FS().UseCaseSensitiveFileNames(),
8180
CurrentDirectory: opts.Host.GetCurrentDirectory(),
8281
},
83-
parseTasks: &fileLoaderWorker[*parseTask]{
82+
filesParser: &filesParser{
8483
wg: core.NewWorkGroup(singleThreaded),
8584
maxDepth: maxNodeModuleJsDepth,
8685
},
87-
projectReferenceParseTasks: &fileLoaderWorker[*projectReferenceParseTask]{
88-
wg: core.NewWorkGroup(singleThreaded),
89-
},
9086
rootTasks: make([]*parseTask, 0, len(rootFiles)+len(compilerOptions.Lib)),
9187
supportedExtensions: core.Flatten(tsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(compilerOptions, supportedExtensions)),
9288
}
93-
loader.addProjectReferenceTasks()
89+
loader.addProjectReferenceTasks(singleThreaded)
9490
loader.resolver = module.NewResolver(loader.projectReferenceFileMapper.host, compilerOptions, opts.TypingsLocation, opts.ProjectName)
9591

9692
var libs []string
@@ -115,7 +111,7 @@ func processAllProgramFiles(
115111
loader.addAutomaticTypeDirectiveTasks()
116112
}
117113

118-
loader.parseTasks.runAndWait(&loader, loader.rootTasks)
114+
loader.filesParser.parse(&loader, loader.rootTasks)
119115
// Clear out loader and host to ensure its not used post program creation
120116
loader.projectReferenceFileMapper.loader = nil
121117
loader.projectReferenceFileMapper.host = nil
@@ -138,7 +134,7 @@ func processAllProgramFiles(
138134
var libFileSet collections.Set[tspath.Path]
139135
fileLoadDiagnostics := &ast.DiagnosticsCollection{}
140136

141-
loader.parseTasks.collect(&loader, loader.rootTasks, func(task *parseTask, _ []tspath.Path) {
137+
loader.filesParser.collect(&loader, loader.rootTasks, func(task *parseTask) {
142138
if task.isRedirected {
143139
return
144140
}
@@ -280,7 +276,7 @@ func (p *fileLoader) resolveAutomaticTypeDirectives(containingFileName string) (
280276
return toParse, typeResolutionsInFile
281277
}
282278

283-
func (p *fileLoader) addProjectReferenceTasks() {
279+
func (p *fileLoader) addProjectReferenceTasks(singleThreaded bool) {
284280
p.projectReferenceFileMapper = &projectReferenceFileMapper{
285281
opts: p.opts,
286282
host: p.opts.Host,
@@ -290,9 +286,12 @@ func (p *fileLoader) addProjectReferenceTasks() {
290286
return
291287
}
292288

289+
parser := &projectReferenceParser{
290+
loader: p,
291+
wg: core.NewWorkGroup(singleThreaded),
292+
}
293293
rootTasks := createProjectReferenceParseTasks(projectReferences)
294-
p.projectReferenceParseTasks.runAndWait(p, rootTasks)
295-
p.projectReferenceFileMapper.init(p, rootTasks)
294+
parser.parse(rootTasks)
296295

297296
// Add files from project references as root if the module kind is 'none'.
298297
// This ensures that files from project references are included in the root tasks
@@ -567,11 +566,6 @@ func getInferredLibraryNameResolveFrom(options *core.CompilerOptions, currentDir
567566
return tspath.CombinePaths(containingDirectory, "__lib_node_modules_lookup_"+libFileName+"__.ts")
568567
}
569568

570-
type resolution struct {
571-
node *ast.Node
572-
resolvedModule *module.ResolvedModule
573-
}
574-
575569
func getModeForTypeReferenceDirectiveInFile(ref *ast.FileReference, file *ast.SourceFile, meta ast.SourceFileMetaData, options *core.CompilerOptions) core.ResolutionMode {
576570
if ref.ResolutionMode != core.ResolutionModeNone {
577571
return ref.ResolutionMode

internal/compiler/fileloadertask.go

Lines changed: 0 additions & 121 deletions
This file was deleted.

internal/compiler/parsetask.go renamed to internal/compiler/filesparser.go

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package compiler
22

33
import (
4+
"math"
5+
"sync"
6+
47
"github.com/microsoft/typescript-go/internal/ast"
8+
"github.com/microsoft/typescript-go/internal/collections"
59
"github.com/microsoft/typescript-go/internal/core"
610
"github.com/microsoft/typescript-go/internal/module"
711
"github.com/microsoft/typescript-go/internal/tsoptions"
@@ -120,30 +124,99 @@ func (t *parseTask) addSubTask(ref resolvedRef, isLib bool) {
120124
t.subTasks = append(t.subTasks, subTask)
121125
}
122126

123-
func (t *parseTask) getSubTasks() []*parseTask {
124-
return t.subTasks
127+
type filesParser struct {
128+
wg core.WorkGroup
129+
tasksByFileName collections.SyncMap[string, *queuedParseTask]
130+
maxDepth int
125131
}
126132

127-
func (t *parseTask) shouldIncreaseDepth() bool {
128-
return t.increaseDepth
133+
type queuedParseTask struct {
134+
task *parseTask
135+
mu sync.Mutex
136+
lowestDepth int
137+
fromExternalLibrary bool
129138
}
130139

131-
func (t *parseTask) shouldElideOnDepth() bool {
132-
return t.elideOnDepth
140+
func (w *filesParser) parse(loader *fileLoader, tasks []*parseTask) {
141+
w.start(loader, tasks, 0, false)
142+
w.wg.RunAndWait()
133143
}
134144

135-
func (t *parseTask) isLoaded() bool {
136-
return t.loaded
137-
}
145+
func (w *filesParser) start(loader *fileLoader, tasks []*parseTask, depth int, isFromExternalLibrary bool) {
146+
for i, task := range tasks {
147+
taskIsFromExternalLibrary := isFromExternalLibrary || task.fromExternalLibrary
148+
newTask := &queuedParseTask{task: task, lowestDepth: math.MaxInt}
149+
loadedTask, loaded := w.tasksByFileName.LoadOrStore(task.FileName(), newTask)
150+
task = loadedTask.task
151+
if loaded {
152+
tasks[i] = task
153+
// Add in the loaded task's external-ness.
154+
taskIsFromExternalLibrary = taskIsFromExternalLibrary || task.fromExternalLibrary
155+
}
156+
157+
w.wg.Queue(func() {
158+
loadedTask.mu.Lock()
159+
defer loadedTask.mu.Unlock()
160+
161+
startSubtasks := false
162+
163+
currentDepth := depth
164+
if task.increaseDepth {
165+
currentDepth++
166+
}
167+
if currentDepth < loadedTask.lowestDepth {
168+
// If we're seeing this task at a lower depth than before,
169+
// reprocess its subtasks to ensure they are loaded.
170+
loadedTask.lowestDepth = currentDepth
171+
startSubtasks = true
172+
}
173+
174+
if !task.root && taskIsFromExternalLibrary && !loadedTask.fromExternalLibrary {
175+
// If we're seeing this task now as an external library,
176+
// reprocess its subtasks to ensure they are also marked as external.
177+
loadedTask.fromExternalLibrary = true
178+
startSubtasks = true
179+
}
138180

139-
func (t *parseTask) isRoot() bool {
140-
return t.root
181+
if task.elideOnDepth && currentDepth > w.maxDepth {
182+
return
183+
}
184+
185+
if !task.loaded {
186+
task.load(loader)
187+
}
188+
189+
if startSubtasks {
190+
w.start(loader, task.subTasks, loadedTask.lowestDepth, loadedTask.fromExternalLibrary)
191+
}
192+
})
193+
}
141194
}
142195

143-
func (t *parseTask) isFromExternalLibrary() bool {
144-
return t.fromExternalLibrary
196+
func (w *filesParser) collect(loader *fileLoader, tasks []*parseTask, iterate func(*parseTask)) []tspath.Path {
197+
// Mark all tasks we saw as external after the fact.
198+
w.tasksByFileName.Range(func(key string, value *queuedParseTask) bool {
199+
if value.fromExternalLibrary {
200+
value.task.fromExternalLibrary = true
201+
}
202+
return true
203+
})
204+
return w.collectWorker(loader, tasks, iterate, collections.Set[*parseTask]{})
145205
}
146206

147-
func (t *parseTask) markFromExternalLibrary() {
148-
t.fromExternalLibrary = true
207+
func (w *filesParser) collectWorker(loader *fileLoader, tasks []*parseTask, iterate func(*parseTask), seen collections.Set[*parseTask]) []tspath.Path {
208+
var results []tspath.Path
209+
for _, task := range tasks {
210+
// ensure we only walk each task once
211+
if !task.loaded || seen.Has(task) {
212+
continue
213+
}
214+
seen.Add(task)
215+
if subTasks := task.subTasks; len(subTasks) > 0 {
216+
w.collectWorker(loader, subTasks, iterate, seen)
217+
}
218+
iterate(task)
219+
results = append(results, loader.toPath(task.FileName()))
220+
}
221+
return results
149222
}

internal/compiler/projectreferencefilemapper.go

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -25,44 +25,6 @@ type projectReferenceFileMapper struct {
2525
realpathDtsToSource collections.SyncMap[tspath.Path, *tsoptions.SourceAndProjectReference]
2626
}
2727

28-
func (mapper *projectReferenceFileMapper) init(loader *fileLoader, rootTasks []*projectReferenceParseTask) {
29-
totalReferences := loader.projectReferenceParseTasks.tasksByFileName.Size() + 1
30-
mapper.loader = loader
31-
mapper.configToProjectReference = make(map[tspath.Path]*tsoptions.ParsedCommandLine, totalReferences)
32-
mapper.referencesInConfigFile = make(map[tspath.Path][]tspath.Path, totalReferences)
33-
mapper.sourceToOutput = make(map[tspath.Path]*tsoptions.OutputDtsAndProjectReference)
34-
mapper.outputDtsToSource = make(map[tspath.Path]*tsoptions.SourceAndProjectReference)
35-
mapper.referencesInConfigFile[mapper.opts.Config.ConfigFile.SourceFile.Path()] = loader.projectReferenceParseTasks.collect(
36-
loader,
37-
rootTasks,
38-
func(task *projectReferenceParseTask, referencesInConfig []tspath.Path) {
39-
path := loader.toPath(task.configName)
40-
mapper.configToProjectReference[path] = task.resolved
41-
if task.resolved == nil || mapper.opts.Config.ConfigFile == task.resolved.ConfigFile {
42-
return
43-
}
44-
mapper.referencesInConfigFile[path] = referencesInConfig
45-
for key, value := range task.resolved.SourceToOutput() {
46-
mapper.sourceToOutput[key] = value
47-
}
48-
for key, value := range task.resolved.OutputDtsToSource() {
49-
mapper.outputDtsToSource[key] = value
50-
}
51-
if mapper.opts.canUseProjectReferenceSource() {
52-
declDir := task.resolved.CompilerOptions().DeclarationDir
53-
if declDir == "" {
54-
declDir = task.resolved.CompilerOptions().OutDir
55-
}
56-
if declDir != "" {
57-
loader.dtsDirectories.Add(loader.toPath(declDir))
58-
}
59-
}
60-
})
61-
if mapper.opts.canUseProjectReferenceSource() && len(loader.projectReferenceFileMapper.outputDtsToSource) != 0 {
62-
mapper.host = newProjectReferenceDtsFakingHost(loader)
63-
}
64-
}
65-
6628
func (mapper *projectReferenceFileMapper) getParseFileRedirect(file ast.HasFileName) string {
6729
if mapper.opts.canUseProjectReferenceSource() {
6830
// Map to source file from project reference

0 commit comments

Comments
 (0)