@@ -62,6 +62,18 @@ namespace ts.projectSystem {
62
62
getLogFileName : ( ) => undefined ,
63
63
} ;
64
64
65
+ function createHasErrorMessageLogger ( ) {
66
+ let hasErrorMsg = false ;
67
+ const { close, hasLevel, loggingEnabled, startGroup, endGroup, info, getLogFileName, perftrc } = nullLogger ;
68
+ const logger : server . Logger = {
69
+ close, hasLevel, loggingEnabled, startGroup, endGroup, info, getLogFileName, perftrc,
70
+ msg : ( ) => {
71
+ hasErrorMsg = true ;
72
+ }
73
+ } ;
74
+ return { logger, hasErrorMsg : ( ) => hasErrorMsg } ;
75
+ }
76
+
65
77
export class TestTypingsInstaller extends TI . TypingsInstaller implements server . ITypingsInstaller {
66
78
protected projectService : server . ProjectService ;
67
79
constructor (
@@ -2917,14 +2929,7 @@ namespace ts.projectSystem {
2917
2929
} ) ;
2918
2930
2919
2931
it ( "Getting errors from closed script info does not throw exception (because of getting project from orphan script info)" , ( ) => {
2920
- let hasErrorMsg = false ;
2921
- const { close, hasLevel, loggingEnabled, startGroup, endGroup, info, getLogFileName, perftrc } = nullLogger ;
2922
- const logger : server . Logger = {
2923
- close, hasLevel, loggingEnabled, startGroup, endGroup, info, getLogFileName, perftrc,
2924
- msg : ( ) => {
2925
- hasErrorMsg = true ;
2926
- }
2927
- } ;
2932
+ const { logger, hasErrorMsg } = createHasErrorMessageLogger ( ) ;
2928
2933
const f1 = {
2929
2934
path : "/a/b/app.ts" ,
2930
2935
content : "let x = 1;"
@@ -2954,7 +2959,7 @@ namespace ts.projectSystem {
2954
2959
files : [ f1 . path ]
2955
2960
}
2956
2961
} ) ;
2957
- assert . isFalse ( hasErrorMsg ) ;
2962
+ assert . isFalse ( hasErrorMsg ( ) ) ;
2958
2963
} ) ;
2959
2964
2960
2965
it ( "Changed module resolution reflected when specifying files list" , ( ) => {
@@ -3320,6 +3325,129 @@ namespace ts.projectSystem {
3320
3325
assert . equal ( info . containingProjects . length , 0 ) ;
3321
3326
}
3322
3327
} ) ;
3328
+
3329
+ it ( "handles delayed directory watch invoke on file creation" , ( ) => {
3330
+ const projectRootPath = "/users/username/projects/project" ;
3331
+ const fileB : File = {
3332
+ path : `${ projectRootPath } /b.ts` ,
3333
+ content : "export const b = 10;"
3334
+ } ;
3335
+ const fileA : File = {
3336
+ path : `${ projectRootPath } /a.ts` ,
3337
+ content : "export const a = 10;"
3338
+ } ;
3339
+ const fileSubA : File = {
3340
+ path : `${ projectRootPath } /sub/a.ts` ,
3341
+ content : fileA . content
3342
+ } ;
3343
+ const config : File = {
3344
+ path : `${ projectRootPath } /tsconfig.json` ,
3345
+ content : "{}"
3346
+ } ;
3347
+ const files = [ fileSubA , fileB , config , libFile ] ;
3348
+ const host = createServerHost ( files ) ;
3349
+ const { logger, hasErrorMsg } = createHasErrorMessageLogger ( ) ;
3350
+ const session = createSession ( host , { canUseEvents : true , noGetErrOnBackgroundUpdate : true , logger } ) ;
3351
+ openFile ( fileB ) ;
3352
+ openFile ( fileSubA ) ;
3353
+
3354
+ const services = session . getProjectService ( ) ;
3355
+ checkNumberOfProjects ( services , { configuredProjects : 1 } ) ;
3356
+ checkProjectActualFiles ( services . configuredProjects . get ( config . path ) ! , files . map ( f => f . path ) ) ;
3357
+ host . checkTimeoutQueueLengthAndRun ( 0 ) ;
3358
+
3359
+ // This should schedule 2 timeouts for ensuring project structure and ensuring projects for open file
3360
+ const filesWithFileA = files . map ( f => f === fileSubA ? fileA : f ) ;
3361
+ host . reloadFS ( files . map ( f => f === fileSubA ? fileA : f ) ) ;
3362
+ host . checkTimeoutQueueLength ( 2 ) ;
3363
+
3364
+ closeFile ( fileSubA ) ;
3365
+ // This should cancel existing updates and schedule new ones
3366
+ host . checkTimeoutQueueLength ( 2 ) ;
3367
+ checkNumberOfProjects ( services , { configuredProjects : 1 } ) ;
3368
+ checkProjectActualFiles ( services . configuredProjects . get ( config . path ) ! , files . map ( f => f . path ) ) ;
3369
+
3370
+ // Open the fileA (as if rename)
3371
+ openFile ( fileA ) ;
3372
+
3373
+ // config project is updated to check if fileA is present in it
3374
+ checkNumberOfProjects ( services , { configuredProjects : 1 } ) ;
3375
+ checkProjectActualFiles ( services . configuredProjects . get ( config . path ) ! , filesWithFileA . map ( f => f . path ) ) ;
3376
+
3377
+ // Run the timeout for updating configured project and ensuring projects for open file
3378
+ host . checkTimeoutQueueLengthAndRun ( 2 ) ;
3379
+ checkProjectActualFiles ( services . configuredProjects . get ( config . path ) ! , filesWithFileA . map ( f => f . path ) ) ;
3380
+
3381
+ // file is deleted but watches are not yet invoked
3382
+ const originalFileExists = host . fileExists ;
3383
+ host . fileExists = s => s === fileA . path ? false : originalFileExists . call ( host , s ) ;
3384
+ closeFile ( fileA ) ;
3385
+ host . checkTimeoutQueueLength ( 2 ) ; // Update configured project and projects for open file
3386
+ checkProjectActualFiles ( services . configuredProjects . get ( config . path ) ! , filesWithFileA . map ( f => f . path ) ) ;
3387
+
3388
+ // host.fileExists = originalFileExists;
3389
+ openFile ( fileSubA ) ;
3390
+ // This should create inferred project since fileSubA not on the disk
3391
+ checkProjectActualFiles ( services . configuredProjects . get ( config . path ) ! , mapDefined ( filesWithFileA , f => f === fileA ? undefined : f . path ) ) ;
3392
+ checkProjectActualFiles ( services . inferredProjects [ 0 ] , [ fileSubA . path , libFile . path ] ) ;
3393
+
3394
+ host . checkTimeoutQueueLengthAndRun ( 2 ) ; // Update configured project and projects for open file
3395
+ host . fileExists = originalFileExists ;
3396
+
3397
+ // Actually trigger the file move
3398
+ host . reloadFS ( files ) ;
3399
+ host . checkTimeoutQueueLength ( 2 ) ;
3400
+ const fileBErrorTimeoutId = host . getNextTimeoutId ( ) ;
3401
+
3402
+ session . executeCommandSeq < protocol . GeterrRequest > ( {
3403
+ command : protocol . CommandTypes . Geterr ,
3404
+ arguments : {
3405
+ files : [ fileB . path , fileSubA . path ] ,
3406
+ delay : 0
3407
+ }
3408
+ } ) ;
3409
+ const getErrSeqId = session . getSeq ( ) ;
3410
+ host . checkTimeoutQueueLength ( 3 ) ;
3411
+
3412
+ session . clearMessages ( ) ;
3413
+ host . runQueuedTimeoutCallbacks ( fileBErrorTimeoutId ) ;
3414
+ checkErrorMessage ( session , "syntaxDiag" , { file : fileB . path , diagnostics : [ ] } ) ;
3415
+
3416
+ session . clearMessages ( ) ;
3417
+ host . runQueuedImmediateCallbacks ( ) ;
3418
+ checkErrorMessage ( session , "semanticDiag" , { file : fileB . path , diagnostics : [ ] } ) ;
3419
+
3420
+ session . clearMessages ( ) ;
3421
+ const fileSubAErrorTimeoutId = host . getNextTimeoutId ( ) ;
3422
+ host . runQueuedImmediateCallbacks ( ) ;
3423
+ checkErrorMessage ( session , "suggestionDiag" , { file : fileB . path , diagnostics : [ ] } ) ;
3424
+
3425
+ session . clearMessages ( ) ;
3426
+ host . checkTimeoutQueueLength ( 3 ) ;
3427
+ host . runQueuedTimeoutCallbacks ( fileSubAErrorTimeoutId ) ;
3428
+ checkCompleteEvent ( session , 1 , getErrSeqId ) ;
3429
+ assert . isFalse ( hasErrorMsg ( ) ) ;
3430
+
3431
+ function openFile ( file : File ) {
3432
+ session . executeCommandSeq < protocol . OpenRequest > ( {
3433
+ command : protocol . CommandTypes . Open ,
3434
+ arguments : {
3435
+ file : file . path ,
3436
+ fileContent : file . content ,
3437
+ projectRootPath
3438
+ }
3439
+ } ) ;
3440
+ }
3441
+
3442
+ function closeFile ( file : File ) {
3443
+ session . executeCommandSeq < protocol . CloseRequest > ( {
3444
+ command : protocol . CommandTypes . Close ,
3445
+ arguments : {
3446
+ file : file . path
3447
+ }
3448
+ } ) ;
3449
+ }
3450
+ } ) ;
3323
3451
} ) ;
3324
3452
3325
3453
describe ( "tsserverProjectSystem Proper errors" , ( ) => {
0 commit comments