@@ -603,8 +603,11 @@ namespace ts.server {
603603 return projects . length > 1 ? deduplicate ( result , areEqual ) : result ;
604604 }
605605
606+ export type ProjectServiceEvent =
607+ { eventName : "context" , data : { project : Project , fileName : string } } | { eventName : "configFileDiag" , data : { triggerFile ?: string , configFileName : string , diagnostics : Diagnostic [ ] } }
608+
606609 export interface ProjectServiceEventHandler {
607- ( eventName : string , project : Project , fileName : string ) : void ;
610+ ( event : ProjectServiceEvent ) : void ;
608611 }
609612
610613 export interface HostConfiguration {
@@ -699,7 +702,8 @@ namespace ts.server {
699702 }
700703
701704 handleProjectFileListChanges ( project : Project ) {
702- const { projectOptions } = this . configFileToProjectOptions ( project . projectFilename ) ;
705+ const { projectOptions, errors } = this . configFileToProjectOptions ( project . projectFilename ) ;
706+ this . reportConfigFileDiagnostics ( project . projectFilename , errors ) ;
703707
704708 const newRootFiles = projectOptions . files . map ( ( f => this . getCanonicalFileName ( f ) ) ) ;
705709 const currentRootFiles = project . getRootFiles ( ) . map ( ( f => this . getCanonicalFileName ( f ) ) ) ;
@@ -717,18 +721,32 @@ namespace ts.server {
717721 }
718722 }
719723
724+ reportConfigFileDiagnostics ( configFileName : string , diagnostics : Diagnostic [ ] , triggerFile ?: string ) {
725+ if ( diagnostics && diagnostics . length > 0 ) {
726+ this . eventHandler ( {
727+ eventName : "configFileDiag" ,
728+ data : { configFileName, diagnostics, triggerFile }
729+ } ) ;
730+ }
731+ }
732+
720733 /**
721734 * This is the callback function when a watched directory has an added tsconfig file.
722735 */
723736 directoryWatchedForTsconfigChanged ( fileName : string ) {
724- if ( ts . getBaseFileName ( fileName ) != "tsconfig.json" ) {
737+ if ( ts . getBaseFileName ( fileName ) !== "tsconfig.json" ) {
725738 this . log ( fileName + " is not tsconfig.json" ) ;
726739 return ;
727740 }
728741
729742 this . log ( "Detected newly added tsconfig file: " + fileName ) ;
730743
731- const { projectOptions } = this . configFileToProjectOptions ( fileName ) ;
744+ const { projectOptions, errors } = this . configFileToProjectOptions ( fileName ) ;
745+ this . reportConfigFileDiagnostics ( fileName , errors ) ;
746+
747+ if ( ! projectOptions ) {
748+ return ;
749+ }
732750
733751 const rootFilesInTsconfig = projectOptions . files . map ( f => this . getCanonicalFileName ( f ) ) ;
734752 const openFileRoots = this . openFileRoots . map ( s => this . getCanonicalFileName ( s . fileName ) ) ;
@@ -748,10 +766,13 @@ namespace ts.server {
748766 return ts . normalizePath ( name ) ;
749767 }
750768
751- watchedProjectConfigFileChanged ( project : Project ) {
769+ watchedProjectConfigFileChanged ( project : Project ) : void {
752770 this . log ( "Config file changed: " + project . projectFilename ) ;
753- this . updateConfiguredProject ( project ) ;
771+ const configFileErrors = this . updateConfiguredProject ( project ) ;
754772 this . updateProjectStructure ( ) ;
773+ if ( configFileErrors && configFileErrors . length > 0 ) {
774+ this . eventHandler ( { eventName : "configFileDiag" , data : { triggerFile : project . projectFilename , configFileName : project . projectFilename , diagnostics : configFileErrors } } ) ;
775+ }
755776 }
756777
757778 log ( msg : string , type = "Err" ) {
@@ -828,13 +849,13 @@ namespace ts.server {
828849 for ( let j = 0 , flen = this . openFileRoots . length ; j < flen ; j ++ ) {
829850 const openFile = this . openFileRoots [ j ] ;
830851 if ( this . eventHandler ) {
831- this . eventHandler ( "context" , openFile . defaultProject , openFile . fileName ) ;
852+ this . eventHandler ( { eventName : "context" , data : { project : openFile . defaultProject , fileName : openFile . fileName } } ) ;
832853 }
833854 }
834855 for ( let j = 0 , flen = this . openFilesReferenced . length ; j < flen ; j ++ ) {
835856 const openFile = this . openFilesReferenced [ j ] ;
836857 if ( this . eventHandler ) {
837- this . eventHandler ( "context" , openFile . defaultProject , openFile . fileName ) ;
858+ this . eventHandler ( { eventName : "context" , data : { project : openFile . defaultProject , fileName : openFile . fileName } } ) ;
838859 }
839860 }
840861 }
@@ -1218,7 +1239,7 @@ namespace ts.server {
12181239 const project = this . findConfiguredProjectByConfigFile ( configFileName ) ;
12191240 if ( ! project ) {
12201241 const configResult = this . openConfigFile ( configFileName , fileName ) ;
1221- if ( ! configResult . success ) {
1242+ if ( ! configResult . project ) {
12221243 return { configFileName, configFileErrors : configResult . errors } ;
12231244 }
12241245 else {
@@ -1234,11 +1255,12 @@ namespace ts.server {
12341255 else {
12351256 this . updateConfiguredProject ( project ) ;
12361257 }
1258+ return { configFileName } ;
12371259 }
12381260 else {
12391261 this . log ( "No config files found." ) ;
12401262 }
1241- return configFileName ? { configFileName } : { } ;
1263+ return { } ;
12421264 }
12431265
12441266 /**
@@ -1328,34 +1350,31 @@ namespace ts.server {
13281350 return undefined ;
13291351 }
13301352
1331- configFileToProjectOptions ( configFilename : string ) : { succeeded : boolean , projectOptions ?: ProjectOptions , errors ? : Diagnostic [ ] } {
1353+ configFileToProjectOptions ( configFilename : string ) : { projectOptions ?: ProjectOptions , errors : Diagnostic [ ] } {
13321354 configFilename = ts . normalizePath ( configFilename ) ;
1355+ let errors : Diagnostic [ ] = [ ] ;
13331356 // file references will be relative to dirPath (or absolute)
13341357 const dirPath = ts . getDirectoryPath ( configFilename ) ;
13351358 const contents = this . host . readFile ( configFilename ) ;
1336- const rawConfig : { config ?: ProjectOptions ; error ?: Diagnostic ; } = ts . parseConfigFileTextToJson ( configFilename , contents ) ;
1337- if ( rawConfig . error ) {
1338- return { succeeded : false , errors : [ rawConfig . error ] } ;
1359+ const { configJsonObject, diagnostics } = ts . parseAndReEmitConfigJSONFile ( contents ) ;
1360+ errors = concatenate ( errors , diagnostics ) ;
1361+ const parsedCommandLine = ts . parseJsonConfigFileContent ( configJsonObject , this . host , dirPath , /*existingOptions*/ { } , configFilename ) ;
1362+ errors = concatenate ( errors , parsedCommandLine . errors ) ;
1363+ Debug . assert ( ! ! parsedCommandLine . fileNames ) ;
1364+
1365+ if ( parsedCommandLine . fileNames . length === 0 ) {
1366+ errors . push ( createCompilerDiagnostic ( Diagnostics . The_config_file_0_found_doesn_t_contain_any_source_files , configFilename ) ) ;
1367+ return { errors } ;
13391368 }
13401369 else {
1341- const parsedCommandLine = ts . parseJsonConfigFileContent ( rawConfig . config , this . host , dirPath , /*existingOptions*/ { } , configFilename ) ;
1342- Debug . assert ( ! ! parsedCommandLine . fileNames ) ;
1343-
1344- if ( parsedCommandLine . errors && ( parsedCommandLine . errors . length > 0 ) ) {
1345- return { succeeded : false , errors : parsedCommandLine . errors } ;
1346- }
1347- else if ( parsedCommandLine . fileNames . length === 0 ) {
1348- const error = createCompilerDiagnostic ( Diagnostics . The_config_file_0_found_doesn_t_contain_any_source_files , configFilename ) ;
1349- return { succeeded : false , errors : [ error ] } ;
1350- }
1351- else {
1352- const projectOptions : ProjectOptions = {
1353- files : parsedCommandLine . fileNames ,
1354- wildcardDirectories : parsedCommandLine . wildcardDirectories ,
1355- compilerOptions : parsedCommandLine . options ,
1356- } ;
1357- return { succeeded : true , projectOptions } ;
1358- }
1370+ // if the project has some files, we can continue with the parsed options and tolerate
1371+ // errors in the parsedCommandLine
1372+ const projectOptions : ProjectOptions = {
1373+ files : parsedCommandLine . fileNames ,
1374+ wildcardDirectories : parsedCommandLine . wildcardDirectories ,
1375+ compilerOptions : parsedCommandLine . options ,
1376+ } ;
1377+ return { projectOptions, errors } ;
13591378 }
13601379 }
13611380
@@ -1377,64 +1396,63 @@ namespace ts.server {
13771396 return false ;
13781397 }
13791398
1380- openConfigFile ( configFilename : string , clientFileName ?: string ) : { success : boolean , project ?: Project , errors ?: Diagnostic [ ] } {
1381- const { succeeded, projectOptions, errors } = this . configFileToProjectOptions ( configFilename ) ;
1382- if ( ! succeeded ) {
1383- return { success : false , errors } ;
1399+ openConfigFile ( configFilename : string , clientFileName ?: string ) : { project ?: Project , errors : Diagnostic [ ] } {
1400+ const parseConfigFileResult = this . configFileToProjectOptions ( configFilename ) ;
1401+ let errors = parseConfigFileResult . errors ;
1402+ if ( ! parseConfigFileResult . projectOptions ) {
1403+ return { errors } ;
13841404 }
1385- else {
1386- if ( ! projectOptions . compilerOptions . disableSizeLimit && projectOptions . compilerOptions . allowJs ) {
1387- if ( this . exceedTotalNonTsFileSizeLimit ( projectOptions . files ) ) {
1388- const project = this . createProject ( configFilename , projectOptions , /*languageServiceDisabled*/ true ) ;
1389-
1390- // for configured projects with languageService disabled, we only watch its config file,
1391- // do not care about the directory changes in the folder.
1392- project . projectFileWatcher = this . host . watchFile (
1393- toPath ( configFilename , configFilename , createGetCanonicalFileName ( sys . useCaseSensitiveFileNames ) ) ,
1394- _ => this . watchedProjectConfigFileChanged ( project ) ) ;
1395- return { success : true , project } ;
1396- }
1405+ const projectOptions = parseConfigFileResult . projectOptions ;
1406+ if ( ! projectOptions . compilerOptions . disableSizeLimit && projectOptions . compilerOptions . allowJs ) {
1407+ if ( this . exceedTotalNonTsFileSizeLimit ( projectOptions . files ) ) {
1408+ const project = this . createProject ( configFilename , projectOptions , /*languageServiceDisabled*/ true ) ;
1409+
1410+ // for configured projects with languageService disabled, we only watch its config file,
1411+ // do not care about the directory changes in the folder.
1412+ project . projectFileWatcher = this . host . watchFile (
1413+ toPath ( configFilename , configFilename , createGetCanonicalFileName ( sys . useCaseSensitiveFileNames ) ) ,
1414+ _ => this . watchedProjectConfigFileChanged ( project ) ) ;
1415+ return { project, errors } ;
13971416 }
1417+ }
13981418
1399- const project = this . createProject ( configFilename , projectOptions ) ;
1400- let errors : Diagnostic [ ] ;
1401- for ( const rootFilename of projectOptions . files ) {
1402- if ( this . host . fileExists ( rootFilename ) ) {
1403- const info = this . openFile ( rootFilename , /*openedByClient*/ clientFileName == rootFilename ) ;
1404- project . addRoot ( info ) ;
1405- }
1406- else {
1407- ( errors || ( errors = [ ] ) ) . push ( createCompilerDiagnostic ( Diagnostics . File_0_not_found , rootFilename ) ) ;
1408- }
1419+ const project = this . createProject ( configFilename , projectOptions ) ;
1420+ for ( const rootFilename of projectOptions . files ) {
1421+ if ( this . host . fileExists ( rootFilename ) ) {
1422+ const info = this . openFile ( rootFilename , /*openedByClient*/ clientFileName == rootFilename ) ;
1423+ project . addRoot ( info ) ;
1424+ }
1425+ else {
1426+ ( errors || ( errors = [ ] ) ) . push ( createCompilerDiagnostic ( Diagnostics . File_0_not_found , rootFilename ) ) ;
14091427 }
1410- project . finishGraph ( ) ;
1411- project . projectFileWatcher = this . host . watchFile ( configFilename , _ => this . watchedProjectConfigFileChanged ( project ) ) ;
1428+ }
1429+ project . finishGraph ( ) ;
1430+ project . projectFileWatcher = this . host . watchFile ( configFilename , _ => this . watchedProjectConfigFileChanged ( project ) ) ;
14121431
1413- const configDirectoryPath = ts . getDirectoryPath ( configFilename ) ;
1432+ const configDirectoryPath = ts . getDirectoryPath ( configFilename ) ;
14141433
1415- this . log ( "Add recursive watcher for: " + configDirectoryPath ) ;
1416- project . directoryWatcher = this . host . watchDirectory (
1417- configDirectoryPath ,
1418- path => this . directoryWatchedForSourceFilesChanged ( project , path ) ,
1419- /*recursive*/ true
1420- ) ;
1434+ this . log ( "Add recursive watcher for: " + configDirectoryPath ) ;
1435+ project . directoryWatcher = this . host . watchDirectory (
1436+ configDirectoryPath ,
1437+ path => this . directoryWatchedForSourceFilesChanged ( project , path ) ,
1438+ /*recursive*/ true
1439+ ) ;
14211440
1422- project . directoriesWatchedForWildcards = reduceProperties ( createMap ( projectOptions . wildcardDirectories ) , ( watchers , flag , directory ) => {
1423- if ( comparePaths ( configDirectoryPath , directory , "." , ! this . host . useCaseSensitiveFileNames ) !== Comparison . EqualTo ) {
1424- const recursive = ( flag & WatchDirectoryFlags . Recursive ) !== 0 ;
1425- this . log ( `Add ${ recursive ? "recursive " : "" } watcher for: ${ directory } ` ) ;
1426- watchers [ directory ] = this . host . watchDirectory (
1427- directory ,
1428- path => this . directoryWatchedForSourceFilesChanged ( project , path ) ,
1429- recursive
1430- ) ;
1431- }
1441+ project . directoriesWatchedForWildcards = reduceProperties ( createMap ( projectOptions . wildcardDirectories ) , ( watchers , flag , directory ) => {
1442+ if ( comparePaths ( configDirectoryPath , directory , "." , ! this . host . useCaseSensitiveFileNames ) !== Comparison . EqualTo ) {
1443+ const recursive = ( flag & WatchDirectoryFlags . Recursive ) !== 0 ;
1444+ this . log ( `Add ${ recursive ? "recursive " : "" } watcher for: ${ directory } ` ) ;
1445+ watchers [ directory ] = this . host . watchDirectory (
1446+ directory ,
1447+ path => this . directoryWatchedForSourceFilesChanged ( project , path ) ,
1448+ recursive
1449+ ) ;
1450+ }
14321451
1433- return watchers ;
1434- } , < Map < FileWatcher > > { } ) ;
1452+ return watchers ;
1453+ } , < Map < FileWatcher > > { } ) ;
14351454
1436- return { success : true , project : project , errors } ;
1437- }
1455+ return { project : project , errors } ;
14381456 }
14391457
14401458 updateConfiguredProject ( project : Project ) : Diagnostic [ ] {
@@ -1443,23 +1461,23 @@ namespace ts.server {
14431461 this . removeProject ( project ) ;
14441462 }
14451463 else {
1446- const { succeeded , projectOptions, errors } = this . configFileToProjectOptions ( project . projectFilename ) ;
1447- if ( ! succeeded ) {
1464+ const { projectOptions, errors } = this . configFileToProjectOptions ( project . projectFilename ) ;
1465+ if ( ! projectOptions ) {
14481466 return errors ;
14491467 }
14501468 else {
14511469 if ( projectOptions . compilerOptions && ! projectOptions . compilerOptions . disableSizeLimit && this . exceedTotalNonTsFileSizeLimit ( projectOptions . files ) ) {
14521470 project . setProjectOptions ( projectOptions ) ;
14531471 if ( project . languageServiceDiabled ) {
1454- return ;
1472+ return errors ;
14551473 }
14561474
14571475 project . disableLanguageService ( ) ;
14581476 if ( project . directoryWatcher ) {
14591477 project . directoryWatcher . close ( ) ;
14601478 project . directoryWatcher = undefined ;
14611479 }
1462- return ;
1480+ return errors ;
14631481 }
14641482
14651483 if ( project . languageServiceDiabled ) {
@@ -1478,7 +1496,7 @@ namespace ts.server {
14781496 }
14791497 }
14801498 project . finishGraph ( ) ;
1481- return ;
1499+ return errors ;
14821500 }
14831501
14841502 // if the project is too large, the root files might not have been all loaded if the total
@@ -1524,6 +1542,7 @@ namespace ts.server {
15241542 project . setProjectOptions ( projectOptions ) ;
15251543 project . finishGraph ( ) ;
15261544 }
1545+ return errors ;
15271546 }
15281547 }
15291548
0 commit comments