Skip to content
Permalink
Browse files
perf(compiler-cli): cache source file for reporting type-checking dia…
…gnostics (#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
  • Loading branch information
JoostK authored and alxhub committed Sep 28, 2022
1 parent 10a3cef commit a4b66fe1e5acaf148069933720499652f21a7bce
Showing 1 changed file with 19 additions and 4 deletions.
@@ -9,7 +9,7 @@
import {ParseSourceSpan} from '@angular/compiler';
import ts from 'typescript';

import {ExternalTemplateSourceMapping, TemplateDiagnostic, TemplateId, TemplateSourceMapping} from '../../api';
import {ExternalTemplateSourceMapping, IndirectTemplateSourceMapping, TemplateDiagnostic, TemplateId, TemplateSourceMapping} from '../../api';

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

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

const TemplateSourceFile = Symbol('TemplateSourceFile');

type TemplateSourceMappingWithSourceFile =
(ExternalTemplateSourceMapping|IndirectTemplateSourceMapping)&{
[TemplateSourceFile]?: ts.SourceFile;
};

function getParsedTemplateSourceFile(
fileName: string, mapping: TemplateSourceMappingWithSourceFile): ts.SourceFile {
if (mapping[TemplateSourceFile] === undefined) {
mapping[TemplateSourceFile] = ts.createSourceFile(
fileName, mapping.template, ts.ScriptTarget.Latest, false, ts.ScriptKind.JSX);
}

return mapping[TemplateSourceFile];
}

export function isTemplateDiagnostic(diagnostic: ts.Diagnostic): diagnostic is TemplateDiagnostic {
return diagnostic.hasOwnProperty('componentFile') &&
ts.isSourceFile((diagnostic as any).componentFile);

0 comments on commit a4b66fe

Please sign in to comment.