Skip to content

Commit 8075a0d

Browse files
committed
store project errors on project so they can be reported later
1 parent 3953d6b commit 8075a0d

9 files changed

Lines changed: 303 additions & 40 deletions

File tree

src/compiler/core.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/// <reference path="types.ts"/>
22
/// <reference path="performance.ts" />
3+
/// <reference path="diagnosticInformationMap.generated.ts" />
34

45

56
/* @internal */

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 215 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ namespace ts {
3636
}
3737

3838
safeFileList = <Path>"";
39-
postInstallActions: (( map: (t: string[]) => string[]) => void)[] = [];
39+
postInstallActions: ((map: (t: string[]) => string[]) => void)[] = [];
4040

4141
runPostInstallActions(map: (t: string[]) => string[]) {
4242
for (const f of this.postInstallActions) {
@@ -281,7 +281,7 @@ namespace ts {
281281
private timeoutCallbacks = new Callbacks();
282282
private immediateCallbacks = new Callbacks();
283283

284-
readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>();
284+
readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>();
285285
readonly watchedFiles = createMap<FileWatcherCallback[]>();
286286

287287
private filesOrFolders: FileOrFolder[];
@@ -2011,7 +2011,7 @@ namespace ts {
20112011

20122012
checkNumberOfProjects(projectService, { configuredProjects: 1 });
20132013
const p = projectService.configuredProjects[0];
2014-
checkProjectActualFiles(p, [ file1.path ]);
2014+
checkProjectActualFiles(p, [file1.path]);
20152015

20162016
assert(host.fileExists(combinePaths(installer.cachePath, "tsd.json")));
20172017

@@ -2021,10 +2021,10 @@ namespace ts {
20212021
return ["jquery/jquery.d.ts"];
20222022
});
20232023
checkNumberOfProjects(projectService, { configuredProjects: 1 });
2024-
checkProjectActualFiles(p, [ file1.path, jquery.path ]);
2024+
checkProjectActualFiles(p, [file1.path, jquery.path]);
20252025
});
20262026

2027-
it ("inferred project (tsd installed)", () => {
2027+
it("inferred project (tsd installed)", () => {
20282028
const file1 = {
20292029
path: "/a/b/app.js",
20302030
content: ""
@@ -2051,7 +2051,7 @@ namespace ts {
20512051

20522052
checkNumberOfProjects(projectService, { inferredProjects: 1 });
20532053
const p = projectService.inferredProjects[0];
2054-
checkProjectActualFiles(p, [ file1.path ]);
2054+
checkProjectActualFiles(p, [file1.path]);
20552055

20562056
assert(host.fileExists(combinePaths(installer.cachePath, "tsd.json")));
20572057

@@ -2061,10 +2061,10 @@ namespace ts {
20612061
return ["jquery/jquery.d.ts"];
20622062
});
20632063
checkNumberOfProjects(projectService, { inferredProjects: 1 });
2064-
checkProjectActualFiles(p, [ file1.path, jquery.path ]);
2064+
checkProjectActualFiles(p, [file1.path, jquery.path]);
20652065
});
20662066

2067-
it ("external project - no typing options, no .d.ts/js files", () => {
2067+
it("external project - no typing options, no .d.ts/js files", () => {
20682068
const file1 = {
20692069
path: "/a/b/app.ts",
20702070
content: ""
@@ -2091,7 +2091,7 @@ namespace ts {
20912091
projectService.checkNumberOfProjects({ externalProjects: 1 });
20922092
});
20932093

2094-
it ("external project - no autoDiscovery in typing options, no .d.ts/js files", () => {
2094+
it("external project - no autoDiscovery in typing options, no .d.ts/js files", () => {
20952095
const file1 = {
20962096
path: "/a/b/app.ts",
20972097
content: ""
@@ -2119,7 +2119,7 @@ namespace ts {
21192119
projectService.checkNumberOfProjects({ externalProjects: 1 });
21202120
});
21212121

2122-
it ("external project - autoDiscovery = true, no .d.ts/js files", () => {
2122+
it("external project - autoDiscovery = true, no .d.ts/js files", () => {
21232123
const file1 = {
21242124
path: "/a/b/app.ts",
21252125
content: ""
@@ -2156,4 +2156,209 @@ namespace ts {
21562156
assert.isTrue(runTsdIsCalled, "expected 'runTsdIsCalled' to be true");
21572157
});
21582158
});
2159+
2160+
describe("Project errors", () => {
2161+
function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: string[]) {
2162+
assert.isTrue(projectFiles !== undefined, "missing project files");
2163+
const errors = projectFiles.projectErrors;
2164+
assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`);
2165+
if (expectedErrors.length) {
2166+
for (let i = 0; i < errors.length; i++) {
2167+
const actualMessage = flattenDiagnosticMessageText(errors[i].messageText, "\n");
2168+
const expectedMessage = expectedErrors[i];
2169+
assert.equal(actualMessage, expectedMessage, "error message does not match");
2170+
}
2171+
}
2172+
}
2173+
2174+
it("external project - diagnostics for missing files", () => {
2175+
const file1 = {
2176+
path: "/a/b/app.ts",
2177+
content: ""
2178+
};
2179+
const file2 = {
2180+
path: "/a/b/lib.ts",
2181+
content: ""
2182+
};
2183+
// only file1 exists - expect error
2184+
const host = createServerHost([file1]);
2185+
const projectService = createProjectService(host);
2186+
const projectFileName = "/a/b/test.csproj";
2187+
2188+
{
2189+
projectService.openExternalProject({
2190+
projectFileName,
2191+
options: {},
2192+
rootFiles: toExternalFiles([file1.path, file2.path])
2193+
});
2194+
2195+
projectService.checkNumberOfProjects({ externalProjects: 1 });
2196+
const knownProjects = projectService.synchronizeProjectList([]);
2197+
checkProjectErrors(knownProjects[0], ["File '/a/b/lib.ts' not found."]);
2198+
}
2199+
// only file2 exists - expect error
2200+
host.reloadFS([file2]);
2201+
{
2202+
projectService.openExternalProject({
2203+
projectFileName,
2204+
options: {},
2205+
rootFiles: toExternalFiles([file1.path, file2.path])
2206+
});
2207+
projectService.checkNumberOfProjects({ externalProjects: 1 });
2208+
const knownProjects = projectService.synchronizeProjectList([]);
2209+
checkProjectErrors(knownProjects[0], ["File '/a/b/app.ts' not found."]);
2210+
}
2211+
2212+
// both files exist - expect no errors
2213+
host.reloadFS([file1, file2]);
2214+
{
2215+
projectService.openExternalProject({
2216+
projectFileName,
2217+
options: {},
2218+
rootFiles: toExternalFiles([file1.path, file2.path])
2219+
});
2220+
2221+
projectService.checkNumberOfProjects({ externalProjects: 1 });
2222+
const knownProjects = projectService.synchronizeProjectList([]);
2223+
checkProjectErrors(knownProjects[0], []);
2224+
}
2225+
});
2226+
2227+
it("configured projects - diagnostics for missing files", () => {
2228+
const file1 = {
2229+
path: "/a/b/app.ts",
2230+
content: ""
2231+
};
2232+
const file2 = {
2233+
path: "/a/b/lib.ts",
2234+
content: ""
2235+
};
2236+
const config = {
2237+
path: "/a/b/tsconfig.json",
2238+
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
2239+
};
2240+
const host = createServerHost([file1, config]);
2241+
const projectService = createProjectService(host);
2242+
2243+
projectService.openClientFile(file1.path);
2244+
projectService.checkNumberOfProjects({ configuredProjects: 1 });
2245+
checkProjectErrors(projectService.synchronizeProjectList([])[0], ["File '/a/b/lib.ts' not found."]);
2246+
2247+
host.reloadFS([file1, file2, config]);
2248+
2249+
projectService.openClientFile(file1.path);
2250+
projectService.checkNumberOfProjects({ configuredProjects: 1 });
2251+
checkProjectErrors(projectService.synchronizeProjectList([])[0], []);
2252+
});
2253+
2254+
it("configured projects - diagnostics for corrupted config 1", () => {
2255+
const file1 = {
2256+
path: "/a/b/app.ts",
2257+
content: ""
2258+
};
2259+
const file2 = {
2260+
path: "/a/b/lib.ts",
2261+
content: ""
2262+
};
2263+
const correctConfig = {
2264+
path: "/a/b/tsconfig.json",
2265+
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
2266+
};
2267+
const corruptedConfig = {
2268+
path: correctConfig.path,
2269+
content: correctConfig.content.substr(1)
2270+
};
2271+
const host = createServerHost([file1, file2, corruptedConfig]);
2272+
const projectService = createProjectService(host);
2273+
2274+
projectService.openClientFile(file1.path);
2275+
{
2276+
projectService.checkNumberOfProjects({ inferredProjects: 1, configuredProjects: 1 });
2277+
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
2278+
assert.isTrue(configuredProject !== undefined, "should find configured project");
2279+
checkProjectErrors(configuredProject, [`Failed to parse file \'/a/b/tsconfig.json\': Unexpected token : in JSON at position 7.`]);
2280+
}
2281+
// fix config and trigger watcher
2282+
host.reloadFS([file1, file2, correctConfig]);
2283+
host.triggerFileWatcherCallback(correctConfig.path, /*false*/);
2284+
{
2285+
projectService.checkNumberOfProjects({ configuredProjects: 1 });
2286+
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
2287+
assert.isTrue(configuredProject !== undefined, "should find configured project");
2288+
checkProjectErrors(configuredProject, []);
2289+
}
2290+
});
2291+
2292+
it("configured projects - diagnostics for corrupted config 2", () => {
2293+
const file1 = {
2294+
path: "/a/b/app.ts",
2295+
content: ""
2296+
};
2297+
const file2 = {
2298+
path: "/a/b/lib.ts",
2299+
content: ""
2300+
};
2301+
const correctConfig = {
2302+
path: "/a/b/tsconfig.json",
2303+
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
2304+
};
2305+
const corruptedConfig = {
2306+
path: correctConfig.path,
2307+
content: correctConfig.content.substr(1)
2308+
};
2309+
const host = createServerHost([file1, file2, correctConfig]);
2310+
const projectService = createProjectService(host);
2311+
2312+
projectService.openClientFile(file1.path);
2313+
{
2314+
projectService.checkNumberOfProjects({ configuredProjects: 1 });
2315+
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
2316+
assert.isTrue(configuredProject !== undefined, "should find configured project");
2317+
checkProjectErrors(configuredProject, []);
2318+
}
2319+
// fix config and trigger watcher
2320+
host.reloadFS([file1, file2, corruptedConfig]);
2321+
host.triggerFileWatcherCallback(corruptedConfig.path, /*false*/);
2322+
{
2323+
projectService.checkNumberOfProjects({ inferredProjects: 1, configuredProjects: 1 });
2324+
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
2325+
assert.isTrue(configuredProject !== undefined, "should find configured project");
2326+
checkProjectErrors(configuredProject, [`Failed to parse file \'/a/b/tsconfig.json\': Unexpected token : in JSON at position 7.`]);
2327+
}
2328+
});
2329+
});
2330+
2331+
describe("Proper errors", () => {
2332+
it("document is not contained in project", () => {
2333+
const file1 = {
2334+
path: "/a/b/app.ts",
2335+
content: ""
2336+
};
2337+
const corruptedConfig = {
2338+
path: "/a/b/tsconfig.json",
2339+
content: "{"
2340+
};
2341+
const host = createServerHost([file1, corruptedConfig]);
2342+
const projectService = createProjectService(host);
2343+
2344+
projectService.openClientFile(file1.path);
2345+
projectService.checkNumberOfProjects({ inferredProjects: 1, configuredProjects: 1 });
2346+
2347+
const project = projectService.findProject(corruptedConfig.path);
2348+
let expectedMessage: string;
2349+
try {
2350+
server.Errors.ThrowProjectDoesNotContainDocument(file1.path, project);
2351+
assert(false, "should not get there");
2352+
}
2353+
catch (e) {
2354+
expectedMessage = (<Error>e).message;
2355+
}
2356+
try {
2357+
project.getScriptInfo(file1.path);
2358+
}
2359+
catch (e) {
2360+
assert.equal((<Error>e).message, expectedMessage, "Unexpected error");
2361+
}
2362+
});
2363+
});
21592364
}

0 commit comments

Comments
 (0)