@@ -130,10 +130,10 @@ namespace ts {
130
130
}
131
131
132
132
function tryReadTypesSection ( packageJsonPath : string , baseDirectory : string , state : ModuleResolutionState ) : string {
133
- let jsonContent : { typings ?: string , types ?: string } ;
133
+ let jsonContent : { typings ?: string , types ?: string , main ?: string } ;
134
134
try {
135
135
const jsonText = state . host . readFile ( packageJsonPath ) ;
136
- jsonContent = jsonText ? < { typings ?: string , types ?: string } > JSON . parse ( jsonText ) : { } ;
136
+ jsonContent = jsonText ? < { typings ?: string , types ?: string , main ?: string } > JSON . parse ( jsonText ) : { } ;
137
137
}
138
138
catch ( e ) {
139
139
// gracefully handle if readFile fails or returns not JSON
@@ -173,6 +173,14 @@ namespace ts {
173
173
}
174
174
return typesFilePath ;
175
175
}
176
+ // Use the main module for inferring types if no types package specified and the allowJs is set
177
+ if ( state . compilerOptions . allowJs && jsonContent . main && typeof jsonContent . main === "string" ) {
178
+ if ( state . traceEnabled ) {
179
+ trace ( state . host , Diagnostics . No_types_specified_in_package_json_but_allowJs_is_set_so_returning_main_value_of_0 , jsonContent . main ) ;
180
+ }
181
+ const mainFilePath = normalizePath ( combinePaths ( baseDirectory , jsonContent . main ) ) ;
182
+ return mainFilePath ;
183
+ }
176
184
return undefined ;
177
185
}
178
186
@@ -610,7 +618,7 @@ namespace ts {
610
618
failedLookupLocations , supportedExtensions , state ) ;
611
619
612
620
let isExternalLibraryImport = false ;
613
- if ( ! resolvedFileName ) {
621
+ if ( ! resolvedFileName ) {
614
622
if ( moduleHasNonRelativeName ( moduleName ) ) {
615
623
if ( traceEnabled ) {
616
624
trace ( host , Diagnostics . Loading_module_0_from_node_modules_folder , moduleName ) ;
@@ -740,12 +748,13 @@ namespace ts {
740
748
const nodeModulesFolder = combinePaths ( directory , "node_modules" ) ;
741
749
const nodeModulesFolderExists = directoryProbablyExists ( nodeModulesFolder , state . host ) ;
742
750
const candidate = normalizePath ( combinePaths ( nodeModulesFolder , moduleName ) ) ;
743
- // Load only typescript files irrespective of allowJs option if loading from node modules
744
- let result = loadModuleFromFile ( candidate , supportedTypeScriptExtensions , failedLookupLocations , ! nodeModulesFolderExists , state ) ;
751
+ const supportedExtensions = getSupportedExtensions ( state . compilerOptions ) ;
752
+
753
+ let result = loadModuleFromFile ( candidate , supportedExtensions , failedLookupLocations , ! nodeModulesFolderExists , state ) ;
745
754
if ( result ) {
746
755
return result ;
747
756
}
748
- result = loadNodeModuleFromDirectory ( supportedTypeScriptExtensions , candidate , failedLookupLocations , ! nodeModulesFolderExists , state ) ;
757
+ result = loadNodeModuleFromDirectory ( supportedExtensions , candidate , failedLookupLocations , ! nodeModulesFolderExists , state ) ;
749
758
if ( result ) {
750
759
return result ;
751
760
}
@@ -1057,6 +1066,23 @@ namespace ts {
1057
1066
let resolvedTypeReferenceDirectives : Map < ResolvedTypeReferenceDirective > = { } ;
1058
1067
let fileProcessingDiagnostics = createDiagnosticCollection ( ) ;
1059
1068
1069
+ // The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules.
1070
+ // This works as imported modules are discovered recursively in a depth first manner, specifically:
1071
+ // - For each root file, findSourceFile is called.
1072
+ // - This calls processImportedModules for each module imported in the source file.
1073
+ // - This calls resolveModuleNames, and then calls findSourceFile for each resolved module.
1074
+ // As all these operations happen - and are nested - within the createProgram call, they close over the below variables.
1075
+ // The current resolution depth is tracked by incrementing/decrementing as the depth first search progresses.
1076
+ const maxNodeModulesJsDepth = typeof options . maxNodeModuleJsDepth === "number" ? options . maxNodeModuleJsDepth : 2 ;
1077
+ let currentNodeModulesJsDepth = 0 ;
1078
+
1079
+ // If a module has some of its imports skipped due to being at the depth limit under node_modules, then track
1080
+ // this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed.
1081
+ const modulesWithElidedImports : Map < boolean > = { } ;
1082
+
1083
+ // Track source files that are JavaScript files found by searching under node_modules, as these shouldn't be compiled.
1084
+ const jsFilesFoundSearchingNodeModules : Map < boolean > = { } ;
1085
+
1060
1086
const start = new Date ( ) . getTime ( ) ;
1061
1087
1062
1088
host = host || createCompilerHost ( options ) ;
@@ -1211,6 +1237,7 @@ namespace ts {
1211
1237
( oldOptions . rootDir !== options . rootDir ) ||
1212
1238
( oldOptions . configFilePath !== options . configFilePath ) ||
1213
1239
( oldOptions . baseUrl !== options . baseUrl ) ||
1240
+ ( oldOptions . maxNodeModuleJsDepth !== options . maxNodeModuleJsDepth ) ||
1214
1241
! arrayIsEqualTo ( oldOptions . typeRoots , oldOptions . typeRoots ) ||
1215
1242
! arrayIsEqualTo ( oldOptions . rootDirs , options . rootDirs ) ||
1216
1243
! mapIsEqualTo ( oldOptions . paths , options . paths ) ) {
@@ -1335,6 +1362,7 @@ namespace ts {
1335
1362
getSourceFile : program . getSourceFile ,
1336
1363
getSourceFileByPath : program . getSourceFileByPath ,
1337
1364
getSourceFiles : program . getSourceFiles ,
1365
+ getFilesFromNodeModules : ( ) => jsFilesFoundSearchingNodeModules ,
1338
1366
writeFile : writeFileCallback || (
1339
1367
( fileName , data , writeByteOrderMark , onError , sourceFiles ) => host . writeFile ( fileName , data , writeByteOrderMark , onError , sourceFiles ) ) ,
1340
1368
isEmitBlocked,
@@ -1869,6 +1897,14 @@ namespace ts {
1869
1897
reportFileNamesDifferOnlyInCasingError ( fileName , file . fileName , refFile , refPos , refEnd ) ;
1870
1898
}
1871
1899
1900
+ // See if we need to reprocess the imports due to prior skipped imports
1901
+ if ( file && lookUp ( modulesWithElidedImports , file . path ) ) {
1902
+ if ( currentNodeModulesJsDepth < maxNodeModulesJsDepth ) {
1903
+ modulesWithElidedImports [ file . path ] = false ;
1904
+ processImportedModules ( file , getDirectoryPath ( fileName ) ) ;
1905
+ }
1906
+ }
1907
+
1872
1908
return file ;
1873
1909
}
1874
1910
@@ -2007,16 +2043,38 @@ namespace ts {
2007
2043
for ( let i = 0 ; i < moduleNames . length ; i ++ ) {
2008
2044
const resolution = resolutions [ i ] ;
2009
2045
setResolvedModule ( file , moduleNames [ i ] , resolution ) ;
2046
+ const resolvedPath = resolution ? toPath ( resolution . resolvedFileName , currentDirectory , getCanonicalFileName ) : undefined ;
2047
+
2010
2048
// add file to program only if:
2011
2049
// - resolution was successful
2012
2050
// - noResolve is falsy
2013
2051
// - module name comes from the list of imports
2014
- const shouldAddFile = resolution &&
2015
- ! options . noResolve &&
2016
- i < file . imports . length ;
2052
+ // - it's not a top level JavaScript module that exceeded the search max
2053
+ const isJsFileUnderNodeModules = resolution && resolution . isExternalLibraryImport &&
2054
+ hasJavaScriptFileExtension ( resolution . resolvedFileName ) ;
2055
+
2056
+ if ( isJsFileUnderNodeModules ) {
2057
+ jsFilesFoundSearchingNodeModules [ resolvedPath ] = true ;
2058
+ currentNodeModulesJsDepth ++ ;
2059
+ }
2060
+
2061
+ const elideImport = isJsFileUnderNodeModules && currentNodeModulesJsDepth > maxNodeModulesJsDepth ;
2062
+ const shouldAddFile = resolution && ! options . noResolve && i < file . imports . length && ! elideImport ;
2063
+
2064
+ if ( elideImport ) {
2065
+ modulesWithElidedImports [ file . path ] = true ;
2066
+ }
2067
+ else if ( shouldAddFile ) {
2068
+ findSourceFile ( resolution . resolvedFileName ,
2069
+ resolvedPath ,
2070
+ /*isDefaultLib*/ false , /*isReference*/ false ,
2071
+ file ,
2072
+ skipTrivia ( file . text , file . imports [ i ] . pos ) ,
2073
+ file . imports [ i ] . end ) ;
2074
+ }
2017
2075
2018
- if ( shouldAddFile ) {
2019
- findSourceFile ( resolution . resolvedFileName , toPath ( resolution . resolvedFileName , currentDirectory , getCanonicalFileName ) , /*isDefaultLib*/ false , /*isReference*/ false , file , skipTrivia ( file . text , file . imports [ i ] . pos ) , file . imports [ i ] . end ) ;
2076
+ if ( isJsFileUnderNodeModules ) {
2077
+ currentNodeModulesJsDepth -- ;
2020
2078
}
2021
2079
}
2022
2080
}
0 commit comments