refactor(compiler): Adds a new phase for parsing extracted styles (#51258)

Adds a new phase that converts previously extracted
ExtractedAttributeOps representing a style attribute into individual
ExtractedAttributeOps representing each of the style properties set in
the style attribute.

PR Close #51258
This commit is contained in:
Miles Malerba 2023-08-02 12:38:02 -07:00 committed by Dylan Hunn
parent 82009a529a
commit d792db8652
4 changed files with 48 additions and 10 deletions

View file

@ -902,3 +902,10 @@ export function transformExpressionsInStatement(
throw new Error(`Unhandled statement kind: ${stmt.constructor.name}`);
}
}
/**
* Checks whether the given expression is a string literal.
*/
export function isStringLiteral(expr: o.Expression): expr is o.LiteralExpr&{value: string} {
return expr instanceof o.LiteralExpr && typeof expr.value === 'string';
}

View file

@ -31,6 +31,7 @@ import {phaseNgContainer} from './phases/ng_container';
import {phaseNoListenersOnTemplates} from './phases/no_listeners_on_templates';
import {phaseNonbindable} from './phases/nonbindable';
import {phaseNullishCoalescing} from './phases/nullish_coalescing';
import {phaseParseExtractedStyles} from './phases/parse_extracted_styles';
import {phasePipeCreation} from './phases/pipe_creation';
import {phasePipeVariadic} from './phases/pipe_variadic';
import {phasePropertyOrdering} from './phases/property_ordering';
@ -58,6 +59,7 @@ export function transformTemplate(job: ComponentCompilationJob): void {
phaseStyleBindingSpecialization(job);
phaseBindingSpecialization(job);
phaseAttributeExtraction(job);
phaseParseExtractedStyles(job);
phaseRemoveEmptyBindings(job);
phaseNoListenersOnTemplates(job);
phasePipeCreation(job);

View file

@ -8,7 +8,6 @@
import {SecurityContext} from '../../../../core';
import * as o from '../../../../output/output_ast';
import * as ir from '../../ir';
import {ComponentCompilationJob, ViewCompilationUnit} from '../compilation';
import {getElementsByXrefId} from '../util/elements';
@ -70,13 +69,6 @@ function lookupElement(
return el;
}
/**
* Checks whether the given expression is a string literal.
*/
function isStringLiteral(expr: o.Expression): expr is o.LiteralExpr&{value: string} {
return expr instanceof o.LiteralExpr && typeof expr.value === 'string';
}
/**
* Extracts an attribute binding.
*/
@ -94,13 +86,13 @@ function extractAttributeOp(
// context, so we emulate that behavior in compaitiblity mode, otherwise we optimize more
// aggressively.
extractable = view.compatibility === ir.CompatibilityMode.TemplateDefinitionBuilder ?
isStringLiteral(op.expression) && op.securityContext === SecurityContext.NONE :
ir.isStringLiteral(op.expression) && op.securityContext === SecurityContext.NONE :
op.expression.isConstant();
} else {
// TemplateDefinitionBuilder only extracted string constants, so we emulate that behavior in
// compaitiblity mode, otherwise we optimize more aggressively.
extractable = view.compatibility === ir.CompatibilityMode.TemplateDefinitionBuilder ?
isStringLiteral(op.expression) :
ir.isStringLiteral(op.expression) :
op.expression.isConstant();
}

View file

@ -0,0 +1,37 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as o from '../../../../output/output_ast';
import {parse as parseStyle} from '../../../../render3/view/style_parser';
import * as ir from '../../ir';
import {ComponentCompilationJob} from '../compilation';
/**
* Parses extracted style attributes into separate ExtractedAttributeOps per style property.
*/
export function phaseParseExtractedStyles(cpl: ComponentCompilationJob) {
for (const [_, view] of cpl.views) {
for (const op of view.create) {
if (op.kind === ir.OpKind.ExtractedAttribute && op.bindingKind === ir.BindingKind.Attribute) {
if (op.name === 'style') {
if (ir.isStringLiteral(op.expression!)) {
const parsedStyles = parseStyle(op.expression.value);
for (let i = 0; i < parsedStyles.length - 1; i += 2) {
ir.OpList.insertBefore<ir.CreateOp>(
ir.createExtractedAttributeOp(
op.target, ir.BindingKind.StyleProperty, parsedStyles[i],
o.literal(parsedStyles[i + 1])),
op);
}
ir.OpList.remove<ir.CreateOp>(op);
}
}
}
}
}
}