Skip to content

Commit a1cacc5

Browse files
clydinAndrewKushnir
authored andcommitted
fix(compiler-cli): avoid fatal diagnostics for missing template files (#58673)
A build will still fail in this case. However, for the language service, this allows the component to exist in the compiler registry and prevents cascading diagnostics within an IDE due to "missing" components. The originating template related errors will still be reported in the IDE. This case is particularly important when a template file either does not exist or is inaccessible to the language service. PR Close #58673
1 parent 0093869 commit a1cacc5

File tree

2 files changed

+109
-46
lines changed

2 files changed

+109
-46
lines changed

packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts

Lines changed: 63 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ import {
166166
} from './metadata';
167167
import {
168168
_extractTemplateStyleUrls,
169+
createEmptyTemplate,
169170
extractComponentStyleUrls,
170171
extractInlineStyleResources,
171172
extractTemplate,
@@ -675,54 +676,70 @@ export class ComponentDecoratorHandler
675676

676677
template = preanalyzed;
677678
} else {
678-
const templateDecl = parseTemplateDeclaration(
679-
node,
680-
decorator,
681-
component,
682-
containingFile,
683-
this.evaluator,
684-
this.depTracker,
685-
this.resourceLoader,
686-
this.defaultPreserveWhitespaces,
687-
);
688-
template = extractTemplate(
689-
node,
690-
templateDecl,
691-
this.evaluator,
692-
this.depTracker,
693-
this.resourceLoader,
694-
{
695-
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
696-
i18nNormalizeLineEndingsInICUs: this.i18nNormalizeLineEndingsInICUs,
697-
usePoisonedData: this.usePoisonedData,
698-
enableBlockSyntax: this.enableBlockSyntax,
699-
enableLetSyntax: this.enableLetSyntax,
700-
preserveSignificantWhitespace: this.i18nPreserveSignificantWhitespace,
701-
},
702-
this.compilationMode,
703-
);
679+
try {
680+
const templateDecl = parseTemplateDeclaration(
681+
node,
682+
decorator,
683+
component,
684+
containingFile,
685+
this.evaluator,
686+
this.depTracker,
687+
this.resourceLoader,
688+
this.defaultPreserveWhitespaces,
689+
);
690+
template = extractTemplate(
691+
node,
692+
templateDecl,
693+
this.evaluator,
694+
this.depTracker,
695+
this.resourceLoader,
696+
{
697+
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
698+
i18nNormalizeLineEndingsInICUs: this.i18nNormalizeLineEndingsInICUs,
699+
usePoisonedData: this.usePoisonedData,
700+
enableBlockSyntax: this.enableBlockSyntax,
701+
enableLetSyntax: this.enableLetSyntax,
702+
preserveSignificantWhitespace: this.i18nPreserveSignificantWhitespace,
703+
},
704+
this.compilationMode,
705+
);
704706

705-
if (
706-
this.compilationMode === CompilationMode.LOCAL &&
707-
template.errors &&
708-
template.errors.length > 0
709-
) {
710-
// Template errors are handled at the type check phase. But we skip this phase in local
711-
// compilation mode. As a result we need to handle the errors now and add them to the diagnostics.
712-
if (diagnostics === undefined) {
713-
diagnostics = [];
714-
}
707+
if (
708+
this.compilationMode === CompilationMode.LOCAL &&
709+
template.errors &&
710+
template.errors.length > 0
711+
) {
712+
// Template errors are handled at the type check phase. But we skip this phase in local
713+
// compilation mode. As a result we need to handle the errors now and add them to the diagnostics.
714+
if (diagnostics === undefined) {
715+
diagnostics = [];
716+
}
715717

716-
diagnostics.push(
717-
...getTemplateDiagnostics(
718-
template.errors,
719-
// Type check ID is required as part of the ype check, mainly for mapping the
720-
// diagnostic back to its source. But here we are generating the diagnostic outside
721-
// of the type check context, and so we skip the template ID.
722-
'' as TypeCheckId,
723-
template.sourceMapping,
724-
),
725-
);
718+
diagnostics.push(
719+
...getTemplateDiagnostics(
720+
template.errors,
721+
// Type check ID is required as part of the ype check, mainly for mapping the
722+
// diagnostic back to its source. But here we are generating the diagnostic outside
723+
// of the type check context, and so we skip the template ID.
724+
'' as TypeCheckId,
725+
template.sourceMapping,
726+
),
727+
);
728+
}
729+
} catch (e) {
730+
if (e instanceof FatalDiagnosticError) {
731+
diagnostics ??= [];
732+
diagnostics.push(e.toDiagnostic());
733+
isPoisoned = true;
734+
// Create an empty template for the missing/invalid template.
735+
// A build will still fail in this case. However, for the language service,
736+
// this allows the component to exist in the compiler registry and prevents
737+
// cascading diagnostics within an IDE due to "missing" components. The
738+
// originating template related errors will still be reported in the IDE.
739+
template = createEmptyTemplate(node, component, containingFile);
740+
} else {
741+
throw e;
742+
}
726743
}
727744
}
728745
const templateResource = template.declaration.isInline

packages/compiler-cli/src/ngtsc/annotations/component/src/resources.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,52 @@ export function extractTemplate(
254254
}
255255
}
256256

257+
export function createEmptyTemplate(
258+
componentClass: ClassDeclaration,
259+
component: Map<string, ts.Expression>,
260+
containingFile: string,
261+
): ParsedTemplateWithSource {
262+
const templateUrl = component.get('templateUrl');
263+
const template = component.get('template');
264+
265+
return {
266+
content: '',
267+
diagNodes: [],
268+
nodes: [],
269+
errors: null,
270+
styles: [],
271+
styleUrls: [],
272+
ngContentSelectors: [],
273+
file: new ParseSourceFile('', ''),
274+
sourceMapping: templateUrl
275+
? {type: 'direct', node: template as ts.StringLiteral}
276+
: {
277+
type: 'external',
278+
componentClass,
279+
node: templateUrl!,
280+
template: '',
281+
templateUrl: 'missing.ng.html',
282+
},
283+
declaration: templateUrl
284+
? {
285+
isInline: false,
286+
interpolationConfig: InterpolationConfig.fromArray(null),
287+
preserveWhitespaces: false,
288+
templateUrlExpression: templateUrl,
289+
templateUrl: 'missing.ng.html',
290+
resolvedTemplateUrl: '/missing.ng.html',
291+
}
292+
: {
293+
isInline: true,
294+
interpolationConfig: InterpolationConfig.fromArray(null),
295+
preserveWhitespaces: false,
296+
expression: template!,
297+
templateUrl: containingFile,
298+
resolvedTemplateUrl: containingFile,
299+
},
300+
};
301+
}
302+
257303
function parseExtractedTemplate(
258304
template: TemplateDeclaration,
259305
sourceStr: string,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy