Skip to content

Commit 36c29cb

Browse files
committed
Share extended config watchers across projects in server
New shared watcher map in ProjectService that stores callbacks per project to be invoked when the file watcher is triggered. The FileWatcher is created with the watch options of the first Project to watch the extended config.
1 parent 60aa8b1 commit 36c29cb

File tree

4 files changed

+83
-38
lines changed

4 files changed

+83
-38
lines changed

src/compiler/watchPublic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,7 @@ namespace ts {
792792
}
793793

794794
function watchExtendedConfigFiles() {
795-
const configFile = builderProgram.getCompilerOptions().configFile;
795+
const { configFile } = builderProgram.getCompilerOptions();
796796
if (configFile) {
797797
updateExtendedConfigFilesWatch(
798798
configFile,

src/compiler/watchUtilities.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,73 @@ namespace ts {
282282
);
283283
}
284284

285+
export interface SharedExtendedConfigFileWatcher<P> {
286+
watcher: FileWatcher;
287+
callbacks: ESMap<P, FileWatcherCallback>;
288+
}
289+
290+
export function updateSharedExtendedConfigFilesWatch<P>(
291+
project: P,
292+
projectCallback: FileWatcherCallback,
293+
configFile: TsConfigSourceFile,
294+
sharedExtendedConfigFilesMap: ESMap<string, SharedExtendedConfigFileWatcher<P>>,
295+
watchFactory: WatchFactory<WatchType, any>,
296+
watchOptions: WatchOptions | undefined
297+
) {
298+
const extendedSourceFiles = configFile.extendedSourceFiles || emptyArray;
299+
const newSharedExtendedConfigFilesMap = arrayToMap(extendedSourceFiles, identity, returnTrue);
300+
301+
sharedExtendedConfigFilesMap.forEach((existingWatcher, key) => {
302+
if (newSharedExtendedConfigFilesMap.has(key)) {
303+
existingWatcher.callbacks.set(project, projectCallback);
304+
}
305+
else {
306+
existingWatcher.callbacks.delete(project);
307+
if (existingWatcher.callbacks.size === 0) {
308+
sharedExtendedConfigFilesMap.delete(key);
309+
closeFileWatcherOf(existingWatcher);
310+
}
311+
}
312+
});
313+
314+
newSharedExtendedConfigFilesMap.forEach((_true, extendedConfigPath) => {
315+
if (!sharedExtendedConfigFilesMap.has(extendedConfigPath)) {
316+
const newWatcher = createSharedExtendedConfigFileWatcher(extendedConfigPath);
317+
sharedExtendedConfigFilesMap.set(extendedConfigPath, newWatcher);
318+
}
319+
});
320+
321+
function createSharedExtendedConfigFileWatcher(extendedConfigPath: string) {
322+
const callbacks = new Map<P, FileWatcherCallback>();
323+
callbacks.set(project, projectCallback);
324+
const watcher = watchFactory.watchFile(
325+
extendedConfigPath,
326+
invokeProjectCallbacks,
327+
PollingInterval.High,
328+
watchOptions,
329+
WatchType.ExtendedConfigFile
330+
);
331+
return { watcher, callbacks };
332+
333+
function invokeProjectCallbacks(fileName: string, eventKind: FileWatcherEventKind) {
334+
return callbacks.forEach((callback) => callback(fileName, eventKind));
335+
}
336+
}
337+
}
338+
339+
export function removeProjectFromSharedExtendedConfigFilesWatch<P>(
340+
project: P,
341+
sharedExtendedConfigFilesMap: ESMap<string, SharedExtendedConfigFileWatcher<P>>
342+
) {
343+
sharedExtendedConfigFilesMap.forEach((existingWatcher, key) => {
344+
existingWatcher.callbacks.delete(project);
345+
if (existingWatcher.callbacks.size === 0) {
346+
sharedExtendedConfigFilesMap.delete(key);
347+
closeFileWatcherOf(existingWatcher);
348+
}
349+
});
350+
}
351+
285352
/**
286353
* Updates the existing missing file watches with the new set of missing files after new program is created
287354
*/

src/server/editorServices.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,9 @@ namespace ts.server {
756756
/*@internal*/
757757
readonly watchFactory: WatchFactory<WatchType, Project>;
758758

759+
/*@internal*/
760+
private sharedExtendedConfigFilesMap = new Map<string, SharedExtendedConfigFileWatcher<Project>>();
761+
759762
/*@internal*/
760763
readonly packageJsonCache: PackageJsonCache;
761764
/*@internal*/
@@ -1384,6 +1387,7 @@ namespace ts.server {
13841387
project.print(/*writeProjectFileNames*/ true);
13851388

13861389
project.close();
1390+
removeProjectFromSharedExtendedConfigFilesWatch(project, this.sharedExtendedConfigFilesMap);
13871391
if (Debug.shouldAssert(AssertionLevel.Normal)) {
13881392
this.filenameToScriptInfo.forEach(info => Debug.assert(
13891393
!info.isAttached(project),
@@ -2156,15 +2160,24 @@ namespace ts.server {
21562160
const lastFileExceededProgramSize = this.getFilenameForExceededTotalSizeLimitForNonTsFiles(project.canonicalConfigFilePath, compilerOptions, parsedCommandLine.fileNames, fileNamePropertyReader);
21572161
if (lastFileExceededProgramSize) {
21582162
project.disableLanguageService(lastFileExceededProgramSize);
2159-
project.stopWatchingExtendedConfigFiles();
21602163
project.stopWatchingWildCards();
2164+
removeProjectFromSharedExtendedConfigFilesWatch(project, this.sharedExtendedConfigFilesMap);
21612165
}
21622166
else {
21632167
project.setCompilerOptions(compilerOptions);
21642168
project.setWatchOptions(parsedCommandLine.watchOptions);
21652169
project.enableLanguageService();
2166-
project.watchExtendedConfigFiles();
21672170
project.watchWildcards(new Map(getEntries(parsedCommandLine.wildcardDirectories!))); // TODO: GH#18217
2171+
if (compilerOptions.configFile) {
2172+
updateSharedExtendedConfigFilesWatch(
2173+
project,
2174+
(fileName) => this.onExtendedConfigChangedForConfiguredProject(project, fileName),
2175+
compilerOptions.configFile,
2176+
this.sharedExtendedConfigFilesMap,
2177+
this.watchFactory,
2178+
this.hostConfiguration.watchOptions,
2179+
);
2180+
}
21682181
}
21692182
project.enablePluginsWithOptions(compilerOptions, this.currentPluginConfigOverrides);
21702183
const filesToAdd = parsedCommandLine.fileNames.concat(project.getExternalFiles());

src/server/project.ts

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2046,8 +2046,6 @@ namespace ts.server {
20462046
export class ConfiguredProject extends Project {
20472047
/* @internal */
20482048
configFileWatcher: FileWatcher | undefined;
2049-
/* @internal */
2050-
private extendedConfigFileWatchers: ESMap<string, FileWatcher> | undefined;
20512049
private directoriesWatchedForWildcards: ESMap<string, WildcardDirectoryWatcher> | undefined;
20522050
readonly canonicalConfigFilePath: NormalizedPath;
20532051

@@ -2261,38 +2259,6 @@ namespace ts.server {
22612259
this.projectErrors = projectErrors;
22622260
}
22632261

2264-
/* @internal */
2265-
createExtendedConfigFileWatcher(extendedConfigFile: string): FileWatcher {
2266-
return this.projectService.watchFactory.watchFile(
2267-
extendedConfigFile,
2268-
(fileName) => this.projectService.onExtendedConfigChangedForConfiguredProject(this, fileName),
2269-
PollingInterval.High,
2270-
this.projectService.getWatchOptions(this),
2271-
WatchType.ExtendedConfigFile,
2272-
this
2273-
);
2274-
}
2275-
2276-
/* @internal */
2277-
watchExtendedConfigFiles() {
2278-
const configFile = this.getCompilerOptions().configFile;
2279-
if (configFile) {
2280-
updateExtendedConfigFilesWatch(
2281-
configFile,
2282-
this.extendedConfigFileWatchers || (this.extendedConfigFileWatchers = new Map()),
2283-
(extendedConfigFile) => this.createExtendedConfigFileWatcher(extendedConfigFile),
2284-
);
2285-
}
2286-
}
2287-
2288-
/* @internal */
2289-
stopWatchingExtendedConfigFiles() {
2290-
if (this.extendedConfigFileWatchers) {
2291-
clearMap(this.extendedConfigFileWatchers, closeFileWatcher);
2292-
this.extendedConfigFileWatchers = undefined;
2293-
}
2294-
}
2295-
22962262
/*@internal*/
22972263
watchWildcards(wildcardDirectories: ESMap<string, WatchDirectoryFlags>) {
22982264
updateWatchingWildcardDirectories(
@@ -2317,7 +2283,6 @@ namespace ts.server {
23172283
this.configFileWatcher = undefined;
23182284
}
23192285

2320-
this.stopWatchingExtendedConfigFiles();
23212286
this.stopWatchingWildCards();
23222287
this.configFileSpecs = undefined;
23232288
this.openFileWatchTriggered.clear();

0 commit comments

Comments
 (0)