@@ -12,6 +12,7 @@ namespace ts.server {
12
12
13
13
const childProcess : {
14
14
fork ( modulePath : string , args : string [ ] , options ?: { execArgv : string [ ] , env ?: MapLike < string > } ) : NodeChildProcess ;
15
+ execFileSync ( file : string , args : string [ ] , options : { stdio : "ignore" , env : MapLike < string > } ) : string | Buffer ;
15
16
} = require ( "child_process" ) ;
16
17
17
18
const os : {
@@ -59,7 +60,7 @@ namespace ts.server {
59
60
60
61
interface NodeChildProcess {
61
62
send ( message : any , sendHandle ?: any ) : void ;
62
- on ( message : "message" , f : ( m : any ) => void ) : void ;
63
+ on ( message : "message" | "exit" , f : ( m : any ) => void ) : void ;
63
64
kill ( ) : void ;
64
65
pid : number ;
65
66
}
@@ -576,7 +577,84 @@ namespace ts.server {
576
577
}
577
578
}
578
579
580
+ function extractWatchDirectoryCacheKey ( path : string , currentDriveKey : string ) {
581
+ path = normalizeSlashes ( path ) ;
582
+ if ( isUNCPath ( path ) ) {
583
+ // UNC path: extract server name
584
+ // //server/location
585
+ // ^ <- from 0 to this position
586
+ const firstSlash = path . indexOf ( directorySeparator , 2 ) ;
587
+ return firstSlash !== - 1 ? path . substring ( 0 , firstSlash ) . toLowerCase ( ) : path ;
588
+ }
589
+ const rootLength = getRootLength ( path ) ;
590
+ if ( rootLength === 0 ) {
591
+ // relative path - assume file is on the current drive
592
+ return currentDriveKey ;
593
+ }
594
+ if ( path . charCodeAt ( 1 ) === CharacterCodes . colon && path . charCodeAt ( 2 ) === CharacterCodes . slash ) {
595
+ // rooted path that starts with c:/... - extract drive letter
596
+ return path . charAt ( 0 ) . toLowerCase ( ) ;
597
+ }
598
+ if ( path . charCodeAt ( 0 ) === CharacterCodes . slash && path . charCodeAt ( 1 ) !== CharacterCodes . slash ) {
599
+ // rooted path that starts with slash - /somename - use key for current drive
600
+ return currentDriveKey ;
601
+ }
602
+ // do not cache any other cases
603
+ return undefined ;
604
+ }
605
+
606
+ function isUNCPath ( s : string ) : boolean {
607
+ return s . length > 2 && s . charCodeAt ( 0 ) === CharacterCodes . slash && s . charCodeAt ( 1 ) === CharacterCodes . slash ;
608
+ }
609
+
579
610
const sys = < ServerHost > ts . sys ;
611
+ // use watchGuard process on Windows when node version is 4 or later
612
+ const useWatchGuard = process . platform === "win32" && getNodeMajorVersion ( ) >= 4 ;
613
+ if ( useWatchGuard ) {
614
+ const currentDrive = extractWatchDirectoryCacheKey ( sys . resolvePath ( sys . getCurrentDirectory ( ) ) , /*currentDriveKey*/ undefined ) ;
615
+ const statusCache = createMap < boolean > ( ) ;
616
+ const originalWatchDirectory = sys . watchDirectory ;
617
+ sys . watchDirectory = function ( path : string , callback : DirectoryWatcherCallback , recursive ?: boolean ) : FileWatcher {
618
+ const cacheKey = extractWatchDirectoryCacheKey ( path , currentDrive ) ;
619
+ let status = cacheKey && statusCache . get ( cacheKey ) ;
620
+ if ( status === undefined ) {
621
+ if ( logger . hasLevel ( LogLevel . verbose ) ) {
622
+ logger . info ( `${ cacheKey } for path ${ path } not found in cache...` ) ;
623
+ }
624
+ try {
625
+ const args = [ combinePaths ( __dirname , "watchGuard.js" ) , path ] ;
626
+ if ( logger . hasLevel ( LogLevel . verbose ) ) {
627
+ logger . info ( `Starting ${ process . execPath } with args ${ JSON . stringify ( args ) } ` ) ;
628
+ }
629
+ childProcess . execFileSync ( process . execPath , args , { stdio : "ignore" , env : { "ELECTRON_RUN_AS_NODE" : "1" } } ) ;
630
+ status = true ;
631
+ if ( logger . hasLevel ( LogLevel . verbose ) ) {
632
+ logger . info ( `WatchGuard for path ${ path } returned: OK` ) ;
633
+ }
634
+ }
635
+ catch ( e ) {
636
+ status = false ;
637
+ if ( logger . hasLevel ( LogLevel . verbose ) ) {
638
+ logger . info ( `WatchGuard for path ${ path } returned: ${ e . message } ` ) ;
639
+ }
640
+ }
641
+ if ( cacheKey ) {
642
+ statusCache . set ( cacheKey , status ) ;
643
+ }
644
+ }
645
+ else if ( logger . hasLevel ( LogLevel . verbose ) ) {
646
+ logger . info ( `watchDirectory for ${ path } uses cached drive information.` ) ;
647
+ }
648
+ if ( status ) {
649
+ // this drive is safe to use - call real 'watchDirectory'
650
+ return originalWatchDirectory . call ( sys , path , callback , recursive ) ;
651
+ }
652
+ else {
653
+ // this drive is unsafe - return no-op watcher
654
+ return { close ( ) { } } ;
655
+ }
656
+ }
657
+ }
580
658
581
659
// Override sys.write because fs.writeSync is not reliable on Node 4
582
660
sys . write = ( s : string ) => writeMessage ( new Buffer ( s , "utf8" ) ) ;
0 commit comments