@@ -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 */
@@ -573,6 +583,7 @@ namespace ts.server {
573
583
this . lastFileExceededProgramSize = lastFileExceededProgramSize ;
574
584
this . builderState = undefined ;
575
585
this . resolutionCache . closeTypeRootsWatch ( ) ;
586
+ this . clearGeneratedFileWatch ( ) ;
576
587
this . projectService . onUpdateLanguageServiceStateForProject ( this , /*languageServiceEnabled*/ false ) ;
577
588
}
578
589
@@ -654,6 +665,7 @@ namespace ts.server {
654
665
clearMap ( this . missingFilesMap , closeFileWatcher ) ;
655
666
this . missingFilesMap = undefined ! ;
656
667
}
668
+ this . clearGeneratedFileWatch ( ) ;
657
669
658
670
// signal language service to release source files acquired from document registry
659
671
this . languageService . dispose ( ) ;
@@ -947,6 +959,39 @@ namespace ts.server {
947
959
missingFilePath => this . addMissingFileWatcher ( missingFilePath )
948
960
) ;
949
961
962
+ if ( this . generatedFilesMap ) {
963
+ const outPath = this . compilerOptions . outFile && this . compilerOptions . out ;
964
+ if ( isGeneratedFileWatcher ( this . generatedFilesMap ) ) {
965
+ // --out
966
+ if ( ! outPath || ! this . isValidGeneratedFileWatcher (
967
+ removeFileExtension ( outPath ) + Extension . Dts ,
968
+ this . generatedFilesMap ,
969
+ ) ) {
970
+ this . clearGeneratedFileWatch ( ) ;
971
+ }
972
+ }
973
+ else {
974
+ // MultiFile
975
+ if ( outPath ) {
976
+ this . clearGeneratedFileWatch ( ) ;
977
+ }
978
+ else {
979
+ this . generatedFilesMap . forEach ( ( watcher , source ) => {
980
+ const sourceFile = this . program ! . getSourceFileByPath ( source as Path ) ;
981
+ if ( ! sourceFile ||
982
+ sourceFile . resolvedPath !== source ||
983
+ ! this . isValidGeneratedFileWatcher (
984
+ getDeclarationEmitOutputFilePathWorker ( sourceFile . fileName , this . compilerOptions , this . currentDirectory , this . program ! . getCommonSourceDirectory ( ) , this . getCanonicalFileName ) ,
985
+ watcher
986
+ ) ) {
987
+ closeFileWatcherOf ( watcher ) ;
988
+ ( this . generatedFilesMap as Map < GeneratedFileWatcher > ) . delete ( source ) ;
989
+ }
990
+ } ) ;
991
+ }
992
+ }
993
+ }
994
+
950
995
// Watch the type locations that would be added to program as part of automatic type resolutions
951
996
if ( this . languageServiceEnabled ) {
952
997
this . resolutionCache . updateTypeRootsWatch ( ) ;
@@ -1011,6 +1056,61 @@ namespace ts.server {
1011
1056
return ! ! this . missingFilesMap && this . missingFilesMap . has ( path ) ;
1012
1057
}
1013
1058
1059
+ /* @internal */
1060
+ addGeneratedFileWatch ( generatedFile : string , sourceFile : string ) {
1061
+ if ( this . compilerOptions . outFile || this . compilerOptions . out ) {
1062
+ // Single watcher
1063
+ if ( ! this . generatedFilesMap ) {
1064
+ this . generatedFilesMap = this . createGeneratedFileWatcher ( generatedFile ) ;
1065
+ }
1066
+ }
1067
+ else {
1068
+ // Map
1069
+ const path = this . toPath ( sourceFile ) ;
1070
+ if ( this . generatedFilesMap ) {
1071
+ if ( isGeneratedFileWatcher ( this . generatedFilesMap ) ) {
1072
+ Debug . fail ( `${ this . projectName } Expected not to have --out watcher for generated file with options: ${ JSON . stringify ( this . compilerOptions ) } ` ) ;
1073
+ return ;
1074
+ }
1075
+ if ( this . generatedFilesMap . has ( path ) ) return ;
1076
+ }
1077
+ else {
1078
+ this . generatedFilesMap = createMap ( ) ;
1079
+ }
1080
+ this . generatedFilesMap . set ( path , this . createGeneratedFileWatcher ( generatedFile ) ) ;
1081
+ }
1082
+ }
1083
+
1084
+ private createGeneratedFileWatcher ( generatedFile : string ) : GeneratedFileWatcher {
1085
+ return {
1086
+ generatedFilePath : this . toPath ( generatedFile ) ,
1087
+ watcher : this . projectService . watchFactory . watchFile (
1088
+ this . projectService . host ,
1089
+ generatedFile ,
1090
+ ( ) => this . projectService . delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles ( this ) ,
1091
+ PollingInterval . High ,
1092
+ WatchType . MissingGeneratedFile ,
1093
+ this
1094
+ )
1095
+ } ;
1096
+ }
1097
+
1098
+ private isValidGeneratedFileWatcher ( generateFile : string , watcher : GeneratedFileWatcher ) {
1099
+ return this . toPath ( generateFile ) === watcher . generatedFilePath ;
1100
+ }
1101
+
1102
+ private clearGeneratedFileWatch ( ) {
1103
+ if ( this . generatedFilesMap ) {
1104
+ if ( isGeneratedFileWatcher ( this . generatedFilesMap ) ) {
1105
+ closeFileWatcherOf ( this . generatedFilesMap ) ;
1106
+ }
1107
+ else {
1108
+ clearMap ( this . generatedFilesMap , closeFileWatcherOf ) ;
1109
+ }
1110
+ this . generatedFilesMap = undefined ;
1111
+ }
1112
+ }
1113
+
1014
1114
getScriptInfoForNormalizedPath ( fileName : NormalizedPath ) : ScriptInfo | undefined {
1015
1115
const scriptInfo = this . projectService . getScriptInfoForPath ( this . toPath ( fileName ) ) ;
1016
1116
if ( scriptInfo && ! scriptInfo . isAttached ( this ) ) {
0 commit comments