@@ -109,12 +109,22 @@ namespace ts.server {
109109 return value instanceof ScriptInfo ;
110110 }
111111
112+ interface GeneratedFileWatcher {
113+ generatedFilePath : Path ;
114+ watcher : FileWatcher ;
115+ }
116+ type GeneratedFileWatcherMap = GeneratedFileWatcher | Map < GeneratedFileWatcher > ;
117+ function isGeneratedFileWatcher ( watch : GeneratedFileWatcherMap ) : watch is GeneratedFileWatcher {
118+ return ( watch as GeneratedFileWatcher ) . generatedFilePath !== undefined ;
119+ }
120+
112121 export abstract class Project implements LanguageServiceHost , ModuleResolutionHost {
113122 private rootFiles : ScriptInfo [ ] = [ ] ;
114123 private rootFilesMap : Map < ProjectRoot > = createMap < ProjectRoot > ( ) ;
115124 private program : Program | undefined ;
116125 private externalFiles : SortedReadonlyArray < string > | undefined ;
117126 private missingFilesMap : Map < FileWatcher > | undefined ;
127+ private generatedFilesMap : GeneratedFileWatcherMap | undefined ;
118128 private plugins : PluginModuleWithName [ ] = [ ] ;
119129
120130 /*@internal */
@@ -568,6 +578,7 @@ namespace ts.server {
568578 this . lastFileExceededProgramSize = lastFileExceededProgramSize ;
569579 this . builderState = undefined ;
570580 this . resolutionCache . closeTypeRootsWatch ( ) ;
581+ this . clearGeneratedFileWatch ( ) ;
571582 this . projectService . onUpdateLanguageServiceStateForProject ( this , /*languageServiceEnabled*/ false ) ;
572583 }
573584
@@ -649,6 +660,7 @@ namespace ts.server {
649660 clearMap ( this . missingFilesMap , closeFileWatcher ) ;
650661 this . missingFilesMap = undefined ! ;
651662 }
663+ this . clearGeneratedFileWatch ( ) ;
652664
653665 // signal language service to release source files acquired from document registry
654666 this . languageService . dispose ( ) ;
@@ -942,6 +954,39 @@ namespace ts.server {
942954 missingFilePath => this . addMissingFileWatcher ( missingFilePath )
943955 ) ;
944956
957+ if ( this . generatedFilesMap ) {
958+ const outPath = this . compilerOptions . outFile && this . compilerOptions . out ;
959+ if ( isGeneratedFileWatcher ( this . generatedFilesMap ) ) {
960+ // --out
961+ if ( ! outPath || ! this . isValidGeneratedFileWatcher (
962+ removeFileExtension ( outPath ) + Extension . Dts ,
963+ this . generatedFilesMap ,
964+ ) ) {
965+ this . clearGeneratedFileWatch ( ) ;
966+ }
967+ }
968+ else {
969+ // MultiFile
970+ if ( outPath ) {
971+ this . clearGeneratedFileWatch ( ) ;
972+ }
973+ else {
974+ this . generatedFilesMap . forEach ( ( watcher , source ) => {
975+ const sourceFile = this . program ! . getSourceFileByPath ( source as Path ) ;
976+ if ( ! sourceFile ||
977+ sourceFile . resolvedPath !== source ||
978+ ! this . isValidGeneratedFileWatcher (
979+ getDeclarationEmitOutputFilePathWorker ( sourceFile . fileName , this . compilerOptions , this . currentDirectory , this . program ! . getCommonSourceDirectory ( ) , this . getCanonicalFileName ) ,
980+ watcher
981+ ) ) {
982+ closeFileWatcherOf ( watcher ) ;
983+ ( this . generatedFilesMap as Map < GeneratedFileWatcher > ) . delete ( source ) ;
984+ }
985+ } ) ;
986+ }
987+ }
988+ }
989+
945990 // Watch the type locations that would be added to program as part of automatic type resolutions
946991 if ( this . languageServiceEnabled ) {
947992 this . resolutionCache . updateTypeRootsWatch ( ) ;
@@ -1006,6 +1051,61 @@ namespace ts.server {
10061051 return ! ! this . missingFilesMap && this . missingFilesMap . has ( path ) ;
10071052 }
10081053
1054+ /* @internal */
1055+ addGeneratedFileWatch ( generatedFile : string , sourceFile : string ) {
1056+ if ( this . compilerOptions . outFile || this . compilerOptions . out ) {
1057+ // Single watcher
1058+ if ( ! this . generatedFilesMap ) {
1059+ this . generatedFilesMap = this . createGeneratedFileWatcher ( generatedFile ) ;
1060+ }
1061+ }
1062+ else {
1063+ // Map
1064+ const path = this . toPath ( sourceFile ) ;
1065+ if ( this . generatedFilesMap ) {
1066+ if ( isGeneratedFileWatcher ( this . generatedFilesMap ) ) {
1067+ Debug . fail ( `${ this . projectName } Expected not to have --out watcher for generated file with options: ${ JSON . stringify ( this . compilerOptions ) } ` ) ;
1068+ return ;
1069+ }
1070+ if ( this . generatedFilesMap . has ( path ) ) return ;
1071+ }
1072+ else {
1073+ this . generatedFilesMap = createMap ( ) ;
1074+ }
1075+ this . generatedFilesMap . set ( path , this . createGeneratedFileWatcher ( generatedFile ) ) ;
1076+ }
1077+ }
1078+
1079+ private createGeneratedFileWatcher ( generatedFile : string ) : GeneratedFileWatcher {
1080+ return {
1081+ generatedFilePath : this . toPath ( generatedFile ) ,
1082+ watcher : this . projectService . watchFactory . watchFile (
1083+ this . projectService . host ,
1084+ generatedFile ,
1085+ ( ) => this . projectService . delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles ( this ) ,
1086+ PollingInterval . High ,
1087+ WatchType . MissingGeneratedFile ,
1088+ this
1089+ )
1090+ } ;
1091+ }
1092+
1093+ private isValidGeneratedFileWatcher ( generateFile : string , watcher : GeneratedFileWatcher ) {
1094+ return this . toPath ( generateFile ) === watcher . generatedFilePath ;
1095+ }
1096+
1097+ private clearGeneratedFileWatch ( ) {
1098+ if ( this . generatedFilesMap ) {
1099+ if ( isGeneratedFileWatcher ( this . generatedFilesMap ) ) {
1100+ closeFileWatcherOf ( this . generatedFilesMap ) ;
1101+ }
1102+ else {
1103+ clearMap ( this . generatedFilesMap , closeFileWatcherOf ) ;
1104+ }
1105+ this . generatedFilesMap = undefined ;
1106+ }
1107+ }
1108+
10091109 getScriptInfoForNormalizedPath ( fileName : NormalizedPath ) : ScriptInfo | undefined {
10101110 const scriptInfo = this . projectService . getScriptInfoForPath ( this . toPath ( fileName ) ) ;
10111111 if ( scriptInfo && ! scriptInfo . isAttached ( this ) ) {
0 commit comments