@@ -109,12 +109,22 @@ namespace ts.server {
109
109
return value instanceof ScriptInfo ;
110
110
}
111
111
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
+
112
121
export abstract class Project implements LanguageServiceHost , ModuleResolutionHost {
113
122
private rootFiles : ScriptInfo [ ] = [ ] ;
114
123
private rootFilesMap : Map < ProjectRoot > = createMap < ProjectRoot > ( ) ;
115
124
private program : Program | undefined ;
116
125
private externalFiles : SortedReadonlyArray < string > | undefined ;
117
126
private missingFilesMap : Map < FileWatcher > | undefined ;
127
+ private generatedFilesMap : GeneratedFileWatcherMap | undefined ;
118
128
private plugins : PluginModuleWithName [ ] = [ ] ;
119
129
120
130
/*@internal */
@@ -568,6 +578,7 @@ namespace ts.server {
568
578
this . lastFileExceededProgramSize = lastFileExceededProgramSize ;
569
579
this . builderState = undefined ;
570
580
this . resolutionCache . closeTypeRootsWatch ( ) ;
581
+ this . clearGeneratedFileWatch ( ) ;
571
582
this . projectService . onUpdateLanguageServiceStateForProject ( this , /*languageServiceEnabled*/ false ) ;
572
583
}
573
584
@@ -649,6 +660,7 @@ namespace ts.server {
649
660
clearMap ( this . missingFilesMap , closeFileWatcher ) ;
650
661
this . missingFilesMap = undefined ! ;
651
662
}
663
+ this . clearGeneratedFileWatch ( ) ;
652
664
653
665
// signal language service to release source files acquired from document registry
654
666
this . languageService . dispose ( ) ;
@@ -942,6 +954,39 @@ namespace ts.server {
942
954
missingFilePath => this . addMissingFileWatcher ( missingFilePath )
943
955
) ;
944
956
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
+
945
990
// Watch the type locations that would be added to program as part of automatic type resolutions
946
991
if ( this . languageServiceEnabled ) {
947
992
this . resolutionCache . updateTypeRootsWatch ( ) ;
@@ -1006,6 +1051,61 @@ namespace ts.server {
1006
1051
return ! ! this . missingFilesMap && this . missingFilesMap . has ( path ) ;
1007
1052
}
1008
1053
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
+
1009
1109
getScriptInfoForNormalizedPath ( fileName : NormalizedPath ) : ScriptInfo | undefined {
1010
1110
const scriptInfo = this . projectService . getScriptInfoForPath ( this . toPath ( fileName ) ) ;
1011
1111
if ( scriptInfo && ! scriptInfo . isAttached ( this ) ) {
0 commit comments