Repository: swc-project/swc (rust)

Issue:
"[variable name] is not defined" when using SWC minifier
### Describe the bug

When using the SWC minifier, it produces output that causes an error in the browser:
  
![Screen Shot 2022-05-03 at 9 09 12 AM](https://user-images.githubusercontent.com/283842/166492820-7bd88e12-f277-4764-9848-36d028463c4d.png)

I have a small repro [in this Gist](https://gist.github.com/nolanlawson/3e5a6a081d23e8dfb87b453b641b468e). To repro, run:

    git clone git@gist.github.com:3e5a6a081d23e8dfb87b453b641b468e.git repro
    cd repro
    npm i
    npm start

Then navigate your browser to `localhost:3000`. (I assume you're using Node.js v16 and npm v8.)

I am using the SWC minifier in Node.js like so:

```js
  minifiedCode = swc.minifySync(transpiledCode, {
    mangle: true,
    compress: true,
  }).code;
```

If I switch to the equivalent code in Terser, the same error does not occur. In the repro, you can use the environment variable `USE_TERSER` to see the working (non-SWC) version:

```
USE_TERSER=1 npm run build
npm start
```

![Screen Shot 2022-05-03 at 9 12 15 AM](https://user-images.githubusercontent.com/283842/166492755-1c312229-7ce4-4ebf-9192-24e2eb7385fd.png)


### Input code

```typescript
// The code is too long to include here. Please see the repro link above.
```


### Config

```json
# N/A, I am not using .swrc
```


### Playground link

_No response_

### Expected behavior

A runtime error in the browser should not occur.

### Actual behavior

A runtime error in the browser occurs.

### Version

1.2.175

### Additional context

_No response_

Pull request description:
fix(es/minifier): skip getter/setter when collpasing vars
<!-- Note: CI script will automatically rebase your PR so please do not rebase unless required -->

<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. -->

**Description:**

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->

**BREAKING CHANGE:**

<!--
If this PR introduces a breaking change, it must contain a notice for it to be included in the CHANGELOG. Add description or remove entirely if not breaking.

You may need to update `MIGRATION.md` for the breaking changes.
-->

**Related issue (if exists):**
closes #4515 

Patch:
diff --git a/crates/swc_ecma_minifier/src/compress/optimize/collapse_vars.rs b/crates/swc_ecma_minifier/src/compress/optimize/collapse_vars.rs
index cb4fb0f587..71998c1fec 100644
--- a/crates/swc_ecma_minifier/src/compress/optimize/collapse_vars.rs
+++ b/crates/swc_ecma_minifier/src/compress/optimize/collapse_vars.rs
@@ -63,7 +63,7 @@ where
             };
 
             report_change!(
-                "collpase_vars: Decided to inline {}{:?}",
+                "collapse_vars: Decided to inline {}{:?}",
                 left.id.sym,
                 left.id.span.ctxt
             );
diff --git a/crates/swc_ecma_minifier/src/compress/pure/vars.rs b/crates/swc_ecma_minifier/src/compress/pure/vars.rs
index c05b3284b0..45e72ac3cf 100644
--- a/crates/swc_ecma_minifier/src/compress/pure/vars.rs
+++ b/crates/swc_ecma_minifier/src/compress/pure/vars.rs
@@ -258,6 +258,10 @@ impl Visit for VarWithOutInitCounter {
 
     fn visit_function(&mut self, _: &Function) {}
 
+    fn visit_getter_prop(&mut self, _: &GetterProp) {}
+
+    fn visit_setter_prop(&mut self, _: &SetterProp) {}
+
     fn visit_var_decl(&mut self, v: &VarDecl) {
         v.visit_children_with(self);
 
@@ -314,6 +318,10 @@ impl VisitMut for VarMover {
     /// Noop
     fn visit_mut_function(&mut self, _: &mut Function) {}
 
+    fn visit_mut_getter_prop(&mut self, _: &mut GetterProp) {}
+
+    fn visit_mut_setter_prop(&mut self, _: &mut SetterProp) {}
+
     fn visit_mut_module_item(&mut self, s: &mut ModuleItem) {
         if let ModuleItem::Stmt(_) = s {
             s.visit_mut_children_with(self);
@@ -411,6 +419,10 @@ impl VisitMut for VarPrepender {
     /// Noop
     fn visit_mut_function(&mut self, _: &mut Function) {}
 
+    fn visit_mut_getter_prop(&mut self, _: &mut GetterProp) {}
+
+    fn visit_mut_setter_prop(&mut self, _: &mut SetterProp) {}
+
     fn visit_mut_var_decl(&mut self, v: &mut VarDecl) {
         if self.vars.is_empty() {
             return;


Test patch:
diff --git a/crates/swc/tests/tsc-references/typeGuardsObjectMethods_es2015.2.minified.js b/crates/swc/tests/tsc-references/typeGuardsObjectMethods_es2015.2.minified.js
index 1b988b416e..0b21c77775 100644
--- a/crates/swc/tests/tsc-references/typeGuardsObjectMethods_es2015.2.minified.js
+++ b/crates/swc/tests/tsc-references/typeGuardsObjectMethods_es2015.2.minified.js
@@ -1,12 +1,14 @@
-var num, strOrNum, var1, var2, var21, obj1 = {
+var num, strOrNum, var1, obj1 = {
     method (param) {
         var var22;
         return num = "string" == typeof var1 && var1.length, num = "string" == typeof var22 && var22.length, num = "string" == typeof param && param.length, strOrNum;
     },
     get prop () {
+        var var2;
         return num = "string" == typeof var1 && var1.length, num = "string" == typeof var2 && var2.length, strOrNum;
     },
     set prop (param){
+        var var21;
         num = "string" == typeof var1 && var1.length, num = "string" == typeof var21 && var21.length, num = "string" == typeof param && param.length;
     }
 };
diff --git a/crates/swc/tests/tsc-references/typeGuardsObjectMethods_es5.2.minified.js b/crates/swc/tests/tsc-references/typeGuardsObjectMethods_es5.2.minified.js
index 0bf44dbb3a..dbdb7b3604 100644
--- a/crates/swc/tests/tsc-references/typeGuardsObjectMethods_es5.2.minified.js
+++ b/crates/swc/tests/tsc-references/typeGuardsObjectMethods_es5.2.minified.js
@@ -1,12 +1,14 @@
-var num, strOrNum, var1, var2, var21, obj1 = {
+var num, strOrNum, var1, obj1 = {
     method: function(param) {
         var var22;
         return num = "string" == typeof var1 && var1.length, num = "string" == typeof var22 && var22.length, num = "string" == typeof param && param.length, strOrNum;
     },
     get prop () {
+        var var2;
         return num = "string" == typeof var1 && var1.length, num = "string" == typeof var2 && var2.length, strOrNum;
     },
     set prop (param){
+        var var21;
         num = "string" == typeof var1 && var1.length, num = "string" == typeof var21 && var21.length, num = "string" == typeof param && param.length;
     }
 };
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/4515/analysis-snapshot.rust-debug b/crates/swc_ecma_minifier/tests/fixture/issues/4515/analysis-snapshot.rust-debug
new file mode 100644
index 0000000000..a2681603da
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/4515/analysis-snapshot.rust-debug
@@ -0,0 +1,198 @@
+TestSnapshot {
+    vars: [
+        (
+            (
+                Atom('B' type=inline),
+                #2,
+            ),
+            VarUsageInfo {
+                inline_prevented: false,
+                ref_count: 1,
+                cond_init: false,
+                declared: false,
+                declared_count: 0,
+                declared_as_fn_param: false,
+                declared_as_fn_expr: false,
+                assign_count: 0,
+                mutation_by_call_count: 0,
+                usage_count: 1,
+                reassigned_with_assignment: false,
+                reassigned_with_var_decl: false,
+                mutated: true,
+                has_property_access: true,
+                has_property_mutation: true,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: true,
+                is_fn_local: true,
+                used_by_nested_fn: false,
+                executed_multiple_time: false,
+                used_in_cond: false,
+                var_kind: None,
+                var_initialized: false,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: false,
+                used_as_callee: false,
+                used_as_arg: false,
+                pure_fn: false,
+                infects: [],
+            },
+        ),
+        (
+            (
+                Atom('a' type=inline),
+                #3,
+            ),
+            VarUsageInfo {
+                inline_prevented: false,
+                ref_count: 3,
+                cond_init: false,
+                declared: true,
+                declared_count: 1,
+                declared_as_fn_param: false,
+                declared_as_fn_expr: false,
+                assign_count: 1,
+                mutation_by_call_count: 0,
+                usage_count: 2,
+                reassigned_with_assignment: true,
+                reassigned_with_var_decl: false,
+                mutated: true,
+                has_property_access: false,
+                has_property_mutation: false,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: false,
+                is_fn_local: true,
+                used_by_nested_fn: false,
+                executed_multiple_time: true,
+                used_in_cond: true,
+                var_kind: Some(
+                    "var",
+                ),
+                var_initialized: true,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: true,
+                used_as_callee: false,
+                used_as_arg: false,
+                pure_fn: false,
+                infects: [],
+            },
+        ),
+        (
+            (
+                Atom('getChildNodes$1' type=dynamic),
+                #1,
+            ),
+            VarUsageInfo {
+                inline_prevented: false,
+                ref_count: 1,
+                cond_init: false,
+                declared: true,
+                declared_count: 1,
+                declared_as_fn_param: false,
+                declared_as_fn_expr: false,
+                assign_count: 1,
+                mutation_by_call_count: 0,
+                usage_count: 0,
+                reassigned_with_assignment: true,
+                reassigned_with_var_decl: false,
+                mutated: true,
+                has_property_access: false,
+                has_property_mutation: false,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: false,
+                is_fn_local: false,
+                used_by_nested_fn: true,
+                executed_multiple_time: false,
+                used_in_cond: false,
+                var_kind: Some(
+                    "var",
+                ),
+                var_initialized: false,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: false,
+                used_as_callee: false,
+                used_as_arg: false,
+                pure_fn: false,
+                infects: [],
+            },
+        ),
+        (
+            (
+                Atom('getChildNodesImpl' type=dynamic),
+                #4,
+            ),
+            VarUsageInfo {
+                inline_prevented: false,
+                ref_count: 1,
+                cond_init: false,
+                declared: true,
+                declared_count: 1,
+                declared_as_fn_param: true,
+                declared_as_fn_expr: false,
+                assign_count: 0,
+                mutation_by_call_count: 0,
+                usage_count: 1,
+                reassigned_with_assignment: false,
+                reassigned_with_var_decl: false,
+                mutated: false,
+                has_property_access: false,
+                has_property_mutation: false,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: false,
+                is_fn_local: true,
+                used_by_nested_fn: true,
+                executed_multiple_time: false,
+                used_in_cond: false,
+                var_kind: None,
+                var_initialized: false,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: false,
+                used_as_callee: false,
+                used_as_arg: false,
+                pure_fn: false,
+                infects: [],
+            },
+        ),
+        (
+            (
+                Atom('setGetChildNodes' type=dynamic),
+                #1,
+            ),
+            VarUsageInfo {
+                inline_prevented: true,
+                ref_count: 0,
+                cond_init: false,
+                declared: true,
+                declared_count: 1,
+                declared_as_fn_param: false,
+                declared_as_fn_expr: false,
+                assign_count: 0,
+                mutation_by_call_count: 0,
+                usage_count: 0,
+                reassigned_with_assignment: false,
+                reassigned_with_var_decl: false,
+                mutated: false,
+                has_property_access: false,
+                has_property_mutation: false,
+                accessed_props: {},
+                exported: false,
+                used_above_decl: false,
+                is_fn_local: true,
+                used_by_nested_fn: false,
+                executed_multiple_time: false,
+                used_in_cond: false,
+                var_kind: None,
+                var_initialized: true,
+                declared_as_catch_param: false,
+                no_side_effect_for_member_access: false,
+                used_as_callee: false,
+                used_as_arg: false,
+                pure_fn: false,
+                infects: [],
+            },
+        ),
+    ],
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/4515/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/4515/input.js
new file mode 100644
index 0000000000..1a1aa857f7
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/4515/input.js
@@ -0,0 +1,10 @@
+B.c = {
+    get foo() {
+        for (var a = 1; a < 10; a++) {}
+    },
+};
+
+var getChildNodes$1;
+export function setGetChildNodes(getChildNodesImpl) {
+    getChildNodes$1 = getChildNodesImpl;
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/4515/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/4515/output.js
new file mode 100644
index 0000000000..a080f57946
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/4515/output.js
@@ -0,0 +1,9 @@
+var getChildNodes$1;
+B.c = {
+    get foo () {
+        for(var a = 1; a < 10; a++);
+    }
+};
+export function setGetChildNodes(getChildNodesImpl) {
+    getChildNodes$1 = getChildNodesImpl;
+}