Skip to content

Commit 4224dd7

Browse files
committed
Refactor shared extended config map and watchers
Remove all server-related utility functions/types from watchUtilities. Store config-project mapping and config file watchers inside ProjectService with new private methods to add or remove projects.
1 parent 42e6860 commit 4224dd7

File tree

2 files changed

+51
-99
lines changed

2 files changed

+51
-99
lines changed

src/compiler/watchUtilities.ts

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -282,73 +282,6 @@ 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-
352285
/**
353286
* Updates the existing missing file watches with the new set of missing files after new program is created
354287
*/

src/server/editorServices.ts

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,9 @@ namespace ts.server {
757757
readonly watchFactory: WatchFactory<WatchType, Project>;
758758

759759
/*@internal*/
760-
private sharedExtendedConfigFilesMap = new Map<string, SharedExtendedConfigFileWatcher<Project>>();
760+
private sharedExtendedConfigFileMap = createMultiMap<Path, ConfiguredProject>();
761+
/*@internal*/
762+
private sharedExtendedConfigFileWatchers = new Map<Path, FileWatcher>();
761763

762764
/*@internal*/
763765
readonly packageJsonCache: PackageJsonCache;
@@ -1355,14 +1357,52 @@ namespace ts.server {
13551357
}
13561358

13571359
/*@internal*/
1358-
onExtendedConfigChangedForConfiguredProject(project: ConfiguredProject, extendedConfigFile: string) {
1359-
this.logExtendedConfigFileWatchUpdate(asNormalizedPath(extendedConfigFile), project.canonicalConfigFilePath, ConfigFileWatcherStatus.ReloadingFiles);
1360+
private updateSharedExtendedConfigFileMap(project: ConfiguredProject) {
1361+
const extendedSourceFiles = project.getCompilerOptions().configFile?.extendedSourceFiles || emptyArray;
1362+
extendedSourceFiles.forEach((extendedSourceFile: string) => {
1363+
const extendedConfigPath = this.toPath(extendedSourceFile);
1364+
if (!this.sharedExtendedConfigFileMap.has(extendedConfigPath)) {
1365+
const watcher = this.watchFactory.watchFile(
1366+
extendedConfigPath,
1367+
() => this.onSharedExtendedConfigChanged(extendedConfigPath),
1368+
PollingInterval.High,
1369+
this.hostConfiguration.watchOptions,
1370+
WatchType.ExtendedConfigFile
1371+
);
1372+
this.sharedExtendedConfigFileWatchers.set(extendedConfigPath, watcher);
1373+
}
1374+
const otherProjects = this.sharedExtendedConfigFileMap.get(extendedConfigPath);
1375+
if (!otherProjects || !otherProjects.includes(project)) {
1376+
this.sharedExtendedConfigFileMap.add(extendedConfigPath, project);
1377+
}
1378+
});
1379+
}
13601380

1361-
// Skip refresh if project is not yet loaded
1362-
if (project.isInitialLoadPending()) return;
1363-
project.pendingReload = ConfigFileProgramReloadLevel.Full;
1364-
project.pendingReloadReason = `Change in extended config file ${extendedConfigFile} detected`;
1365-
this.delayUpdateProjectGraph(project);
1381+
/*@internal*/
1382+
private removeProjectFromSharedExtendedConfigFileMap(project: ConfiguredProject) {
1383+
for (const key of arrayFrom(this.sharedExtendedConfigFileMap.keys())) {
1384+
this.sharedExtendedConfigFileMap.remove(key, project);
1385+
const otherProjects = this.sharedExtendedConfigFileMap.get(key) || emptyArray;
1386+
if (otherProjects.length === 0) {
1387+
const watcher = this.sharedExtendedConfigFileWatchers.get(key);
1388+
if (watcher) {
1389+
watcher.close();
1390+
this.sharedExtendedConfigFileWatchers.delete(key);
1391+
}
1392+
}
1393+
}
1394+
}
1395+
1396+
/*@internal*/
1397+
private onSharedExtendedConfigChanged(extendedConfigPath: Path) {
1398+
const projects = this.sharedExtendedConfigFileMap.get(extendedConfigPath) || emptyArray;
1399+
projects.forEach((project: ConfiguredProject) => {
1400+
// Skip refresh if project is not yet loaded
1401+
if (project.isInitialLoadPending()) return;
1402+
project.pendingReload = ConfigFileProgramReloadLevel.Full;
1403+
project.pendingReloadReason = `Change in extended config file ${extendedConfigPath} detected`;
1404+
this.delayUpdateProjectGraph(project);
1405+
});
13661406
}
13671407

13681408
/**
@@ -1388,7 +1428,6 @@ namespace ts.server {
13881428
project.print(/*writeProjectFileNames*/ true);
13891429

13901430
project.close();
1391-
removeProjectFromSharedExtendedConfigFilesWatch(project, this.sharedExtendedConfigFilesMap);
13921431
if (Debug.shouldAssert(AssertionLevel.Normal)) {
13931432
this.filenameToScriptInfo.forEach(info => Debug.assert(
13941433
!info.isAttached(project),
@@ -1421,6 +1460,7 @@ namespace ts.server {
14211460
this.configuredProjects.delete((<ConfiguredProject>project).canonicalConfigFilePath);
14221461
this.projectToSizeMap.delete((project as ConfiguredProject).canonicalConfigFilePath);
14231462
this.setConfigFileExistenceInfoByClosedConfiguredProject(<ConfiguredProject>project);
1463+
this.removeProjectFromSharedExtendedConfigFileMap(project as ConfiguredProject);
14241464
break;
14251465
case ProjectKind.Inferred:
14261466
unorderedRemoveItem(this.inferredProjects, <InferredProject>project);
@@ -1689,18 +1729,6 @@ namespace ts.server {
16891729
this.logger.info(`ConfigFilePresence:: Current Watches: ${watches}:: File: ${configFileName} Currently impacted open files: RootsOfInferredProjects: ${inferredRoots} OtherOpenFiles: ${otherFiles} Status: ${status}`);
16901730
}
16911731

1692-
/*@internal*/
1693-
private logExtendedConfigFileWatchUpdate(extendedConfigFile: NormalizedPath, canonicalConfigFilePath: string, status: ConfigFileWatcherStatus) {
1694-
if (!this.logger.hasLevel(LogLevel.verbose)) {
1695-
return;
1696-
}
1697-
const watches: WatchType[] = [];
1698-
if (this.configuredProjects.has(canonicalConfigFilePath)) {
1699-
watches.push(WatchType.ExtendedConfigFile);
1700-
}
1701-
this.logger.info(`ExtendedConfigFilePresence:: Current Watches: ${watches}:: File: ${extendedConfigFile} Status: ${status}`);
1702-
}
1703-
17041732
/**
17051733
* Create the watcher for the configFileExistenceInfo
17061734
*/
@@ -2162,23 +2190,14 @@ namespace ts.server {
21622190
if (lastFileExceededProgramSize) {
21632191
project.disableLanguageService(lastFileExceededProgramSize);
21642192
project.stopWatchingWildCards();
2165-
removeProjectFromSharedExtendedConfigFilesWatch(project, this.sharedExtendedConfigFilesMap);
2193+
this.removeProjectFromSharedExtendedConfigFileMap(project);
21662194
}
21672195
else {
21682196
project.setCompilerOptions(compilerOptions);
21692197
project.setWatchOptions(parsedCommandLine.watchOptions);
21702198
project.enableLanguageService();
21712199
project.watchWildcards(new Map(getEntries(parsedCommandLine.wildcardDirectories!))); // TODO: GH#18217
2172-
if (compilerOptions.configFile) {
2173-
updateSharedExtendedConfigFilesWatch(
2174-
project,
2175-
(fileName) => this.onExtendedConfigChangedForConfiguredProject(project, fileName),
2176-
compilerOptions.configFile,
2177-
this.sharedExtendedConfigFilesMap,
2178-
this.watchFactory,
2179-
this.hostConfiguration.watchOptions,
2180-
);
2181-
}
2200+
this.updateSharedExtendedConfigFileMap(project);
21822201
}
21832202
project.enablePluginsWithOptions(compilerOptions, this.currentPluginConfigOverrides);
21842203
const filesToAdd = parsedCommandLine.fileNames.concat(project.getExternalFiles());

0 commit comments

Comments
 (0)