@@ -62,8 +62,7 @@ namespace ts {
62
62
63
63
interface ModuleResolutionState {
64
64
host : ModuleResolutionHost ;
65
- // We only use this subset of the compiler options.
66
- compilerOptions : { rootDirs ?: string [ ] , baseUrl ?: string , paths ?: MapLike < string [ ] > } ;
65
+ compilerOptions : CompilerOptions ;
67
66
traceEnabled : boolean ;
68
67
}
69
68
@@ -128,7 +127,9 @@ namespace ts {
128
127
currentDirectory = host . getCurrentDirectory ( ) ;
129
128
}
130
129
131
- return currentDirectory !== undefined && getDefaultTypeRoots ( currentDirectory , host ) ;
130
+ if ( currentDirectory !== undefined ) {
131
+ return getDefaultTypeRoots ( currentDirectory , host ) ;
132
+ }
132
133
}
133
134
134
135
/**
@@ -142,20 +143,12 @@ namespace ts {
142
143
}
143
144
144
145
let typeRoots : string [ ] ;
145
-
146
- while ( true ) {
147
- const atTypes = combinePaths ( currentDirectory , nodeModulesAtTypes ) ;
146
+ forEachAncestorDirectory ( currentDirectory , directory => {
147
+ const atTypes = combinePaths ( directory , nodeModulesAtTypes ) ;
148
148
if ( host . directoryExists ( atTypes ) ) {
149
149
( typeRoots || ( typeRoots = [ ] ) ) . push ( atTypes ) ;
150
150
}
151
-
152
- const parent = getDirectoryPath ( currentDirectory ) ;
153
- if ( parent === currentDirectory ) {
154
- break ;
155
- }
156
- currentDirectory = parent ;
157
- }
158
-
151
+ } ) ;
159
152
return typeRoots ;
160
153
}
161
154
const nodeModulesAtTypes = combinePaths ( "node_modules" , "@types" ) ;
@@ -233,7 +226,7 @@ namespace ts {
233
226
if ( traceEnabled ) {
234
227
trace ( host , Diagnostics . Looking_up_in_node_modules_folder_initial_location_0 , initialLocationForSecondaryLookup ) ;
235
228
}
236
- resolvedFile = resolvedTypeScriptOnly ( loadModuleFromNodeModules ( Extensions . DtsOnly , typeReferenceDirectiveName , initialLocationForSecondaryLookup , failedLookupLocations , moduleResolutionState , /*checkOneLevel*/ false ) ) ;
229
+ resolvedFile = resolvedTypeScriptOnly ( loadModuleFromNodeModules ( Extensions . DtsOnly , typeReferenceDirectiveName , initialLocationForSecondaryLookup , failedLookupLocations , moduleResolutionState ) ) ;
237
230
if ( traceEnabled ) {
238
231
if ( resolvedFile ) {
239
232
trace ( host , Diagnostics . Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2 , typeReferenceDirectiveName , resolvedFile , false ) ;
@@ -562,7 +555,7 @@ namespace ts {
562
555
if ( traceEnabled ) {
563
556
trace ( host , Diagnostics . Loading_module_0_from_node_modules_folder , moduleName ) ;
564
557
}
565
- const resolved = loadModuleFromNodeModules ( extensions , moduleName , containingDirectory , failedLookupLocations , state , /*checkOneLevel*/ false ) ;
558
+ const resolved = loadModuleFromNodeModules ( extensions , moduleName , containingDirectory , failedLookupLocations , state ) ;
566
559
return resolved && { resolved, isExternalLibraryImport : true } ;
567
560
}
568
561
else {
@@ -649,7 +642,7 @@ namespace ts {
649
642
}
650
643
651
644
/** Return the file if it exists. */
652
- function tryFile ( fileName : string , failedLookupLocation : string [ ] , onlyRecordFailures : boolean , state : ModuleResolutionState ) : string | undefined {
645
+ function tryFile ( fileName : string , failedLookupLocations : string [ ] , onlyRecordFailures : boolean , state : ModuleResolutionState ) : string | undefined {
653
646
if ( ! onlyRecordFailures && state . host . fileExists ( fileName ) ) {
654
647
if ( state . traceEnabled ) {
655
648
trace ( state . host , Diagnostics . File_0_exist_use_it_as_a_name_resolution_result , fileName ) ;
@@ -660,12 +653,12 @@ namespace ts {
660
653
if ( state . traceEnabled ) {
661
654
trace ( state . host , Diagnostics . File_0_does_not_exist , fileName ) ;
662
655
}
663
- failedLookupLocation . push ( fileName ) ;
656
+ failedLookupLocations . push ( fileName ) ;
664
657
return undefined ;
665
658
}
666
659
}
667
660
668
- function loadNodeModuleFromDirectory ( extensions : Extensions , candidate : string , failedLookupLocation : string [ ] , onlyRecordFailures : boolean , state : ModuleResolutionState ) : Resolved | undefined {
661
+ function loadNodeModuleFromDirectory ( extensions : Extensions , candidate : string , failedLookupLocations : string [ ] , onlyRecordFailures : boolean , state : ModuleResolutionState ) : Resolved | undefined {
669
662
const packageJsonPath = pathToPackageJson ( candidate ) ;
670
663
const directoryExists = ! onlyRecordFailures && directoryProbablyExists ( candidate , state . host ) ;
671
664
@@ -677,12 +670,12 @@ namespace ts {
677
670
if ( typesFile ) {
678
671
const onlyRecordFailures = ! directoryProbablyExists ( getDirectoryPath ( typesFile ) , state . host ) ;
679
672
// A package.json "typings" may specify an exact filename, or may choose to omit an extension.
680
- const fromFile = tryFile ( typesFile , failedLookupLocation , onlyRecordFailures , state ) ;
673
+ const fromFile = tryFile ( typesFile , failedLookupLocations , onlyRecordFailures , state ) ;
681
674
if ( fromFile ) {
682
675
// Note: this would allow a package.json to specify a ".js" file as typings. Maybe that should be forbidden.
683
676
return resolvedFromAnyFile ( fromFile ) ;
684
677
}
685
- const x = tryAddingExtensions ( typesFile , Extensions . TypeScript , failedLookupLocation , onlyRecordFailures , state ) ;
678
+ const x = tryAddingExtensions ( typesFile , Extensions . TypeScript , failedLookupLocations , onlyRecordFailures , state ) ;
686
679
if ( x ) {
687
680
return x ;
688
681
}
@@ -698,10 +691,10 @@ namespace ts {
698
691
trace ( state . host , Diagnostics . File_0_does_not_exist , packageJsonPath ) ;
699
692
}
700
693
// record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results
701
- failedLookupLocation . push ( packageJsonPath ) ;
694
+ failedLookupLocations . push ( packageJsonPath ) ;
702
695
}
703
696
704
- return loadModuleFromFile ( extensions , combinePaths ( candidate , "index" ) , failedLookupLocation , ! directoryExists , state ) ;
697
+ return loadModuleFromFile ( extensions , combinePaths ( candidate , "index" ) , failedLookupLocations , ! directoryExists , state ) ;
705
698
}
706
699
707
700
function pathToPackageJson ( directory : string ) : string {
@@ -717,34 +710,30 @@ namespace ts {
717
710
loadNodeModuleFromDirectory ( extensions , candidate , failedLookupLocations , ! nodeModulesFolderExists , state ) ;
718
711
}
719
712
720
- function loadModuleFromNodeModules ( extensions : Extensions , moduleName : string , directory : string , failedLookupLocations : string [ ] , state : ModuleResolutionState , checkOneLevel : boolean ) : Resolved | undefined {
721
- return loadModuleFromNodeModulesWorker ( extensions , moduleName , directory , failedLookupLocations , state , checkOneLevel , /*typesOnly*/ false ) ;
713
+ function loadModuleFromNodeModules ( extensions : Extensions , moduleName : string , directory : string , failedLookupLocations : string [ ] , state : ModuleResolutionState ) : Resolved | undefined {
714
+ return loadModuleFromNodeModulesWorker ( extensions , moduleName , directory , failedLookupLocations , state , /*typesOnly*/ false ) ;
722
715
}
723
716
function loadModuleFromNodeModulesAtTypes ( moduleName : string , directory : string , failedLookupLocations : string [ ] , state : ModuleResolutionState ) : Resolved | undefined {
724
- return loadModuleFromNodeModulesWorker ( Extensions . TypeScript , moduleName , directory , failedLookupLocations , state , /*checkOneLevel*/ false , /*typesOnly*/ true ) ;
717
+ // Extensions parameter here doesn't actually matter, because typesOnly ensures we're just doing @types lookup, which is always DtsOnly.
718
+ return loadModuleFromNodeModulesWorker ( Extensions . DtsOnly , moduleName , directory , failedLookupLocations , state , /*typesOnly*/ true ) ;
725
719
}
726
720
727
- function loadModuleFromNodeModulesWorker ( extensions : Extensions , moduleName : string , directory : string , failedLookupLocations : string [ ] , state : ModuleResolutionState , checkOneLevel : boolean , typesOnly : boolean ) : Resolved | undefined {
728
- directory = normalizeSlashes ( directory ) ;
729
- while ( true ) {
730
- if ( getBaseFileName ( directory ) !== "node_modules" ) {
731
- const resolved = tryInDirectory ( ) ;
732
- if ( resolved ) {
733
- return resolved ;
734
- }
735
- }
736
-
737
- const parentPath = getDirectoryPath ( directory ) ;
738
- if ( parentPath === directory || checkOneLevel ) {
739
- return undefined ;
721
+ function loadModuleFromNodeModulesWorker ( extensions : Extensions , moduleName : string , directory : string , failedLookupLocations : string [ ] , state : ModuleResolutionState , typesOnly : boolean ) : Resolved | undefined {
722
+ return forEachAncestorDirectory ( normalizeSlashes ( directory ) , ancestorDirectory => {
723
+ if ( getBaseFileName ( ancestorDirectory ) !== "node_modules" ) {
724
+ return loadModuleFromNodeModulesOneLevel ( extensions , moduleName , ancestorDirectory , failedLookupLocations , state , typesOnly ) ;
740
725
}
726
+ } ) ;
727
+ }
741
728
742
- directory = parentPath ;
729
+ /** Load a module from a single node_modules directory, but not from any ancestors' node_modules directories. */
730
+ function loadModuleFromNodeModulesOneLevel ( extensions : Extensions , moduleName : string , directory : string , failedLookupLocations : string [ ] , state : ModuleResolutionState , typesOnly = false ) : Resolved | undefined {
731
+ const packageResult = typesOnly ? undefined : loadModuleFromNodeModulesFolder ( extensions , moduleName , directory , failedLookupLocations , state ) ;
732
+ if ( packageResult ) {
733
+ return packageResult ;
743
734
}
744
-
745
- function tryInDirectory ( ) : Resolved | undefined {
746
- const packageResult = typesOnly ? undefined : loadModuleFromNodeModulesFolder ( extensions , moduleName , directory , failedLookupLocations , state ) ;
747
- return packageResult || loadModuleFromNodeModulesFolder ( extensions , combinePaths ( "@types" , moduleName ) , directory , failedLookupLocations , state ) ;
735
+ if ( extensions !== Extensions . JavaScript ) {
736
+ return loadModuleFromNodeModulesFolder ( Extensions . DtsOnly , combinePaths ( "@types" , moduleName ) , directory , failedLookupLocations , state ) ;
748
737
}
749
738
}
750
739
@@ -764,7 +753,11 @@ namespace ts {
764
753
}
765
754
766
755
if ( moduleHasNonRelativeName ( moduleName ) ) {
767
- const resolved = loadModuleFromAncestorDirectories ( extensions , moduleName , containingDirectory , failedLookupLocations , state ) ;
756
+ // Climb up parent directories looking for a module.
757
+ const resolved = forEachAncestorDirectory ( containingDirectory , directory => {
758
+ const searchName = normalizePath ( combinePaths ( directory , moduleName ) ) ;
759
+ return loadModuleFromFile ( extensions , searchName , failedLookupLocations , /*onlyRecordFailures*/ false , state ) ;
760
+ } ) ;
768
761
if ( resolved ) {
769
762
return resolved ;
770
763
}
@@ -780,22 +773,6 @@ namespace ts {
780
773
}
781
774
}
782
775
783
- /** Climb up parent directories looking for a module. */
784
- function loadModuleFromAncestorDirectories ( extensions : Extensions , moduleName : string , containingDirectory : string , failedLookupLocations : string [ ] , state : ModuleResolutionState ) : Resolved | undefined {
785
- while ( true ) {
786
- const searchName = normalizePath ( combinePaths ( containingDirectory , moduleName ) ) ;
787
- const referencedSourceFile = loadModuleFromFile ( extensions , searchName , failedLookupLocations , /*onlyRecordFailures*/ false , state ) ;
788
- if ( referencedSourceFile ) {
789
- return referencedSourceFile ;
790
- }
791
- const parentPath = getDirectoryPath ( containingDirectory ) ;
792
- if ( parentPath === containingDirectory ) {
793
- return undefined ;
794
- }
795
- containingDirectory = parentPath ;
796
- }
797
- }
798
-
799
776
/**
800
777
* LSHost may load a module from a global cache of typings.
801
778
* This is the minumum code needed to expose that functionality; the rest is in LSHost.
@@ -808,8 +785,24 @@ namespace ts {
808
785
}
809
786
const state : ModuleResolutionState = { compilerOptions, host, traceEnabled } ;
810
787
const failedLookupLocations : string [ ] = [ ] ;
811
- const resolved = loadModuleFromNodeModules ( Extensions . TypeScript , moduleName , globalCache , failedLookupLocations , state , /*checkOneLevel*/ true ) ||
812
- loadModuleFromNodeModules ( Extensions . JavaScript , moduleName , globalCache , failedLookupLocations , state , /*checkOneLevel*/ true ) ;
788
+ const resolved = loadModuleFromNodeModulesOneLevel ( Extensions . DtsOnly , moduleName , globalCache , failedLookupLocations , state ) ;
813
789
return createResolvedModuleWithFailedLookupLocations ( resolved , /*isExternalLibraryImport*/ true , failedLookupLocations ) ;
814
790
}
791
+
792
+ /** Calls `callback` on `directory` and every ancestor directory it has, returning the first defined result. */
793
+ function forEachAncestorDirectory < T > ( directory : string , callback : ( directory : string ) => T | undefined ) : T | undefined {
794
+ while ( true ) {
795
+ const result = callback ( directory ) ;
796
+ if ( result !== undefined ) {
797
+ return result ;
798
+ }
799
+
800
+ const parentPath = getDirectoryPath ( directory ) ;
801
+ if ( parentPath === directory ) {
802
+ return undefined ;
803
+ }
804
+
805
+ directory = parentPath ;
806
+ }
807
+ }
815
808
}
0 commit comments