Repository: swc-project/swc (rust)

Issue:
Empty RegExp becomes comment when compress and preserveAllComments are enabled
### Describe the bug

An empty RegExp (`new RegExp('')`) is minified as `//`.

### Input code

```typescript
bar(new RegExp(''))
```


### Config

```json
{
  "sourceMaps": true,
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true,
      "dynamicImport": true,
      "decorators": true
    },
    "preserveAllComments": true,
    "transform": null,
    "target": "es2017",
    "loose": false,
    "externalHelpers": false,
    "keepClassNames": false,
    "minify": {
      "compress": {
        "arguments": false,
        "arrows": true,
        "booleans": true,
        "booleans_as_integers": false,
        "collapse_vars": true,
        "comparisons": true,
        "computed_props": false,
        "conditionals": false,
        "dead_code": false,
        "directives": false,
        "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": false,
        "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
      }
    }
  }
}
```


### Playground link

https://play.swc.rs/?version=1.2.163&code=H4sIAAAAAAAAA0tKLNLISy1XCEpNd60o0FBX19QEAHsw7AwTAAAA&config=H4sIAAAAAAAAA22TS47bMAyG76J1Fm03BbIbDFC0i%2FYKhiJRiVpJNEgqY2PQu5dKPLKd1CvpEx8%2FH343jJUc%2FLQjm6NQhYP5zc4c381oiYHaiecidjJHI%2FMI7CiOYg5GePrw8HOxObofeUSSDsEhWUFaAv89mJFAQ17hJaVXzBmK9KRCtnBAyuZYakoKLJ1BgxngL58%2Bf9WECZHBHINNrA4wCVCx6TukEVqOhf8BGF%2BTZf5lM6w4xxLD3KpxmJsObmfNURcZi50lwreu6oSYQIU93gfLQywC521mhylpG2G4WuoeLZulyFh2qAr4YSQct%2B7FR4moNa3Qg%2FWDQ7%2FW7SOBk3jd1OY1jhoVVnF76OFUz%2Bc2xXtquNpUrUC%2FT7dWaNLud8HIMoRa%2BAEtau%2BOd3QvdLGKYSCQSqXvEcay60UbzeDabMpuNjcedBZ7y%2FAfq1iCDlLmTnUpVlUFzlrcEGPoFTbVQBKhGxH46qBV6NbgC9wVxNHDACFou7szv0Vxl42q9ktg6O%2FaNhvWIdyvw8dS7Wnbgyf4TWXJdh4Lz1Yuj4znfML0FCKDXNA%2FYe2E4CMkXeFpfKS1eNA%2Bg988VG7X24%2Bs3z%2FyqyzLOAQAAA%3D%3D

### Expected behavior

Keep `new RegExp('')` or `new RegExp()` in the output, since the current output (`//`) results in a syntax error.

### Actual behavior

_No response_

### Version

1.2.163

### Additional context

_No response_

Pull request:
fix(es/minifier): handle empty RegExp
<!-- 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):**
- fix #4234 

Patch:
diff --git a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs
index 3cf900b8f3..11f65a8b28 100644
--- a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs
+++ b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs
@@ -1266,6 +1266,14 @@ where
             }
         };
 
+        if pattern.is_empty() {
+            // For some expressions `RegExp()` and `RegExp("")`
+            // Theoretically we can use `/(?:)/` to achieve shorter code
+            // But some browsers released in 2015 don't support them yet.
+            args[0].expr = pattern.into();
+            return;
+        }
+
         let flags = args
             .get_mut(1)
             .map(|v| v.expr.take())


Test patch:
diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/analysis-snapshot.rust-debug b/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/analysis-snapshot.rust-debug
new file mode 100644
index 0000000000..c4e5194c9f
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/analysis-snapshot.rust-debug
@@ -0,0 +1,80 @@
+TestSnapshot {
+    vars: [
+        (
+            (
+                Atom('RegExp' type=static),
+                #1,
+            ),
+            VarUsageInfo {
+                inline_prevented: false,
+                ref_count: 4,
+                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: 4,
+                usage_count: 4,
+                reassigned_with_assignment: false,
+                reassigned_with_var_decl: false,
+                mutated: true,
+                has_property_access: false,
+                has_property_mutation: false,
+                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('bar' type=inline),
+                #1,
+            ),
+            VarUsageInfo {
+                inline_prevented: false,
+                ref_count: 4,
+                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: 4,
+                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: 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: true,
+                used_as_arg: false,
+                pure_fn: false,
+                infects: [],
+            },
+        ),
+    ],
+}
diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/config.json b/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/config.json
new file mode 100644
index 0000000000..7958a4828c
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/config.json
@@ -0,0 +1,44 @@
+{
+    "arguments": false,
+    "arrows": true,
+    "booleans": true,
+    "booleans_as_integers": false,
+    "collapse_vars": true,
+    "comparisons": true,
+    "computed_props": false,
+    "conditionals": false,
+    "dead_code": false,
+    "directives": false,
+    "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": false,
+    "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
+}
diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/input.js b/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/input.js
new file mode 100644
index 0000000000..ccca88d665
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/input.js
@@ -0,0 +1,5 @@
+bar(new RegExp(""));
+bar(new RegExp("", "u"));
+
+bar(new RegExp("a"));
+bar(new RegExp("a", "u"));
diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/output.js b/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/output.js
new file mode 100644
index 0000000000..3fc08cfc73
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/compress/fixture/issues/4234/output.js
@@ -0,0 +1,4 @@
+bar(new RegExp(""));
+bar(new RegExp("", "u"));
+bar(/a/);
+bar(/a/u);