Repository: swc-project/swc (rust)

Issue:
Issue with hoist_props when rendering objects dynamically
### Describe the bug

I'm using SWC to transpile a react app. This app uses Server-driven UI at runtime. The client code maintains a map of the items that may be rendered and determines what is rendered at runtime. 

Hard to explain; hopefully, the code makes things more clear.

### Input code

```typescript
const Blocks = {
    Block1: () => {
        return <>'Block1xx'</>;
    },
    Block2: () => {
        return <>'Block2xx'</>;
    },
    Layout1: () => {
        // In the final code, Blocks does not have a 'Block1' key
        return RenderLayout(Blocks, ['Block1'])
    }
};

function RenderLayout(Comps, items) {
    return items.map((item) => {
        return Comps[item]
    })
}

export function render() {
    return <Blocks.Layout1 />
}
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true
    },
    "target": "es5",
    "loose": false,
    "minify": {
      "compress": {
        "arguments": false,
        "arrows": true,
        "booleans": true,
        "booleans_as_integers": false,
        "collapse_vars": true,
        "comparisons": true,
        "computed_props": true,
        "conditionals": true,
        "dead_code": true,
        "directives": true,
        "drop_console": false,
        "drop_debugger": true,
        "evaluate": true,
        "expression": false,
        "hoist_funs": false,
        "hoist_props": true,
        "hoist_vars": false,
        "if_return": true,
        "join_vars": true,
        "keep_classnames": false,
        "keep_fargs": true,
        "keep_fnames": false,
        "keep_infinity": false,
        "loops": true,
        "negate_iife": true,
        "properties": true,
        "reduce_funcs": false,
        "reduce_vars": false,
        "side_effects": true,
        "switches": true,
        "typeofs": true,
        "unsafe": false,
        "unsafe_arrows": false,
        "unsafe_comps": false,
        "unsafe_Function": false,
        "unsafe_math": false,
        "unsafe_symbols": false,
        "unsafe_methods": false,
        "unsafe_proto": false,
        "unsafe_regexp": false,
        "unsafe_undefined": false,
        "unused": true,
        "const_to_let": true,
        "pristine_globals": true
      },
      "mangle": {
        "toplevel": false,
        "keep_classnames": false,
        "keep_fnames": false,
        "keep_private_props": false,
        "ie8": false,
        "safari10": false
      }
    }
  },
  "module": {
    "type": "es6"
  },
  "minify": false,
  "isModule": true
}
```


### Playground link

https://play.swc.rs/?version=1.3.74&code=H4sIAAAAAAAAA4WQP2vDMBDF9%2FsUb7MMISYZG8dDOhUydQ0ZhH0mJrFkJLk4FH%2F32pLSgjH0pvv3u%2Fc4KrWyDqeHLu8WR3wTpvDl7g0ixbGIvTkMu94o5EUSNoYhybPi4Ofj5g%2Fd%2F4vuV9CzfOrerchmGT4U3I1RN6qRD5S64pfnSrOyicNNfjEkorMEd35C156aUrs08smqYhMkRTi1weVFX9NgjMYDUd2r0jV6wbzrtpuQxnFr02g23va9bSs7IeZ0%2FROev8zza9BKaSTiodPG4VfSeEmxEMiD4W38GLKCxh%2F9UZmCygEAAA%3D%3D&config=H4sIAAAAAAAAA42VzW7bMAyA732KwOcdtsOKoQ%2Bw255BUCzKUSeLhkilCYq8e2nZTr2GNnYxbH78ESmSfn86HJpXapuXw7u8ysdgM0G%2Bf4uErontRSQNXwegNoeBm28LZRoR5wJVcptAwzZ3wKMR0M9Zu4mIBCLzNhLMsj6k4K%2FreC32QwailUyk4q%2F0kJj%2BtZ9Zxjeaj7GSHxEj2LRDjCUTEkMHWXPcYox2IDBnmxUv40ltDoRaiBEWBmeGjIPKkwscMEnMR%2BrAOtOiAwWFDC2HM2hmEkvMEkl6Sj4VOziWrqt3%2FMUazjYWy0pMuNQrkdMqXk8YiI0vSSvhBDdqMMG5uF8tgzcZuOT0aPeKIW3cyV8AqUC0RMn2oPmtGl76acva71qG5KVl%2Bapw6W8tywSdFNWE4JXKjpWBzEG7zQyutDBWttWOM%2BON8lFwYMB76RXFNb0Fbk9a0HHI0StA7td6rasmYO5TuMHHgdjBvyVL1hts1ugtn7YpXfsjxp0APfAJ3Y6CXAXjNs6yJS7DNi%2FJgbQGOFWlUAWPS0AGgNHEuiwfekPGQzyaLuLxc03MCrf7Du5t6uq8r%2FYl4xDhDHGrjf9jRHapnO08dvUy2Q%2FTC7%2B0prQyeOHH9wUtqTwtz5pU06Mrq4RqT07%2FkufmU2n5c9yDNIH%2BLJa1ULcPPrcNX%2BAGAAA%3D

### Expected behavior

I'd expect the Blocks object in the output code to contain all possible keys (including 'Block1', 'Block2')

### Actual behavior

Keys are removed from the output code resulting in the layout not rendering all of it's Blocks.

### Version

1.3.77

### Additional context

_No response_

Pull request description:
fix(es/minifier): Preserve more analysis data upon inlining
**Description:**


**Related issue:**

 - Closes #7821.


Patch:
diff --git a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs
index 8dcca0954e..2e12bdb081 100644
--- a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs
+++ b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs
@@ -245,10 +245,35 @@ impl Optimizer<'_> {
             {
                 self.mode.store(id.clone(), &*init);
 
-                let usage_count = usage.usage_count;
+                let VarUsageInfo {
+                    used_as_arg,
+                    used_as_ref,
+                    indexed_with_dynamic_key,
+                    usage_count,
+                    has_property_access,
+                    has_property_mutation,
+                    used_above_decl,
+                    executed_multiple_time,
+                    used_in_cond,
+                    used_recursively,
+                    no_side_effect_for_member_access,
+                    ..
+                } = *usage;
                 let mut inc_usage = || {
                     if let Expr::Ident(i) = &*init {
                         if let Some(u) = self.data.vars.get_mut(&i.to_id()) {
+                            u.used_as_arg |= used_as_arg;
+                            u.used_as_ref |= used_as_ref;
+                            u.indexed_with_dynamic_key |= indexed_with_dynamic_key;
+                            u.has_property_access |= has_property_access;
+                            u.has_property_mutation |= has_property_mutation;
+                            u.used_above_decl |= used_above_decl;
+                            u.executed_multiple_time |= executed_multiple_time;
+                            u.used_in_cond |= used_in_cond;
+                            u.used_recursively |= used_recursively;
+
+                            u.no_side_effect_for_member_access &= no_side_effect_for_member_access;
+
                             u.ref_count += ref_count;
                             u.usage_count += usage_count;
                         }


Test patch:
diff --git a/crates/swc/tests/fixture/issues-7xxx/7821/input/.swcrc b/crates/swc/tests/fixture/issues-7xxx/7821/input/.swcrc
new file mode 100644
index 0000000000..5ae3aa2390
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7821/input/.swcrc
@@ -0,0 +1,71 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "typescript",
+            "tsx": true
+        },
+        "target": "es5",
+        "loose": false,
+        "minify": {
+            "compress": {
+                "arguments": false,
+                "arrows": true,
+                "booleans": true,
+                "booleans_as_integers": false,
+                "collapse_vars": true,
+                "comparisons": true,
+                "computed_props": true,
+                "conditionals": true,
+                "dead_code": true,
+                "directives": true,
+                "drop_console": false,
+                "drop_debugger": true,
+                "evaluate": true,
+                "expression": false,
+                "hoist_funs": false,
+                "hoist_props": true,
+                "hoist_vars": false,
+                "if_return": true,
+                "join_vars": true,
+                "keep_classnames": false,
+                "keep_fargs": true,
+                "keep_fnames": false,
+                "keep_infinity": false,
+                "loops": true,
+                "negate_iife": true,
+                "properties": true,
+                "reduce_funcs": false,
+                "reduce_vars": false,
+                "side_effects": true,
+                "switches": true,
+                "typeofs": true,
+                "unsafe": false,
+                "unsafe_arrows": false,
+                "unsafe_comps": false,
+                "unsafe_Function": false,
+                "unsafe_math": false,
+                "unsafe_symbols": false,
+                "unsafe_methods": false,
+                "unsafe_proto": false,
+                "unsafe_regexp": false,
+                "unsafe_undefined": false,
+                "unused": true,
+                "const_to_let": true,
+                "pristine_globals": true
+            },
+            "mangle": {
+                "toplevel": false,
+                "keep_classnames": false,
+                "keep_fnames": false,
+                "keep_private_props": false,
+                "ie8": false,
+                "safari10": false
+            }
+        }
+    },
+    "module": {
+        "type": "es6"
+    },
+    "minify": false,
+    "isModule": true
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-7xxx/7821/input/1.js b/crates/swc/tests/fixture/issues-7xxx/7821/input/1.js
new file mode 100644
index 0000000000..c00cb529d7
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7821/input/1.js
@@ -0,0 +1,22 @@
+const Blocks = {
+    Block1: () => {
+        return <>'Block1xx'</>;
+    },
+    Block2: () => {
+        return <>'Block2xx'</>;
+    },
+    Layout1: () => {
+        // In the final code, Blocks does not have a 'Block1' key
+        return RenderLayout(Blocks, ['Block1'])
+    }
+};
+
+function RenderLayout(Comps, items) {
+    return items.map((item) => {
+        return Comps[item]
+    })
+}
+
+export function render() {
+    return <Blocks.Layout1 />
+}
diff --git a/crates/swc/tests/fixture/issues-7xxx/7821/output/1.js b/crates/swc/tests/fixture/issues-7xxx/7821/output/1.js
new file mode 100644
index 0000000000..046ffbdabf
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-7xxx/7821/output/1.js
@@ -0,0 +1,18 @@
+var Blocks = {
+    Block1: function() {
+        return React.createElement(React.Fragment, null, "'Block1xx'");
+    },
+    Block2: function() {
+        return React.createElement(React.Fragment, null, "'Block2xx'");
+    },
+    Layout1: function() {
+        return [
+            "Block1"
+        ].map(function(e) {
+            return Blocks[e];
+        });
+    }
+};
+export function render() {
+    return React.createElement(Blocks.Layout1, null);
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/7821/config.json b/crates/swc_ecma_minifier/tests/fixture/issues/7821/config.json
new file mode 100644
index 0000000000..e24c2f770e
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/7821/config.json
@@ -0,0 +1,46 @@
+{
+    "arguments": false,
+    "arrows": true,
+    "booleans": true,
+    "booleans_as_integers": false,
+    "collapse_vars": true,
+    "comparisons": true,
+    "computed_props": true,
+    "conditionals": true,
+    "dead_code": true,
+    "directives": true,
+    "drop_console": false,
+    "drop_debugger": true,
+    "evaluate": true,
+    "expression": false,
+    "hoist_funs": false,
+    "hoist_props": true,
+    "hoist_vars": false,
+    "if_return": true,
+    "join_vars": true,
+    "keep_classnames": false,
+    "keep_fargs": true,
+    "keep_fnames": false,
+    "keep_infinity": false,
+    "loops": true,
+    "negate_iife": true,
+    "properties": true,
+    "reduce_funcs": false,
+    "reduce_vars": false,
+    "side_effects": true,
+    "switches": true,
+    "typeofs": true,
+    "unsafe": false,
+    "unsafe_arrows": false,
+    "unsafe_comps": false,
+    "unsafe_Function": false,
+    "unsafe_math": false,
+    "unsafe_symbols": false,
+    "unsafe_methods": false,
+    "unsafe_proto": false,
+    "unsafe_regexp": false,
+    "unsafe_undefined": false,
+    "unused": true,
+    "const_to_let": true,
+    "pristine_globals": true
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/7821/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/7821/input.js
new file mode 100644
index 0000000000..e4cc23b6aa
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/7821/input.js
@@ -0,0 +1,21 @@
+var Blocks = {
+    Block1: function () {
+        return React.createElement(React.Fragment, null, "'Block1xx'");
+    },
+    Block2: function () {
+        return React.createElement(React.Fragment, null, "'Block2xx'");
+    },
+    Layout1: function () {
+        return RenderLayout(Blocks, [
+            "Block1"
+        ]);
+    }
+};
+function RenderLayout(Comps, items) {
+    return items.map(function (item) {
+        return Comps[item];
+    });
+}
+export function render() {
+    return React.createElement(Blocks.Layout1, null);
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/7821/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/7821/output.js
new file mode 100644
index 0000000000..18d8c16e37
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/7821/output.js
@@ -0,0 +1,21 @@
+var Blocks = {
+    Block1: function() {
+        return React.createElement(React.Fragment, null, "'Block1xx'");
+    },
+    Block2: function() {
+        return React.createElement(React.Fragment, null, "'Block2xx'");
+    },
+    Layout1: function() {
+        return RenderLayout(Blocks, [
+            "Block1"
+        ]);
+    }
+};
+function RenderLayout(Comps, items) {
+    return items.map(function(item) {
+        return Comps[item];
+    });
+}
+export function render() {
+    return React.createElement(Blocks.Layout1, null);
+}