From cdedd00d128b82ac03d810ee151de2f64e88daca Mon Sep 17 00:00:00 2001 From: Dylan Hunn Date: Mon, 11 Dec 2023 15:38:26 -0800 Subject: [PATCH] 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 --- .../host_bindings/GOLDEN_PARTIAL.js | 37 +++++++++++++++++++ .../host_bindings/TEST_CASES.json | 18 +++++++++ .../host_with_ts_expression_node.ts | 19 ++++++++++ .../host_with_ts_expression_node_template.js | 5 +++ ...th_ts_expression_node_template.pipeline.js | 5 +++ .../template/pipeline/ir/src/expression.ts | 2 + 6 files changed, 86 insertions(+) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.pipeline.js diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js index 60691bb57b6..f8a773740b7 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js @@ -970,3 +970,40 @@ export declare class MyComponent { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * 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; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json index fc762d049a6..5835b76e337 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json @@ -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" + } + ] + } + ] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node.ts new file mode 100644 index 00000000000..f23bc20d5e5 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node.ts @@ -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 { +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.js new file mode 100644 index 00000000000..6b99daa74ed --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.js @@ -0,0 +1,5 @@ +// NOTE: TODO: This is difference between Pipeline and TDB acceptable? + +hostAttrs: ["foo", BAR_CONST] +… +template: function MyComponent_Template(rf, ctx) { } \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.pipeline.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.pipeline.js new file mode 100644 index 00000000000..39c443d3d86 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.pipeline.js @@ -0,0 +1,5 @@ +function MyComponent_HostBindings(rf, ctx) { + if (rf & 2) { + i0.ɵɵattribute("foo", BAR_CONST); + } +} \ No newline at end of file diff --git a/packages/compiler/src/template/pipeline/ir/src/expression.ts b/packages/compiler/src/template/pipeline/ir/src/expression.ts index 5b653b28493..82965731f26 100644 --- a/packages/compiler/src/template/pipeline/ir/src/expression.ts +++ b/packages/compiler/src/template/pipeline/ir/src/expression.ts @@ -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) {