Only when typings file change for the project, schedule the update for the project (#42428)

* Update and add test when typings dont change because of import name

* Update project scheduling only when typings are set

* Schedule update graph only if typings change
Fixes #39326
This commit is contained in:
Sheetal Nandi 2021-01-22 11:16:07 -08:00 committed by GitHub
parent 4c62f6e7d6
commit 80dfc6a45b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 52 additions and 27 deletions

View File

@ -16,6 +16,8 @@ namespace ts {
setFilesWithInvalidatedNonRelativeUnresolvedImports(filesWithUnresolvedImports: ESMap<Path, readonly string[]>): void;
createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution;
hasChangedAutomaticTypeDirectiveNames(): boolean;
isFileWithInvalidatedNonRelativeUnresolvedImports(path: Path): boolean;
startCachingPerDirectoryResolution(): void;
finishCachingPerDirectoryResolution(): void;
@ -208,6 +210,7 @@ namespace ts {
invalidateResolutionsOfFailedLookupLocations,
setFilesWithInvalidatedNonRelativeUnresolvedImports,
createHasInvalidatedResolution,
isFileWithInvalidatedNonRelativeUnresolvedImports,
updateTypeRootsWatch,
closeTypeRootsWatch,
clear

View File

@ -922,13 +922,12 @@ namespace ts.server {
case ActionSet:
// Update the typing files and update the project
project.updateTypingFiles(this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typeAcquisition, response.unresolvedImports, response.typings));
break;
return;
case ActionInvalidate:
// Do not clear resolution cache, there was changes detected in typings, so enque typing request and let it get us correct results
this.typingsCache.enqueueInstallTypingsForProject(project, project.lastCachedUnresolvedImportsList, /*forceRefresh*/ true);
return;
}
this.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(project);
}
/*@internal*/

View File

@ -1057,13 +1057,16 @@ namespace ts.server {
/*@internal*/
updateTypingFiles(typingFiles: SortedReadonlyArray<string>) {
enumerateInsertsAndDeletes<string, string>(typingFiles, this.typingFiles, getStringComparer(!this.useCaseSensitiveFileNames()),
if (enumerateInsertsAndDeletes<string, string>(typingFiles, this.typingFiles, getStringComparer(!this.useCaseSensitiveFileNames()),
/*inserted*/ noop,
removed => this.detachScriptInfoFromProject(removed)
);
this.typingFiles = typingFiles;
// Invalidate files with unresolved imports
this.resolutionCache.setFilesWithInvalidatedNonRelativeUnresolvedImports(this.cachedUnresolvedImportsPerFile);
)) {
// If typing files changed, then only schedule project update
this.typingFiles = typingFiles;
// Invalidate files with unresolved imports
this.resolutionCache.setFilesWithInvalidatedNonRelativeUnresolvedImports(this.cachedUnresolvedImportsPerFile);
this.projectService.delayUpdateProjectGraphAndEnsureProjectStructureForOpenFiles(this);
}
}
/* @internal */

View File

@ -409,7 +409,7 @@ namespace ts.projectSystem {
checkErrors([serverUtilities.path, app.path]);
function checkErrors(openFiles: [string, string]) {
verifyGetErrRequestNoErrors({ session, host, files: openFiles, existingTimeouts: 2 });
verifyGetErrRequestNoErrors({ session, host, files: openFiles });
}
});

View File

@ -419,7 +419,7 @@ namespace ts.projectSystem {
unresolvedImports: response.unresolvedImports,
});
host.checkTimeoutQueueLengthAndRun(1);
host.checkTimeoutQueueLength(0);
assert.isUndefined(request);
});

View File

@ -198,10 +198,7 @@ namespace ts.projectSystem {
checkNumberOfProjects(service, { inferredProjects: 1 });
session.clearMessages();
host.checkTimeoutQueueLengthAndRun(2);
checkProjectUpdatedInBackgroundEvent(session, [file.path]);
host.checkTimeoutQueueLength(0);
verifyGetErrRequest({
session,
host,
@ -240,10 +237,7 @@ namespace ts.projectSystem {
checkNumberOfProjects(service, { inferredProjects: 1 });
session.clearMessages();
host.checkTimeoutQueueLengthAndRun(2);
checkProjectUpdatedInBackgroundEvent(session, [file.path]);
host.checkTimeoutQueueLength(0);
verifyGetErrRequest({
session,
host,

View File

@ -244,7 +244,7 @@ namespace ts.projectSystem {
checkProjectActualFiles(p, [jqueryJs.path]);
installer.installAll(/*expectedCount*/ 0);
host.checkTimeoutQueueLengthAndRun(2);
host.checkTimeoutQueueLength(0);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
// files should not be removed from project if ATA is skipped
checkProjectActualFiles(p, [jqueryJs.path]);
@ -1024,9 +1024,8 @@ namespace ts.projectSystem {
service.openClientFile(f.path);
installer.checkPendingCommands(/*expectedCount*/ 0);
host.writeFile(fixedPackageJson.path, fixedPackageJson.content);
host.checkTimeoutQueueLengthAndRun(2); // To refresh the project and refresh inferred projects
host.checkTimeoutQueueLength(0);
// expected install request
installer.installAll(/*expectedCount*/ 1);
host.checkTimeoutQueueLengthAndRun(2);
@ -1212,7 +1211,8 @@ namespace ts.projectSystem {
}
};
session.executeCommand(changeRequest);
host.checkTimeoutQueueLengthAndRun(2); // This enqueues the updategraph and refresh inferred projects
host.checkTimeoutQueueLength(0);
proj.updateGraph();
const version2 = proj.lastCachedUnresolvedImportsList;
assert.strictEqual(version1, version2, "set of unresolved imports should change");
});
@ -1837,6 +1837,7 @@ namespace ts.projectSystem {
const appPath = "/a/b/app.js" as Path;
const foooPath = "/a/b/node_modules/fooo/index.d.ts";
function verifyResolvedModuleOfFooo(project: server.Project) {
server.updateProjectIfDirty(project);
const foooResolution = project.getLanguageService().getProgram()!.getSourceFileByPath(appPath)!.resolvedModules!.get("fooo")!;
assert.equal(foooResolution.resolvedFileName, foooPath);
return foooResolution;
@ -1851,6 +1852,7 @@ namespace ts.projectSystem {
path: foooPath,
content: `export var x: string;`
};
const host = createServerHost([app, fooo]);
const installer = new (class extends Installer {
constructor() {
@ -1873,6 +1875,17 @@ namespace ts.projectSystem {
checkProjectActualFiles(proj, typingFiles.map(f => f.path).concat(app.path, fooo.path));
const foooResolution2 = verifyResolvedModuleOfFooo(proj);
assert.strictEqual(foooResolution1, foooResolution2);
projectService.applyChangesInOpenFiles(/*openFiles*/ undefined, arrayIterator([{
fileName: app.path,
changes: arrayIterator([{
span: { start: 0, length: 0 },
newText: `import * as bar from "bar";`
}])
}]));
host.runQueuedTimeoutCallbacks(); // Update the graph
// Update the typing
host.checkTimeoutQueueLength(0);
assert.isFalse(proj.resolutionCache.isFileWithInvalidatedNonRelativeUnresolvedImports(app.path as Path));
}
it("correctly invalidate the resolutions with typing names", () => {
@ -1883,6 +1896,10 @@ namespace ts.projectSystem {
});
it("correctly invalidate the resolutions with typing names that are trimmed", () => {
const fooIndex: File = {
path: `${globalTypingsCacheLocation}/node_modules/foo/index.d.ts`,
content: "export function aa(): void;"
};
const fooAA: File = {
path: `${globalTypingsCacheLocation}/node_modules/foo/a/a.d.ts`,
content: "export function a (): void;"
@ -1899,7 +1916,7 @@ namespace ts.projectSystem {
import * as a from "foo/a/a";
import * as b from "foo/a/b";
import * as c from "foo/a/c";
`, ["foo"], [fooAA, fooAB, fooAC]);
`, ["foo"], [fooIndex, fooAA, fooAB, fooAC]);
});
it("should handle node core modules", () => {
@ -1958,12 +1975,21 @@ declare module "stream" {
host.checkTimeoutQueueLengthAndRun(2);
checkProjectActualFiles(proj, [file.path, libFile.path, nodeTyping.path]);
// Here, since typings doesnt contain node typings and resolution fails and
// node typings go out of project,
// but because we handle core node modules when resolving from typings cache
// node typings are included in the project
host.checkTimeoutQueueLengthAndRun(2);
// Here, since typings dont change, there is no timeout scheduled
host.checkTimeoutQueueLength(0);
checkProjectActualFiles(proj, [file.path, libFile.path, nodeTyping.path]);
projectService.applyChangesInOpenFiles(/*openFiles*/ undefined, arrayIterator([{
fileName: file.path,
changes: arrayIterator([{
span: { start: file.content.indexOf("const"), length: 0 },
newText: `const bar = require("bar");`
}])
}]));
proj.updateGraph(); // Update the graph
checkProjectActualFiles(proj, [file.path, libFile.path, nodeTyping.path]);
// Update the typing
host.checkTimeoutQueueLength(0);
assert.isFalse(proj.resolutionCache.isFileWithInvalidatedNonRelativeUnresolvedImports(file.path as Path));
});
});