Skip to content

Commit 0abae8b

Browse files
committed
fix(@ngtools/webpack): emit diagnostic for unsupported conditional templateUrl expressions
Fixes #27611
1 parent f1ed025 commit 0abae8b

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

packages/ngtools/webpack/src/transformers/replace_resources.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export function replaceResources(
4141
resourceImportDeclarations,
4242
moduleKind,
4343
inlineStyleFileExtension,
44+
node,
4445
),
4546
),
4647
...(ts.getModifiers(node) ?? []),
@@ -87,6 +88,7 @@ function visitDecorator(
8788
resourceImportDeclarations: ts.ImportDeclaration[],
8889
moduleKind?: ts.ModuleKind,
8990
inlineStyleFileExtension?: string,
91+
classDeclaration?: ts.ClassDeclaration,
9092
): ts.Decorator {
9193
if (!isComponentDecorator(node, typeChecker)) {
9294
return node;
@@ -106,6 +108,8 @@ function visitDecorator(
106108
const objectExpression = args[0];
107109
const styleReplacements: ts.Expression[] = [];
108110

111+
const className = classDeclaration?.name?.text ?? 'Unknown';
112+
109113
// visit all properties
110114
let properties = ts.visitNodes(objectExpression.properties, (node) =>
111115
ts.isObjectLiteralElementLike(node)
@@ -116,6 +120,7 @@ function visitDecorator(
116120
resourceImportDeclarations,
117121
moduleKind,
118122
inlineStyleFileExtension,
123+
className,
119124
)
120125
: node,
121126
) as ts.NodeArray<ts.ObjectLiteralElementLike>;
@@ -148,6 +153,7 @@ function visitComponentMetadata(
148153
resourceImportDeclarations: ts.ImportDeclaration[],
149154
moduleKind: ts.ModuleKind = ts.ModuleKind.ES2015,
150155
inlineStyleFileExtension?: string,
156+
className?: string,
151157
): ts.ObjectLiteralElementLike | undefined {
152158
if (!ts.isPropertyAssignment(node) || ts.isComputedPropertyName(node.name)) {
153159
return node;
@@ -161,7 +167,14 @@ function visitComponentMetadata(
161167
case 'templateUrl': {
162168
const url = getResourceUrl(node.initializer);
163169
if (!url) {
164-
return node;
170+
const sourceFile = node.getSourceFile();
171+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.initializer.getStart());
172+
173+
throw new Error(
174+
`Component '${className}' in '${sourceFile.fileName}' contains a non-string literal ` +
175+
`'templateUrl' value at line ${line + 1}. The 'templateUrl' property must be a ` +
176+
`string literal. Expressions, variables, or other dynamic values are not supported.`,
177+
);
165178
}
166179

167180
const importName = createResourceImport(

packages/ngtools/webpack/src/transformers/replace_resources_spec.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,4 +469,46 @@ describe('find_resources', () => {
469469
const result = transform(input, false);
470470
expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`);
471471
});
472+
473+
it('should throw an error when templateUrl is a conditional expression', () => {
474+
const input = tags.stripIndent`
475+
import { Component } from '@angular/core';
476+
477+
@Component({
478+
selector: 'app-root',
479+
templateUrl: true === true
480+
? './app.component.html'
481+
: './app.component.copy.html',
482+
styleUrls: ['./app.component.css', './app.component.2.css']
483+
})
484+
export class AppComponent {
485+
title = 'app';
486+
}
487+
`;
488+
489+
expect(() => transform(input)).toThrowError(
490+
/Component 'AppComponent'.*contains a non-string literal 'templateUrl' value/,
491+
);
492+
});
493+
494+
it('should throw an error when templateUrl is a variable reference', () => {
495+
const input = tags.stripIndent`
496+
import { Component } from '@angular/core';
497+
498+
const myTemplate = './app.component.html';
499+
500+
@Component({
501+
selector: 'app-root',
502+
templateUrl: myTemplate,
503+
styleUrls: ['./app.component.css']
504+
})
505+
export class AppComponent {
506+
title = 'app';
507+
}
508+
`;
509+
510+
expect(() => transform(input)).toThrowError(
511+
/Component 'AppComponent'.*contains a non-string literal 'templateUrl' value/,
512+
);
513+
});
472514
});

0 commit comments

Comments
 (0)