Skip to content

Commit 828030d

Browse files
crisbetoalan-agius4
authored andcommitted
fix(@ngtools/webpack): account for styles specified as string literals and styleUrl
An upcoming change in Angular will allow `style` specified as strings, in addition to a new `styleUrl` property. These changes update the Webpack transform to support the change.
1 parent 29d3ff1 commit 828030d

File tree

2 files changed

+102
-33
lines changed

2 files changed

+102
-33
lines changed

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

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -180,42 +180,37 @@ function visitComponentMetadata(
180180
importName,
181181
);
182182
case 'styles':
183+
case 'styleUrl':
183184
case 'styleUrls':
184-
if (!ts.isArrayLiteralExpression(node.initializer)) {
185+
const isInlineStyle = name === 'styles';
186+
let styles: Iterable<ts.Expression>;
187+
188+
if (ts.isStringLiteralLike(node.initializer)) {
189+
styles = [
190+
transformInlineStyleLiteral(
191+
node.initializer,
192+
nodeFactory,
193+
isInlineStyle,
194+
inlineStyleFileExtension,
195+
resourceImportDeclarations,
196+
moduleKind,
197+
) as ts.StringLiteralLike,
198+
];
199+
} else if (ts.isArrayLiteralExpression(node.initializer)) {
200+
styles = ts.visitNodes(node.initializer.elements, (node) =>
201+
transformInlineStyleLiteral(
202+
node,
203+
nodeFactory,
204+
isInlineStyle,
205+
inlineStyleFileExtension,
206+
resourceImportDeclarations,
207+
moduleKind,
208+
),
209+
) as ts.NodeArray<ts.Expression>;
210+
} else {
185211
return node;
186212
}
187213

188-
const isInlineStyle = name === 'styles';
189-
const styles = ts.visitNodes(node.initializer.elements, (node) => {
190-
if (!ts.isStringLiteral(node) && !ts.isNoSubstitutionTemplateLiteral(node)) {
191-
return node;
192-
}
193-
194-
let url;
195-
if (isInlineStyle) {
196-
if (inlineStyleFileExtension) {
197-
const data = Buffer.from(node.text).toString('base64');
198-
const containingFile = node.getSourceFile().fileName;
199-
// app.component.ts.css?ngResource!=!@ngtools/webpack/src/loaders/inline-resource.js?data=...!app.component.ts
200-
url =
201-
`${containingFile}.${inlineStyleFileExtension}?${NG_COMPONENT_RESOURCE_QUERY}` +
202-
`!=!${InlineAngularResourceLoaderPath}?data=${encodeURIComponent(
203-
data,
204-
)}!${containingFile}`;
205-
} else {
206-
return nodeFactory.createStringLiteral(node.text);
207-
}
208-
} else {
209-
url = getResourceUrl(node);
210-
}
211-
212-
if (!url) {
213-
return node;
214-
}
215-
216-
return createResourceImport(nodeFactory, url, resourceImportDeclarations, moduleKind);
217-
}) as ts.NodeArray<ts.Expression>;
218-
219214
// Styles should be placed first
220215
if (isInlineStyle) {
221216
styleReplacements.unshift(...styles);
@@ -229,9 +224,44 @@ function visitComponentMetadata(
229224
}
230225
}
231226

227+
function transformInlineStyleLiteral(
228+
node: ts.Node,
229+
nodeFactory: ts.NodeFactory,
230+
isInlineStyle: boolean,
231+
inlineStyleFileExtension: string | undefined,
232+
resourceImportDeclarations: ts.ImportDeclaration[],
233+
moduleKind: ts.ModuleKind,
234+
) {
235+
if (!ts.isStringLiteralLike(node)) {
236+
return node;
237+
}
238+
239+
if (!isInlineStyle) {
240+
const url = getResourceUrl(node);
241+
242+
return url
243+
? createResourceImport(nodeFactory, url, resourceImportDeclarations, moduleKind)
244+
: node;
245+
}
246+
247+
if (!inlineStyleFileExtension) {
248+
return nodeFactory.createStringLiteral(node.text);
249+
}
250+
251+
const data = Buffer.from(node.text).toString('base64');
252+
const containingFile = node.getSourceFile().fileName;
253+
254+
// app.component.ts.css?ngResource!=!@ngtools/webpack/src/loaders/inline-resource.js?data=...!app.component.ts
255+
const url =
256+
`${containingFile}.${inlineStyleFileExtension}?${NG_COMPONENT_RESOURCE_QUERY}` +
257+
`!=!${InlineAngularResourceLoaderPath}?data=${encodeURIComponent(data)}!${containingFile}`;
258+
259+
return createResourceImport(nodeFactory, url, resourceImportDeclarations, moduleKind);
260+
}
261+
232262
export function getResourceUrl(node: ts.Node): string | null {
233263
// only analyze strings
234-
if (!ts.isStringLiteral(node) && !ts.isNoSubstitutionTemplateLiteral(node)) {
264+
if (!ts.isStringLiteralLike(node)) {
235265
return null;
236266
}
237267

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,45 @@ describe('@ngtools/webpack transformers', () => {
299299
expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`);
300300
});
301301

302+
it('should replace resources specified as string literals', () => {
303+
const input = tags.stripIndent`
304+
import { Component } from '@angular/core';
305+
306+
@Component({
307+
selector: 'app-root',
308+
templateUrl: './app.component.html',
309+
styles: 'h2 {font-size: 10px}',
310+
styleUrl: './app.component.css'
311+
})
312+
export class AppComponent {
313+
title = 'app';
314+
}
315+
`;
316+
const output = tags.stripIndent`
317+
import { __decorate } from "tslib";
318+
import __NG_CLI_RESOURCE__0 from "./app.component.html?ngResource";
319+
import __NG_CLI_RESOURCE__1 from "./app.component.css?ngResource";
320+
import { Component } from '@angular/core';
321+
322+
let AppComponent = class AppComponent {
323+
constructor() {
324+
this.title = 'app';
325+
}
326+
};
327+
AppComponent = __decorate([
328+
Component({
329+
selector: 'app-root',
330+
template: __NG_CLI_RESOURCE__0,
331+
styles: ["h2 {font-size: 10px}", __NG_CLI_RESOURCE__1]
332+
})
333+
], AppComponent);
334+
export { AppComponent };
335+
`;
336+
337+
const result = transform(input);
338+
expect(tags.oneLine`${result}`).toEqual(tags.oneLine`${output}`);
339+
});
340+
302341
it('should not replace resources if not in Component decorator', () => {
303342
const input = tags.stripIndent`
304343
import { Component } from '@angular/core';

0 commit comments

Comments
 (0)