Repository: swc-project/swc (rust)

Issue:
unknown field `eval`, expected one of `properties`, ...
### Describe the bug

Same kind of issue as #7738 but for a different `.terserrc`

### Input code


.terserrc

```json
{
  "mangle": {
    "eval": true,
    "properties": {
      "regex": "^(?!__core-js_shared__).*_$"
    }
  },
  "compress": {
    "unsafe": true,
    "arguments": true,
    "ecma": "2020",
    "passes": 4,
    "unsafe_arrows": true
  },
  "format": {
    "ecma": 2020
  }
}
```


### Config

_No response_

### Playground link

_No response_

### Expected behavior

No error and similar compression behavior as terser

### Actual behavior


[parcel.zip](https://github.com/swc-project/swc/files/12262228/parcel.zip)


```
daniel@mmmmmmmmmm parcel % yarn build
🚨 Build failed.

@parcel/optimizer-swc: unknown field `eval`, expected one of `properties`, `props`, `toplevel`, `topLevel`, `keep_classnames`, `keepClassNames`, `keep_fnames`, `keepFnNames`, `keep_private_props`, `keepPrivateProps`, `ie8`, 
`safari10`, `reserved` at line 1 column 324

  Error: unknown field `eval`, expected one of `properties`, `props`, `toplevel`, `topLevel`, `keep_classnames`, `keepClassNames`, `keep_fnames`, `keepFnNames`, `keep_private_props`, `keepPrivateProps`, `ie8`, `safari10`, 
  `reserved` at line 1 column 324

daniel@mmmmmmmmmm parcel % 
```

### Version

`@parcel/optimizer-swc@2.9.4`

### Additional context

_No response_

Pull request description:
feat(es/minifier): Support `mangle.eval`
**Description:**


**Related issue:**

 - Closes #7754

Patch:
diff --git a/crates/swc_ecma_minifier/benches/full.rs b/crates/swc_ecma_minifier/benches/full.rs
index b351c0c1d4..f0c0935256 100644
--- a/crates/swc_ecma_minifier/benches/full.rs
+++ b/crates/swc_ecma_minifier/benches/full.rs
@@ -84,7 +84,7 @@ fn run(src: &str) {
                         keep_private_props: false,
                         ie8: false,
                         safari10: false,
-                        reserved: Default::default(),
+                        ..Default::default()
                     }),
                     wrap: false,
                     enclose: false,
diff --git a/crates/swc_ecma_minifier/src/option/mod.rs b/crates/swc_ecma_minifier/src/option/mod.rs
index e085545b57..8940c188e3 100644
--- a/crates/swc_ecma_minifier/src/option/mod.rs
+++ b/crates/swc_ecma_minifier/src/option/mod.rs
@@ -81,6 +81,10 @@ pub struct MangleOptions {
 
     #[serde(default, alias = "reserved")]
     pub reserved: Vec<JsWord>,
+
+    /// mangle names visible in scopes where eval or with are used
+    #[serde(default)]
+    pub eval: bool,
 }
 
 #[derive(Debug, Clone, Default, Serialize, Deserialize, Merge)]
diff --git a/crates/swc_ecma_minifier/src/pass/mangle_names/mod.rs b/crates/swc_ecma_minifier/src/pass/mangle_names/mod.rs
index e23fe09694..3ff75820f8 100644
--- a/crates/swc_ecma_minifier/src/pass/mangle_names/mod.rs
+++ b/crates/swc_ecma_minifier/src/pass/mangle_names/mod.rs
@@ -28,7 +28,8 @@ pub(crate) fn name_mangler(
             swc_ecma_transforms_base::hygiene::Config {
                 keep_class_names: options.keep_class_names,
                 safari_10: options.safari10,
-                top_level_mark
+                top_level_mark,
+                ignore_eval: options.eval,
             },
             ManglingRenamer { chars, preserved }
         )
diff --git a/crates/swc_ecma_transforms_base/src/hygiene/mod.rs b/crates/swc_ecma_transforms_base/src/hygiene/mod.rs
index 33887ce3c7..971bcf5be3 100644
--- a/crates/swc_ecma_transforms_base/src/hygiene/mod.rs
+++ b/crates/swc_ecma_transforms_base/src/hygiene/mod.rs
@@ -19,6 +19,9 @@ pub struct Config {
     /// The marks derived from this marks will treated as `specified by user`
     /// and other marks will be treated as `generated by swc`.
     pub top_level_mark: Mark,
+
+    /// Mangle even if vars are visible to `eval` or `with`.
+    pub ignore_eval: bool,
 }
 
 /// See [hygiene_with_config] for doc. Creates a `hygiene` pass with default
diff --git a/crates/swc_ecma_transforms_base/src/rename/mod.rs b/crates/swc_ecma_transforms_base/src/rename/mod.rs
index a35cf30dfd..09d1e50030 100644
--- a/crates/swc_ecma_transforms_base/src/rename/mod.rs
+++ b/crates/swc_ecma_transforms_base/src/rename/mod.rs
@@ -187,7 +187,7 @@ macro_rules! unit {
     ($name:ident, $T:ty) => {
         /// Only called if `eval` exists
         fn $name(&mut self, n: &mut $T) {
-            if contains_eval(n, true) {
+            if !self.config.ignore_eval && contains_eval(n, true) {
                 n.visit_mut_children_with(self);
             } else {
                 let map = self.get_map(n, false, false, false);
@@ -199,7 +199,7 @@ macro_rules! unit {
     ($name:ident, $T:ty, true) => {
         /// Only called if `eval` exists
         fn $name(&mut self, n: &mut $T) {
-            if contains_eval(n, true) {
+            if !self.config.ignore_eval && contains_eval(n, true) {
                 n.visit_mut_children_with(self);
             } else {
                 let map = self.get_map(n, true, false, false);
@@ -239,7 +239,7 @@ where
     fn visit_mut_module(&mut self, m: &mut Module) {
         self.preserved = self.renamer.preserved_ids_for_module(m);
 
-        let has_eval = contains_eval(m, true);
+        let has_eval = !self.config.ignore_eval && contains_eval(m, true);
 
         self.unresolved = self.get_unresolved(m, has_eval);
 
@@ -276,7 +276,7 @@ where
     fn visit_mut_script(&mut self, m: &mut Script) {
         self.preserved = self.renamer.preserved_ids_for_script(m);
 
-        let has_eval = contains_eval(m, true);
+        let has_eval = !self.config.ignore_eval && contains_eval(m, true);
 
         self.unresolved = self.get_unresolved(m, has_eval);
 


Test patch:
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/7754/1/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/7754/1/input.js
new file mode 100644
index 0000000000..b027a4534b
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/7754/1/input.js
@@ -0,0 +1,4 @@
+const foo = 1;
+console.log(foo);
+
+eval(`console.log(foo)`)
\ No newline at end of file
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/7754/1/mangle.json b/crates/swc_ecma_minifier/tests/fixture/issues/7754/1/mangle.json
new file mode 100644
index 0000000000..09da21f0f7
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/7754/1/mangle.json
@@ -0,0 +1,4 @@
+{
+    "toplevel": true,
+    "eval": true
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/7754/1/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/7754/1/output.js
new file mode 100644
index 0000000000..7c31012811
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/7754/1/output.js
@@ -0,0 +1,2 @@
+const o = 1;
+console.log(o), eval("console.log(foo)");
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/7754/2/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/7754/2/input.js
new file mode 100644
index 0000000000..b027a4534b
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/7754/2/input.js
@@ -0,0 +1,4 @@
+const foo = 1;
+console.log(foo);
+
+eval(`console.log(foo)`)
\ No newline at end of file
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/7754/2/mangle.json b/crates/swc_ecma_minifier/tests/fixture/issues/7754/2/mangle.json
new file mode 100644
index 0000000000..9fa9d30387
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/7754/2/mangle.json
@@ -0,0 +1,3 @@
+{
+    "toplevel": true
+}
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/7754/2/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/7754/2/output.js
new file mode 100644
index 0000000000..670ec93d81
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/7754/2/output.js
@@ -0,0 +1,2 @@
+const foo = 1;
+console.log(foo), eval("console.log(foo)");