@@ -603,8 +603,11 @@ namespace ts.server {
603
603
return projects . length > 1 ? deduplicate ( result , areEqual ) : result ;
604
604
}
605
605
606
+ export type ProjectServiceEvent =
607
+ { eventName : "context" , data : { project : Project , fileName : string } } | { eventName : "configFileDiag" , data : { triggerFile ?: string , configFileName : string , diagnostics : Diagnostic [ ] } }
608
+
606
609
export interface ProjectServiceEventHandler {
607
- ( eventName : string , project : Project , fileName : string ) : void ;
610
+ ( event : ProjectServiceEvent ) : void ;
608
611
}
609
612
610
613
export interface HostConfiguration {
@@ -699,7 +702,8 @@ namespace ts.server {
699
702
}
700
703
701
704
handleProjectFileListChanges ( project : Project ) {
702
- const { projectOptions } = this . configFileToProjectOptions ( project . projectFilename ) ;
705
+ const { projectOptions, errors } = this . configFileToProjectOptions ( project . projectFilename ) ;
706
+ this . reportConfigFileDiagnostics ( project . projectFilename , errors ) ;
703
707
704
708
const newRootFiles = projectOptions . files . map ( ( f => this . getCanonicalFileName ( f ) ) ) ;
705
709
const currentRootFiles = project . getRootFiles ( ) . map ( ( f => this . getCanonicalFileName ( f ) ) ) ;
@@ -717,18 +721,32 @@ namespace ts.server {
717
721
}
718
722
}
719
723
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
+
720
733
/**
721
734
* This is the callback function when a watched directory has an added tsconfig file.
722
735
*/
723
736
directoryWatchedForTsconfigChanged ( fileName : string ) {
724
- if ( ts . getBaseFileName ( fileName ) != "tsconfig.json" ) {
737
+ if ( ts . getBaseFileName ( fileName ) !== "tsconfig.json" ) {
725
738
this . log ( fileName + " is not tsconfig.json" ) ;
726
739
return ;
727
740
}
728
741
729
742
this . log ( "Detected newly added tsconfig file: " + fileName ) ;
730
743
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
+ }
732
750
733
751
const rootFilesInTsconfig = projectOptions . files . map ( f => this . getCanonicalFileName ( f ) ) ;
734
752
const openFileRoots = this . openFileRoots . map ( s => this . getCanonicalFileName ( s . fileName ) ) ;
@@ -748,10 +766,13 @@ namespace ts.server {
748
766
return ts . normalizePath ( name ) ;
749
767
}
750
768
751
- watchedProjectConfigFileChanged ( project : Project ) {
769
+ watchedProjectConfigFileChanged ( project : Project ) : void {
752
770
this . log ( "Config file changed: " + project . projectFilename ) ;
753
- this . updateConfiguredProject ( project ) ;
771
+ const configFileErrors = this . updateConfiguredProject ( project ) ;
754
772
this . updateProjectStructure ( ) ;
773
+ if ( configFileErrors && configFileErrors . length > 0 ) {
774
+ this . eventHandler ( { eventName : "configFileDiag" , data : { triggerFile : project . projectFilename , configFileName : project . projectFilename , diagnostics : configFileErrors } } ) ;
775
+ }
755
776
}
756
777
757
778
log ( msg : string , type = "Err" ) {
@@ -828,13 +849,13 @@ namespace ts.server {
828
849
for ( let j = 0 , flen = this . openFileRoots . length ; j < flen ; j ++ ) {
829
850
const openFile = this . openFileRoots [ j ] ;
830
851
if ( this . eventHandler ) {
831
- this . eventHandler ( "context" , openFile . defaultProject , openFile . fileName ) ;
852
+ this . eventHandler ( { eventName : "context" , data : { project : openFile . defaultProject , fileName : openFile . fileName } } ) ;
832
853
}
833
854
}
834
855
for ( let j = 0 , flen = this . openFilesReferenced . length ; j < flen ; j ++ ) {
835
856
const openFile = this . openFilesReferenced [ j ] ;
836
857
if ( this . eventHandler ) {
837
- this . eventHandler ( "context" , openFile . defaultProject , openFile . fileName ) ;
858
+ this . eventHandler ( { eventName : "context" , data : { project : openFile . defaultProject , fileName : openFile . fileName } } ) ;
838
859
}
839
860
}
840
861
}
@@ -1218,7 +1239,7 @@ namespace ts.server {
1218
1239
const project = this . findConfiguredProjectByConfigFile ( configFileName ) ;
1219
1240
if ( ! project ) {
1220
1241
const configResult = this . openConfigFile ( configFileName , fileName ) ;
1221
- if ( ! configResult . success ) {
1242
+ if ( ! configResult . project ) {
1222
1243
return { configFileName, configFileErrors : configResult . errors } ;
1223
1244
}
1224
1245
else {
@@ -1234,11 +1255,12 @@ namespace ts.server {
1234
1255
else {
1235
1256
this . updateConfiguredProject ( project ) ;
1236
1257
}
1258
+ return { configFileName } ;
1237
1259
}
1238
1260
else {
1239
1261
this . log ( "No config files found." ) ;
1240
1262
}
1241
- return configFileName ? { configFileName } : { } ;
1263
+ return { } ;
1242
1264
}
1243
1265
1244
1266
/**
@@ -1328,34 +1350,31 @@ namespace ts.server {
1328
1350
return undefined ;
1329
1351
}
1330
1352
1331
- configFileToProjectOptions ( configFilename : string ) : { succeeded : boolean , projectOptions ?: ProjectOptions , errors ? : Diagnostic [ ] } {
1353
+ configFileToProjectOptions ( configFilename : string ) : { projectOptions ?: ProjectOptions , errors : Diagnostic [ ] } {
1332
1354
configFilename = ts . normalizePath ( configFilename ) ;
1355
+ let errors : Diagnostic [ ] = [ ] ;
1333
1356
// file references will be relative to dirPath (or absolute)
1334
1357
const dirPath = ts . getDirectoryPath ( configFilename ) ;
1335
1358
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 } ;
1339
1368
}
1340
1369
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 } ;
1359
1378
}
1360
1379
}
1361
1380
@@ -1377,64 +1396,63 @@ namespace ts.server {
1377
1396
return false ;
1378
1397
}
1379
1398
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 } ;
1384
1404
}
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 } ;
1397
1416
}
1417
+ }
1398
1418
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 ) ) ;
1409
1427
}
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 ) ) ;
1412
1431
1413
- const configDirectoryPath = ts . getDirectoryPath ( configFilename ) ;
1432
+ const configDirectoryPath = ts . getDirectoryPath ( configFilename ) ;
1414
1433
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
+ ) ;
1421
1440
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
+ }
1432
1451
1433
- return watchers ;
1434
- } , < Map < FileWatcher > > { } ) ;
1452
+ return watchers ;
1453
+ } , < Map < FileWatcher > > { } ) ;
1435
1454
1436
- return { success : true , project : project , errors } ;
1437
- }
1455
+ return { project : project , errors } ;
1438
1456
}
1439
1457
1440
1458
updateConfiguredProject ( project : Project ) : Diagnostic [ ] {
@@ -1443,23 +1461,23 @@ namespace ts.server {
1443
1461
this . removeProject ( project ) ;
1444
1462
}
1445
1463
else {
1446
- const { succeeded , projectOptions, errors } = this . configFileToProjectOptions ( project . projectFilename ) ;
1447
- if ( ! succeeded ) {
1464
+ const { projectOptions, errors } = this . configFileToProjectOptions ( project . projectFilename ) ;
1465
+ if ( ! projectOptions ) {
1448
1466
return errors ;
1449
1467
}
1450
1468
else {
1451
1469
if ( projectOptions . compilerOptions && ! projectOptions . compilerOptions . disableSizeLimit && this . exceedTotalNonTsFileSizeLimit ( projectOptions . files ) ) {
1452
1470
project . setProjectOptions ( projectOptions ) ;
1453
1471
if ( project . languageServiceDiabled ) {
1454
- return ;
1472
+ return errors ;
1455
1473
}
1456
1474
1457
1475
project . disableLanguageService ( ) ;
1458
1476
if ( project . directoryWatcher ) {
1459
1477
project . directoryWatcher . close ( ) ;
1460
1478
project . directoryWatcher = undefined ;
1461
1479
}
1462
- return ;
1480
+ return errors ;
1463
1481
}
1464
1482
1465
1483
if ( project . languageServiceDiabled ) {
@@ -1478,7 +1496,7 @@ namespace ts.server {
1478
1496
}
1479
1497
}
1480
1498
project . finishGraph ( ) ;
1481
- return ;
1499
+ return errors ;
1482
1500
}
1483
1501
1484
1502
// 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 {
1524
1542
project . setProjectOptions ( projectOptions ) ;
1525
1543
project . finishGraph ( ) ;
1526
1544
}
1545
+ return errors ;
1527
1546
}
1528
1547
}
1529
1548
0 commit comments