Skip to content

Commit ba0fb29

Browse files
authored
Implement maxNodeModuleJsDepth, noResolve (#1189)
1 parent 7c1f54e commit ba0fb29

File tree

335 files changed

+11056
-6264
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

335 files changed

+11056
-6264
lines changed

internal/ast/parseoptions.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,19 @@ func getExternalModuleIndicator(file *SourceFile, opts ExternalModuleIndicatorOp
104104

105105
func isFileProbablyExternalModule(sourceFile *SourceFile) *Node {
106106
for _, statement := range sourceFile.Statements.Nodes {
107-
if IsExternalModuleIndicator(statement) {
107+
if isAnExternalModuleIndicatorNode(statement) {
108108
return statement
109109
}
110110
}
111111
return getImportMetaIfNecessary(sourceFile)
112112
}
113113

114+
func isAnExternalModuleIndicatorNode(node *Node) bool {
115+
return HasSyntacticModifier(node, ModifierFlagsExport) ||
116+
IsImportEqualsDeclaration(node) && IsExternalModuleReference(node.AsImportEqualsDeclaration().ModuleReference) ||
117+
IsImportDeclaration(node) || IsExportAssignment(node) || IsExportDeclaration(node)
118+
}
119+
114120
func getImportMetaIfNecessary(sourceFile *SourceFile) *Node {
115121
if sourceFile.AsNode().Flags&NodeFlagsPossiblyContainsImportMeta != 0 {
116122
return findChildNode(sourceFile.AsNode(), IsImportMeta)

internal/ast/utilities.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,9 +1615,8 @@ func isCommonJSContainingModuleKind(kind core.ModuleKind) bool {
16151615
}
16161616

16171617
func IsExternalModuleIndicator(node *Statement) bool {
1618-
return HasSyntacticModifier(node, ModifierFlagsExport) ||
1619-
IsImportEqualsDeclaration(node) && IsExternalModuleReference(node.AsImportEqualsDeclaration().ModuleReference) ||
1620-
IsImportDeclaration(node) || IsExportAssignment(node) || IsExportDeclaration(node)
1618+
// Exported top-level member indicates moduleness
1619+
return IsAnyImportOrReExport(node) || IsExportAssignment(node) || HasSyntacticModifier(node, ModifierFlagsExport)
16211620
}
16221621

16231622
func IsExportNamespaceAsDefaultDeclaration(node *Node) bool {

internal/compiler/fileloader.go

Lines changed: 110 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/microsoft/typescript-go/internal/ast"
1111
"github.com/microsoft/typescript-go/internal/collections"
1212
"github.com/microsoft/typescript-go/internal/core"
13+
"github.com/microsoft/typescript-go/internal/diagnostics"
1314
"github.com/microsoft/typescript-go/internal/module"
1415
"github.com/microsoft/typescript-go/internal/tsoptions"
1516
"github.com/microsoft/typescript-go/internal/tspath"
@@ -64,6 +65,10 @@ func processAllProgramFiles(
6465
compilerOptions := opts.Config.CompilerOptions()
6566
rootFiles := opts.Config.FileNames()
6667
supportedExtensions := tsoptions.GetSupportedExtensions(compilerOptions, nil /*extraFileExtensions*/)
68+
var maxNodeModuleJsDepth int
69+
if p := opts.Config.CompilerOptions().MaxNodeModuleJsDepth; p != nil {
70+
maxNodeModuleJsDepth = *p
71+
}
6772
loader := fileLoader{
6873
opts: opts,
6974
defaultLibraryPath: tspath.GetNormalizedAbsolutePath(opts.Host.DefaultLibraryPath(), opts.Host.GetCurrentDirectory()),
@@ -72,12 +77,11 @@ func processAllProgramFiles(
7277
CurrentDirectory: opts.Host.GetCurrentDirectory(),
7378
},
7479
parseTasks: &fileLoaderWorker[*parseTask]{
75-
wg: core.NewWorkGroup(singleThreaded),
76-
getSubTasks: getSubTasksOfParseTask,
80+
wg: core.NewWorkGroup(singleThreaded),
81+
maxDepth: maxNodeModuleJsDepth,
7782
},
7883
projectReferenceParseTasks: &fileLoaderWorker[*projectReferenceParseTask]{
79-
wg: core.NewWorkGroup(singleThreaded),
80-
getSubTasks: getSubTasksOfProjectReferenceParseTask,
84+
wg: core.NewWorkGroup(singleThreaded),
8185
},
8286
rootTasks: make([]*parseTask, 0, len(rootFiles)+len(libs)),
8387
supportedExtensions: core.Flatten(tsoptions.GetSupportedExtensionsWithJsonIfResolveJsonModule(compilerOptions, supportedExtensions)),
@@ -289,30 +293,36 @@ func (p *fileLoader) parseSourceFile(t *parseTask) *ast.SourceFile {
289293
return sourceFile
290294
}
291295

292-
func (p *fileLoader) resolveTripleslashPathReference(moduleName string, containingFile string) string {
296+
func (p *fileLoader) resolveTripleslashPathReference(moduleName string, containingFile string) resolvedRef {
293297
basePath := tspath.GetDirectoryPath(containingFile)
294298
referencedFileName := moduleName
295299

296300
if !tspath.IsRootedDiskPath(moduleName) {
297301
referencedFileName = tspath.CombinePaths(basePath, moduleName)
298302
}
299-
return tspath.NormalizePath(referencedFileName)
303+
return resolvedRef{
304+
fileName: tspath.NormalizePath(referencedFileName),
305+
}
300306
}
301307

302308
func (p *fileLoader) resolveTypeReferenceDirectives(file *ast.SourceFile, meta ast.SourceFileMetaData) (
303-
toParse []string,
309+
toParse []resolvedRef,
304310
typeResolutionsInFile module.ModeAwareCache[*module.ResolvedTypeReferenceDirective],
305311
) {
306312
if len(file.TypeReferenceDirectives) != 0 {
307-
toParse = make([]string, 0, len(file.TypeReferenceDirectives))
313+
toParse = make([]resolvedRef, 0, len(file.TypeReferenceDirectives))
308314
typeResolutionsInFile = make(module.ModeAwareCache[*module.ResolvedTypeReferenceDirective], len(file.TypeReferenceDirectives))
309315
for _, ref := range file.TypeReferenceDirectives {
310316
redirect := p.projectReferenceFileMapper.getRedirectForResolution(file)
311317
resolutionMode := getModeForTypeReferenceDirectiveInFile(ref, file, meta, module.GetCompilerOptionsWithRedirect(p.opts.Config.CompilerOptions(), redirect))
312318
resolved := p.resolver.ResolveTypeReferenceDirective(ref.FileName, file.FileName(), resolutionMode, redirect)
313319
typeResolutionsInFile[module.ModeAwareCacheKey{Name: ref.FileName, Mode: resolutionMode}] = resolved
314320
if resolved.IsResolved() {
315-
toParse = append(toParse, resolved.ResolvedFileName)
321+
toParse = append(toParse, resolvedRef{
322+
fileName: resolved.ResolvedFileName,
323+
increaseDepth: resolved.IsExternalLibraryImport,
324+
elideOnDepth: false,
325+
})
316326
}
317327
}
318328
}
@@ -322,19 +332,12 @@ func (p *fileLoader) resolveTypeReferenceDirectives(file *ast.SourceFile, meta a
322332
const externalHelpersModuleNameText = "tslib" // TODO(jakebailey): dedupe
323333

324334
func (p *fileLoader) resolveImportsAndModuleAugmentations(file *ast.SourceFile, meta ast.SourceFileMetaData) (
325-
toParse []string,
335+
toParse []resolvedRef,
326336
resolutionsInFile module.ModeAwareCache[*module.ResolvedModule],
327337
importHelpersImportSpecifier *ast.Node,
328338
jsxRuntimeImportSpecifier_ *jsxRuntimeImportSpecifier,
329339
) {
330340
moduleNames := make([]*ast.Node, 0, len(file.Imports())+len(file.ModuleAugmentations)+2)
331-
moduleNames = append(moduleNames, file.Imports()...)
332-
for _, imp := range file.ModuleAugmentations {
333-
if imp.Kind == ast.KindStringLiteral {
334-
moduleNames = append(moduleNames, imp)
335-
}
336-
// Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
337-
}
338341

339342
isJavaScriptFile := ast.IsSourceFileJS(file)
340343
isExternalModuleFile := ast.IsExternalModule(file)
@@ -359,51 +362,118 @@ func (p *fileLoader) resolveImportsAndModuleAugmentations(file *ast.SourceFile,
359362
}
360363
}
361364

365+
importsStart := len(moduleNames)
366+
367+
moduleNames = append(moduleNames, file.Imports()...)
368+
for _, imp := range file.ModuleAugmentations {
369+
if imp.Kind == ast.KindStringLiteral {
370+
moduleNames = append(moduleNames, imp)
371+
}
372+
// Do nothing if it's an Identifier; we don't need to do module resolution for `declare global`.
373+
}
374+
362375
if len(moduleNames) != 0 {
363-
toParse = make([]string, 0, len(moduleNames))
376+
toParse = make([]resolvedRef, 0, len(moduleNames))
377+
resolutionsInFile = make(module.ModeAwareCache[*module.ResolvedModule], len(moduleNames))
364378

365-
resolutions := p.resolveModuleNames(moduleNames, file, meta, redirect)
379+
for index, entry := range moduleNames {
380+
moduleName := entry.Text()
381+
if moduleName == "" {
382+
continue
383+
}
366384

367-
resolutionsInFile = make(module.ModeAwareCache[*module.ResolvedModule], len(resolutions))
385+
mode := getModeForUsageLocation(file.FileName(), meta, entry, module.GetCompilerOptionsWithRedirect(p.opts.Config.CompilerOptions(), redirect))
386+
resolvedModule := p.resolver.ResolveModuleName(moduleName, file.FileName(), mode, redirect)
387+
resolutionsInFile[module.ModeAwareCacheKey{Name: moduleName, Mode: mode}] = resolvedModule
368388

369-
for _, resolution := range resolutions {
370-
resolvedFileName := resolution.resolvedModule.ResolvedFileName
371-
// TODO(ercornel): !!!: check if from node modules
389+
if !resolvedModule.IsResolved() {
390+
continue
391+
}
372392

373-
mode := getModeForUsageLocation(file.FileName(), meta, resolution.node, optionsForFile)
374-
resolutionsInFile[module.ModeAwareCacheKey{Name: resolution.node.Text(), Mode: mode}] = resolution.resolvedModule
393+
resolvedFileName := resolvedModule.ResolvedFileName
394+
isFromNodeModulesSearch := resolvedModule.IsExternalLibraryImport
395+
// Don't treat redirected files as JS files.
396+
isJsFile := !tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedTSExtensionsWithJsonFlat) && p.projectReferenceFileMapper.getRedirectForResolution(ast.NewHasFileName(resolvedFileName, p.toPath(resolvedFileName))) == nil
397+
isJsFileFromNodeModules := isFromNodeModulesSearch && isJsFile && strings.Contains(resolvedFileName, "/node_modules/")
375398

376399
// add file to program only if:
377400
// - resolution was successful
378401
// - noResolve is falsy
379402
// - module name comes from the list of imports
380403
// - it's not a top level JavaScript module that exceeded the search max
381404

382-
// const elideImport = isJSFileFromNodeModules && currentNodeModulesDepth > maxNodeModuleJsDepth;
405+
importIndex := index - importsStart
383406

384-
// Don't add the file if it has a bad extension (e.g. 'tsx' if we don't have '--allowJs')
385-
// This may still end up being an untyped module -- the file won't be included but imports will be allowed.
386-
hasAllowedExtension := false
387-
if optionsForFile.GetResolveJsonModule() {
388-
hasAllowedExtension = tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedTSExtensionsWithJsonFlat)
389-
} else if optionsForFile.AllowJs.IsTrue() {
390-
hasAllowedExtension = tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedJSExtensionsFlat) || tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedTSExtensionsFlat)
391-
} else {
392-
hasAllowedExtension = tspath.FileExtensionIsOneOf(resolvedFileName, tspath.SupportedTSExtensionsFlat)
393-
}
394-
shouldAddFile := resolution.resolvedModule.IsResolved() && hasAllowedExtension
395-
// TODO(ercornel): !!!: other checks on whether or not to add the file
407+
shouldAddFile := moduleName != "" &&
408+
getResolutionDiagnostic(optionsForFile, resolvedModule, file) == nil &&
409+
!optionsForFile.NoResolve.IsTrue() &&
410+
!(isJsFile && !optionsForFile.GetAllowJS()) &&
411+
(importIndex < 0 || (importIndex < len(file.Imports()) && (ast.IsInJSFile(file.Imports()[importIndex]) || file.Imports()[importIndex].Flags&ast.NodeFlagsJSDoc == 0)))
396412

397413
if shouldAddFile {
398-
// p.findSourceFile(resolvedFileName, FileIncludeReason{Import, 0})
399-
toParse = append(toParse, resolvedFileName)
414+
toParse = append(toParse, resolvedRef{
415+
fileName: resolvedFileName,
416+
increaseDepth: resolvedModule.IsExternalLibraryImport,
417+
elideOnDepth: isJsFileFromNodeModules,
418+
})
400419
}
401420
}
402421
}
403422

404423
return toParse, resolutionsInFile, importHelpersImportSpecifier, jsxRuntimeImportSpecifier_
405424
}
406425

426+
// Returns a DiagnosticMessage if we won't include a resolved module due to its extension.
427+
// The DiagnosticMessage's parameters are the imported module name, and the filename it resolved to.
428+
// This returns a diagnostic even if the module will be an untyped module.
429+
func getResolutionDiagnostic(options *core.CompilerOptions, resolvedModule *module.ResolvedModule, file *ast.SourceFile) *diagnostics.Message {
430+
needJsx := func() *diagnostics.Message {
431+
if options.Jsx != core.JsxEmitNone {
432+
return nil
433+
}
434+
return diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set
435+
}
436+
437+
needAllowJs := func() *diagnostics.Message {
438+
if options.GetAllowJS() || !options.NoImplicitAny.DefaultIfUnknown(options.Strict).IsTrue() {
439+
return nil
440+
}
441+
return diagnostics.Module_0_was_resolved_to_1_but_resolveJsonModule_is_not_used
442+
}
443+
444+
needResolveJsonModule := func() *diagnostics.Message {
445+
if options.GetResolveJsonModule() {
446+
return nil
447+
}
448+
return diagnostics.Module_0_was_resolved_to_1_but_resolveJsonModule_is_not_used
449+
}
450+
451+
needAllowArbitraryExtensions := func() *diagnostics.Message {
452+
if file.IsDeclarationFile || options.AllowArbitraryExtensions.IsTrue() {
453+
return nil
454+
}
455+
return diagnostics.Module_0_was_resolved_to_1_but_allowArbitraryExtensions_is_not_set
456+
}
457+
458+
switch resolvedModule.Extension {
459+
case tspath.ExtensionTs, tspath.ExtensionDts,
460+
tspath.ExtensionMts, tspath.ExtensionDmts,
461+
tspath.ExtensionCts, tspath.ExtensionDcts:
462+
// These are always allowed.
463+
return nil
464+
case tspath.ExtensionTsx:
465+
return needJsx()
466+
case tspath.ExtensionJsx:
467+
return core.Coalesce(needJsx(), needAllowJs())
468+
case tspath.ExtensionJs, tspath.ExtensionMjs, tspath.ExtensionCjs:
469+
return needAllowJs()
470+
case tspath.ExtensionJson:
471+
return needResolveJsonModule()
472+
default:
473+
return needAllowArbitraryExtensions()
474+
}
475+
}
476+
407477
func (p *fileLoader) resolveModuleNames(entries []*ast.Node, file *ast.SourceFile, meta ast.SourceFileMetaData, redirect *tsoptions.ParsedCommandLine) []*resolution {
408478
if len(entries) == 0 {
409479
return nil

internal/compiler/fileloadertask.go

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,75 @@
11
package compiler
22

33
import (
4+
"math"
5+
"sync"
6+
47
"github.com/microsoft/typescript-go/internal/collections"
58
"github.com/microsoft/typescript-go/internal/core"
69
"github.com/microsoft/typescript-go/internal/tspath"
710
)
811

9-
type fileLoaderWorkerTask interface {
12+
type fileLoaderWorkerTask[T any] interface {
1013
comparable
1114
FileName() string
12-
start(loader *fileLoader)
15+
isLoaded() bool
16+
load(loader *fileLoader)
17+
getSubTasks() []T
18+
shouldIncreaseDepth() bool
19+
shouldElideOnDepth() bool
1320
}
1421

15-
type fileLoaderWorker[K fileLoaderWorkerTask] struct {
22+
type fileLoaderWorker[K fileLoaderWorkerTask[K]] struct {
1623
wg core.WorkGroup
17-
tasksByFileName collections.SyncMap[string, K]
18-
getSubTasks func(t K) []K
24+
tasksByFileName collections.SyncMap[string, *queuedTask[K]]
25+
maxDepth int
26+
}
27+
28+
type queuedTask[K fileLoaderWorkerTask[K]] struct {
29+
task K
30+
mu sync.Mutex
31+
lowestDepth int
1932
}
2033

2134
func (w *fileLoaderWorker[K]) runAndWait(loader *fileLoader, tasks []K) {
22-
w.start(loader, tasks)
35+
w.start(loader, tasks, 0)
2336
w.wg.RunAndWait()
2437
}
2538

26-
func (w *fileLoaderWorker[K]) start(loader *fileLoader, tasks []K) {
27-
if len(tasks) > 0 {
28-
for i, task := range tasks {
29-
loadedTask, loaded := w.tasksByFileName.LoadOrStore(task.FileName(), task)
30-
if loaded {
31-
// dedup tasks to ensure correct file order, regardless of which task would be started first
32-
tasks[i] = loadedTask
33-
} else {
34-
w.wg.Queue(func() {
35-
task.start(loader)
36-
subTasks := w.getSubTasks(task)
37-
w.start(loader, subTasks)
38-
})
39-
}
39+
func (w *fileLoaderWorker[K]) start(loader *fileLoader, tasks []K, depth int) {
40+
for i, task := range tasks {
41+
newTask := &queuedTask[K]{task: task, lowestDepth: math.MaxInt}
42+
loadedTask, loaded := w.tasksByFileName.LoadOrStore(task.FileName(), newTask)
43+
task = loadedTask.task
44+
if loaded {
45+
tasks[i] = task
46+
}
47+
48+
currentDepth := depth
49+
if task.shouldIncreaseDepth() {
50+
currentDepth++
51+
}
52+
53+
if task.shouldElideOnDepth() && currentDepth > w.maxDepth {
54+
continue
4055
}
56+
57+
w.wg.Queue(func() {
58+
loadedTask.mu.Lock()
59+
defer loadedTask.mu.Unlock()
60+
61+
if !task.isLoaded() {
62+
task.load(loader)
63+
}
64+
65+
if currentDepth < loadedTask.lowestDepth {
66+
// If we're seeing this task at a lower depth than before,
67+
// reprocess its subtasks to ensure they are loaded.
68+
loadedTask.lowestDepth = currentDepth
69+
subTasks := task.getSubTasks()
70+
w.start(loader, subTasks, currentDepth)
71+
}
72+
})
4173
}
4274
}
4375

@@ -49,12 +81,12 @@ func (w *fileLoaderWorker[K]) collectWorker(loader *fileLoader, tasks []K, itera
4981
var results []tspath.Path
5082
for _, task := range tasks {
5183
// ensure we only walk each task once
52-
if seen.Has(task) {
84+
if !task.isLoaded() || seen.Has(task) {
5385
continue
5486
}
5587
seen.Add(task)
5688
var subResults []tspath.Path
57-
if subTasks := w.getSubTasks(task); len(subTasks) > 0 {
89+
if subTasks := task.getSubTasks(); len(subTasks) > 0 {
5890
subResults = w.collectWorker(loader, subTasks, iterate, seen)
5991
}
6092
iterate(task, subResults)

0 commit comments

Comments
 (0)