mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(compiler): handle nested brackets in host object bindings
Fixes that we were parsing bindings in the `host` object with a regex that didn't account for nested brackets which may come up with something like Tailwind. Fixes #68039.
This commit is contained in:
parent
9218140348
commit
2ce0e98f79
3 changed files with 24 additions and 35 deletions
|
|
@ -308,7 +308,7 @@ import * as i0 from "@angular/core";
|
|||
export class MyComponent {
|
||||
expr = true;
|
||||
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
||||
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, isStandalone: true, selector: "ng-component", host: { properties: { "class.text-primary/80": "expr", "class.data-active:text-green-300/80": "expr", "class.data-[size='large'": "expr" } }, ngImport: i0, template: ``, isInline: true });
|
||||
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, isStandalone: true, selector: "ng-component", host: { properties: { "class.text-primary/80": "expr", "class.data-active:text-green-300/80": "expr", "class.data-[size='large']:p-8": "expr" } }, ngImport: i0, template: ``, isInline: true });
|
||||
}
|
||||
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{
|
||||
type: Component,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ $r3$.ɵɵdefineComponent({
|
|||
hostVars: 6,
|
||||
hostBindings: function MyComponent_HostBindings(rf, ctx) {
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵclassProp("text-primary/80", ctx.expr)("data-active:text-green-300/80", ctx.expr)("data-[size='large'", ctx.expr);
|
||||
$r3$.ɵɵclassProp("text-primary/80", ctx.expr)("data-active:text-green-300/80", ctx.expr)("data-[size='large']:p-8", ctx.expr);
|
||||
}
|
||||
},
|
||||
…
|
||||
|
|
|
|||
|
|
@ -514,38 +514,42 @@ function createHostBindingsFunction(
|
|||
return emitHostBindingFunction(hostJob);
|
||||
}
|
||||
|
||||
const HOST_REG_EXP = /^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/;
|
||||
// Represents the groups in the above regex.
|
||||
const enum HostBindingGroup {
|
||||
// group 1: "prop" from "[prop]", or "attr.role" from "[attr.role]", or @anim from [@anim]
|
||||
Binding = 1,
|
||||
|
||||
// group 2: "event" from "(event)"
|
||||
Event = 2,
|
||||
}
|
||||
|
||||
// Defines Host Bindings structure that contains attributes, listeners, and properties,
|
||||
// parsed from the `host` object defined for a Type.
|
||||
export interface ParsedHostBindings {
|
||||
attributes: {[key: string]: o.Expression};
|
||||
listeners: {[key: string]: string};
|
||||
properties: {[key: string]: string};
|
||||
attributes: Record<string, o.Expression>;
|
||||
listeners: Record<string, string>;
|
||||
properties: Record<string, string>;
|
||||
specialAttributes: {styleAttr?: string; classAttr?: string};
|
||||
}
|
||||
|
||||
export function parseHostBindings(host: {
|
||||
[key: string]: string | o.Expression;
|
||||
}): ParsedHostBindings {
|
||||
const attributes: {[key: string]: o.Expression} = {};
|
||||
const listeners: {[key: string]: string} = {};
|
||||
const properties: {[key: string]: string} = {};
|
||||
const attributes: Record<string, o.Expression> = {};
|
||||
const listeners: Record<string, string> = {};
|
||||
const properties: Record<string, string> = {};
|
||||
const specialAttributes: {styleAttr?: string; classAttr?: string} = {};
|
||||
|
||||
for (const key of Object.keys(host)) {
|
||||
const value = host[key];
|
||||
const matches = key.match(HOST_REG_EXP);
|
||||
|
||||
if (matches === null) {
|
||||
if (key.startsWith('(') && key.endsWith(')')) {
|
||||
if (typeof value !== 'string') {
|
||||
// TODO(alxhub): make this a diagnostic.
|
||||
throw new Error(`Event binding must be string`);
|
||||
}
|
||||
listeners[key.slice(1, -1)] = value;
|
||||
} else if (key.startsWith('[') && key.endsWith(']')) {
|
||||
if (typeof value !== 'string') {
|
||||
// TODO(alxhub): make this a diagnostic.
|
||||
throw new Error(`Property binding must be string`);
|
||||
}
|
||||
// synthetic properties (the ones that have a `@` as a prefix)
|
||||
// are still treated the same as regular properties. Therefore
|
||||
// there is no point in storing them in a separate map.
|
||||
properties[key.slice(1, -1)] = value;
|
||||
} else {
|
||||
switch (key) {
|
||||
case 'class':
|
||||
if (typeof value !== 'string') {
|
||||
|
|
@ -568,21 +572,6 @@ export function parseHostBindings(host: {
|
|||
attributes[key] = value;
|
||||
}
|
||||
}
|
||||
} else if (matches[HostBindingGroup.Binding] != null) {
|
||||
if (typeof value !== 'string') {
|
||||
// TODO(alxhub): make this a diagnostic.
|
||||
throw new Error(`Property binding must be string`);
|
||||
}
|
||||
// synthetic properties (the ones that have a `@` as a prefix)
|
||||
// are still treated the same as regular properties. Therefore
|
||||
// there is no point in storing them in a separate map.
|
||||
properties[matches[HostBindingGroup.Binding]] = value;
|
||||
} else if (matches[HostBindingGroup.Event] != null) {
|
||||
if (typeof value !== 'string') {
|
||||
// TODO(alxhub): make this a diagnostic.
|
||||
throw new Error(`Event binding must be string`);
|
||||
}
|
||||
listeners[matches[HostBindingGroup.Event]] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue