Skip to content

Commit 8fcadaa

Browse files
JoostKpkozlowski-opensource
authored andcommitted
perf(compiler-cli): cache source file for reporting type-checking diagnostics (#47471)
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 #47471
1 parent 8cef5dd commit 8fcadaa

File tree

1 file changed

+18
-3
lines changed
  • packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src

1 file changed

+18
-3
lines changed

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

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

1212
import {addDiagnosticChain, makeDiagnosticChain} from '../../../diagnostics';
13-
import {ExternalTemplateSourceMapping, TemplateDiagnostic, TemplateId, TemplateSourceMapping} from '../../api';
13+
import {ExternalTemplateSourceMapping, IndirectTemplateSourceMapping, TemplateDiagnostic, TemplateId, TemplateSourceMapping} from '../../api';
1414

1515
/**
1616
* Constructs a `ts.Diagnostic` for a given `ParseSourceSpan` within a template.
@@ -82,7 +82,7 @@ export function makeTemplateDiagnostic(
8282

8383
let sf: ts.SourceFile;
8484
try {
85-
sf = parseTemplateAsSourceFile(fileName, mapping.template);
85+
sf = getParsedTemplateSourceFile(fileName, mapping);
8686
} catch (e) {
8787
const failureChain = makeDiagnosticChain(
8888
`Failed to report an error in '${fileName}' at ${span.start.line + 1}:${
@@ -135,6 +135,22 @@ export function makeTemplateDiagnostic(
135135
}
136136
}
137137

138+
const TemplateSourceFile = Symbol('TemplateSourceFile');
139+
140+
type TemplateSourceMappingWithSourceFile =
141+
(ExternalTemplateSourceMapping|IndirectTemplateSourceMapping)&{
142+
[TemplateSourceFile]?: ts.SourceFile;
143+
};
144+
145+
function getParsedTemplateSourceFile(
146+
fileName: string, mapping: TemplateSourceMappingWithSourceFile): ts.SourceFile {
147+
if (mapping[TemplateSourceFile] === undefined) {
148+
mapping[TemplateSourceFile] = parseTemplateAsSourceFile(fileName, mapping.template);
149+
}
150+
151+
return mapping[TemplateSourceFile];
152+
}
153+
138154
let parseTemplateAsSourceFileForTest: typeof parseTemplateAsSourceFile|null = null;
139155

140156
export function setParseTemplateAsSourceFileForTest(fn: typeof parseTemplateAsSourceFile): void {
@@ -152,7 +168,6 @@ function parseTemplateAsSourceFile(fileName: string, template: string): ts.Sourc
152168

153169
// TODO(alxhub): investigate creating a fake `ts.SourceFile` here instead of invoking the TS
154170
// parser against the template (HTML is just really syntactically invalid TypeScript code ;).
155-
// Also investigate caching the file to avoid running the parser multiple times.
156171
return ts.createSourceFile(
157172
fileName, template, ts.ScriptTarget.Latest, /* setParentNodes */ false, ts.ScriptKind.JSX);
158173
}

0 commit comments

Comments
 (0)