mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
store project errors on project so they can be reported later
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
+25
-5
@@ -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
|
||||
|
||||
Vendored
+4
@@ -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[];
|
||||
|
||||
+15
-1
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user