Do not report config file errors if the file opened isnt from configured project and that project doesnt have the config errors

Fixes #16635
This commit is contained in:
Sheetal Nandi 2017-10-04 13:25:56 -07:00
parent 8b2a219dd4
commit 249725d4b7
6 changed files with 116 additions and 28 deletions

View File

@ -7,7 +7,7 @@ namespace ts.projectSystem {
const file = makeFile("/a.js");
const et = new EventTracker([file]);
et.service.openClientFile(file.path);
assert.equal(et.getEvents().length, 0);
assert.equal(et.getEventsWithName(ts.server.ProjectInfoTelemetryEvent).length, 0);
});
it("only sends an event once", () => {
@ -25,12 +25,12 @@ namespace ts.projectSystem {
et.service.openClientFile(file2.path);
checkNumberOfProjects(et.service, { inferredProjects: 1 });
assert.equal(et.getEvents().length, 0);
assert.equal(et.getEventsWithName(ts.server.ProjectInfoTelemetryEvent).length, 0);
et.service.openClientFile(file.path);
checkNumberOfProjects(et.service, { configuredProjects: 1, inferredProjects: 1 });
assert.equal(et.getEvents().length, 0);
assert.equal(et.getEventsWithName(ts.server.ProjectInfoTelemetryEvent).length, 0);
});
it("counts files by extension", () => {
@ -219,7 +219,7 @@ namespace ts.projectSystem {
const et = new EventTracker([tsconfig, file]);
et.host.getFileSize = () => server.maxProgramSizeForNonTsFiles + 1;
et.service.openClientFile(file.path);
et.getEvent<server.ProjectLanguageServiceStateEvent>(server.ProjectLanguageServiceStateEvent, /*mayBeMore*/ true);
et.getEvent<server.ProjectLanguageServiceStateEvent>(server.ProjectLanguageServiceStateEvent);
et.assertProjectInfoTelemetryEvent({
projectId: Harness.mockHash("/jsconfig.json"),
fileStats: fileStats({ js: 1 }),
@ -255,6 +255,17 @@ namespace ts.projectSystem {
return events;
}
getEventsWithName<T extends server.ProjectServiceEvent>(eventName: T["eventName"]): ReadonlyArray<T> {
let events: T[];
removeWhere(this.events, event => {
if (event.eventName === eventName) {
(events || (events = [])).push(event as T);
return true;
}
});
return events || emptyArray;
}
assertProjectInfoTelemetryEvent(partial: Partial<server.ProjectInfoTelemetryEventData>, configFile?: string): void {
assert.deepEqual(this.getEvent<server.ProjectInfoTelemetryEvent>(ts.server.ProjectInfoTelemetryEvent), {
projectId: Harness.mockHash(configFile || "/tsconfig.json"),
@ -278,10 +289,17 @@ namespace ts.projectSystem {
});
}
getEvent<T extends server.ProjectServiceEvent>(eventName: T["eventName"], mayBeMore = false): T["data"] {
if (mayBeMore) { assert(this.events.length !== 0); }
else { assert.equal(this.events.length, 1); }
const event = this.events.shift();
getEvent<T extends server.ProjectServiceEvent>(eventName: T["eventName"]): T["data"] {
let event: server.ProjectServiceEvent;
removeWhere(this.events, e => {
if (e.eventName === eventName) {
if (event) {
assert(false, "more than one event found");
}
event = e;
return true;
}
});
assert.equal(event.eventName, eventName);
return event.data;
}

View File

@ -463,7 +463,7 @@ namespace ts.projectSystem {
const { configFileName, configFileErrors } = projectService.openClientFile(file1.path);
assert(configFileName, "should find config file");
assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
assert.isTrue(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
checkNumberOfInferredProjects(projectService, 0);
checkNumberOfConfiguredProjects(projectService, 1);
@ -503,7 +503,7 @@ namespace ts.projectSystem {
const { configFileName, configFileErrors } = projectService.openClientFile(file1.path);
assert(configFileName, "should find config file");
assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
assert.isTrue(!configFileErrors || configFileErrors.length === 0, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
checkNumberOfInferredProjects(projectService, 0);
checkNumberOfConfiguredProjects(projectService, 1);
@ -3169,6 +3169,69 @@ namespace ts.projectSystem {
host.runQueuedTimeoutCallbacks();
serverEventManager.checkEventCountOfType("configFileDiag", 3);
});
it("are generated when the config file doesnot include file opened but has errors", () => {
const serverEventManager = new TestServerEventManager();
const file = {
path: "/a/b/app.ts",
content: "let x = 10"
};
const file2 = {
path: "/a/b/test.ts",
content: "let x = 10"
};
const configFile = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {
"foo": "bar",
"allowJS": true
},
"files": ["app.ts"]
}`
};
const host = createServerHost([file, file2, libFile, configFile]);
const session = createSession(host, {
canUseEvents: true,
eventHandler: serverEventManager.handler
});
openFilesForSession([file2], session);
serverEventManager.checkEventCountOfType("configFileDiag", 1);
for (const event of serverEventManager.events) {
if (event.eventName === "configFileDiag") {
assert.equal(event.data.configFileName, configFile.path);
assert.equal(event.data.triggerFile, file2.path);
return;
}
}
});
it("are not generated when the config file doesnot include file opened and doesnt contain any errors", () => {
const serverEventManager = new TestServerEventManager();
const file = {
path: "/a/b/app.ts",
content: "let x = 10"
};
const file2 = {
path: "/a/b/test.ts",
content: "let x = 10"
};
const configFile = {
path: "/a/b/tsconfig.json",
content: `{
"files": ["app.ts"]
}`
};
const host = createServerHost([file, file2, libFile, configFile]);
const session = createSession(host, {
canUseEvents: true,
eventHandler: serverEventManager.handler
});
openFilesForSession([file2], session);
serverEventManager.checkEventCountOfType("configFileDiag", 0);
});
});
describe("skipLibCheck", () => {

View File

@ -1561,14 +1561,17 @@ namespace ts.server {
project.watchWildcards(projectOptions.wildcardDirectories);
}
this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typeAcquisition, projectOptions.compileOnSave);
this.sendConfigFileDiagEvent(project, configFileName);
}
private sendConfigFileDiagEvent(project: ConfiguredProject, triggerFile: NormalizedPath) {
if (!this.eventHandler) {
return;
}
this.eventHandler(<ConfigFileDiagEvent>{
eventName: ConfigFileDiagEvent,
data: { configFileName, diagnostics: project.getGlobalProjectErrors() || [], triggerFile: configFileName }
data: { configFileName: project.getConfigFilePath(), diagnostics: project.getAllProjectErrors(), triggerFile }
});
}
@ -1888,6 +1891,7 @@ namespace ts.server {
openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult {
let configFileName: NormalizedPath;
let sendConfigFileDiagEvent = false;
let configFileErrors: ReadonlyArray<Diagnostic>;
const info = this.getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName, fileContent, scriptKind, hasMixedContent);
@ -1898,14 +1902,8 @@ namespace ts.server {
project = this.findConfiguredProjectByProjectName(configFileName);
if (!project) {
project = this.createConfiguredProject(configFileName);
// even if opening config file was successful, it could still
// contain errors that were tolerated.
const errors = project.getGlobalProjectErrors();
if (errors && errors.length > 0) {
// set configFileErrors only when the errors array is non-empty
configFileErrors = errors;
}
// Send the event only if the project got created as part of this open request
sendConfigFileDiagEvent = true;
}
}
}
@ -1919,10 +1917,21 @@ namespace ts.server {
// At this point if file is part of any any configured or external project, then it would be present in the containing projects
// So if it still doesnt have any containing projects, it needs to be part of inferred project
if (info.isOrphan()) {
// Since the file isnt part of configured project,
// report config file and its error only if config file found had errors (and hence may be didnt include the file)
if (sendConfigFileDiagEvent && !project.getAllProjectErrors().length) {
configFileName = undefined;
sendConfigFileDiagEvent = false;
}
this.assignOrphanScriptInfoToInferredProject(info, projectRootPath);
}
this.addToListOfOpenFiles(info);
if (sendConfigFileDiagEvent) {
configFileErrors = project.getAllProjectErrors();
this.sendConfigFileDiagEvent(project as ConfiguredProject, fileName);
}
// Remove the configured projects that have zero references from open files.
// This was postponed from closeOpenFile to after opening next file,
// so that we can reuse the project if we need to right away
@ -1938,6 +1947,7 @@ namespace ts.server {
// the file from that old project is reopened because of opening file from here.
this.deleteOrphanScriptInfoNotInAnyProject();
this.printProjects();
return { configFileName, configFileErrors };
}

View File

@ -1271,14 +1271,14 @@ namespace ts.server {
* Get the errors that dont have any file name associated
*/
getGlobalProjectErrors(): ReadonlyArray<Diagnostic> {
return filter(this.projectErrors, diagnostic => !diagnostic.file);
return filter(this.projectErrors, diagnostic => !diagnostic.file) || emptyArray;
}
/**
* Get all the project errors
*/
getAllProjectErrors(): ReadonlyArray<Diagnostic> {
return this.projectErrors;
return this.projectErrors || emptyArray;
}
setProjectErrors(projectErrors: Diagnostic[]) {
@ -1335,6 +1335,8 @@ namespace ts.server {
}
this.stopWatchingWildCards();
this.projectErrors = undefined;
this.configFileSpecs = undefined;
}
addOpenRef() {

View File

@ -969,13 +969,7 @@ namespace ts.server {
* @param fileContent is a version of the file content that is known to be more up to date than the one on disk
*/
private openClientFile(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: NormalizedPath) {
const { configFileName, configFileErrors } = this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath);
if (this.eventHandler) {
this.eventHandler({
eventName: "configFileDiag",
data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || emptyArray }
});
}
this.projectService.openClientFileWithNormalizedPath(fileName, fileContent, scriptKind, /*hasMixedContent*/ false, projectRootPath);
}
private getPosition(args: protocol.FileLocationRequestArgs, scriptInfo: ScriptInfo): number {

View File

@ -7517,6 +7517,7 @@ declare namespace ts.server {
private createConfiguredProject(configFileName);
private updateNonInferredProjectFiles<T>(project, files, propertyReader);
private updateNonInferredProject<T>(project, newUncheckedFiles, propertyReader, newOptions, newTypeAcquisition, compileOnSave);
private sendConfigFileDiagEvent(project, triggerFile);
private getOrCreateInferredProjectForProjectRootPathIfEnabled(info, projectRootPath);
private getOrCreateSingleInferredProjectIfEnabled();
private createInferredProject(rootDirectoryForResolution, isSingleInferredProject?, projectRootPath?);