Repository: swc-project/swc (rust)

Issue:
for await... does not unwrap promises
### Describe the bug

When `y` is a sync iterable of Promises`for await (const x of y)...` should unwrap the promises results into `x`.

This works correctly in Node 16 with https://github.com/microsoft/TypeScript but SWC produces code that does not unwrap the promises.

### Input code

```typescript
const a1 = [1, 2, 3];
const a2 = a1.map((x) => Promise.resolve(x));

async function f() {
  for await (const v of a2) {
    console.log(v);
  }
}

f();
```


### Config

```json
{
  "compilerOptions": {
    "outDir": "./built",
    "allowJs": true,
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "lib": ["dom", "esnext"],
    "strict": true,
    "jsx": "preserve"
  },
  "include": ["src/**/*", "libraries/**/*"],
  "exclude": ["src/**/*.spec.*", "src/**/*.test.*"],
  "ts-node": {
    "swc": true
  }
}
```


### Playground link

https://play.swc.rs/?version=1.2.222&code=H4sIAAAAAAAAAyXMwQoCIRSF4f19irNUGASnpUzP0D5aiGgIjjfUrIh59wy33%2BH8jnNtsBobrnrBuuB0M%2BSmrkOtVrt9CPGW2M64FN5j9ar4yqn7odIQ2frJDuGZXYucEYTEl4DABfZlY4OYwQ4OozpX4I%2BcvEp8F310gIMOonE39APe40qjmAAAAA%3D%3D&config=H4sIAAAAAAAAAy2MSw6AIAxE7zJrt7rgNg1WgxEkbU0khLsLxt3Mm0%2FFoR6uIpMoy1BaktEDByuZ1UvIhjbBSHa2jllnTIghha2Mvr9iFlaF2%2BhU7hGl%2FeTftj6N13oPUL%2FL72JBay9cWA1%2BfAAAAA%3D%3D

### Expected behavior

It prints

```
1
2
3
```

### Actual behavior

It prints

```
Promise { 1 }
Promise { 2 }
Promise { 3 }
```

### Version

1.2.223

### Additional context

_No response_

Pull request:
fix(es/helpers): Fix `for await`
**Description:**



**Related issue (if exists):**

 - Closes #5383

Patch:
diff --git a/crates/swc_ecma_transforms_base/src/helpers/_async_iterator.js b/crates/swc_ecma_transforms_base/src/helpers/_async_iterator.js
index 5a27a83f29..52ed87b9ac 100644
--- a/crates/swc_ecma_transforms_base/src/helpers/_async_iterator.js
+++ b/crates/swc_ecma_transforms_base/src/helpers/_async_iterator.js
@@ -1,17 +1,42 @@
 function _asyncIterator(iterable) {
-  var method;
-
-  if (typeof Symbol === "function") {
-    if (Symbol.asyncIterator) {
-      method = iterable[Symbol.asyncIterator];
-      if (method != null) return method.call(iterable);
-    }
-
-    if (Symbol.iterator) {
-      method = iterable[Symbol.iterator];
-      if (method != null) return method.call(iterable);
-    }
+  var method, async, sync, retry = 2;
+  for ("undefined" != typeof Symbol && (async = Symbol.asyncIterator, sync = Symbol.iterator); retry--;) {
+    if (async && null != (method = iterable[async])) return method.call(iterable);
+    if (sync && null != (method = iterable[sync])) return new AsyncFromSyncIterator(method.call(iterable));
+    async = "@@asyncIterator", sync = "@@iterator";
   }
-
   throw new TypeError("Object is not async iterable");
 }
+
+function AsyncFromSyncIterator(s) {
+  function AsyncFromSyncIteratorContinuation(r) {
+    if (Object(r) !== r) return Promise.reject(new TypeError(r + " is not an object."));
+    var done = r.done;
+    return Promise.resolve(r.value).then(function (value) {
+      return {
+        value: value,
+        done: done
+      };
+    });
+  }
+  return AsyncFromSyncIterator = function (s) {
+    this.s = s, this.n = s.next;
+  }, AsyncFromSyncIterator.prototype = {
+    s: null,
+    n: null,
+    next: function () {
+      return AsyncFromSyncIteratorContinuation(this.n.apply(this.s, arguments));
+    },
+    return: function (value) {
+      var ret = this.s.return;
+      return void 0 === ret ? Promise.resolve({
+        value: value,
+        done: !0
+      }) : AsyncFromSyncIteratorContinuation(ret.apply(this.s, arguments));
+    },
+    throw: function (value) {
+      var thr = this.s.return;
+      return void 0 === thr ? Promise.reject(value) : AsyncFromSyncIteratorContinuation(thr.apply(this.s, arguments));
+    }
+  }, new AsyncFromSyncIterator(s);
+}
\ No newline at end of file
diff --git a/packages/swc-helpers/src/_async_iterator.mjs b/packages/swc-helpers/src/_async_iterator.mjs
index 9f67652fb0..764a961da7 100644
--- a/packages/swc-helpers/src/_async_iterator.mjs
+++ b/packages/swc-helpers/src/_async_iterator.mjs
@@ -1,17 +1,44 @@
 export default function _asyncIterator(iterable) {
-  var method;
-
-  if (typeof Symbol === "function") {
-    if (Symbol.asyncIterator) {
-      method = iterable[Symbol.asyncIterator];
-      if (method != null) return method.call(iterable);
-    }
-
-    if (Symbol.iterator) {
-      method = iterable[Symbol.iterator];
-      if (method != null) return method.call(iterable);
-    }
+  var method, async, sync, retry = 2;
+  for ("undefined" != typeof Symbol && (async = Symbol.asyncIterator, sync = Symbol.iterator); retry--;) {
+    if (async && null != (method = iterable[async])) return method.call(iterable);
+    if (sync && null != (method = iterable[sync])) return new AsyncFromSyncIterator(method.call(iterable));
+    async = "@@asyncIterator", sync = "@@iterator";
   }
-
   throw new TypeError("Object is not async iterable");
 }
+
+
+
+function AsyncFromSyncIterator(s) {
+  function AsyncFromSyncIteratorContinuation(r) {
+    if (Object(r) !== r) return Promise.reject(new TypeError(r + " is not an object."));
+    var done = r.done;
+    return Promise.resolve(r.value).then(function (value) {
+      return {
+        value: value,
+        done: done
+      };
+    });
+  }
+  return AsyncFromSyncIterator = function (s) {
+    this.s = s, this.n = s.next;
+  }, AsyncFromSyncIterator.prototype = {
+    s: null,
+    n: null,
+    next: function () {
+      return AsyncFromSyncIteratorContinuation(this.n.apply(this.s, arguments));
+    },
+    return: function (value) {
+      var ret = this.s.return;
+      return void 0 === ret ? Promise.resolve({
+        value: value,
+        done: !0
+      }) : AsyncFromSyncIteratorContinuation(ret.apply(this.s, arguments));
+    },
+    throw: function (value) {
+      var thr = this.s.return;
+      return void 0 === thr ? Promise.reject(value) : AsyncFromSyncIteratorContinuation(thr.apply(this.s, arguments));
+    }
+  }, new AsyncFromSyncIterator(s);
+}
\ No newline at end of file


Test patch:
diff --git a/crates/swc/tests/exec/git-keep/js/exec.js b/crates/swc/tests/exec/git-keep/js/exec.js
deleted file mode 100644
index b2c376c655..0000000000
--- a/crates/swc/tests/exec/git-keep/js/exec.js
+++ /dev/null
@@ -1,1 +0,0 @@
-console.log("Ensure that the directory is tracked by git");
diff --git a/crates/swc/tests/exec/git-keep/ts/exec.ts b/crates/swc/tests/exec/git-keep/ts/exec.ts
deleted file mode 100644
index b2c376c655..0000000000
--- a/crates/swc/tests/exec/git-keep/ts/exec.ts
+++ /dev/null
@@ -1,1 +0,0 @@
-console.log("Ensure that the directory is tracked by git");
diff --git a/crates/swc/tests/exec/issues-5xxx/5383/exec.js b/crates/swc/tests/exec/issues-5xxx/5383/exec.js
new file mode 100644
index 0000000000..42c0bf8d81
--- /dev/null
+++ b/crates/swc/tests/exec/issues-5xxx/5383/exec.js
@@ -0,0 +1,10 @@
+const a1 = [1, 2, 3];
+const a2 = a1.map((x) => Promise.resolve(x));
+
+async function f() {
+  for await (const v of a2) {
+    console.log(v);
+  }
+}
+
+f();
\ No newline at end of file
diff --git a/crates/swc_ecma_transforms_compat/tests/async-to-generator/issue-5383/1/exec.js b/crates/swc_ecma_transforms_compat/tests/async-to-generator/issue-5383/1/exec.js
new file mode 100644
index 0000000000..30786880f7
--- /dev/null
+++ b/crates/swc_ecma_transforms_compat/tests/async-to-generator/issue-5383/1/exec.js
@@ -0,0 +1,10 @@
+const a1 = [1, 2, 3];
+const a2 = a1.map((x) => Promise.resolve(x));
+
+async function f() {
+    for await (const v of a2) {
+        console.log(v);
+    }
+}
+
+f();
\ No newline at end of file