Repository: swc-project/swc (rust)

Issue:
SWC can't use reserved keyword `async` as a variable name when it is actually valid
### Describe the bug

SWC can't use reserved keyword `async` as a variable name when it is actually valid.

### Input code

```typescript
const async = '1';
let script = document.createElement("script")
    script.async = async
    script.onload = resolve(key)
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "ecmascript",
      "jsx": false
    },
    "target": "es2022",
    "loose": false,
    "minify": {
      "compress": false,
      "mangle": false
    }
  },
  "module": {
    "type": "es6"
  },
  "minify": false,
  "isModule": false
}
```


### Playground link

https://play.swc.rs/?version=1.2.196&code=H4sIAAAAAAAAA0vOzysuUUgsrsxLVrBVUDdUt%2BbKSS1RKE4uyiwoAYqk5CeX5qbmleglF6UmlqS65qSCeBpKEAVKmlwKQADh6MFMAdPIEvl5OfmJKUCZotTi%2FJyyVI3s1EpNAAvg9a56AAAA&config=H4sIAAAAAAAAA0WMOw4DIQxE7%2BKaIqJIsXfIISziXRHxk%2B1IQYi7BzZE241m3rwGL3GwNSjIQjyT1KT4gQ3IRRTHviiYgY1qxyDUDSjyQToRsTdrxxxyFlqAgeiT3%2BuUuRwLk8g1YTrCn%2BzDFfPzPYsGWgudzjv0y7F%2BXh4L%2FD2%2F%2F2Nc37kAAAA%3D

### Expected behavior

There shouldn't be any `SyntaxError`.

### Actual behavior

_No response_

### Version

1.2.196

### Additional context

_No response_

Pull request description:
fix(es/parser): break line after async
<!-- 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 #4873 

Patch:
diff --git a/crates/swc_ecma_parser/src/parser/expr.rs b/crates/swc_ecma_parser/src/parser/expr.rs
index e5efef566f..165153a232 100644
--- a/crates/swc_ecma_parser/src/parser/expr.rs
+++ b/crates/swc_ecma_parser/src/parser/expr.rs
@@ -287,7 +287,10 @@ impl<I: Tokens> Parser<I> {
                         }
                     }
 
-                    if can_be_arrow && peeked_is!(self, '(') {
+                    if can_be_arrow
+                        && peeked_is!(self, '(')
+                        && !self.input.has_linebreak_between_cur_and_peeked()
+                    {
                         expect!(self, "async");
                         let async_span = self.input.prev_span();
                         return self.parse_paren_expr_or_arrow_fn(can_be_arrow, Some(async_span));
@@ -380,7 +383,11 @@ impl<I: Tokens> Parser<I> {
                 );
             }
 
-            if can_be_arrow && id.sym == js_word!("async") && is!(self, BindingIdent) {
+            if can_be_arrow
+                && id.sym == js_word!("async")
+                && !self.input.had_line_break_before_cur()
+                && is!(self, BindingIdent)
+            {
                 let ident = self.parse_binding_ident()?;
                 if self.input.syntax().typescript()
                     && ident.id.sym == js_word!("as")
diff --git a/package.json b/package.json
index a8eb886bc3..47a9fc34a6 100644
--- a/package.json
+++ b/package.json
@@ -71,16 +71,16 @@
         "*.rs": [
             "rustfmt --"
         ],
-        "*.js": [
+        "!(**/tests/**/*)*.js": [
             "prettier --write"
         ],
-        "*.ts": [
+        "!(**/tests/**/*)*.ts": [
             "prettier --write"
         ],
-        "*.jsx": [
+        "!(**/tests/**/*)*.jsx": [
             "prettier --write"
         ],
-        "*.tsx": [
+        "!(**/tests/**/*)*.tsx": [
             "prettier --write"
         ]
     },


Test patch:
diff --git a/crates/swc/tests/fixture/issues-4xxx/4873/1/input/.swcrc b/crates/swc/tests/fixture/issues-4xxx/4873/1/input/.swcrc
new file mode 100644
index 0000000000..41424b3394
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-4xxx/4873/1/input/.swcrc
@@ -0,0 +1,18 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": false
+        },
+        "target": "es2022",
+        "loose": false,
+        "minify": {
+            "compress": false,
+            "mangle": false
+        }
+    },
+    "module": {
+        "type": "es6"
+    },
+    "minify": false
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-4xxx/4873/1/input/index.js b/crates/swc/tests/fixture/issues-4xxx/4873/1/input/index.js
new file mode 100644
index 0000000000..c300024ea7
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-4xxx/4873/1/input/index.js
@@ -0,0 +1,4 @@
+const async = '1';
+let script = document.createElement("script")
+    script.async = async
+    script.onload = resolve(key)
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-4xxx/4873/1/output/index.js b/crates/swc/tests/fixture/issues-4xxx/4873/1/output/index.js
new file mode 100644
index 0000000000..2b80e46364
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-4xxx/4873/1/output/index.js
@@ -0,0 +1,4 @@
+const async = '1';
+let script = document.createElement("script");
+script.async = async;
+script.onload = resolve(key);
diff --git a/crates/swc/tests/fixture/issues-4xxx/4873/2/input/.swcrc b/crates/swc/tests/fixture/issues-4xxx/4873/2/input/.swcrc
new file mode 100644
index 0000000000..41424b3394
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-4xxx/4873/2/input/.swcrc
@@ -0,0 +1,18 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": false
+        },
+        "target": "es2022",
+        "loose": false,
+        "minify": {
+            "compress": false,
+            "mangle": false
+        }
+    },
+    "module": {
+        "type": "es6"
+    },
+    "minify": false
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-4xxx/4873/2/input/index.js b/crates/swc/tests/fixture/issues-4xxx/4873/2/input/index.js
new file mode 100644
index 0000000000..26d1b0655a
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-4xxx/4873/2/input/index.js
@@ -0,0 +1,5 @@
+const async = "1"
+const foo = async
+function bar() {
+    return 42
+}
diff --git a/crates/swc/tests/fixture/issues-4xxx/4873/2/output/index.js b/crates/swc/tests/fixture/issues-4xxx/4873/2/output/index.js
new file mode 100644
index 0000000000..919244e8e0
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-4xxx/4873/2/output/index.js
@@ -0,0 +1,5 @@
+const async = "1";
+const foo = async;
+function bar() {
+    return 42;
+}
diff --git a/crates/swc/tests/fixture/issues-4xxx/4873/3/input/.swcrc b/crates/swc/tests/fixture/issues-4xxx/4873/3/input/.swcrc
new file mode 100644
index 0000000000..41424b3394
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-4xxx/4873/3/input/.swcrc
@@ -0,0 +1,18 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": false
+        },
+        "target": "es2022",
+        "loose": false,
+        "minify": {
+            "compress": false,
+            "mangle": false
+        }
+    },
+    "module": {
+        "type": "es6"
+    },
+    "minify": false
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-4xxx/4873/3/input/index.js b/crates/swc/tests/fixture/issues-4xxx/4873/3/input/index.js
new file mode 100644
index 0000000000..2aa4423ba7
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-4xxx/4873/3/input/index.js
@@ -0,0 +1,3 @@
+const async = "1"
+const foo = async
+x => x
diff --git a/crates/swc/tests/fixture/issues-4xxx/4873/3/output/index.js b/crates/swc/tests/fixture/issues-4xxx/4873/3/output/index.js
new file mode 100644
index 0000000000..b6f9b46adc
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-4xxx/4873/3/output/index.js
@@ -0,0 +1,3 @@
+const async = "1";
+const foo = async;
+(x)=>x;
diff --git a/crates/swc_ecma_parser/tests/errors/async-line-break/1/input.js b/crates/swc_ecma_parser/tests/errors/async-line-break/1/input.js
new file mode 100644
index 0000000000..6987e1df48
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/errors/async-line-break/1/input.js
@@ -0,0 +1,2 @@
+foo = async
+() => 42
diff --git a/crates/swc_ecma_parser/tests/errors/async-line-break/1/input.js.stderr b/crates/swc_ecma_parser/tests/errors/async-line-break/1/input.js.stderr
new file mode 100644
index 0000000000..7382e2a856
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/errors/async-line-break/1/input.js.stderr
@@ -0,0 +1,13 @@
+
+  x Expected ';', '}' or <eof>
+   ,-[$DIR/tests/errors/async-line-break/1/input.js:2:1]
+ 2 | () => 42
+   :    ^^
+   `----
+
+Error: 
+  > This is the expression part of an expression statement
+   ,-[$DIR/tests/errors/async-line-break/1/input.js:1:1]
+ 1 | ,-> foo = async
+ 2 | `-> () => 42
+   `----
diff --git a/crates/swc_ecma_parser/tests/errors/async-line-break/2/input.js b/crates/swc_ecma_parser/tests/errors/async-line-break/2/input.js
new file mode 100644
index 0000000000..8944c3b7db
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/errors/async-line-break/2/input.js
@@ -0,0 +1,4 @@
+foo = async
+function() {
+    return 42
+}
\ No newline at end of file
diff --git a/crates/swc_ecma_parser/tests/errors/async-line-break/2/input.js.stderr b/crates/swc_ecma_parser/tests/errors/async-line-break/2/input.js.stderr
new file mode 100644
index 0000000000..b5ccabac44
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/errors/async-line-break/2/input.js.stderr
@@ -0,0 +1,6 @@
+
+  x Expected ident
+   ,-[$DIR/tests/errors/async-line-break/2/input.js:2:1]
+ 2 | function() {
+   :         ^
+   `----