diff --git a/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/diagnostic.ts b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/diagnostic.ts index dbaaf842a528c..da778fdd91fe8 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/diagnostic.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/diagnostics/src/diagnostic.ts @@ -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);