Instead of watching files in typing installer, forward it to projectService (#54028)

This commit is contained in:
Sheetal Nandi
2023-04-26 13:31:36 -07:00
committed by GitHub
parent f22898d577
commit a956bbc831
86 changed files with 1098 additions and 1040 deletions
+6
View File
@@ -176,6 +176,7 @@ import {
ThrottledOperations,
toNormalizedPath,
TypingsCache,
WatchTypingLocations,
} from "./_namespaces/ts.server";
import * as protocol from "./protocol";
@@ -1153,6 +1154,11 @@ export class ProjectService {
}
}
/** @internal */
watchTypingLocations(response: WatchTypingLocations) {
this.findProject(response.projectName)?.watchTypingLocations(response.files);
}
/** @internal */
delayEnsureProjectForOpenFiles() {
if (!this.openFiles.size) return;
+119
View File
@@ -13,16 +13,19 @@ import {
closeFileWatcher,
closeFileWatcherOf,
combinePaths,
comparePaths,
CompilerHost,
CompilerOptions,
concatenate,
ConfigFileProgramReloadLevel,
containsPath,
createCacheableExportInfoMap,
createLanguageService,
createResolutionCache,
createSymlinkCache,
Debug,
Diagnostic,
directorySeparator,
DirectoryStructureHost,
DirectoryWatcherCallback,
DocumentPositionMapper,
@@ -45,6 +48,7 @@ import {
generateDjb2Hash,
getAllowJSCompilerOption,
getAutomaticTypeDirectiveNames,
getBaseFileName,
GetCanonicalFileName,
getDeclarationEmitOutputFilePathWorker,
getDefaultCompilerOptions,
@@ -126,6 +130,7 @@ import {
WatchType,
} from "./_namespaces/ts";
import {
ActionInvalidate,
asNormalizedPath,
createModuleSpecifierCache,
emptyArray,
@@ -287,6 +292,14 @@ export interface EmitResult {
diagnostics: readonly Diagnostic[];
}
const enum TypingWatcherType {
FileWatcher = "FileWatcher",
DirectoryWatcher = "DirectoryWatcher"
}
type TypingWatchers = Map<Path, FileWatcher> & { isInvoked?: boolean; };
export abstract class Project implements LanguageServiceHost, ModuleResolutionHost {
private rootFiles: ScriptInfo[] = [];
private rootFilesMap = new Map<string, ProjectRootFile>();
@@ -370,6 +383,9 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
/** @internal */
typingFiles: SortedReadonlyArray<string> = emptyArray;
/** @internal */
private typingWatchers: TypingWatchers | undefined;
/** @internal */
originalConfiguredProjects: Set<NormalizedPath> | undefined;
@@ -1013,6 +1029,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
close() {
this.projectService.typingsCache.onProjectClosed(this);
this.closeWatchingTypingLocations();
if (this.program) {
// if we have a program - release all files that are enlisted in program but arent root
// The releasing of the roots happens later
@@ -1361,6 +1378,108 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
}
}
/** @internal */
private closeWatchingTypingLocations() {
if (this.typingWatchers) clearMap(this.typingWatchers, closeFileWatcher);
this.typingWatchers = undefined;
}
/** @internal */
private onTypingInstallerWatchInvoke() {
this.typingWatchers!.isInvoked = true;
this.projectService.updateTypingsForProject({ projectName: this.getProjectName(), kind: ActionInvalidate });
}
/** @internal */
watchTypingLocations(files: readonly string[] | undefined) {
if (!files) {
this.typingWatchers!.isInvoked = false;
return;
}
if (!files.length) {
// shut down existing watchers
this.closeWatchingTypingLocations();
return;
}
const toRemove = new Map(this.typingWatchers);
if (!this.typingWatchers) this.typingWatchers = new Map();
// handler should be invoked once for the entire set of files since it will trigger full rediscovery of typings
this.typingWatchers.isInvoked = false;
const createProjectWatcher = (path: string, typingsWatcherType: TypingWatcherType) => {
const canonicalPath = this.toPath(path);
toRemove.delete(canonicalPath);
if (!this.typingWatchers!.has(canonicalPath)) {
this.typingWatchers!.set(canonicalPath, typingsWatcherType === TypingWatcherType.FileWatcher ?
this.projectService.watchFactory.watchFile(
path,
() => !this.typingWatchers!.isInvoked ?
this.onTypingInstallerWatchInvoke() :
this.writeLog(`TypingWatchers already invoked`),
PollingInterval.High,
this.projectService.getWatchOptions(this),
WatchType.TypingInstallerLocationFile,
this,
) :
this.projectService.watchFactory.watchDirectory(
path,
f => {
if (this.typingWatchers!.isInvoked) return this.writeLog(`TypingWatchers already invoked`);
if (!fileExtensionIs(f, Extension.Json)) return this.writeLog(`Ignoring files that are not *.json`);
if (comparePaths(f, combinePaths(this.projectService.typingsInstaller.globalTypingsCacheLocation!, "package.json"), !this.useCaseSensitiveFileNames())) return this.writeLog(`Ignoring package.json change at global typings location`);
this.onTypingInstallerWatchInvoke();
},
WatchDirectoryFlags.Recursive,
this.projectService.getWatchOptions(this),
WatchType.TypingInstallerLocationDirectory,
this,
)
);
}
};
// Create watches from list of files
for (const file of files) {
const basename = getBaseFileName(file);
if (basename === "package.json" || basename === "bower.json") {
// package.json or bower.json exists, watch the file to detect changes and update typings
createProjectWatcher(file, TypingWatcherType.FileWatcher);
continue;
}
// path in projectRoot, watch project root
if (containsPath(this.currentDirectory, file, this.currentDirectory, !this.useCaseSensitiveFileNames())) {
const subDirectory = file.indexOf(directorySeparator, this.currentDirectory.length + 1);
if (subDirectory !== -1) {
// Watch subDirectory
createProjectWatcher(file.substr(0, subDirectory), TypingWatcherType.DirectoryWatcher);
}
else {
// Watch the directory itself
createProjectWatcher(file, TypingWatcherType.DirectoryWatcher);
}
continue;
}
// path in global cache, watch global cache
if (containsPath(this.projectService.typingsInstaller.globalTypingsCacheLocation!, file, this.currentDirectory, !this.useCaseSensitiveFileNames())) {
createProjectWatcher(this.projectService.typingsInstaller.globalTypingsCacheLocation!, TypingWatcherType.DirectoryWatcher);
continue;
}
// watch node_modules or bower_components
createProjectWatcher(file, TypingWatcherType.DirectoryWatcher);
}
// Remove unused watches
toRemove.forEach((watch, path) => {
watch.close();
this.typingWatchers!.delete(path);
});
}
/** @internal */
getCurrentProgram(): Program | undefined {
return this.program;
-1
View File
@@ -46,7 +46,6 @@ export function createInstallTypingsRequest(project: Project, typeAcquisition: T
projectName: project.getProjectName(),
fileNames: project.getFileNames(/*excludeFilesFromExternalLibraries*/ true, /*excludeConfigFiles*/ true).concat(project.getExcludedFiles() as NormalizedPath[]),
compilerOptions: project.getCompilationSettings(),
watchOptions: project.projectService.getWatchOptions(project),
typeAcquisition,
unresolvedImports,
projectRootPath: project.getCurrentDirectory() as Path,