Skip to content

Watch extended configs if present #41493

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Dec 11, 2020
35 changes: 35 additions & 0 deletions src/compiler/tsbuildPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ namespace ts {
readonly allWatchedWildcardDirectories: ESMap<ResolvedConfigFilePath, ESMap<string, WildcardDirectoryWatcher>>;
readonly allWatchedInputFiles: ESMap<ResolvedConfigFilePath, ESMap<Path, FileWatcher>>;
readonly allWatchedConfigFiles: ESMap<ResolvedConfigFilePath, FileWatcher>;
readonly allWatchedExtendedConfigFiles: ESMap<Path, SharedExtendedConfigFileWatcher<ResolvedConfigFilePath>>;

timerToBuildInvalidatedProject: any;
reportFileChangeDetected: boolean;
Expand Down Expand Up @@ -325,6 +326,7 @@ namespace ts {
allWatchedWildcardDirectories: new Map(),
allWatchedInputFiles: new Map(),
allWatchedConfigFiles: new Map(),
allWatchedExtendedConfigFiles: new Map(),

timerToBuildInvalidatedProject: undefined,
reportFileChangeDetected: false,
Expand Down Expand Up @@ -462,6 +464,15 @@ namespace ts {
{ onDeleteValue: closeFileWatcher }
);

state.allWatchedExtendedConfigFiles.forEach(watcher => {
watcher.projects.forEach(project => {
if (!currentProjects.has(project)) {
watcher.projects.delete(project);
}
});
watcher.close();
});

mutateMapSkippingNewValues(
state.allWatchedWildcardDirectories,
currentProjects,
Expand Down Expand Up @@ -1165,6 +1176,7 @@ namespace ts {

if (reloadLevel === ConfigFileProgramReloadLevel.Full) {
watchConfigFile(state, project, projectPath, config);
watchExtendedConfigFiles(state, projectPath, config);
watchWildCardDirectories(state, project, projectPath, config);
watchInputFiles(state, project, projectPath, config);
}
Expand Down Expand Up @@ -1789,6 +1801,24 @@ namespace ts {
));
}

function watchExtendedConfigFiles(state: SolutionBuilderState, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine | undefined) {
updateSharedExtendedConfigFileWatcher(
resolvedPath,
parsed,
state.allWatchedExtendedConfigFiles,
(extendedConfigFileName, extendedConfigFilePath) => state.watchFile(
extendedConfigFileName,
() => state.allWatchedExtendedConfigFiles.get(extendedConfigFilePath)?.projects.forEach(projectConfigFilePath =>
invalidateProjectAndScheduleBuilds(state, projectConfigFilePath, ConfigFileProgramReloadLevel.Full)
),
PollingInterval.High,
parsed?.watchOptions,
WatchType.ExtendedConfigFile,
),
fileName => toPath(state, fileName),
);
}

function watchWildCardDirectories(state: SolutionBuilderState, resolved: ResolvedConfigFileName, resolvedPath: ResolvedConfigFilePath, parsed: ParsedCommandLine) {
if (!state.watch) return;
updateWatchingWildcardDirectories(
Expand Down Expand Up @@ -1846,6 +1876,7 @@ namespace ts {
const cfg = parseConfigFile(state, resolved, resolvedPath);
// Watch this file
watchConfigFile(state, resolved, resolvedPath, cfg);
watchExtendedConfigFiles(state, resolvedPath, cfg);
if (cfg) {
// Update watchers for wildcard directories
watchWildCardDirectories(state, resolved, resolvedPath, cfg);
Expand All @@ -1858,6 +1889,10 @@ namespace ts {

function stopWatching(state: SolutionBuilderState) {
clearMap(state.allWatchedConfigFiles, closeFileWatcher);
clearMap(state.allWatchedExtendedConfigFiles, watcher => {
watcher.projects.clear();
watcher.close();
});
clearMap(state.allWatchedWildcardDirectories, watchedWildcardDirectories => clearMap(watchedWildcardDirectories, closeFileWatcherOf));
clearMap(state.allWatchedInputFiles, watchedWildcardDirectories => clearMap(watchedWildcardDirectories, closeFileWatcher));
}
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,7 @@ namespace ts {
export type WatchType = WatchTypeRegistry[keyof WatchTypeRegistry];
export const WatchType: WatchTypeRegistry = {
ConfigFile: "Config file",
ExtendedConfigFile: "Extended config file",
SourceFile: "Source file",
MissingFile: "Missing file",
WildcardDirectory: "Wild card directory",
Expand All @@ -418,6 +419,7 @@ namespace ts {

export interface WatchTypeRegistry {
ConfigFile: "Config file",
ExtendedConfigFile: "Extended config file",
SourceFile: "Source file",
MissingFile: "Missing file",
WildcardDirectory: "Wild card directory",
Expand Down
29 changes: 29 additions & 0 deletions src/compiler/watchPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ namespace ts {

let builderProgram: T;
let reloadLevel: ConfigFileProgramReloadLevel; // level to indicate if the program needs to be reloaded from config file/just filenames etc
let extendedConfigFilesMap: ESMap<Path, FileWatcher>; // Map of file watchers for the extended config files
let missingFilesMap: ESMap<Path, FileWatcher>; // Map of file watchers for the missing files
let watchedWildcardDirectories: ESMap<string, WildcardDirectoryWatcher>; // map of watchers for the wild card directories in the config file
let timerToUpdateProgram: any; // timer callback to recompile the program
Expand Down Expand Up @@ -337,6 +338,9 @@ namespace ts {
// Update the wild card directory watch
watchConfigFileWildCardDirectories();

// Update extended config file watch
watchExtendedConfigFiles();

return configFileName ?
{ getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, close } :
{ getCurrentProgram: getCurrentBuilderProgram, getProgram: updateProgram, updateRootFileNames, close };
Expand All @@ -354,6 +358,10 @@ namespace ts {
configFileWatcher.close();
configFileWatcher = undefined;
}
if (extendedConfigFilesMap) {
clearMap(extendedConfigFilesMap, closeFileWatcher);
extendedConfigFilesMap = undefined!;
}
if (watchedWildcardDirectories) {
clearMap(watchedWildcardDirectories, closeFileWatcherOf);
watchedWildcardDirectories = undefined!;
Expand Down Expand Up @@ -657,6 +665,9 @@ namespace ts {

// Update the wild card directory watch
watchConfigFileWildCardDirectories();

// Update extended config file watch
watchExtendedConfigFiles();
}

function parseConfigFile() {
Expand Down Expand Up @@ -777,5 +788,23 @@ namespace ts {
WatchType.WildcardDirectory
);
}

function watchExtendedConfigFiles() {
// Update the extended config files watcher
mutateMap(
extendedConfigFilesMap ||= new Map(),
arrayToMap(compilerOptions.configFile?.extendedSourceFiles || emptyArray, toPath),
{
// Watch the extended config files
createNewValue: watchExtendedConfigFile,
// Config files that are no longer extended should no longer be watched.
onDeleteValue: closeFileWatcher
}
);
}

function watchExtendedConfigFile(extendedConfigFile: Path) {
return watchFile(extendedConfigFile, scheduleProgramReload, PollingInterval.High, watchOptions, WatchType.ExtendedConfigFile);
}
}
}
45 changes: 45 additions & 0 deletions src/compiler/watchUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,51 @@ namespace ts {
Full
}

export interface SharedExtendedConfigFileWatcher<T> extends FileWatcher {
fileWatcher: FileWatcher;
projects: Set<T>;
}

/**
* Updates the map of shared extended config file watches with a new set of extended config files from a base config file of the project
*/
export function updateSharedExtendedConfigFileWatcher<T>(
projectPath: T,
parsed: ParsedCommandLine | undefined,
extendedConfigFilesMap: ESMap<Path, SharedExtendedConfigFileWatcher<T>>,
createExtendedConfigFileWatch: (extendedConfigPath: string, extendedConfigFilePath: Path) => FileWatcher,
toPath: (fileName: string) => Path,
) {
const extendedConfigs = arrayToMap(parsed?.options.configFile?.extendedSourceFiles || emptyArray, toPath);
// remove project from all unrelated watchers
extendedConfigFilesMap.forEach((watcher, extendedConfigFilePath) => {
if (!extendedConfigs.has(extendedConfigFilePath)) {
watcher.projects.delete(projectPath);
watcher.close();
}
});
// Update the extended config files watcher
extendedConfigs.forEach((extendedConfigFileName, extendedConfigFilePath) => {
const existing = extendedConfigFilesMap.get(extendedConfigFilePath);
if (existing) {
existing.projects.add(projectPath);
}
else {
// start watching previously unseen extended config
extendedConfigFilesMap.set(extendedConfigFilePath, {
projects: new Set([projectPath]),
fileWatcher: createExtendedConfigFileWatch(extendedConfigFileName, extendedConfigFilePath),
close: () => {
const existing = extendedConfigFilesMap.get(extendedConfigFilePath);
if (!existing || existing.projects.size !== 0) return;
existing.fileWatcher.close();
extendedConfigFilesMap.delete(extendedConfigFilePath);
},
});
}
});
}

/**
* Updates the existing missing file watches with the new set of missing files after new program is created
*/
Expand Down
43 changes: 42 additions & 1 deletion src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,9 @@ namespace ts.server {
/*@internal*/
readonly watchFactory: WatchFactory<WatchType, Project>;

/*@internal*/
private readonly sharedExtendedConfigFileWatchers = new Map<Path, SharedExtendedConfigFileWatcher<NormalizedPath>>();

/*@internal*/
readonly packageJsonCache: PackageJsonCache;
/*@internal*/
Expand Down Expand Up @@ -1350,6 +1353,43 @@ namespace ts.server {
}
}

/*@internal*/
updateSharedExtendedConfigFileMap({ canonicalConfigFilePath }: ConfiguredProject, parsedCommandLine: ParsedCommandLine) {
updateSharedExtendedConfigFileWatcher(
canonicalConfigFilePath,
parsedCommandLine,
this.sharedExtendedConfigFileWatchers,
(extendedConfigFileName, extendedConfigFilePath) => this.watchFactory.watchFile(
extendedConfigFileName,
() => {
let ensureProjectsForOpenFiles = false;
this.sharedExtendedConfigFileWatchers.get(extendedConfigFilePath)?.projects.forEach(canonicalPath => {
const project = this.configuredProjects.get(canonicalPath);
// Skip refresh if project is not yet loaded
if (!project || project.isInitialLoadPending()) return;
project.pendingReload = ConfigFileProgramReloadLevel.Full;
project.pendingReloadReason = `Change in extended config file ${extendedConfigFileName} detected`;
this.delayUpdateProjectGraph(project);
ensureProjectsForOpenFiles = true;
});
if (ensureProjectsForOpenFiles) this.delayEnsureProjectForOpenFiles();
},
PollingInterval.High,
this.hostConfiguration.watchOptions,
WatchType.ExtendedConfigFile
),
fileName => this.toPath(fileName),
);
}

/*@internal*/
removeProjectFromSharedExtendedConfigFileMap(project: ConfiguredProject) {
this.sharedExtendedConfigFileWatchers.forEach(watcher => {
watcher.projects.delete(project.canonicalConfigFilePath);
watcher.close();
});
}

/**
* This is the callback function for the config file add/remove/change at any location
* that matters to open script info but doesnt have configured project open
Expand Down Expand Up @@ -2051,7 +2091,6 @@ namespace ts.server {
this,
this.documentRegistry,
cachedDirectoryStructureHost);
// TODO: We probably should also watch the configFiles that are extended
project.createConfigFileWatcher();
this.configuredProjects.set(project.canonicalConfigFilePath, project);
this.setConfigFileExistenceByNewConfiguredProject(project);
Expand Down Expand Up @@ -2134,12 +2173,14 @@ namespace ts.server {
if (lastFileExceededProgramSize) {
project.disableLanguageService(lastFileExceededProgramSize);
project.stopWatchingWildCards();
this.removeProjectFromSharedExtendedConfigFileMap(project);
}
else {
project.setCompilerOptions(compilerOptions);
project.setWatchOptions(parsedCommandLine.watchOptions);
project.enableLanguageService();
project.watchWildcards(new Map(getEntries(parsedCommandLine.wildcardDirectories!))); // TODO: GH#18217
this.updateSharedExtendedConfigFileMap(project, parsedCommandLine);
}
project.enablePluginsWithOptions(compilerOptions, this.currentPluginConfigOverrides);
const filesToAdd = parsedCommandLine.fileNames.concat(project.getExternalFiles());
Expand Down
1 change: 1 addition & 0 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2292,6 +2292,7 @@ namespace ts.server {
}

this.stopWatchingWildCards();
this.projectService.removeProjectFromSharedExtendedConfigFileMap(this);
this.projectErrors = undefined;
this.openFileWatchTriggered.clear();
this.compilerHost = undefined;
Expand Down
Loading