fix(compiler-cli): enableResourceInlining handles both styles and styleUrls (#22688)
When both are present, the inlined styles are appended to the end of the styles PR Close #22688
This commit is contained in:
parent
123efba388
commit
40315bef3d
|
@ -88,10 +88,7 @@ def _ngc_tsconfig(ctx, files, srcs, **kwargs):
|
||||||
|
|
||||||
return dict(tsc_wrapped_tsconfig(ctx, files, srcs, **kwargs), **{
|
return dict(tsc_wrapped_tsconfig(ctx, files, srcs, **kwargs), **{
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
# Always assume that resources can be loaded statically at build time
|
"enableResourceInlining": ctx.attr.inline_resources,
|
||||||
# TODO(alexeagle): if someone has a legitimate use case for dynamic
|
|
||||||
# template loading, maybe we need to make this configurable.
|
|
||||||
"enableResourceInlining": True,
|
|
||||||
"generateCodeForLibraries": False,
|
"generateCodeForLibraries": False,
|
||||||
"allowEmptyCodegenFiles": True,
|
"allowEmptyCodegenFiles": True,
|
||||||
"enableSummariesForJit": True,
|
"enableSummariesForJit": True,
|
||||||
|
@ -346,6 +343,8 @@ NG_MODULE_ATTRIBUTES = {
|
||||||
|
|
||||||
"type_check": attr.bool(default = True),
|
"type_check": attr.bool(default = True),
|
||||||
|
|
||||||
|
"inline_resources": attr.bool(default = True),
|
||||||
|
|
||||||
"no_i18n": attr.bool(default = False),
|
"no_i18n": attr.bool(default = False),
|
||||||
|
|
||||||
"compiler": attr.label(
|
"compiler": attr.label(
|
||||||
|
|
|
@ -67,13 +67,17 @@ export class InlineResourcesMetadataTransformer implements MetadataTransformer {
|
||||||
arg['template'] = loader.get(arg['templateUrl']);
|
arg['template'] = loader.get(arg['templateUrl']);
|
||||||
delete arg.templateUrl;
|
delete arg.templateUrl;
|
||||||
}
|
}
|
||||||
if (arg['styleUrls']) {
|
|
||||||
const styleUrls = arg['styleUrls'];
|
const styles = arg['styles'] || [];
|
||||||
if (Array.isArray(styleUrls)) {
|
const styleUrls = arg['styleUrls'] || [];
|
||||||
arg['styles'] = styleUrls.map(styleUrl => loader.get(styleUrl));
|
if (!Array.isArray(styles)) throw new Error('styles should be an array');
|
||||||
|
if (!Array.isArray(styleUrls)) throw new Error('styleUrls should be an array');
|
||||||
|
|
||||||
|
styles.push(...styleUrls.map(styleUrl => loader.get(styleUrl)));
|
||||||
|
if (styles.length > 0) {
|
||||||
|
arg['styles'] = styles;
|
||||||
delete arg.styleUrls;
|
delete arg.styleUrls;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
@ -262,49 +266,59 @@ function updateComponentProperties(
|
||||||
// argument
|
// argument
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
const newArgument = ts.updateObjectLiteral(
|
|
||||||
componentArg, ts.visitNodes(componentArg.properties, (node: ts.ObjectLiteralElementLike) => {
|
const newProperties: ts.ObjectLiteralElementLike[] = [];
|
||||||
if (!ts.isPropertyAssignment(node)) {
|
const newStyleExprs: ts.Expression[] = [];
|
||||||
// Error: unsupported
|
componentArg.properties.forEach(prop => {
|
||||||
return node;
|
if (!ts.isPropertyAssignment(prop) || ts.isComputedPropertyName(prop.name)) {
|
||||||
|
newProperties.push(prop);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ts.isComputedPropertyName(node.name)) {
|
switch (prop.name.text) {
|
||||||
// computed names are not supported
|
case 'styles':
|
||||||
return node;
|
if (!ts.isArrayLiteralExpression(prop.initializer)) {
|
||||||
|
throw new Error('styles takes an array argument');
|
||||||
}
|
}
|
||||||
|
newStyleExprs.push(...prop.initializer.elements);
|
||||||
|
break;
|
||||||
|
|
||||||
const name = node.name.text;
|
|
||||||
switch (name) {
|
|
||||||
case 'styleUrls':
|
case 'styleUrls':
|
||||||
if (!ts.isArrayLiteralExpression(node.initializer)) {
|
if (!ts.isArrayLiteralExpression(prop.initializer)) {
|
||||||
// Error: unsupported
|
throw new Error('styleUrls takes an array argument');
|
||||||
return node;
|
}
|
||||||
|
newStyleExprs.push(...prop.initializer.elements.map((expr: ts.Expression) => {
|
||||||
|
if (!ts.isStringLiteral(expr) && !ts.isNoSubstitutionTemplateLiteral(expr)) {
|
||||||
|
throw new Error(
|
||||||
|
'Can only accept string literal arguments to styleUrls. ' + PRECONDITIONS_TEXT);
|
||||||
}
|
}
|
||||||
const styleUrls = node.initializer.elements;
|
|
||||||
|
|
||||||
return ts.updatePropertyAssignment(
|
|
||||||
node, ts.createIdentifier('styles'),
|
|
||||||
ts.createArrayLiteral(ts.visitNodes(styleUrls, (expr: ts.Expression) => {
|
|
||||||
if (ts.isStringLiteral(expr)) {
|
|
||||||
const styles = loader.get(expr.text);
|
const styles = loader.get(expr.text);
|
||||||
return ts.createLiteral(styles);
|
return ts.createLiteral(styles);
|
||||||
}
|
}));
|
||||||
return expr;
|
break;
|
||||||
})));
|
|
||||||
|
|
||||||
|
|
||||||
case 'templateUrl':
|
case 'templateUrl':
|
||||||
if (ts.isStringLiteral(node.initializer)) {
|
if (!ts.isStringLiteral(prop.initializer) &&
|
||||||
const template = loader.get(node.initializer.text);
|
!ts.isNoSubstitutionTemplateLiteral(prop.initializer)) {
|
||||||
return ts.updatePropertyAssignment(
|
throw new Error(
|
||||||
node, ts.createIdentifier('template'), ts.createLiteral(template));
|
'Can only accept a string literal argument to templateUrl. ' + PRECONDITIONS_TEXT);
|
||||||
}
|
}
|
||||||
return node;
|
const template = loader.get(prop.initializer.text);
|
||||||
|
newProperties.push(ts.updatePropertyAssignment(
|
||||||
|
prop, ts.createIdentifier('template'), ts.createLiteral(template)));
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return node;
|
newProperties.push(prop);
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
return ts.createNodeArray<ts.Expression>([newArgument]);
|
|
||||||
|
// Add the non-inline styles
|
||||||
|
if (newStyleExprs.length > 0) {
|
||||||
|
const newStyles = ts.createPropertyAssignment(
|
||||||
|
ts.createIdentifier('styles'), ts.createArrayLiteral(newStyleExprs));
|
||||||
|
newProperties.push(newStyles);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts.createNodeArray([ts.updateObjectLiteral(componentArg, newProperties)]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,13 @@ describe('inline resources transformer', () => {
|
||||||
expect(actual).not.toContain('templateUrl:');
|
expect(actual).not.toContain('templateUrl:');
|
||||||
expect(actual.replace(/\s+/g, ' '))
|
expect(actual.replace(/\s+/g, ' '))
|
||||||
.toContain(
|
.toContain(
|
||||||
'Foo = __decorate([ core_1.Component({ template: "Some template", otherProp: 3, }) ], Foo)');
|
'Foo = __decorate([ core_1.Component({ template: "Some template", otherProp: 3 }) ], Foo)');
|
||||||
|
});
|
||||||
|
it('should allow different quotes', () => {
|
||||||
|
const actual = convert(`import {Component} from '@angular/core';
|
||||||
|
@Component({"templateUrl": \`./thing.html\`}) export class Foo {}`);
|
||||||
|
expect(actual).not.toContain('templateUrl:');
|
||||||
|
expect(actual).toContain('{ template: "Some template" }');
|
||||||
});
|
});
|
||||||
it('should replace styleUrls', () => {
|
it('should replace styleUrls', () => {
|
||||||
const actual = convert(`import {Component} from '@angular/core';
|
const actual = convert(`import {Component} from '@angular/core';
|
||||||
|
@ -58,11 +64,21 @@ describe('inline resources transformer', () => {
|
||||||
expect(actual).not.toContain('styleUrls:');
|
expect(actual).not.toContain('styleUrls:');
|
||||||
expect(actual).toContain('styles: [".some_style {}", ".some_other_style {}"]');
|
expect(actual).toContain('styles: [".some_style {}", ".some_other_style {}"]');
|
||||||
});
|
});
|
||||||
|
it('should preserve existing styles', () => {
|
||||||
|
const actual = convert(`import {Component} from '@angular/core';
|
||||||
|
@Component({
|
||||||
|
styles: ['h1 { color: blue }'],
|
||||||
|
styleUrls: ['./thing1.css'],
|
||||||
|
})
|
||||||
|
export class Foo {}`);
|
||||||
|
expect(actual).not.toContain('styleUrls:');
|
||||||
|
expect(actual).toContain(`styles: ['h1 { color: blue }', ".some_style {}"]`);
|
||||||
|
});
|
||||||
it('should handle empty styleUrls', () => {
|
it('should handle empty styleUrls', () => {
|
||||||
const actual = convert(`import {Component} from '@angular/core';
|
const actual = convert(`import {Component} from '@angular/core';
|
||||||
@Component({styleUrls: []}) export class Foo {}`);
|
@Component({styleUrls: [], styles: []}) export class Foo {}`);
|
||||||
expect(actual).not.toContain('styleUrls:');
|
expect(actual).not.toContain('styleUrls:');
|
||||||
expect(actual).toContain('styles: []');
|
expect(actual).not.toContain('styles:');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('annotation input', () => {
|
describe('annotation input', () => {
|
||||||
|
@ -115,6 +131,7 @@ describe('metadata transformer', () => {
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './thing.html',
|
templateUrl: './thing.html',
|
||||||
styleUrls: ['./thing1.css', './thing2.css'],
|
styleUrls: ['./thing1.css', './thing2.css'],
|
||||||
|
styles: ['h1 { color: red }'],
|
||||||
})
|
})
|
||||||
export class Foo {}
|
export class Foo {}
|
||||||
`;
|
`;
|
||||||
|
@ -135,7 +152,7 @@ describe('metadata transformer', () => {
|
||||||
expect(JSON.stringify(classData)).toContain('"template":"Some template"');
|
expect(JSON.stringify(classData)).toContain('"template":"Some template"');
|
||||||
expect(JSON.stringify(classData)).not.toContain('styleUrls');
|
expect(JSON.stringify(classData)).not.toContain('styleUrls');
|
||||||
expect(JSON.stringify(classData))
|
expect(JSON.stringify(classData))
|
||||||
.toContain('"styles":[".some_style {}",".some_other_style {}"]');
|
.toContain('"styles":["h1 { color: red }",".some_style {}",".some_other_style {}"]');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue