Skip to content

Commit f0c8831

Browse files
committed
fix(language-server): Do best-effort to initialize composite project when HTML is opened first
An upstream TypeScript performance-motivated change ( microsoft/TypeScript@02b07a1) made it so composite projects are not opened unless the opened file is in the includes/files list of the `tsconfig`. This means that external HTML files do not trigger project loading when they are the first and only file opened. As a best-effort, we attempt to open a TS file with the same name, which will most often be the file for the component of the external template. This is not guaranteed to be the case, so the only way to guarantee this works correctly would be for developers to add the `**/*.html` files to the `includes` list of their `tsconfig[.lib].json`. resolves #2149
1 parent ef85116 commit f0c8831

File tree

1 file changed

+28
-17
lines changed

1 file changed

+28
-17
lines changed

server/src/session.ts

+28-17
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,20 @@ export class Session {
821821
// The content could be newer than that on disk. This could be due to
822822
// buffer in the user's editor which has not been saved to disk.
823823
// See https://github.com/angular/vscode-ng-language-service/issues/632
824-
const result = this.projectService.openClientFile(filePath, text, scriptKind);
824+
let result = this.projectService.openClientFile(filePath, text, scriptKind);
825+
// If the first opened file is an HTML file and the project is a composite/solution-style project with references,
826+
// TypeScript will _not_ open a project unless the file is explicitly included in the files/includes list.
827+
// This is quite unlikely to be the case for HTML files. As a best-effort to fix this, we attempt to open
828+
// a TS file with the same name. Most of the time, this is going to be the component file for the external template.
829+
// https://github.com/angular/vscode-ng-language-service/issues/2149
830+
if (result.configFileName === undefined && languageId === LanguageId.HTML) {
831+
const maybeComponentTsPath = filePath.replace(/\.html$/, '.ts');
832+
if (!this.projectService.openFiles.has(this.projectService.toPath(maybeComponentTsPath))) {
833+
this.projectService.openClientFile(maybeComponentTsPath);
834+
this.projectService.closeClientFile(maybeComponentTsPath);
835+
result = this.projectService.openClientFile(filePath, text, scriptKind);
836+
}
837+
}
825838

826839
const {configFileName, configFileErrors} = result;
827840
if (configFileErrors && configFileErrors.length) {
@@ -831,24 +844,22 @@ export class Session {
831844
const project = configFileName ?
832845
this.projectService.findProject(configFileName) :
833846
this.projectService.getScriptInfo(filePath)?.containingProjects.find(isConfiguredProject);
834-
if (!project) {
847+
if (!project?.languageServiceEnabled) {
835848
return;
836849
}
837-
if (project.languageServiceEnabled) {
838-
// The act of opening a file can cause the text storage to switchToScriptVersionCache for
839-
// version tracking, which results in an identity change for the source file. This isn't
840-
// typically an issue but the identity can change during an update operation for template
841-
// type-checking, when we _only_ expect the typecheck files to change. This _is_ an issue
842-
// because the because template type-checking should not modify the identity of any other
843-
// source files (other than the generated typecheck files). We need to ensure that the
844-
// compiler is aware of this change that shouldn't have happened and recompiles the file
845-
// because we store references to some string expressions (inline templates, style/template
846-
// urls).
847-
// Note: markAsDirty() is not a public API
848-
(project as any).markAsDirty();
849-
// Show initial diagnostics
850-
this.requestDiagnosticsOnOpenOrChangeFile(filePath, `Opening ${filePath}`);
851-
}
850+
// The act of opening a file can cause the text storage to switchToScriptVersionCache for
851+
// version tracking, which results in an identity change for the source file. This isn't
852+
// typically an issue but the identity can change during an update operation for template
853+
// type-checking, when we _only_ expect the typecheck files to change. This _is_ an issue
854+
// because the because template type-checking should not modify the identity of any other
855+
// source files (other than the generated typecheck files). We need to ensure that the
856+
// compiler is aware of this change that shouldn't have happened and recompiles the file
857+
// because we store references to some string expressions (inline templates, style/template
858+
// urls).
859+
// Note: markAsDirty() is not a public API
860+
(project as any).markAsDirty();
861+
// Show initial diagnostics
862+
this.requestDiagnosticsOnOpenOrChangeFile(filePath, `Opening ${filePath}`);
852863
} catch (error) {
853864
if (this.isProjectLoading) {
854865
this.isProjectLoading = false;

0 commit comments

Comments
 (0)