Repository: swc-project/swc (rust)

Issue:
SWC outputs an invalid destructuring assignment with the target Safari >= 10.1
### Describe the bug

In the example below, SWC transpiles the code `const { as } = truc` to `var { _$as  } = truc;`.

### Input code

```typescript
const truc = { as: "OK"}

function x(as) {
    return function g() {
    const { as } = truc
    console.log(as)
    }
    as
}

x()();
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "ecmascript",
      "jsx": false
    },
    "target": "es5",
    "loose": false,
    "minify": {
      "compress": false,
      "mangle": false
    }
  },
  "module": {
    "type": "es6"
  },
  "minify": false,
  "isModule": true,
  "env": {
    "targets": "safari >=10.1"
  }
}
```


### Playground link

https://play.swc.rs/?version=1.3.58&code=H4sIAAAAAAAAA0vOzysuUSgpKk1WsFWoVkgstlJQ8vdWquXiSivNSy7JzM9TqNBILNZUqOZSAIKi1JLSojwFuFy6BkwmGWwQyASFWqBRIBPh4vk5qXo5%2Bekgc8BitWAysZgLaE2FhqaGpjUAWiAaqYcAAAA%3D&config=H4sIAAAAAAAAA1WPTQ6CMBCF95yimbUxstCFid7AQ0zqQEroTzrFSAh3t5QWYdd57837OlMlBHQs4S6m%2BIyDQ8%2FktzkqPJqA36gASY0svXIBTsXteLEa7JmSNK8OBPQthbTF1xyH3lqmEs%2BaVkY14x4orXaemI%2FBJYqm7emIqzIStH0PycyHhNHRir%2FBP1RgWzEofpXN4IdVI%2FPZFaVLlt8AY4Neieejvpzr1FrNP1qfiZhDAQAA

### Expected behavior

When run, both the input and output code should print "OK".

### Actual behavior

When run, the input prints "OK", the output code prints "undefined".

### Version

1.3.58

### Additional context

_No response_

Pull request:
fix(es/compat): Fix destructuring handling of `block_scoping`
**Description:**


**Related issue:**

 - Closes #7418.

Patch:
diff --git a/crates/swc_ecma_transforms_compat/src/es2015/block_scoping/operator.rs b/crates/swc_ecma_transforms_compat/src/es2015/block_scoping/operator.rs
index 906e53805b..0e4e2d99e8 100644
--- a/crates/swc_ecma_transforms_compat/src/es2015/block_scoping/operator.rs
+++ b/crates/swc_ecma_transforms_compat/src/es2015/block_scoping/operator.rs
@@ -1,4 +1,4 @@
-use swc_common::{collections::AHashMap, SyntaxContext};
+use swc_common::{collections::AHashMap, SyntaxContext, DUMMY_SP};
 use swc_ecma_ast::*;
 use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
 use swc_trace_macro::swc_trace;
@@ -26,6 +26,51 @@ impl VisitMut for Rename {
         }
     }
 
+    fn visit_mut_member_prop(&mut self, n: &mut MemberProp) {
+        if let MemberProp::Computed(n) = n {
+            n.visit_mut_with(self);
+        }
+    }
+
+    fn visit_mut_object_pat_prop(&mut self, i: &mut ObjectPatProp) {
+        match i {
+            ObjectPatProp::Assign(p) => {
+                p.value.visit_mut_with(self);
+
+                let orig = p.key.clone();
+                p.key.visit_mut_with(self);
+
+                if orig.to_id() == p.key.to_id() {
+                    return;
+                }
+
+                match p.value.take() {
+                    Some(default) => {
+                        *i = ObjectPatProp::KeyValue(KeyValuePatProp {
+                            key: PropName::Ident(orig),
+                            value: Box::new(Pat::Assign(AssignPat {
+                                span: DUMMY_SP,
+                                left: Box::new(Pat::Ident(p.key.clone().into())),
+                                right: default,
+                                type_ann: Default::default(),
+                            })),
+                        });
+                    }
+                    None => {
+                        *i = ObjectPatProp::KeyValue(KeyValuePatProp {
+                            key: PropName::Ident(orig),
+                            value: Box::new(Pat::Ident(p.key.clone().into())),
+                        });
+                    }
+                }
+            }
+
+            _ => {
+                i.visit_mut_children_with(self);
+            }
+        }
+    }
+
     fn visit_mut_prop(&mut self, n: &mut Prop) {
         if let Prop::Shorthand(ident) = n {
             if let Some(id) = self.map.get(&ident.to_id()) {
@@ -42,12 +87,6 @@ impl VisitMut for Rename {
         n.visit_mut_children_with(self);
     }
 
-    fn visit_mut_member_prop(&mut self, n: &mut MemberProp) {
-        if let MemberProp::Computed(n) = n {
-            n.visit_mut_with(self);
-        }
-    }
-
     fn visit_mut_prop_name(&mut self, n: &mut PropName) {
         if let PropName::Computed(n) = n {
             n.visit_mut_with(self);
diff --git a/crates/swc_ecma_utils/src/lib.rs b/crates/swc_ecma_utils/src/lib.rs
index df0fbd412f..1db5aea3f0 100644
--- a/crates/swc_ecma_utils/src/lib.rs
+++ b/crates/swc_ecma_utils/src/lib.rs
@@ -2922,6 +2922,71 @@ impl VisitMut for IdentRenamer<'_> {
 
     visit_mut_obj_and_computed!();
 
+    fn visit_mut_export_named_specifier(&mut self, node: &mut ExportNamedSpecifier) {
+        if node.exported.is_some() {
+            node.orig.visit_mut_children_with(self);
+            return;
+        }
+
+        match &mut node.orig {
+            ModuleExportName::Ident(orig) => {
+                if let Some(new) = self.map.get(&orig.to_id()) {
+                    node.exported = Some(ModuleExportName::Ident(orig.clone()));
+
+                    orig.sym = new.0.clone();
+                    orig.span.ctxt = new.1;
+                }
+            }
+            ModuleExportName::Str(_) => {}
+        }
+    }
+
+    fn visit_mut_ident(&mut self, node: &mut Ident) {
+        if let Some(new) = self.map.get(&node.to_id()) {
+            node.sym = new.0.clone();
+            node.span.ctxt = new.1;
+        }
+    }
+
+    fn visit_mut_object_pat_prop(&mut self, i: &mut ObjectPatProp) {
+        match i {
+            ObjectPatProp::Assign(p) => {
+                p.value.visit_mut_with(self);
+
+                let orig = p.key.clone();
+                p.key.visit_mut_with(self);
+
+                if orig.to_id() == p.key.to_id() {
+                    return;
+                }
+
+                match p.value.take() {
+                    Some(default) => {
+                        *i = ObjectPatProp::KeyValue(KeyValuePatProp {
+                            key: PropName::Ident(orig),
+                            value: Box::new(Pat::Assign(AssignPat {
+                                span: DUMMY_SP,
+                                left: Box::new(Pat::Ident(p.key.clone().into())),
+                                right: default,
+                                type_ann: Default::default(),
+                            })),
+                        });
+                    }
+                    None => {
+                        *i = ObjectPatProp::KeyValue(KeyValuePatProp {
+                            key: PropName::Ident(orig),
+                            value: Box::new(Pat::Ident(p.key.clone().into())),
+                        });
+                    }
+                }
+            }
+
+            _ => {
+                i.visit_mut_children_with(self);
+            }
+        }
+    }
+
     fn visit_mut_prop(&mut self, node: &mut Prop) {
         match node {
             Prop::Shorthand(i) => {
@@ -2942,32 +3007,6 @@ impl VisitMut for IdentRenamer<'_> {
             }
         }
     }
-
-    fn visit_mut_ident(&mut self, node: &mut Ident) {
-        if let Some(new) = self.map.get(&node.to_id()) {
-            node.sym = new.0.clone();
-            node.span.ctxt = new.1;
-        }
-    }
-
-    fn visit_mut_export_named_specifier(&mut self, node: &mut ExportNamedSpecifier) {
-        if node.exported.is_some() {
-            node.orig.visit_mut_children_with(self);
-            return;
-        }
-
-        match &mut node.orig {
-            ModuleExportName::Ident(orig) => {
-                if let Some(new) = self.map.get(&orig.to_id()) {
-                    node.exported = Some(ModuleExportName::Ident(orig.clone()));
-
-                    orig.sym = new.0.clone();
-                    orig.span.ctxt = new.1;
-                }
-            }
-            ModuleExportName::Str(_) => {}
-        }
-    }
 }
 
 #[cfg(test)]
diff --git a/crates/swc_plugin_runner/src/transform_executor.rs b/crates/swc_plugin_runner/src/transform_executor.rs
index fa02340748..7010758d9c 100644
--- a/crates/swc_plugin_runner/src/transform_executor.rs
+++ b/crates/swc_plugin_runner/src/transform_executor.rs
@@ -297,7 +297,7 @@ impl TransformExecutor {
         let memory = instance.exports.get_memory("memory")?;
         import_object.define("env", "memory", memory.clone());
 
-        let alloc = instance.exports.get_typed_function(&mut store, "__alloc")?;
+        let alloc = instance.exports.get_typed_function(&store, "__alloc")?;
 
         // Unlike wasmer@2, have to manually `import` memory / necessary functions from
         // the guest into env.


Test patch:
diff --git a/crates/swc_ecma_transforms_compat/tests/block-scoping/issue-7418/1/exec.js b/crates/swc_ecma_transforms_compat/tests/block-scoping/issue-7418/1/exec.js
new file mode 100644
index 0000000000..2ae84181bb
--- /dev/null
+++ b/crates/swc_ecma_transforms_compat/tests/block-scoping/issue-7418/1/exec.js
@@ -0,0 +1,11 @@
+const truc = { as: "OK" }
+
+function x(as) {
+    return function g() {
+        const { as } = truc
+        console.log(as)
+    }
+    as
+}
+
+x()();
\ No newline at end of file
diff --git a/crates/swc_ecma_transforms_compat/tests/es2015_destructuring.rs b/crates/swc_ecma_transforms_compat/tests/es2015_destructuring.rs
index 3b010b4663..5329445470 100644
--- a/crates/swc_ecma_transforms_compat/tests/es2015_destructuring.rs
+++ b/crates/swc_ecma_transforms_compat/tests/es2015_destructuring.rs
@@ -2280,3 +2280,22 @@ test!(
     "let [] = [...[1, 2, 3]];",
     "let _ref = [...[1, 2, 3]];"
 );
+
+test_exec!(
+    syntax(),
+    |_| tr(),
+    issue_7418,
+    r###"
+    const truc = { as: "OK"}
+
+    function x(as) {
+        return function g() {
+        const { as } = truc
+        console.log(as)
+        }
+        as
+    }
+
+    x()();
+    "###
+);