@@ -420,6 +420,9 @@ namespace ts {
420420 }
421421 }
422422
423+ const intialVersion = 1 ;
424+ const intialVersionString = "1" ;
425+
423426 /**
424427 * Creates the watch from the host for root files and compiler options
425428 */
@@ -429,19 +432,25 @@ namespace ts {
429432 */
430433 export function createWatchProgram < T extends BuilderProgram > ( host : WatchCompilerHostOfConfigFile < T > ) : WatchOfConfigFile < T > ;
431434 export function createWatchProgram < T extends BuilderProgram > ( host : WatchCompilerHostOfFilesAndCompilerOptions < T > & WatchCompilerHostOfConfigFile < T > ) : WatchOfFilesAndCompilerOptions < T > | WatchOfConfigFile < T > {
432- interface HostFileInfo {
435+ interface FilePresentOnHost {
433436 version : number ;
434437 sourceFile : SourceFile ;
435438 fileWatcher : FileWatcher ;
436439 }
440+ type FileMissingOnHost = string ;
441+ interface FilePresenceUnknownOnHost {
442+ version : number ;
443+ }
444+ type FileMayBePresentOnHost = FilePresentOnHost | FilePresenceUnknownOnHost ;
445+ type HostFileInfo = FilePresentOnHost | FileMissingOnHost | FilePresenceUnknownOnHost ;
437446
438447 let builderProgram : T ;
439448 let reloadLevel : ConfigFileProgramReloadLevel ; // level to indicate if the program needs to be reloaded from config file/just filenames etc
440449 let missingFilesMap : Map < FileWatcher > ; // Map of file watchers for the missing files
441450 let watchedWildcardDirectories : Map < WildcardDirectoryWatcher > ; // map of watchers for the wild card directories in the config file
442451 let timerToUpdateProgram : any ; // timer callback to recompile the program
443452
444- const sourceFilesCache = createMap < HostFileInfo | string > ( ) ; // Cache that stores the source file and version info
453+ const sourceFilesCache = createMap < HostFileInfo > ( ) ; // Cache that stores the source file and version info
445454 let missingFilePathsRequestedForRelease : Path [ ] ; // These paths are held temparirly so that we can remove the entry from source file cache if the file is not tracked by missing files
446455 let hasChangedCompilerOptions = false ; // True if the compiler options have changed between compilations
447456 let hasChangedAutomaticTypeDirectiveNames = false ; // True if the automatic type directives have changed
@@ -627,11 +636,20 @@ namespace ts {
627636 return ts . toPath ( fileName , currentDirectory , getCanonicalFileName ) ;
628637 }
629638
639+ function isFileMissingOnHost ( hostSourceFile : HostFileInfo ) : hostSourceFile is FileMissingOnHost {
640+ return isString ( hostSourceFile ) ;
641+ }
642+
643+ function isFilePresentOnHost ( hostSourceFile : FileMayBePresentOnHost ) : hostSourceFile is FilePresentOnHost {
644+ return ! ! ( hostSourceFile as FilePresentOnHost ) . sourceFile ;
645+ }
646+
630647 function fileExists ( fileName : string ) {
631648 const path = toPath ( fileName ) ;
632- const hostSourceFileInfo = sourceFilesCache . get ( path ) ;
633- if ( hostSourceFileInfo !== undefined ) {
634- return ! isString ( hostSourceFileInfo ) ;
649+ // If file is missing on host from cache, we can definitely say file doesnt exist
650+ // otherwise we need to ensure from the disk
651+ if ( isFileMissingOnHost ( sourceFilesCache . get ( path ) ) ) {
652+ return true ;
635653 }
636654
637655 return directoryStructureHost . fileExists ( fileName ) ;
@@ -640,39 +658,42 @@ namespace ts {
640658 function getVersionedSourceFileByPath ( fileName : string , path : Path , languageVersion : ScriptTarget , onError ?: ( message : string ) => void , shouldCreateNewSourceFile ?: boolean ) : SourceFile {
641659 const hostSourceFile = sourceFilesCache . get ( path ) ;
642660 // No source file on the host
643- if ( isString ( hostSourceFile ) ) {
661+ if ( isFileMissingOnHost ( hostSourceFile ) ) {
644662 return undefined ;
645663 }
646664
647665 // Create new source file if requested or the versions dont match
648- if ( ! hostSourceFile || shouldCreateNewSourceFile || hostSourceFile . version . toString ( ) !== hostSourceFile . sourceFile . version ) {
666+ if ( ! hostSourceFile || shouldCreateNewSourceFile || ! isFilePresentOnHost ( hostSourceFile ) || hostSourceFile . version . toString ( ) !== hostSourceFile . sourceFile . version ) {
649667 const sourceFile = getNewSourceFile ( ) ;
650668 if ( hostSourceFile ) {
651669 if ( shouldCreateNewSourceFile ) {
652670 hostSourceFile . version ++ ;
653671 }
672+
654673 if ( sourceFile ) {
655- hostSourceFile . sourceFile = sourceFile ;
674+ // Set the source file and create file watcher now that file was present on the disk
675+ ( hostSourceFile as FilePresentOnHost ) . sourceFile = sourceFile ;
656676 sourceFile . version = hostSourceFile . version . toString ( ) ;
657- if ( ! hostSourceFile . fileWatcher ) {
658- hostSourceFile . fileWatcher = watchFilePath ( host , fileName , onSourceFileChange , path , writeLog ) ;
677+ if ( ! ( hostSourceFile as FilePresentOnHost ) . fileWatcher ) {
678+ ( hostSourceFile as FilePresentOnHost ) . fileWatcher = watchFilePath ( host , fileName , onSourceFileChange , path , writeLog ) ;
659679 }
660680 }
661681 else {
662682 // There is no source file on host any more, close the watch, missing file paths will track it
663- hostSourceFile . fileWatcher . close ( ) ;
683+ if ( isFilePresentOnHost ( hostSourceFile ) ) {
684+ hostSourceFile . fileWatcher . close ( ) ;
685+ }
664686 sourceFilesCache . set ( path , hostSourceFile . version . toString ( ) ) ;
665687 }
666688 }
667689 else {
668- let fileWatcher : FileWatcher ;
669690 if ( sourceFile ) {
670- sourceFile . version = "1" ;
671- fileWatcher = watchFilePath ( host , fileName , onSourceFileChange , path , writeLog ) ;
672- sourceFilesCache . set ( path , { sourceFile, version : 1 , fileWatcher } ) ;
691+ sourceFile . version = intialVersionString ;
692+ const fileWatcher = watchFilePath ( host , fileName , onSourceFileChange , path , writeLog ) ;
693+ sourceFilesCache . set ( path , { sourceFile, version : intialVersion , fileWatcher } ) ;
673694 }
674695 else {
675- sourceFilesCache . set ( path , "0" ) ;
696+ sourceFilesCache . set ( path , intialVersionString ) ;
676697 }
677698 }
678699 return sourceFile ;
@@ -697,20 +718,22 @@ namespace ts {
697718 }
698719 }
699720
700- function removeSourceFile ( path : Path ) {
721+ function nextSourceFileVersion ( path : Path ) {
701722 const hostSourceFile = sourceFilesCache . get ( path ) ;
702723 if ( hostSourceFile !== undefined ) {
703- if ( ! isString ( hostSourceFile ) ) {
704- hostSourceFile . fileWatcher . close ( ) ;
705- resolutionCache . invalidateResolutionOfFile ( path ) ;
724+ if ( isFileMissingOnHost ( hostSourceFile ) ) {
725+ // The next version, lets set it as presence unknown file
726+ sourceFilesCache . set ( path , { version : Number ( hostSourceFile ) + 1 } ) ;
727+ }
728+ else {
729+ hostSourceFile . version ++ ;
706730 }
707- sourceFilesCache . delete ( path ) ;
708731 }
709732 }
710733
711734 function getSourceVersion ( path : Path ) : string {
712735 const hostSourceFile = sourceFilesCache . get ( path ) ;
713- return ! hostSourceFile || isString ( hostSourceFile ) ? undefined : hostSourceFile . version . toString ( ) ;
736+ return ! hostSourceFile || isFileMissingOnHost ( hostSourceFile ) ? undefined : hostSourceFile . version . toString ( ) ;
714737 }
715738
716739 function onReleaseOldSourceFile ( oldSourceFile : SourceFile , _oldOptions : CompilerOptions ) {
@@ -721,10 +744,10 @@ namespace ts {
721744 // there was version update and new source file was created.
722745 if ( hostSourceFileInfo ) {
723746 // record the missing file paths so they can be removed later if watchers arent tracking them
724- if ( isString ( hostSourceFileInfo ) ) {
747+ if ( isFileMissingOnHost ( hostSourceFileInfo ) ) {
725748 ( missingFilePathsRequestedForRelease || ( missingFilePathsRequestedForRelease = [ ] ) ) . push ( oldSourceFile . path ) ;
726749 }
727- else if ( hostSourceFileInfo . sourceFile === oldSourceFile ) {
750+ else if ( ( hostSourceFileInfo as FilePresentOnHost ) . sourceFile === oldSourceFile ) {
728751 sourceFilesCache . delete ( oldSourceFile . path ) ;
729752 resolutionCache . removeResolutionsOfFile ( oldSourceFile . path ) ;
730753 }
@@ -808,27 +831,12 @@ namespace ts {
808831
809832 function onSourceFileChange ( fileName : string , eventKind : FileWatcherEventKind , path : Path ) {
810833 updateCachedSystemWithFile ( fileName , path , eventKind ) ;
811- const hostSourceFile = sourceFilesCache . get ( path ) ;
812- if ( hostSourceFile ) {
813- // Update the cache
814- if ( eventKind === FileWatcherEventKind . Deleted ) {
815- resolutionCache . invalidateResolutionOfFile ( path ) ;
816- if ( ! isString ( hostSourceFile ) ) {
817- hostSourceFile . fileWatcher . close ( ) ;
818- sourceFilesCache . set ( path , ( ++ hostSourceFile . version ) . toString ( ) ) ;
819- }
820- }
821- else {
822- // Deleted file created
823- if ( isString ( hostSourceFile ) ) {
824- sourceFilesCache . delete ( path ) ;
825- }
826- else {
827- // file changed - just update the version
828- hostSourceFile . version ++ ;
829- }
830- }
834+
835+ // Update the source file cache
836+ if ( eventKind === FileWatcherEventKind . Deleted && sourceFilesCache . get ( path ) ) {
837+ resolutionCache . invalidateResolutionOfFile ( path ) ;
831838 }
839+ nextSourceFileVersion ( path ) ;
832840
833841 // Update the program
834842 scheduleProgramUpdate ( ) ;
@@ -856,7 +864,7 @@ namespace ts {
856864 missingFilesMap . delete ( missingFilePath ) ;
857865
858866 // Delete the entry in the source files cache so that new source file is created
859- removeSourceFile ( missingFilePath ) ;
867+ nextSourceFileVersion ( missingFilePath ) ;
860868
861869 // When a missing file is created, we should update the graph.
862870 scheduleProgramUpdate ( ) ;
@@ -885,17 +893,10 @@ namespace ts {
885893 const fileOrDirectoryPath = toPath ( fileOrDirectory ) ;
886894
887895 // Since the file existance changed, update the sourceFiles cache
888- const result = cachedDirectoryStructureHost && cachedDirectoryStructureHost . addOrDeleteFileOrDirectory ( fileOrDirectory , fileOrDirectoryPath ) ;
889-
890- // Instead of deleting the file, mark it as changed instead
891- // Many times node calls add/remove/file when watching directories recursively
892- const hostSourceFile = sourceFilesCache . get ( fileOrDirectoryPath ) ;
893- if ( hostSourceFile && ! isString ( hostSourceFile ) && ( result ? result . fileExists : directoryStructureHost . fileExists ( fileOrDirectory ) ) ) {
894- hostSourceFile . version ++ ;
895- }
896- else {
897- removeSourceFile ( fileOrDirectoryPath ) ;
896+ if ( cachedDirectoryStructureHost ) {
897+ cachedDirectoryStructureHost . addOrDeleteFileOrDirectory ( fileOrDirectory , fileOrDirectoryPath ) ;
898898 }
899+ nextSourceFileVersion ( fileOrDirectoryPath ) ;
899900
900901 // If the the added or created file or directory is not supported file name, ignore the file
901902 // But when watched directory is added/removed, we need to reload the file list
0 commit comments