mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-21 13:14:43 -06:00
store project errors on project so they can be reported later
This commit is contained in:
parent
3953d6ba96
commit
8075a0dd72
@ -1,5 +1,6 @@
|
||||
/// <reference path="types.ts"/>
|
||||
/// <reference path="performance.ts" />
|
||||
/// <reference path="diagnosticInformationMap.generated.ts" />
|
||||
|
||||
|
||||
/* @internal */
|
||||
|
||||
@ -36,7 +36,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
safeFileList = <Path>"";
|
||||
postInstallActions: (( map: (t: string[]) => string[]) => void)[] = [];
|
||||
postInstallActions: ((map: (t: string[]) => string[]) => void)[] = [];
|
||||
|
||||
runPostInstallActions(map: (t: string[]) => string[]) {
|
||||
for (const f of this.postInstallActions) {
|
||||
@ -281,7 +281,7 @@ namespace ts {
|
||||
private timeoutCallbacks = new Callbacks();
|
||||
private immediateCallbacks = new Callbacks();
|
||||
|
||||
readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>();
|
||||
readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>();
|
||||
readonly watchedFiles = createMap<FileWatcherCallback[]>();
|
||||
|
||||
private filesOrFolders: FileOrFolder[];
|
||||
@ -2011,7 +2011,7 @@ namespace ts {
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
const p = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(p, [ file1.path ]);
|
||||
checkProjectActualFiles(p, [file1.path]);
|
||||
|
||||
assert(host.fileExists(combinePaths(installer.cachePath, "tsd.json")));
|
||||
|
||||
@ -2021,10 +2021,10 @@ namespace ts {
|
||||
return ["jquery/jquery.d.ts"];
|
||||
});
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(p, [ file1.path, jquery.path ]);
|
||||
checkProjectActualFiles(p, [file1.path, jquery.path]);
|
||||
});
|
||||
|
||||
it ("inferred project (tsd installed)", () => {
|
||||
it("inferred project (tsd installed)", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.js",
|
||||
content: ""
|
||||
@ -2051,7 +2051,7 @@ namespace ts {
|
||||
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
const p = projectService.inferredProjects[0];
|
||||
checkProjectActualFiles(p, [ file1.path ]);
|
||||
checkProjectActualFiles(p, [file1.path]);
|
||||
|
||||
assert(host.fileExists(combinePaths(installer.cachePath, "tsd.json")));
|
||||
|
||||
@ -2061,10 +2061,10 @@ namespace ts {
|
||||
return ["jquery/jquery.d.ts"];
|
||||
});
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkProjectActualFiles(p, [ file1.path, jquery.path ]);
|
||||
checkProjectActualFiles(p, [file1.path, jquery.path]);
|
||||
});
|
||||
|
||||
it ("external project - no typing options, no .d.ts/js files", () => {
|
||||
it("external project - no typing options, no .d.ts/js files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
@ -2091,7 +2091,7 @@ namespace ts {
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
});
|
||||
|
||||
it ("external project - no autoDiscovery in typing options, no .d.ts/js files", () => {
|
||||
it("external project - no autoDiscovery in typing options, no .d.ts/js files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
@ -2119,7 +2119,7 @@ namespace ts {
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
});
|
||||
|
||||
it ("external project - autoDiscovery = true, no .d.ts/js files", () => {
|
||||
it("external project - autoDiscovery = true, no .d.ts/js files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
@ -2156,4 +2156,209 @@ namespace ts {
|
||||
assert.isTrue(runTsdIsCalled, "expected 'runTsdIsCalled' to be true");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Project errors", () => {
|
||||
function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: string[]) {
|
||||
assert.isTrue(projectFiles !== undefined, "missing project files");
|
||||
const errors = projectFiles.projectErrors;
|
||||
assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`);
|
||||
if (expectedErrors.length) {
|
||||
for (let i = 0; i < errors.length; i++) {
|
||||
const actualMessage = flattenDiagnosticMessageText(errors[i].messageText, "\n");
|
||||
const expectedMessage = expectedErrors[i];
|
||||
assert.equal(actualMessage, expectedMessage, "error message does not match");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("external project - diagnostics for missing files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
// only file1 exists - expect error
|
||||
const host = createServerHost([file1]);
|
||||
const projectService = createProjectService(host);
|
||||
const projectFileName = "/a/b/test.csproj";
|
||||
|
||||
{
|
||||
projectService.openExternalProject({
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: toExternalFiles([file1.path, file2.path])
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
const knownProjects = projectService.synchronizeProjectList([]);
|
||||
checkProjectErrors(knownProjects[0], ["File '/a/b/lib.ts' not found."]);
|
||||
}
|
||||
// only file2 exists - expect error
|
||||
host.reloadFS([file2]);
|
||||
{
|
||||
projectService.openExternalProject({
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: toExternalFiles([file1.path, file2.path])
|
||||
});
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
const knownProjects = projectService.synchronizeProjectList([]);
|
||||
checkProjectErrors(knownProjects[0], ["File '/a/b/app.ts' not found."]);
|
||||
}
|
||||
|
||||
// both files exist - expect no errors
|
||||
host.reloadFS([file1, file2]);
|
||||
{
|
||||
projectService.openExternalProject({
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: toExternalFiles([file1.path, file2.path])
|
||||
});
|
||||
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
const knownProjects = projectService.synchronizeProjectList([]);
|
||||
checkProjectErrors(knownProjects[0], []);
|
||||
}
|
||||
});
|
||||
|
||||
it("configured projects - diagnostics for missing files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const config = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
|
||||
};
|
||||
const host = createServerHost([file1, config]);
|
||||
const projectService = createProjectService(host);
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectErrors(projectService.synchronizeProjectList([])[0], ["File '/a/b/lib.ts' not found."]);
|
||||
|
||||
host.reloadFS([file1, file2, config]);
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectErrors(projectService.synchronizeProjectList([])[0], []);
|
||||
});
|
||||
|
||||
it("configured projects - diagnostics for corrupted config 1", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const correctConfig = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
|
||||
};
|
||||
const corruptedConfig = {
|
||||
path: correctConfig.path,
|
||||
content: correctConfig.content.substr(1)
|
||||
};
|
||||
const host = createServerHost([file1, file2, corruptedConfig]);
|
||||
const projectService = createProjectService(host);
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ inferredProjects: 1, configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, [`Failed to parse file \'/a/b/tsconfig.json\': Unexpected token : in JSON at position 7.`]);
|
||||
}
|
||||
// fix config and trigger watcher
|
||||
host.reloadFS([file1, file2, correctConfig]);
|
||||
host.triggerFileWatcherCallback(correctConfig.path, /*false*/);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, []);
|
||||
}
|
||||
});
|
||||
|
||||
it("configured projects - diagnostics for corrupted config 2", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const file2 = {
|
||||
path: "/a/b/lib.ts",
|
||||
content: ""
|
||||
};
|
||||
const correctConfig = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: JSON.stringify({ files: [file1, file2].map(f => getBaseFileName(f.path)) })
|
||||
};
|
||||
const corruptedConfig = {
|
||||
path: correctConfig.path,
|
||||
content: correctConfig.content.substr(1)
|
||||
};
|
||||
const host = createServerHost([file1, file2, correctConfig]);
|
||||
const projectService = createProjectService(host);
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, []);
|
||||
}
|
||||
// fix config and trigger watcher
|
||||
host.reloadFS([file1, file2, corruptedConfig]);
|
||||
host.triggerFileWatcherCallback(corruptedConfig.path, /*false*/);
|
||||
{
|
||||
projectService.checkNumberOfProjects({ inferredProjects: 1, configuredProjects: 1 });
|
||||
const configuredProject = forEach(projectService.synchronizeProjectList([]), f => f.info.projectName === corruptedConfig.path && f);
|
||||
assert.isTrue(configuredProject !== undefined, "should find configured project");
|
||||
checkProjectErrors(configuredProject, [`Failed to parse file \'/a/b/tsconfig.json\': Unexpected token : in JSON at position 7.`]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("Proper errors", () => {
|
||||
it("document is not contained in project", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
};
|
||||
const corruptedConfig = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: "{"
|
||||
};
|
||||
const host = createServerHost([file1, corruptedConfig]);
|
||||
const projectService = createProjectService(host);
|
||||
|
||||
projectService.openClientFile(file1.path);
|
||||
projectService.checkNumberOfProjects({ inferredProjects: 1, configuredProjects: 1 });
|
||||
|
||||
const project = projectService.findProject(corruptedConfig.path);
|
||||
let expectedMessage: string;
|
||||
try {
|
||||
server.Errors.ThrowProjectDoesNotContainDocument(file1.path, project);
|
||||
assert(false, "should not get there");
|
||||
}
|
||||
catch (e) {
|
||||
expectedMessage = (<Error>e).message;
|
||||
}
|
||||
try {
|
||||
project.getScriptInfo(file1.path);
|
||||
}
|
||||
catch (e) {
|
||||
assert.equal((<Error>e).message, expectedMessage, "Unexpected error");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -30,7 +30,7 @@ namespace ts.server {
|
||||
|
||||
interface ConfigFileConversionResult {
|
||||
success: boolean;
|
||||
errors?: Diagnostic[];
|
||||
configFileErrors?: Diagnostic[];
|
||||
|
||||
projectOptions?: ProjectOptions;
|
||||
}
|
||||
@ -73,6 +73,10 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
function createFileNotFoundDiagnostic(fileName: string) {
|
||||
return createCompilerDiagnostic(Diagnostics.File_0_not_found, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: enforce invariants:
|
||||
* - script info can be never migrate to state - root file in inferred project, this is only a starting point
|
||||
@ -655,7 +659,7 @@ namespace ts.server {
|
||||
|
||||
const configObj = parseConfigFileTextToJson(configFilename, this.host.readFile(configFilename));
|
||||
if (configObj.error) {
|
||||
return { success: false, errors: [configObj.error] };
|
||||
return { success: false, configFileErrors: [configObj.error] };
|
||||
}
|
||||
|
||||
const parsedCommandLine = parseJsonConfigFileContent(
|
||||
@ -668,12 +672,12 @@ namespace ts.server {
|
||||
Debug.assert(!!parsedCommandLine.fileNames);
|
||||
|
||||
if (parsedCommandLine.errors && (parsedCommandLine.errors.length > 0)) {
|
||||
return { success: false, errors: parsedCommandLine.errors };
|
||||
return { success: false, configFileErrors: parsedCommandLine.errors };
|
||||
}
|
||||
|
||||
if (parsedCommandLine.fileNames.length === 0) {
|
||||
const error = createCompilerDiagnostic(Diagnostics.The_config_file_0_found_doesn_t_contain_any_source_files, configFilename);
|
||||
return { success: false, errors: [error] };
|
||||
return { success: false, configFileErrors: [error] };
|
||||
}
|
||||
|
||||
const projectOptions: ProjectOptions = {
|
||||
@ -714,10 +718,9 @@ namespace ts.server {
|
||||
/*languageServiceEnabled*/ !this.exceededTotalSizeLimitForNonTsFiles(options, files, externalFilePropertyReader),
|
||||
options.compileOnSave === undefined ? true : options.compileOnSave);
|
||||
|
||||
const errors = this.addFilesToProjectAndUpdateGraph(project, files, externalFilePropertyReader, /*clientFileName*/ undefined, typingOptions);
|
||||
|
||||
this.addFilesToProjectAndUpdateGraph(project, files, externalFilePropertyReader, /*clientFileName*/ undefined, typingOptions);
|
||||
this.externalProjects.push(project);
|
||||
return { project, errors };
|
||||
return project;
|
||||
}
|
||||
|
||||
private createAndAddConfiguredProject(configFileName: NormalizedPath, projectOptions: ProjectOptions, clientFileName?: string) {
|
||||
@ -732,7 +735,7 @@ namespace ts.server {
|
||||
/*languageServiceEnabled*/ !sizeLimitExceeded,
|
||||
projectOptions.compileOnSave === undefined ? false : projectOptions.compileOnSave);
|
||||
|
||||
const errors = this.addFilesToProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typingOptions);
|
||||
this.addFilesToProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typingOptions);
|
||||
|
||||
project.watchConfigFile(project => this.onConfigChangedForConfiguredProject(project));
|
||||
if (!sizeLimitExceeded) {
|
||||
@ -741,7 +744,7 @@ namespace ts.server {
|
||||
project.watchWildcards((project, path) => this.onSourceFileInDirectoryChangedForConfiguredProject(project, path));
|
||||
|
||||
this.configuredProjects.push(project);
|
||||
return { project, errors };
|
||||
return project;
|
||||
}
|
||||
|
||||
private watchConfigDirectoryForProject(project: ConfiguredProject, options: ProjectOptions): void {
|
||||
@ -750,7 +753,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typingOptions: TypingOptions): Diagnostic[] {
|
||||
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typingOptions: TypingOptions): void {
|
||||
let errors: Diagnostic[];
|
||||
for (const f of files) {
|
||||
const rootFilename = propertyReader.getFileName(f);
|
||||
@ -761,32 +764,37 @@ namespace ts.server {
|
||||
project.addRoot(info);
|
||||
}
|
||||
else {
|
||||
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.File_0_not_found, rootFilename));
|
||||
(errors || (errors = [])).push(createFileNotFoundDiagnostic(rootFilename));
|
||||
}
|
||||
}
|
||||
project.setProjectErrors(errors);
|
||||
project.setTypingOptions(typingOptions);
|
||||
project.updateGraph();
|
||||
return errors;
|
||||
}
|
||||
|
||||
private openConfigFile(configFileName: NormalizedPath, clientFileName?: string): OpenConfigFileResult {
|
||||
const conversionResult = this.convertConfigFileContentToProjectOptions(configFileName);
|
||||
if (!conversionResult.success) {
|
||||
return { success: false, errors: conversionResult.errors };
|
||||
// open project with no files and set errors on the project
|
||||
const project = this.createAndAddConfiguredProject(configFileName, { files: [], compilerOptions: {} }, clientFileName);
|
||||
project.setProjectErrors(conversionResult.configFileErrors);
|
||||
return { success: false, errors: conversionResult.configFileErrors };
|
||||
}
|
||||
const { project, errors } = this.createAndAddConfiguredProject(configFileName, conversionResult.projectOptions, clientFileName);
|
||||
return { success: true, project, errors };
|
||||
const project = this.createAndAddConfiguredProject(configFileName, conversionResult.projectOptions, clientFileName);
|
||||
return { success: true, project, errors: project.getProjectErrors() };
|
||||
}
|
||||
|
||||
private updateNonInferredProject<T>(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader<T>, newOptions: CompilerOptions, newTypingOptions: TypingOptions, compileOnSave: boolean) {
|
||||
private updateNonInferredProject<T>(project: ExternalProject | ConfiguredProject, newUncheckedFiles: T[], propertyReader: FilePropertyReader<T>, newOptions: CompilerOptions, newTypingOptions: TypingOptions, compileOnSave: boolean, configFileErrors: Diagnostic[]) {
|
||||
const oldRootScriptInfos = project.getRootScriptInfos();
|
||||
const newRootScriptInfos: ScriptInfo[] = [];
|
||||
const newRootScriptInfoMap: NormalizedPathMap<ScriptInfo> = createNormalizedPathMap<ScriptInfo>();
|
||||
|
||||
let projectErrors: Diagnostic[];
|
||||
let rootFilesChanged = false;
|
||||
for (const f of newUncheckedFiles) {
|
||||
const newRootFile = propertyReader.getFileName(f);
|
||||
if (!this.host.fileExists(newRootFile)) {
|
||||
(projectErrors || (projectErrors = [])).push(createFileNotFoundDiagnostic(newRootFile));
|
||||
continue;
|
||||
}
|
||||
const normalizedPath = toNormalizedPath(newRootFile);
|
||||
@ -840,6 +848,8 @@ namespace ts.server {
|
||||
project.setCompilerOptions(newOptions);
|
||||
(<ExternalProject | ConfiguredProject>project).setTypingOptions(newTypingOptions);
|
||||
project.compileOnSaveEnabled = !!compileOnSave;
|
||||
project.setProjectErrors(concatenate(configFileErrors, projectErrors));
|
||||
|
||||
project.updateGraph();
|
||||
}
|
||||
|
||||
@ -850,9 +860,11 @@ namespace ts.server {
|
||||
return;
|
||||
}
|
||||
|
||||
const { success, projectOptions, errors } = this.convertConfigFileContentToProjectOptions(project.configFileName);
|
||||
const { success, projectOptions, configFileErrors } = this.convertConfigFileContentToProjectOptions(project.configFileName);
|
||||
if (!success) {
|
||||
return errors;
|
||||
// reset project settings to default
|
||||
this.updateNonInferredProject(project, [], fileNamePropertyReader, {}, {}, /*compileOnSave*/false, configFileErrors);
|
||||
return configFileErrors;
|
||||
}
|
||||
|
||||
if (this.exceededTotalSizeLimitForNonTsFiles(projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader)) {
|
||||
@ -869,7 +881,7 @@ namespace ts.server {
|
||||
project.enableLanguageService();
|
||||
}
|
||||
this.watchConfigDirectoryForProject(project, projectOptions);
|
||||
this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typingOptions, projectOptions.compileOnSave);
|
||||
this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typingOptions, projectOptions.compileOnSave, configFileErrors);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1052,15 +1064,15 @@ namespace ts.server {
|
||||
this.printProjects();
|
||||
}
|
||||
|
||||
private collectChanges(lastKnownProjectVersions: protocol.ProjectVersionInfo[], currentProjects: Project[], result: protocol.ProjectFiles[]): void {
|
||||
private collectChanges(lastKnownProjectVersions: protocol.ProjectVersionInfo[], currentProjects: Project[], result: ProjectFilesWithTSDiagnostics[]): void {
|
||||
for (const proj of currentProjects) {
|
||||
const knownProject = forEach(lastKnownProjectVersions, p => p.projectName === proj.getProjectName() && p);
|
||||
result.push(proj.getChangesSinceVersion(knownProject && knownProject.version));
|
||||
}
|
||||
}
|
||||
|
||||
synchronizeProjectList(knownProjects: protocol.ProjectVersionInfo[]): protocol.ProjectFiles[] {
|
||||
const files: protocol.ProjectFiles[] = [];
|
||||
synchronizeProjectList(knownProjects: protocol.ProjectVersionInfo[]): ProjectFilesWithTSDiagnostics[] {
|
||||
const files: ProjectFilesWithTSDiagnostics[] = [];
|
||||
this.collectChanges(knownProjects, this.externalProjects, files);
|
||||
this.collectChanges(knownProjects, this.configuredProjects, files);
|
||||
this.collectChanges(knownProjects, this.inferredProjects, files);
|
||||
@ -1139,7 +1151,7 @@ namespace ts.server {
|
||||
openExternalProject(proj: protocol.ExternalProject): void {
|
||||
const externalProject = this.findExternalProjectByProjectName(proj.projectFileName);
|
||||
if (externalProject) {
|
||||
this.updateNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, proj.options, proj.typingOptions, proj.options.compileOnSave);
|
||||
this.updateNonInferredProject(externalProject, proj.rootFiles, externalFilePropertyReader, proj.options, proj.typingOptions, proj.options.compileOnSave, /*configFileErrors*/ undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -76,6 +76,10 @@ namespace ts.server {
|
||||
return this.compilationSettings;
|
||||
}
|
||||
|
||||
useCaseSensitiveFileNames() {
|
||||
return this.host.useCaseSensitiveFileNames;
|
||||
}
|
||||
|
||||
getCancellationToken() {
|
||||
return this.cancellationToken;
|
||||
}
|
||||
|
||||
@ -26,6 +26,10 @@ namespace ts.server {
|
||||
return project.getRootScriptInfos().every(f => fileExtensionIsAny(f.fileName, jsOrDts));
|
||||
}
|
||||
|
||||
export interface ProjectFilesWithTSDiagnostics extends protocol.ProjectFiles {
|
||||
projectErrors: Diagnostic[];
|
||||
}
|
||||
|
||||
export abstract class Project {
|
||||
private rootFiles: ScriptInfo[] = [];
|
||||
private rootFilesMap: FileMap<ScriptInfo> = createFileMap<ScriptInfo>();
|
||||
@ -57,6 +61,8 @@ namespace ts.server {
|
||||
|
||||
private typingFiles: TypingsArray;
|
||||
|
||||
protected projectErrors: Diagnostic[];
|
||||
|
||||
constructor(
|
||||
readonly projectKind: ProjectKind,
|
||||
readonly projectService: ProjectService,
|
||||
@ -87,6 +93,10 @@ namespace ts.server {
|
||||
this.markAsDirty();
|
||||
}
|
||||
|
||||
getProjectErrors() {
|
||||
return this.projectErrors;
|
||||
}
|
||||
|
||||
getLanguageService(ensureSynchronized = true): LanguageService {
|
||||
if (ensureSynchronized) {
|
||||
this.updateGraph();
|
||||
@ -329,7 +339,9 @@ namespace ts.server {
|
||||
|
||||
getScriptInfoForNormalizedPath(fileName: NormalizedPath) {
|
||||
const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ false);
|
||||
Debug.assert(!scriptInfo || scriptInfo.isAttached(this));
|
||||
if (scriptInfo && !scriptInfo.isAttached(this)) {
|
||||
return Errors.ThrowProjectDoesNotContainDocument(fileName, this);
|
||||
}
|
||||
return scriptInfo;
|
||||
}
|
||||
|
||||
@ -371,7 +383,7 @@ namespace ts.server {
|
||||
return false;
|
||||
}
|
||||
|
||||
getChangesSinceVersion(lastKnownVersion?: number): protocol.ProjectFiles {
|
||||
getChangesSinceVersion(lastKnownVersion?: number): ProjectFilesWithTSDiagnostics {
|
||||
this.updateGraph();
|
||||
|
||||
const info = {
|
||||
@ -384,7 +396,7 @@ namespace ts.server {
|
||||
if (this.lastReportedFileNames && lastKnownVersion === this.lastReportedVersion) {
|
||||
// if current structure version is the same - return info witout any changes
|
||||
if (this.projectStructureVersion == this.lastReportedVersion) {
|
||||
return { info };
|
||||
return { info, projectErrors: this.projectErrors };
|
||||
}
|
||||
// compute and return the difference
|
||||
const lastReportedFileNames = this.lastReportedFileNames;
|
||||
@ -406,14 +418,14 @@ namespace ts.server {
|
||||
|
||||
this.lastReportedFileNames = currentFiles;
|
||||
this.lastReportedVersion = this.projectStructureVersion;
|
||||
return { info, changes: { added, removed } };
|
||||
return { info, changes: { added, removed }, projectErrors: this.projectErrors };
|
||||
}
|
||||
else {
|
||||
// unknown version - return everything
|
||||
const projectFileNames = this.getFileNames();
|
||||
this.lastReportedFileNames = arrayToMap(projectFileNames, x => x);
|
||||
this.lastReportedVersion = this.projectStructureVersion;
|
||||
return { info, files: projectFileNames };
|
||||
return { info, files: projectFileNames, projectErrors: this.projectErrors };
|
||||
}
|
||||
}
|
||||
|
||||
@ -544,6 +556,10 @@ namespace ts.server {
|
||||
super(ProjectKind.Configured, projectService, documentRegistry, hasExplicitListOfFiles, languageServiceEnabled, compilerOptions, compileOnSaveEnabled);
|
||||
}
|
||||
|
||||
setProjectErrors(projectErrors: Diagnostic[]) {
|
||||
this.projectErrors = projectErrors;
|
||||
}
|
||||
|
||||
setTypingOptions(newTypingOptions: TypingOptions): void {
|
||||
this.typingOptions = newTypingOptions;
|
||||
}
|
||||
@ -636,6 +652,10 @@ namespace ts.server {
|
||||
return this.typingOptions;
|
||||
}
|
||||
|
||||
setProjectErrors(projectErrors: Diagnostic[]) {
|
||||
this.projectErrors = projectErrors;
|
||||
}
|
||||
|
||||
setTypingOptions(newTypingOptions: TypingOptions): void {
|
||||
if (!newTypingOptions) {
|
||||
// set default typings options
|
||||
|
||||
4
src/server/protocol.d.ts
vendored
4
src/server/protocol.d.ts
vendored
@ -535,6 +535,10 @@ declare namespace ts.server.protocol {
|
||||
changes?: ProjectChanges;
|
||||
}
|
||||
|
||||
export interface ProjectFilesWithDiagnostics extends ProjectFiles {
|
||||
projectErrors: DiagnosticWithLinePosition[];
|
||||
}
|
||||
|
||||
export interface ChangedOpenFile {
|
||||
fileName: string;
|
||||
changes: ts.TextChange[];
|
||||
|
||||
@ -1261,7 +1261,21 @@ namespace ts.server {
|
||||
},
|
||||
[CommandNames.SynchronizeProjectList]: (request: protocol.SynchronizeProjectListRequest) => {
|
||||
const result = this.projectService.synchronizeProjectList(request.arguments.knownProjects);
|
||||
return this.requiredResponse(result);
|
||||
if (!result.some(p => p.projectErrors && p.projectErrors.length !== 0)) {
|
||||
return this.requiredResponse(result);
|
||||
}
|
||||
const converted = map(result, p => {
|
||||
if (!p.projectErrors || p.projectErrors.length === 0) {
|
||||
return p;
|
||||
}
|
||||
return {
|
||||
info: p.info,
|
||||
changes: p.changes,
|
||||
files: p.files,
|
||||
projectErrors: this.convertToDiagnosticsWithLinePosition(p.projectErrors, /*scriptInfo*/ undefined)
|
||||
};
|
||||
});
|
||||
return this.requiredResponse(converted);
|
||||
},
|
||||
[CommandNames.ApplyChangedToOpenFiles]: (request: protocol.ApplyChangedToOpenFilesRequest) => {
|
||||
this.projectService.applyChangesInOpenFiles(request.arguments.openFiles, request.arguments.changedFiles, request.arguments.closedFiles);
|
||||
|
||||
@ -64,6 +64,9 @@ namespace ts.server {
|
||||
export function ThrowProjectLanguageServiceDisabled(): never {
|
||||
throw new Error("The project's language service is disabled.");
|
||||
}
|
||||
export function ThrowProjectDoesNotContainDocument(fileName: string, project: Project): never {
|
||||
throw new Error(`Project '${project.getProjectName()}' does not contain document '${fileName}'`);
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultFormatCodeSettings(host: ServerHost): FormatCodeSettings {
|
||||
|
||||
@ -3053,7 +3053,7 @@ namespace ts {
|
||||
let program: Program;
|
||||
let lastProjectVersion: string;
|
||||
|
||||
const useCaseSensitivefileNames = false;
|
||||
const useCaseSensitivefileNames = host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames();
|
||||
const cancellationToken = new CancellationTokenObject(host.getCancellationToken && host.getCancellationToken());
|
||||
|
||||
const currentDirectory = host.getCurrentDirectory();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user