refactor(compiler): Support o.WrappedNodeExpr inside expression conversion (#53478)

`o.WrappedNodeExpr` can show up in some cases, when a host binding's value is inside a TS expression.

It's an open question whether we will need to support all of the TS expression types as a result.

PR Close #53478
This commit is contained in:
Dylan Hunn
2023-12-11 15:38:26 -08:00
committed by Alex Rickabaugh
parent 0427d7ac7e
commit cdedd00d12
6 changed files with 86 additions and 0 deletions
@@ -970,3 +970,40 @@ export declare class MyComponent {
static ɵcmp: i0.ɵɵComponentDeclaration<MyComponent, "my-cmp", never, {}, {}, never, never, true, never>;
}
/****************************************************************************************************
* PARTIAL FILE: host_with_ts_expression_node.js
****************************************************************************************************/
import { Component } from '@angular/core';
import * as i0 from "@angular/core";
export function getBar() {
console.log('This function cannot be extracted.');
return `${Math.random()}`;
}
export const BAR_CONST = getBar();
export class MyComponent {
}
MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, isStandalone: true, selector: "my-cmp", host: { attributes: { "foo": BAR_CONST } }, ngImport: i0, template: ``, isInline: true });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{
type: Component,
args: [{
selector: 'my-cmp',
standalone: true,
host: {
'foo': BAR_CONST,
},
template: ``
}]
}] });
/****************************************************************************************************
* PARTIAL FILE: host_with_ts_expression_node.d.ts
****************************************************************************************************/
import * as i0 from "@angular/core";
export declare function getBar(): string;
export declare const BAR_CONST: string;
export declare class MyComponent {
static ɵfac: i0.ɵɵFactoryDeclaration<MyComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<MyComponent, "my-cmp", never, {}, {}, never, never, true, never>;
}
@@ -350,6 +350,24 @@
]
}
]
},
{
"description": "should properly compiler host bindings that read a TS wrapped node",
"inputFiles": [
"host_with_ts_expression_node.ts"
],
"expectations": [
{
"failureMessage": "Invalid host binding code",
"files": [
{
"expected": "host_with_ts_expression_node_template.js",
"templatePipelineExpected": "host_with_ts_expression_node_template.pipeline.js",
"generated": "host_with_ts_expression_node.js"
}
]
}
]
}
]
}
@@ -0,0 +1,19 @@
import {Component} from '@angular/core';
export function getBar(): string {
console.log('This function cannot be extracted.');
return `${Math.random()}`;
}
export const BAR_CONST = getBar();
@Component({
selector: 'my-cmp',
standalone: true,
host: {
'foo': BAR_CONST,
},
template: ``
})
export class MyComponent {
}
@@ -0,0 +1,5 @@
// NOTE: TODO: This is difference between Pipeline and TDB acceptable?
hostAttrs: ["foo", BAR_CONST]
…
template: function MyComponent_Template(rf, ctx) { }
@@ -0,0 +1,5 @@
function MyComponent_HostBindings(rf, ctx) {
if (rf & 2) {
i0.ɵɵattribute("foo", BAR_CONST);
}
}
@@ -1069,6 +1069,8 @@ export function transformExpressionsInExpression(
expr.tag = transformExpressionsInExpression(expr.tag, transform, flags);
expr.template.expressions =
expr.template.expressions.map(e => transformExpressionsInExpression(e, transform, flags));
} else if (expr instanceof o.WrappedNodeExpr) {
// TODO: Do we need to transform any TS nodes nested inside of this expression?
} else if (
expr instanceof o.ReadVarExpr || expr instanceof o.ExternalExpr ||
expr instanceof o.LiteralExpr) {