@@ -420,6 +420,8 @@ namespace ts {
420420 }
421421 }
422422
423+ const initialVersion = 1 ;
424+
423425 /**
424426 * Creates the watch from the host for root files and compiler options
425427 */
@@ -429,19 +431,25 @@ namespace ts {
429431 */
430432 export function createWatchProgram < T extends BuilderProgram > ( host : WatchCompilerHostOfConfigFile < T > ) : WatchOfConfigFile < T > ;
431433 export function createWatchProgram < T extends BuilderProgram > ( host : WatchCompilerHostOfFilesAndCompilerOptions < T > & WatchCompilerHostOfConfigFile < T > ) : WatchOfFilesAndCompilerOptions < T > | WatchOfConfigFile < T > {
432- interface HostFileInfo {
434+ interface FilePresentOnHost {
433435 version : number ;
434436 sourceFile : SourceFile ;
435437 fileWatcher : FileWatcher ;
436438 }
439+ type FileMissingOnHost = number ;
440+ interface FilePresenceUnknownOnHost {
441+ version : number ;
442+ }
443+ type FileMayBePresentOnHost = FilePresentOnHost | FilePresenceUnknownOnHost ;
444+ type HostFileInfo = FilePresentOnHost | FileMissingOnHost | FilePresenceUnknownOnHost ;
437445
438446 let builderProgram : T ;
439447 let reloadLevel : ConfigFileProgramReloadLevel ; // level to indicate if the program needs to be reloaded from config file/just filenames etc
440448 let missingFilesMap : Map < FileWatcher > ; // Map of file watchers for the missing files
441449 let watchedWildcardDirectories : Map < WildcardDirectoryWatcher > ; // map of watchers for the wild card directories in the config file
442450 let timerToUpdateProgram : any ; // timer callback to recompile the program
443451
444- const sourceFilesCache = createMap < HostFileInfo | string > ( ) ; // Cache that stores the source file and version info
452+ const sourceFilesCache = createMap < HostFileInfo > ( ) ; // Cache that stores the source file and version info
445453 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
446454 let hasChangedCompilerOptions = false ; // True if the compiler options have changed between compilations
447455 let hasChangedAutomaticTypeDirectiveNames = false ; // True if the automatic type directives have changed
@@ -480,14 +488,14 @@ namespace ts {
480488 const watchFilePath = compilerOptions . extendedDiagnostics ? ts . addFilePathWatcherWithLogging : ts . addFilePathWatcher ;
481489 const watchDirectoryWorker = compilerOptions . extendedDiagnostics ? ts . addDirectoryWatcherWithLogging : ts . addDirectoryWatcher ;
482490
491+ const getCanonicalFileName = createGetCanonicalFileName ( useCaseSensitiveFileNames ) ;
492+ let newLine = updateNewLine ( ) ;
493+
483494 writeLog ( `Current directory: ${ currentDirectory } CaseSensitiveFileNames: ${ useCaseSensitiveFileNames } ` ) ;
484495 if ( configFileName ) {
485496 watchFile ( host , configFileName , scheduleProgramReload , writeLog ) ;
486497 }
487498
488- const getCanonicalFileName = createGetCanonicalFileName ( useCaseSensitiveFileNames ) ;
489- let newLine = updateNewLine ( ) ;
490-
491499 const compilerHost : CompilerHost & ResolutionCacheHost = {
492500 // Members for CompilerHost
493501 getSourceFile : ( fileName , languageVersion , onError ?, shouldCreateNewSourceFile ?) => getVersionedSourceFileByPath ( fileName , toPath ( fileName ) , languageVersion , onError , shouldCreateNewSourceFile ) ,
@@ -575,7 +583,9 @@ namespace ts {
575583
576584 // Compile the program
577585 if ( loggingEnabled ) {
578- writeLog ( `CreatingProgramWith::\n roots: ${ JSON . stringify ( rootFileNames ) } \n options: ${ JSON . stringify ( compilerOptions ) } ` ) ;
586+ writeLog ( `CreatingProgramWith::` ) ;
587+ writeLog ( ` roots: ${ JSON . stringify ( rootFileNames ) } ` ) ;
588+ writeLog ( ` options: ${ JSON . stringify ( compilerOptions ) } ` ) ;
579589 }
580590
581591 const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || ! program ;
@@ -627,11 +637,20 @@ namespace ts {
627637 return ts . toPath ( fileName , currentDirectory , getCanonicalFileName ) ;
628638 }
629639
640+ function isFileMissingOnHost ( hostSourceFile : HostFileInfo ) : hostSourceFile is FileMissingOnHost {
641+ return typeof hostSourceFile === "number" ;
642+ }
643+
644+ function isFilePresentOnHost ( hostSourceFile : FileMayBePresentOnHost ) : hostSourceFile is FilePresentOnHost {
645+ return ! ! ( hostSourceFile as FilePresentOnHost ) . sourceFile ;
646+ }
647+
630648 function fileExists ( fileName : string ) {
631649 const path = toPath ( fileName ) ;
632- const hostSourceFileInfo = sourceFilesCache . get ( path ) ;
633- if ( hostSourceFileInfo !== undefined ) {
634- return ! isString ( hostSourceFileInfo ) ;
650+ // If file is missing on host from cache, we can definitely say file doesnt exist
651+ // otherwise we need to ensure from the disk
652+ if ( isFileMissingOnHost ( sourceFilesCache . get ( path ) ) ) {
653+ return true ;
635654 }
636655
637656 return directoryStructureHost . fileExists ( fileName ) ;
@@ -640,39 +659,42 @@ namespace ts {
640659 function getVersionedSourceFileByPath ( fileName : string , path : Path , languageVersion : ScriptTarget , onError ?: ( message : string ) => void , shouldCreateNewSourceFile ?: boolean ) : SourceFile {
641660 const hostSourceFile = sourceFilesCache . get ( path ) ;
642661 // No source file on the host
643- if ( isString ( hostSourceFile ) ) {
662+ if ( isFileMissingOnHost ( hostSourceFile ) ) {
644663 return undefined ;
645664 }
646665
647666 // Create new source file if requested or the versions dont match
648- if ( ! hostSourceFile || shouldCreateNewSourceFile || hostSourceFile . version . toString ( ) !== hostSourceFile . sourceFile . version ) {
667+ if ( ! hostSourceFile || shouldCreateNewSourceFile || ! isFilePresentOnHost ( hostSourceFile ) || hostSourceFile . version . toString ( ) !== hostSourceFile . sourceFile . version ) {
649668 const sourceFile = getNewSourceFile ( ) ;
650669 if ( hostSourceFile ) {
651670 if ( shouldCreateNewSourceFile ) {
652671 hostSourceFile . version ++ ;
653672 }
673+
654674 if ( sourceFile ) {
655- hostSourceFile . sourceFile = sourceFile ;
675+ // Set the source file and create file watcher now that file was present on the disk
676+ ( hostSourceFile as FilePresentOnHost ) . sourceFile = sourceFile ;
656677 sourceFile . version = hostSourceFile . version . toString ( ) ;
657- if ( ! hostSourceFile . fileWatcher ) {
658- hostSourceFile . fileWatcher = watchFilePath ( host , fileName , onSourceFileChange , path , writeLog ) ;
678+ if ( ! ( hostSourceFile as FilePresentOnHost ) . fileWatcher ) {
679+ ( hostSourceFile as FilePresentOnHost ) . fileWatcher = watchFilePath ( host , fileName , onSourceFileChange , path , writeLog ) ;
659680 }
660681 }
661682 else {
662683 // There is no source file on host any more, close the watch, missing file paths will track it
663- hostSourceFile . fileWatcher . close ( ) ;
664- sourceFilesCache . set ( path , hostSourceFile . version . toString ( ) ) ;
684+ if ( isFilePresentOnHost ( hostSourceFile ) ) {
685+ hostSourceFile . fileWatcher . close ( ) ;
686+ }
687+ sourceFilesCache . set ( path , hostSourceFile . version ) ;
665688 }
666689 }
667690 else {
668- let fileWatcher : FileWatcher ;
669691 if ( sourceFile ) {
670- sourceFile . version = "1" ;
671- fileWatcher = watchFilePath ( host , fileName , onSourceFileChange , path , writeLog ) ;
672- sourceFilesCache . set ( path , { sourceFile, version : 1 , fileWatcher } ) ;
692+ sourceFile . version = initialVersion . toString ( ) ;
693+ const fileWatcher = watchFilePath ( host , fileName , onSourceFileChange , path , writeLog ) ;
694+ sourceFilesCache . set ( path , { sourceFile, version : initialVersion , fileWatcher } ) ;
673695 }
674696 else {
675- sourceFilesCache . set ( path , "0" ) ;
697+ sourceFilesCache . set ( path , initialVersion ) ;
676698 }
677699 }
678700 return sourceFile ;
@@ -697,20 +719,22 @@ namespace ts {
697719 }
698720 }
699721
700- function removeSourceFile ( path : Path ) {
722+ function nextSourceFileVersion ( path : Path ) {
701723 const hostSourceFile = sourceFilesCache . get ( path ) ;
702724 if ( hostSourceFile !== undefined ) {
703- if ( ! isString ( hostSourceFile ) ) {
704- hostSourceFile . fileWatcher . close ( ) ;
705- resolutionCache . invalidateResolutionOfFile ( path ) ;
725+ if ( isFileMissingOnHost ( hostSourceFile ) ) {
726+ // The next version, lets set it as presence unknown file
727+ sourceFilesCache . set ( path , { version : Number ( hostSourceFile ) + 1 } ) ;
728+ }
729+ else {
730+ hostSourceFile . version ++ ;
706731 }
707- sourceFilesCache . delete ( path ) ;
708732 }
709733 }
710734
711735 function getSourceVersion ( path : Path ) : string {
712736 const hostSourceFile = sourceFilesCache . get ( path ) ;
713- return ! hostSourceFile || isString ( hostSourceFile ) ? undefined : hostSourceFile . version . toString ( ) ;
737+ return ! hostSourceFile || isFileMissingOnHost ( hostSourceFile ) ? undefined : hostSourceFile . version . toString ( ) ;
714738 }
715739
716740 function onReleaseOldSourceFile ( oldSourceFile : SourceFile , _oldOptions : CompilerOptions ) {
@@ -721,10 +745,10 @@ namespace ts {
721745 // there was version update and new source file was created.
722746 if ( hostSourceFileInfo ) {
723747 // record the missing file paths so they can be removed later if watchers arent tracking them
724- if ( isString ( hostSourceFileInfo ) ) {
748+ if ( isFileMissingOnHost ( hostSourceFileInfo ) ) {
725749 ( missingFilePathsRequestedForRelease || ( missingFilePathsRequestedForRelease = [ ] ) ) . push ( oldSourceFile . path ) ;
726750 }
727- else if ( hostSourceFileInfo . sourceFile === oldSourceFile ) {
751+ else if ( ( hostSourceFileInfo as FilePresentOnHost ) . sourceFile === oldSourceFile ) {
728752 sourceFilesCache . delete ( oldSourceFile . path ) ;
729753 resolutionCache . removeResolutionsOfFile ( oldSourceFile . path ) ;
730754 }
@@ -808,27 +832,12 @@ namespace ts {
808832
809833 function onSourceFileChange ( fileName : string , eventKind : FileWatcherEventKind , path : Path ) {
810834 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- }
835+
836+ // Update the source file cache
837+ if ( eventKind === FileWatcherEventKind . Deleted && sourceFilesCache . get ( path ) ) {
838+ resolutionCache . invalidateResolutionOfFile ( path ) ;
831839 }
840+ nextSourceFileVersion ( path ) ;
832841
833842 // Update the program
834843 scheduleProgramUpdate ( ) ;
@@ -856,7 +865,7 @@ namespace ts {
856865 missingFilesMap . delete ( missingFilePath ) ;
857866
858867 // Delete the entry in the source files cache so that new source file is created
859- removeSourceFile ( missingFilePath ) ;
868+ nextSourceFileVersion ( missingFilePath ) ;
860869
861870 // When a missing file is created, we should update the graph.
862871 scheduleProgramUpdate ( ) ;
@@ -885,17 +894,10 @@ namespace ts {
885894 const fileOrDirectoryPath = toPath ( fileOrDirectory ) ;
886895
887896 // 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 ) ;
897+ if ( cachedDirectoryStructureHost ) {
898+ cachedDirectoryStructureHost . addOrDeleteFileOrDirectory ( fileOrDirectory , fileOrDirectoryPath ) ;
898899 }
900+ nextSourceFileVersion ( fileOrDirectoryPath ) ;
899901
900902 // If the the added or created file or directory is not supported file name, ignore the file
901903 // But when watched directory is added/removed, we need to reload the file list
0 commit comments