Skip to content

Commit a4b66fe

Browse files
JoostKalxhub
authored andcommitted
perf(compiler-cli): cache source file for reporting type-checking diagnostics (#47508)
When reporting type-checking diagnostics in external templates we create a `ts.SourceFile` of the template text, as this is needed to report Angular template diagnostics using TypeScript's diagnostics infrastructure. Each reported diagnostic would create its own `ts.SourceFile`, resulting in repeatedly parsing of the template text and potentially high memory usage if the template is large and there are many diagnostics reported. This commit caches the parsed template in the template mapping, such that all reported diagnostics get to reuse the same `ts.SourceFile`. Closes #47470 PR Close #47508
1 parent 10a3cef commit a4b66fe

File tree

1 file changed

+19
-4
lines changed
  • packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src

1 file changed

+19
-4
lines changed

packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/diagnostic.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import {ParseSourceSpan} from '@angular/compiler';
1010
import ts from 'typescript';
1111

12-
import {ExternalTemplateSourceMapping, TemplateDiagnostic, TemplateId, TemplateSourceMapping} from '../../api';
12+
import {ExternalTemplateSourceMapping, IndirectTemplateSourceMapping, TemplateDiagnostic, TemplateId, TemplateSourceMapping} from '../../api';
1313

1414
/**
1515
* Constructs a `ts.Diagnostic` for a given `ParseSourceSpan` within a template.
@@ -66,9 +66,7 @@ export function makeTemplateDiagnostic(
6666
(mapping as ExternalTemplateSourceMapping).templateUrl;
6767
// TODO(alxhub): investigate creating a fake `ts.SourceFile` here instead of invoking the TS
6868
// parser against the template (HTML is just really syntactically invalid TypeScript code ;).
69-
// Also investigate caching the file to avoid running the parser multiple times.
70-
const sf = ts.createSourceFile(
71-
fileName, mapping.template, ts.ScriptTarget.Latest, false, ts.ScriptKind.JSX);
69+
const sf = getParsedTemplateSourceFile(fileName, mapping);
7270

7371
let relatedInformation: ts.DiagnosticRelatedInformation[] = [];
7472
if (relatedMessages !== undefined) {
@@ -113,6 +111,23 @@ export function makeTemplateDiagnostic(
113111
}
114112
}
115113

114+
const TemplateSourceFile = Symbol('TemplateSourceFile');
115+
116+
type TemplateSourceMappingWithSourceFile =
117+
(ExternalTemplateSourceMapping|IndirectTemplateSourceMapping)&{
118+
[TemplateSourceFile]?: ts.SourceFile;
119+
};
120+
121+
function getParsedTemplateSourceFile(
122+
fileName: string, mapping: TemplateSourceMappingWithSourceFile): ts.SourceFile {
123+
if (mapping[TemplateSourceFile] === undefined) {
124+
mapping[TemplateSourceFile] = ts.createSourceFile(
125+
fileName, mapping.template, ts.ScriptTarget.Latest, false, ts.ScriptKind.JSX);
126+
}
127+
128+
return mapping[TemplateSourceFile];
129+
}
130+
116131
export function isTemplateDiagnostic(diagnostic: ts.Diagnostic): diagnostic is TemplateDiagnostic {
117132
return diagnostic.hasOwnProperty('componentFile') &&
118133
ts.isSourceFile((diagnostic as any).componentFile);

0 commit comments

Comments
 (0)