Skip to content

Commit 3aa479a

Browse files
committed
fix(@ngtools/webpack): extend non-string literal validation to styleUrls and styleUrl
Apply the same diagnostic thrown for non-string-literal templateUrl values to styleUrls array elements and styleUrl (singular), and use a template literal for the templateUrl error message.
1 parent 0abae8b commit 3aa479a

File tree

2 files changed

+109
-3
lines changed

2 files changed

+109
-3
lines changed

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

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,9 @@ function visitComponentMetadata(
171171
const { line } = sourceFile.getLineAndCharacterOfPosition(node.initializer.getStart());
172172

173173
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.`,
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.`,
177177
);
178178
}
179179

@@ -211,6 +211,22 @@ function visitComponentMetadata(
211211
) as ts.StringLiteralLike,
212212
];
213213
} else if (ts.isArrayLiteralExpression(node.initializer)) {
214+
if (!isInlineStyle) {
215+
// Validate each element is a string literal for styleUrls
216+
for (const element of node.initializer.elements) {
217+
if (!ts.isStringLiteralLike(element)) {
218+
const sourceFile = node.getSourceFile();
219+
const { line } = sourceFile.getLineAndCharacterOfPosition(element.getStart());
220+
221+
throw new Error(
222+
`Component '${className}' in '${sourceFile.fileName}' contains a non-string` +
223+
` literal '${name}' value at line ${line + 1}. The '${name}' property must` +
224+
` contain string literals. Expressions, variables, or other dynamic values` +
225+
` are not supported.`,
226+
);
227+
}
228+
}
229+
}
214230
styles = ts.visitNodes(node.initializer.elements, (node) =>
215231
transformInlineStyleLiteral(
216232
node,
@@ -221,6 +237,16 @@ function visitComponentMetadata(
221237
moduleKind,
222238
),
223239
) as ts.NodeArray<ts.Expression>;
240+
} else if (!isInlineStyle) {
241+
// styleUrl or styleUrls with a non-string, non-array initializer
242+
const sourceFile = node.getSourceFile();
243+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.initializer.getStart());
244+
245+
throw new Error(
246+
`Component '${className}' in '${sourceFile.fileName}' contains a non-string literal` +
247+
` '${name}' value at line ${line + 1}. The '${name}' property must be a` +
248+
` string literal. Expressions, variables, or other dynamic values are not supported.`,
249+
);
224250
} else {
225251
return node;
226252
}

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,4 +511,84 @@ describe('find_resources', () => {
511511
/Component 'AppComponent'.*contains a non-string literal 'templateUrl' value/,
512512
);
513513
});
514+
515+
it('should throw an error when styleUrls contains a conditional expression', () => {
516+
const input = tags.stripIndent`
517+
import { Component } from '@angular/core';
518+
519+
@Component({
520+
selector: 'app-root',
521+
templateUrl: './app.component.html',
522+
styleUrls: [true === true ? './app.component.css' : './app.component.dark.css']
523+
})
524+
export class AppComponent {
525+
title = 'app';
526+
}
527+
`;
528+
529+
expect(() => transform(input)).toThrowError(
530+
/Component 'AppComponent'.*contains a non-string literal 'styleUrls' value/,
531+
);
532+
});
533+
534+
it('should throw an error when styleUrls contains a variable reference', () => {
535+
const input = tags.stripIndent`
536+
import { Component } from '@angular/core';
537+
538+
const myStyle = './app.component.css';
539+
540+
@Component({
541+
selector: 'app-root',
542+
templateUrl: './app.component.html',
543+
styleUrls: [myStyle]
544+
})
545+
export class AppComponent {
546+
title = 'app';
547+
}
548+
`;
549+
550+
expect(() => transform(input)).toThrowError(
551+
/Component 'AppComponent'.*contains a non-string literal 'styleUrls' value/,
552+
);
553+
});
554+
555+
it('should throw an error when styleUrl is a conditional expression', () => {
556+
const input = tags.stripIndent`
557+
import { Component } from '@angular/core';
558+
559+
@Component({
560+
selector: 'app-root',
561+
templateUrl: './app.component.html',
562+
styleUrl: true === true ? './app.component.css' : './app.component.dark.css'
563+
})
564+
export class AppComponent {
565+
title = 'app';
566+
}
567+
`;
568+
569+
expect(() => transform(input)).toThrowError(
570+
/Component 'AppComponent'.*contains a non-string literal 'styleUrl' value/,
571+
);
572+
});
573+
574+
it('should throw an error when styleUrl is a variable reference', () => {
575+
const input = tags.stripIndent`
576+
import { Component } from '@angular/core';
577+
578+
const myStyle = './app.component.css';
579+
580+
@Component({
581+
selector: 'app-root',
582+
templateUrl: './app.component.html',
583+
styleUrl: myStyle
584+
})
585+
export class AppComponent {
586+
title = 'app';
587+
}
588+
`;
589+
590+
expect(() => transform(input)).toThrowError(
591+
/Component 'AppComponent'.*contains a non-string literal 'styleUrl' value/,
592+
);
593+
});
514594
});

0 commit comments

Comments
 (0)