mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-14 19:16:17 -06:00
Invalidate resolutions from typeRoots watch event as a fallback mechanism
There are times when the directory watcher for failed lookup locations isnt invoked and is not very determinitstic So as a fallback, when we receive typeRoots watch event, handle failed lookup locations as well
This commit is contained in:
parent
67670d8726
commit
306ed1291b
@ -361,9 +361,13 @@ namespace ts {
|
||||
return { dir: rootDir, dirPath: rootPath };
|
||||
}
|
||||
|
||||
let dir = getDirectoryPath(getNormalizedAbsolutePath(failedLookupLocation, getCurrentDirectory()));
|
||||
let dirPath = getDirectoryPath(failedLookupLocationPath);
|
||||
return getDirectoryToWatchFromFailedLookupLocationDirectory(
|
||||
getDirectoryPath(getNormalizedAbsolutePath(failedLookupLocation, getCurrentDirectory())),
|
||||
getDirectoryPath(failedLookupLocationPath)
|
||||
);
|
||||
}
|
||||
|
||||
function getDirectoryToWatchFromFailedLookupLocationDirectory(dir: string, dirPath: Path) {
|
||||
// If directory path contains node module, get the most parent node_modules directory for watching
|
||||
while (stringContains(dirPath, "/node_modules/")) {
|
||||
dir = getDirectoryPath(dir);
|
||||
@ -621,7 +625,19 @@ namespace ts {
|
||||
clearMap(typeRootsWatches, closeFileWatcher);
|
||||
}
|
||||
|
||||
function createTypeRootsWatch(_typeRootPath: string, typeRoot: string): FileWatcher {
|
||||
function getDirectoryToWatchFailedLookupLocationFromTypeRoot(typeRoot: string, typeRootPath: Path): Path | undefined {
|
||||
if (allFilesHaveInvalidatedResolution) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (isInDirectoryPath(rootPath, typeRootPath)) {
|
||||
return rootPath;
|
||||
}
|
||||
const { dirPath, ignore } = getDirectoryToWatchFromFailedLookupLocationDirectory(typeRoot, typeRootPath);
|
||||
return !ignore && directoryWatchesOfFailedLookups.has(dirPath) && dirPath;
|
||||
}
|
||||
|
||||
function createTypeRootsWatch(typeRootPath: Path, typeRoot: string): FileWatcher {
|
||||
// Create new watch and recursive info
|
||||
return resolutionHost.watchTypeRootsDirectory(typeRoot, fileOrDirectory => {
|
||||
const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory);
|
||||
@ -634,6 +650,13 @@ namespace ts {
|
||||
// We could potentially store more data here about whether it was/would be really be used or not
|
||||
// and with that determine to trigger compilation but for now this is enough
|
||||
resolutionHost.onChangedAutomaticTypeDirectiveNames();
|
||||
|
||||
// Since directory watchers invoked are flaky, the failed lookup location events might not be triggered
|
||||
// So handle to failed lookup locations here as well to ensure we are invalidating resolutions
|
||||
const dirPath = getDirectoryToWatchFailedLookupLocationFromTypeRoot(typeRoot, typeRootPath);
|
||||
if (dirPath && invalidateResolutionOfFailedLookupLocation(fileOrDirectoryPath, dirPath === fileOrDirectoryPath)) {
|
||||
resolutionHost.onInvalidatedResolution();
|
||||
}
|
||||
}, WatchDirectoryFlags.Recursive);
|
||||
}
|
||||
|
||||
|
||||
@ -6211,6 +6211,44 @@ namespace ts.projectSystem {
|
||||
verifyNpmInstall(/*timeoutDuringPartialInstallation*/ false);
|
||||
});
|
||||
});
|
||||
|
||||
it("when node_modules dont receive event for the @types file addition", () => {
|
||||
const projectLocation = "/user/username/folder/myproject";
|
||||
const app: FileOrFolder = {
|
||||
path: `${projectLocation}/app.ts`,
|
||||
content: `import * as debug from "debug"`
|
||||
};
|
||||
const tsconfig: FileOrFolder = {
|
||||
path: `${projectLocation}/tsconfig.json`,
|
||||
content: ""
|
||||
};
|
||||
|
||||
const files = [app, tsconfig, libFile];
|
||||
const host = createServerHost(files);
|
||||
const service = createProjectService(host);
|
||||
service.openClientFile(app.path);
|
||||
|
||||
const project = service.configuredProjects.get(tsconfig.path);
|
||||
checkProjectActualFiles(project, files.map(f => f.path));
|
||||
assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(app.path).map(diag => diag.messageText), ["Cannot find module 'debug'."]);
|
||||
|
||||
const debugTypesFile: FileOrFolder = {
|
||||
path: `${projectLocation}/node_modules/@types/debug/index.d.ts`,
|
||||
content: "export {}"
|
||||
};
|
||||
files.push(debugTypesFile);
|
||||
// Do not invoke recursive directory watcher for anything other than node_module/@types
|
||||
const invoker = host.invokeWatchedDirectoriesRecursiveCallback;
|
||||
host.invokeWatchedDirectoriesRecursiveCallback = (fullPath, relativePath) => {
|
||||
if (fullPath.endsWith("@types")) {
|
||||
invoker.call(host, fullPath, relativePath);
|
||||
}
|
||||
};
|
||||
host.reloadFS(files);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
checkProjectActualFiles(project, files.map(f => f.path));
|
||||
assert.deepEqual(project.getLanguageService().getSemanticDiagnostics(app.path).map(diag => diag.messageText), []);
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem ProjectsChangedInBackground", () => {
|
||||
|
||||
@ -547,8 +547,8 @@ interface Array<T> {}`
|
||||
// Invoke directory and recursive directory watcher for the folder
|
||||
// Here we arent invoking recursive directory watchers for the base folders
|
||||
// since that is something we would want to do for both file as well as folder we are deleting
|
||||
invokeWatcherCallbacks(this.watchedDirectories.get(fileOrDirectory.path), cb => this.directoryCallback(cb, relativePath));
|
||||
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(fileOrDirectory.path), cb => this.directoryCallback(cb, relativePath));
|
||||
this.invokeWatchedDirectoriesCallback(fileOrDirectory.fullPath, relativePath);
|
||||
this.invokeWatchedDirectoriesRecursiveCallback(fileOrDirectory.fullPath, relativePath);
|
||||
}
|
||||
|
||||
if (basePath !== fileOrDirectory.path) {
|
||||
@ -561,9 +561,17 @@ interface Array<T> {}`
|
||||
}
|
||||
}
|
||||
|
||||
private invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind) {
|
||||
const callbacks = this.watchedFiles.get(this.toPath(fileFullPath));
|
||||
invokeWatcherCallbacks(callbacks, ({ cb }) => cb(fileFullPath, eventKind));
|
||||
// For overriding the methods
|
||||
invokeWatchedDirectoriesCallback(folderFullPath: string, relativePath: string) {
|
||||
invokeWatcherCallbacks(this.watchedDirectories.get(this.toPath(folderFullPath)), cb => this.directoryCallback(cb, relativePath));
|
||||
}
|
||||
|
||||
invokeWatchedDirectoriesRecursiveCallback(folderFullPath: string, relativePath: string) {
|
||||
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(this.toPath(folderFullPath)), cb => this.directoryCallback(cb, relativePath));
|
||||
}
|
||||
|
||||
invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind, useFileNameInCallback?: boolean) {
|
||||
invokeWatcherCallbacks(this.watchedFiles.get(this.toPath(fileFullPath)), ({ cb, fileName }) => cb(useFileNameInCallback ? fileName : fileFullPath, eventKind));
|
||||
}
|
||||
|
||||
private getRelativePathToDirectory(directoryFullPath: string, fileFullPath: string) {
|
||||
@ -576,8 +584,8 @@ interface Array<T> {}`
|
||||
private invokeDirectoryWatcher(folderFullPath: string, fileName: string) {
|
||||
const relativePath = this.getRelativePathToDirectory(folderFullPath, fileName);
|
||||
// Folder is changed when the directory watcher is invoked
|
||||
invokeWatcherCallbacks(this.watchedFiles.get(this.toPath(folderFullPath)), ({ cb, fileName }) => cb(fileName, FileWatcherEventKind.Changed));
|
||||
invokeWatcherCallbacks(this.watchedDirectories.get(this.toPath(folderFullPath)), cb => this.directoryCallback(cb, relativePath));
|
||||
this.invokeFileWatcher(folderFullPath, FileWatcherEventKind.Changed, /*useFileNameInCallback*/ true);
|
||||
this.invokeWatchedDirectoriesCallback(folderFullPath, relativePath);
|
||||
this.invokeRecursiveDirectoryWatcher(folderFullPath, fileName);
|
||||
}
|
||||
|
||||
@ -590,7 +598,7 @@ interface Array<T> {}`
|
||||
*/
|
||||
private invokeRecursiveDirectoryWatcher(fullPath: string, fileName: string) {
|
||||
const relativePath = this.getRelativePathToDirectory(fullPath, fileName);
|
||||
invokeWatcherCallbacks(this.watchedDirectoriesRecursive.get(this.toPath(fullPath)), cb => this.directoryCallback(cb, relativePath));
|
||||
this.invokeWatchedDirectoriesRecursiveCallback(fullPath, relativePath);
|
||||
const basePath = getDirectoryPath(fullPath);
|
||||
if (this.getCanonicalFileName(fullPath) !== this.getCanonicalFileName(basePath)) {
|
||||
this.invokeRecursiveDirectoryWatcher(basePath, fileName);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user