@@ -9,10 +9,10 @@ namespace ts {
99 /* @internal */ export let ioWriteTime = 0 ;
1010
1111 /** The version of the TypeScript compiler release */
12+ export const version = "1.9.0" ;
1213
1314 const emptyArray : any [ ] = [ ] ;
14-
15- export const version = "1.9.0" ;
15+ const startsWithDotSlashOrDotDotSlash = / ^ ( \. \/ | \. \. \/ ) / ;
1616
1717 export function findConfigFile ( searchPath : string , fileExists : ( fileName : string ) => boolean ) : string {
1818 let fileName = "tsconfig.json" ;
@@ -79,9 +79,7 @@ namespace ts {
7979 return false ;
8080 }
8181
82- const i = moduleName . lastIndexOf ( "./" , 1 ) ;
83- const startsWithDotSlashOrDotDotSlash = i === 0 || ( i === 1 && moduleName . charCodeAt ( 0 ) === CharacterCodes . dot ) ;
84- return ! startsWithDotSlashOrDotDotSlash ;
82+ return ! startsWithDotSlashOrDotDotSlash . test ( moduleName ) ;
8583 }
8684
8785 interface ModuleResolutionState {
@@ -448,11 +446,11 @@ namespace ts {
448446 trace ( state . host , Diagnostics . Found_package_json_at_0 , packageJsonPath ) ;
449447 }
450448
451- let jsonContent : { typings ?: string } ;
449+ let jsonContent : { typings ?: string ; main ?: string } ;
452450
453451 try {
454452 const jsonText = state . host . readFile ( packageJsonPath ) ;
455- jsonContent = jsonText ? < { typings ?: string } > JSON . parse ( jsonText ) : { typings : undefined } ;
453+ jsonContent = jsonText ? < { typings ?: string ; main ?: string } > JSON . parse ( jsonText ) : { typings : undefined , main : undefined } ;
456454 }
457455 catch ( e ) {
458456 // gracefully handle if readFile fails or returns not JSON
@@ -465,7 +463,7 @@ namespace ts {
465463 if ( state . traceEnabled ) {
466464 trace ( state . host , Diagnostics . package_json_has_typings_field_0_that_references_1 , jsonContent . typings , typingsFile ) ;
467465 }
468- const result = loadModuleFromFile ( typingsFile , extensions , failedLookupLocation , ! directoryProbablyExists ( getDirectoryPath ( typingsFile ) , state . host ) , state ) ;
466+ const result = loadModuleFromFile ( typingsFile , /* don't add extension */ [ "" ] , failedLookupLocation , ! directoryProbablyExists ( getDirectoryPath ( typingsFile ) , state . host ) , state ) ;
469467 if ( result ) {
470468 return result ;
471469 }
@@ -479,6 +477,15 @@ namespace ts {
479477 trace ( state . host , Diagnostics . package_json_does_not_have_typings_field ) ;
480478 }
481479 }
480+ // TODO (billti): tracing as per above
481+ if ( typeof jsonContent . main === "string" ) {
482+ // If 'main' points to 'foo.js', we still want to try and load 'foo.d.ts' and 'foo.ts' first (and only 'foo.js' if 'allowJs' is set).
483+ const mainFile = normalizePath ( combinePaths ( candidate , removeFileExtension ( jsonContent . main ) ) ) ;
484+ const result = loadModuleFromFile ( mainFile , extensions , failedLookupLocation , ! directoryProbablyExists ( getDirectoryPath ( mainFile ) , state . host ) , state ) ;
485+ if ( result ) {
486+ return result ;
487+ }
488+ }
482489 }
483490 else {
484491 if ( state . traceEnabled ) {
@@ -499,12 +506,13 @@ namespace ts {
499506 const nodeModulesFolder = combinePaths ( directory , "node_modules" ) ;
500507 const nodeModulesFolderExists = directoryProbablyExists ( nodeModulesFolder , state . host ) ;
501508 const candidate = normalizePath ( combinePaths ( nodeModulesFolder , moduleName ) ) ;
502- // Load only typescript files irrespective of allowJs option if loading from node modules
503- let result = loadModuleFromFile ( candidate , supportedTypeScriptExtensions , failedLookupLocations , ! nodeModulesFolderExists , state ) ;
509+
510+ const supportedExtensions = getSupportedExtensions ( state . compilerOptions ) ;
511+ let result = loadModuleFromFile ( candidate , supportedExtensions , failedLookupLocations , ! nodeModulesFolderExists , state ) ;
504512 if ( result ) {
505513 return result ;
506514 }
507- result = loadNodeModuleFromDirectory ( supportedTypeScriptExtensions , candidate , failedLookupLocations , ! nodeModulesFolderExists , state ) ;
515+ result = loadNodeModuleFromDirectory ( supportedExtensions , candidate , failedLookupLocations , ! nodeModulesFolderExists , state ) ;
508516 if ( result ) {
509517 return result ;
510518 }
@@ -1397,7 +1405,7 @@ namespace ts {
13971405 }
13981406
13991407 // Get source file from normalized fileName
1400- function findSourceFile ( fileName : string , path : Path , isDefaultLib : boolean , refFile ?: SourceFile , refPos ?: number , refEnd ?: number ) : SourceFile {
1408+ function findSourceFile ( fileName : string , path : Path , isDefaultLib : boolean , refFile ?: SourceFile , refPos ?: number , refEnd ?: number , isFileFromNodeSearch ?: boolean ) : SourceFile {
14011409 if ( filesByName . contains ( path ) ) {
14021410 const file = filesByName . get ( path ) ;
14031411 // try to check if we've already seen this file but with a different casing in path
@@ -1406,6 +1414,13 @@ namespace ts {
14061414 reportFileNamesDifferOnlyInCasingError ( fileName , file . fileName , refFile , refPos , refEnd ) ;
14071415 }
14081416
1417+ // If this was a file found by a node_modules search, set the nodeModuleSearchDistance to parent distance + 1.
1418+ if ( isFileFromNodeSearch ) {
1419+ const newDistance = ( refFile && refFile . nodeModuleSearchDistance ) === undefined ? 1 : refFile . nodeModuleSearchDistance + 1 ;
1420+ // If already set on the file, don't overwrite if it was already found closer (which may be '0' if added as a root file)
1421+ file . nodeModuleSearchDistance = ( typeof file . nodeModuleSearchDistance === "number" ) ? Math . min ( file . nodeModuleSearchDistance , newDistance ) : newDistance ;
1422+ }
1423+
14091424 return file ;
14101425 }
14111426
@@ -1424,6 +1439,12 @@ namespace ts {
14241439 if ( file ) {
14251440 file . path = path ;
14261441
1442+ // Default to same distance as parent. Add one if found by a search.
1443+ file . nodeModuleSearchDistance = ( refFile && refFile . nodeModuleSearchDistance ) || 0 ;
1444+ if ( isFileFromNodeSearch ) {
1445+ file . nodeModuleSearchDistance ++ ;
1446+ }
1447+
14271448 if ( host . useCaseSensitiveFileNames ( ) ) {
14281449 // for case-sensitive file systems check if we've already seen some file with similar filename ignoring case
14291450 const existingFile = filesByNameIgnoreCase . get ( path ) ;
@@ -1468,28 +1489,37 @@ namespace ts {
14681489 }
14691490
14701491 function processImportedModules ( file : SourceFile , basePath : string ) {
1492+ const maxJsNodeModuleSearchDistance = options . maxNodeModuleJsDepth || 0 ;
14711493 collectExternalModuleReferences ( file ) ;
14721494 if ( file . imports . length || file . moduleAugmentations . length ) {
14731495 file . resolvedModules = { } ;
14741496 const moduleNames = map ( concatenate ( file . imports , file . moduleAugmentations ) , getTextOfLiteral ) ;
14751497 const resolutions = resolveModuleNamesWorker ( moduleNames , getNormalizedAbsolutePath ( file . fileName , currentDirectory ) ) ;
1498+ file . nodeModuleSearchDistance = file . nodeModuleSearchDistance || 0 ;
14761499 for ( let i = 0 ; i < moduleNames . length ; i ++ ) {
14771500 const resolution = resolutions [ i ] ;
14781501 setResolvedModule ( file , moduleNames [ i ] , resolution ) ;
14791502 // add file to program only if:
14801503 // - resolution was successful
14811504 // - noResolve is falsy
14821505 // - module name come from the list fo imports
1483- const shouldAddFile = resolution &&
1484- ! options . noResolve &&
1485- i < file . imports . length ;
1506+ // - it's not a top level JavaScript module that exceeded the search max
1507+ const exceedsJsSearchDepth = resolution && resolution . isExternalLibraryImport &&
1508+ hasJavaScriptFileExtension ( resolution . resolvedFileName ) &&
1509+ file . nodeModuleSearchDistance >= maxJsNodeModuleSearchDistance ;
1510+ const shouldAddFile = resolution && ! options . noResolve && i < file . imports . length && ! exceedsJsSearchDepth ;
14861511
14871512 if ( shouldAddFile ) {
1488- const importedFile = findSourceFile ( resolution . resolvedFileName , toPath ( resolution . resolvedFileName , currentDirectory , getCanonicalFileName ) , /*isDefaultLib*/ false , file , skipTrivia ( file . text , file . imports [ i ] . pos ) , file . imports [ i ] . end ) ;
1489-
1490- if ( importedFile && resolution . isExternalLibraryImport ) {
1491- // Since currently irrespective of allowJs, we only look for supportedTypeScript extension external module files,
1492- // this check is ok. Otherwise this would be never true for javascript file
1513+ const importedFile = findSourceFile ( resolution . resolvedFileName ,
1514+ toPath ( resolution . resolvedFileName , currentDirectory , getCanonicalFileName ) ,
1515+ /*isDefaultLib*/ false ,
1516+ file ,
1517+ skipTrivia ( file . text , file . imports [ i ] . pos ) ,
1518+ file . imports [ i ] . end ,
1519+ resolution . isExternalLibraryImport ) ;
1520+
1521+ // TODO (billti): Should we check here if a JavaScript file is a CommonJS file, or doesn't have /// references?
1522+ if ( importedFile && resolution . isExternalLibraryImport && ! hasJavaScriptFileExtension ( importedFile . fileName ) ) {
14931523 if ( ! isExternalModule ( importedFile ) && importedFile . statements . length ) {
14941524 const start = getTokenPosOfNode ( file . imports [ i ] , file ) ;
14951525 fileProcessingDiagnostics . add ( createFileDiagnostic ( file , start , file . imports [ i ] . end - start , Diagnostics . Exported_external_package_typings_file_0_is_not_a_module_Please_contact_the_package_author_to_update_the_package_definition , importedFile . fileName ) ) ;
0 commit comments