Repository: swc-project/swc (rust)

Issue:
SWC misunderstands precedence when using `yield*` expression
### Describe the bug

Given this expression `yield* (0, async_iterator)` SWC will remove the parentheses and turn it into `yield* 0, async_iterator`, probably in an effort to remove bytes.

However, the precedence makes that actually say `(yield* 0), async_iterator` which is wrong and causing issues.

### Input code

```typescript
async function* level1(){
  yield 1;
  yield 2;
  yield 3;
}

async function* level2(){
  yield* (0, level1());
}

(async () => {
for await(const v of level2()){
  console.log(v);
}
})();
```


### Config

```json
Using parcel, I can share parcel config if needed. The issue is also reproducible on the REPL, please see attached link.
```


### Playground link

https://play.swc.rs/?version=1.2.237&code=H4sIAAAAAAAAA23MwQrCMBCE4XueYo67RcTWY9B3CTGRwJIFGyOl9N2LEaIHb8PA%2F7l5yR7xmX1JmgdIqEFG4tUASwpyw2j7nL7zbM1mjPtXTz%2F1ADodOsotok9FjMsVq4n6gHu5VMhrngsqNHaoSe9fJRxF71SbsTGx3QGHOfQjuwAAAA%3D%3D&config=H4sIAAAAAAAAA02OsQ6DMAxEfwV5ZmgZOjB37UdYwUGpQoxsFxWh%2FHsTCmo3%2B3z3fBs81UG%2FwYyiJHXSNRm%2BoQdbZ1InYTZowbRIHqNSLotgUs8yVX%2BkEd16J8eCxgVh8qIWhlN4kOGAhn9xlJGsfCDtLt210COz0mFoYQop%2BLWyHU%2BzkOrvhGmMpzMX1sTDqwrbXndn3iD%2FGEcu6OMwfttRWvbM3qTgIaJac218EPL8bhYSDZwg5w%2Bx293IIgEAAA%3D%3D

### Expected behavior

When exececuted, logs
```
1
2
3
```
to the console

Expected output code by SWC:

```typescript
async function* level1() {
    yield 1;
    yield 2;
    yield 3;
}
async function* level2() {
    yield* (0, level1());
}
(async ()=>{
    for await (const v of level2()){
        console.log(v);
    }
})();
```

### Actual behavior

When executed throws with: `Uncaught (in promise) TypeError: 0 is not iterable`

Actual output code by SWC:

```ts
async function* level1() {
    yield 1;
    yield 2;
    yield 3;
}
async function* level2() {
    yield* 0, level1();
}
(async ()=>{
    for await (const v of level2()){
        console.log(v);
    }
})();
```

### Version

1.2.237

### Additional context

This only happened by "doing a second pass" of SWC I guess?
So we built code to a library using parcel (`parcel build` with scope hoisting). Then we included that library in a project and built (`parcel serve`) it again and everything fell apart due to the aforementioned issue.

Pull request description:
fix(es/fixer): Wrap with paren for yield expr
<!-- 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:**

And this PR removed some useless paren in await expr.

<!-- 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 #5530 

Patch:
diff --git a/crates/swc_ecma_transforms_base/src/fixer.rs b/crates/swc_ecma_transforms_base/src/fixer.rs
index c581794995..756ebdc244 100644
--- a/crates/swc_ecma_transforms_base/src/fixer.rs
+++ b/crates/swc_ecma_transforms_base/src/fixer.rs
@@ -194,15 +194,18 @@ impl VisitMut for Fixer<'_> {
         self.ctx = old;
 
         match &*expr.arg {
-            Expr::Cond(..)
-            | Expr::Assign(..)
-            | Expr::Bin(..)
-            | Expr::Unary(..)
-            | Expr::Update(..) => self.wrap(&mut expr.arg),
+            Expr::Cond(..) | Expr::Assign(..) | Expr::Bin(..) => self.wrap(&mut expr.arg),
             _ => {}
         }
     }
 
+    fn visit_mut_yield_expr(&mut self, expr: &mut YieldExpr) {
+        let old = self.ctx;
+        self.ctx = Context::ForcedExpr;
+        expr.arg.visit_mut_with(self);
+        self.ctx = old;
+    }
+
     fn visit_mut_bin_expr(&mut self, expr: &mut BinExpr) {
         expr.left.visit_mut_with(self);
         let ctx = self.ctx;


Test patch:
diff --git a/crates/swc/tests/fixture/issues-5xxx/5530/input/.swcrc b/crates/swc/tests/fixture/issues-5xxx/5530/input/.swcrc
new file mode 100644
index 0000000000..1a275fa287
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-5xxx/5530/input/.swcrc
@@ -0,0 +1,9 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript"
+        },
+        "target": "es2022"
+    },
+    "isModule": true
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-5xxx/5530/input/index.js b/crates/swc/tests/fixture/issues-5xxx/5530/input/index.js
new file mode 100644
index 0000000000..c0eae867f4
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-5xxx/5530/input/index.js
@@ -0,0 +1,22 @@
+async function* level1() {
+    yield 1;
+    yield 2;
+    yield 3;
+}
+
+async function* level2() {
+    yield* (0, level1());
+}
+
+async function* foo() {
+    yield (foo, bar);
+    await (foo, bar);
+    yield* (0, level1());
+    await (0, level1());
+    yield (foo ? bar : baz);
+    await (foo ? bar : baz);
+    yield (++foo);
+    await (++foo);
+    yield (!foo);
+    await (!foo);
+}
diff --git a/crates/swc/tests/fixture/issues-5xxx/5530/output/index.js b/crates/swc/tests/fixture/issues-5xxx/5530/output/index.js
new file mode 100644
index 0000000000..10f4242766
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-5xxx/5530/output/index.js
@@ -0,0 +1,20 @@
+async function* level1() {
+    yield 1;
+    yield 2;
+    yield 3;
+}
+async function* level2() {
+    yield* (0, level1());
+}
+async function* foo() {
+    yield (foo, bar);
+    await (foo, bar);
+    yield* (0, level1());
+    await (0, level1());
+    yield foo ? bar : baz;
+    await (foo ? bar : baz);
+    yield ++foo;
+    await ++foo;
+    yield !foo;
+    await !foo;
+}