@@ -339,6 +339,12 @@ namespace ts.server {
339339 RootOfInferredProjectFalse = "Open file was set as not inferred root" ,
340340 }
341341
342+ const enum ConfigFileActionResult {
343+ Continue ,
344+ Return ,
345+ Break ,
346+ }
347+
342348 /*@internal */
343349 interface ConfigFileExistenceInfo {
344350 /**
@@ -1496,7 +1502,7 @@ namespace ts.server {
14961502 * The server must start searching from the directory containing
14971503 * the newly opened file.
14981504 */
1499- private forEachConfigFileLocation ( info : OpenScriptInfoOrClosedFileInfo , action : ( configFileName : NormalizedPath , canonicalConfigFilePath : string ) => boolean | void ) {
1505+ private forEachConfigFileLocation ( info : OpenScriptInfoOrClosedFileInfo , action : ( configFileName : NormalizedPath , canonicalConfigFilePath : string ) => ConfigFileActionResult | void ) {
15001506 if ( this . syntaxOnly ) {
15011507 return undefined ;
15021508 }
@@ -1512,17 +1518,24 @@ namespace ts.server {
15121518 do {
15131519 const canonicalSearchPath = normalizedPathToPath ( searchPath , this . currentDirectory , this . toCanonicalFileName ) ;
15141520 const tsconfigFileName = asNormalizedPath ( combinePaths ( searchPath , "tsconfig.json" ) ) ;
1515- let result = action ( tsconfigFileName , combinePaths ( canonicalSearchPath , "tsconfig.json" ) ) ;
1516- if ( result ) {
1521+ let tsResult = action ( tsconfigFileName , combinePaths ( canonicalSearchPath , "tsconfig.json" ) ) ;
1522+ if ( tsResult === ConfigFileActionResult . Return ) {
15171523 return tsconfigFileName ;
15181524 }
15191525
15201526 const jsconfigFileName = asNormalizedPath ( combinePaths ( searchPath , "jsconfig.json" ) ) ;
1521- result = action ( jsconfigFileName , combinePaths ( canonicalSearchPath , "jsconfig.json" ) ) ;
1522- if ( result ) {
1527+ let jsResult = action ( jsconfigFileName , combinePaths ( canonicalSearchPath , "jsconfig.json" ) ) ;
1528+ if ( jsResult === ConfigFileActionResult . Return ) {
15231529 return jsconfigFileName ;
15241530 }
15251531
1532+ if ( tsResult === ConfigFileActionResult . Break || jsResult === ConfigFileActionResult . Break ) {
1533+ break ;
1534+ }
1535+
1536+ Debug . assert ( ! tsResult ) ;
1537+ Debug . assert ( ! jsResult ) ;
1538+
15261539 const parentPath = asNormalizedPath ( getDirectoryPath ( searchPath ) ) ;
15271540 if ( parentPath === searchPath ) {
15281541 break ;
@@ -1536,7 +1549,7 @@ namespace ts.server {
15361549 /*@internal */
15371550 findDefaultConfiguredProject ( info : ScriptInfo ) {
15381551 if ( ! info . isScriptOpen ( ) ) return undefined ;
1539- const configFileName = this . getConfigFileNameForFile ( info ) ;
1552+ const configFileName = this . getConfigFileNameForFile ( info , /*stopAtNodeModules*/ false ) ;
15401553 return configFileName &&
15411554 this . findConfiguredProjectByProjectName ( configFileName ) ;
15421555 }
@@ -1551,11 +1564,15 @@ namespace ts.server {
15511564 * If script info is passed in, it is asserted to be open script info
15521565 * otherwise just file name
15531566 */
1554- private getConfigFileNameForFile ( info : OpenScriptInfoOrClosedFileInfo ) {
1567+ private getConfigFileNameForFile ( info : OpenScriptInfoOrClosedFileInfo , stopAtNodeModules : boolean ) {
15551568 if ( isOpenScriptInfo ( info ) ) Debug . assert ( info . isScriptOpen ( ) ) ;
15561569 this . logger . info ( `Search path: ${ getDirectoryPath ( info . fileName ) } ` ) ;
15571570 const configFileName = this . forEachConfigFileLocation ( info , ( configFileName , canonicalConfigFilePath ) =>
1558- this . configFileExists ( configFileName , canonicalConfigFilePath , info ) ) ;
1571+ this . configFileExists ( configFileName , canonicalConfigFilePath , info )
1572+ ? ConfigFileActionResult . Return
1573+ : stopAtNodeModules && isNodeModulesDirectory ( getDirectoryPath ( canonicalConfigFilePath ) as Path )
1574+ ? ConfigFileActionResult . Break
1575+ : ConfigFileActionResult . Continue ) ;
15591576 if ( configFileName ) {
15601577 this . logger . info ( `For info: ${ info . fileName } :: Config file name: ${ configFileName } ` ) ;
15611578 }
@@ -2517,7 +2534,7 @@ namespace ts.server {
25172534 // we first detect if there is already a configured project created for it: if so,
25182535 // we re- read the tsconfig file content and update the project only if we havent already done so
25192536 // otherwise we create a new one.
2520- const configFileName = this . getConfigFileNameForFile ( info ) ;
2537+ const configFileName = this . getConfigFileNameForFile ( info , /*stopAtNodeModules*/ true ) ;
25212538 if ( configFileName ) {
25222539 const project = this . findConfiguredProjectByProjectName ( configFileName ) || this . createConfiguredProject ( configFileName ) ;
25232540 if ( ! updatedProjects . has ( configFileName ) ) {
@@ -2614,7 +2631,7 @@ namespace ts.server {
26142631 if ( ! this . getScriptInfo ( fileName ) && ! this . host . fileExists ( fileName ) ) return undefined ;
26152632
26162633 const originalFileInfo : OriginalFileInfo = { fileName : toNormalizedPath ( fileName ) , path : this . toPath ( fileName ) } ;
2617- const configFileName = this . getConfigFileNameForFile ( originalFileInfo ) ;
2634+ const configFileName = this . getConfigFileNameForFile ( originalFileInfo , /*stopAtNodeModules*/ false ) ;
26182635 if ( ! configFileName ) return undefined ;
26192636
26202637 const configuredProject = this . findConfiguredProjectByProjectName ( configFileName ) ||
@@ -2668,7 +2685,7 @@ namespace ts.server {
26682685 let project : ConfiguredProject | ExternalProject | undefined = this . findExternalProjectContainingOpenScriptInfo ( info ) ;
26692686 let defaultConfigProject : ConfiguredProject | undefined ;
26702687 if ( ! project && ! this . syntaxOnly ) { // Checking syntaxOnly is an optimization
2671- configFileName = this . getConfigFileNameForFile ( info ) ;
2688+ configFileName = this . getConfigFileNameForFile ( info , /*stopAtNodeModules*/ true ) ;
26722689 if ( configFileName ) {
26732690 project = this . findConfiguredProjectByProjectName ( configFileName ) ;
26742691 if ( ! project ) {
0 commit comments