Repository: swc-project/swc (rust)

Issue:
Unterminated regexp literal
### Describe the bug

The code poc.js can be run by node and transpiled by babel, but it report error by swc
```shell
Unterminated regexp literal
```

### Input code

```typescript
var commands;
var command;
function retornaTempoArenaEmMilisegundos(distancia, velocidade) {
    let conv = distancia * class LogExit {
        constructor({logger}) {
            this.logger = logger;
        }
        handle(commands) {
            commands.forEach(command => command.close.subscribe(({exitCode}) => {
                this.logger.logCommandEvent(`${ command.command } exited with code ${ exitCode }`, command);
            }));
            return commands;
        }
    } / velocidade * 1000;
    return Math.round(conv);
}
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "ecmascript",
      "jsx": false
    },
    "target": "es5",
    "loose": false,
    "minify": {
      "compress": false,
      "mangle": false
    }
  },
  "module": {
    "type": "es6",
    "strict": false,
    "strictMode": false,
    "lazy": false,
    "noInterop": false
  },
  "minify": false
}
```


### Playground link

https://play.swc.rs/?version=1.2.239&code=H4sIAAAAAAAAA11RwW6DMAy98xU%2B7BCmirEz6qSp4rbe9gFNExcihWRKApuE8u8zglDAlyR%2Bz8923sAdCNt13EhfZcPzVWWP3oigrAGHwTrDv7H7sZ8ODa%2B7q9LKY9MbaT2TygduhOInGFBboSSXmMOYAYXGQJJmgDOsPHgFobn38GWb%2Bk%2BFhToFUX1wvaCObNS2adDFfINPEVrlixkj1flSrYy43lpaQyNL6x1lUr54WFdz0SYinD8SVghtPRa%2Bv3vh1B0ZG5HmvViJNBXx9oqH4abjMgvVA5rAbi%2FjU3lpFmFSRAm%2FKrSESgRipS4Qb6dUkle7ZjE%2FJMim3pmNm%2FsfifC2sYcseC%2FLciYtlVce2sJZMpVNjpF8%2FAfy8GtGHwIAAA%3D%3D&config=H4sIAAAAAAAAA0WMSwrDMAxE76K1t93kDj2EcJXg4B8aBWqM714bUrKSmPdmOp3wtHWqrBBdH1o2%2FtJG4hPDa6hGbmoz2jlChiNjPcSWgtdksRTITR2lkMPe1pIvqaoAD%2BJ8xL855lAqn2sFnazVeVcllXyCxjN0lwPet216yfgBCKaI9boAAAA%3D

### Expected behavior

I'm not sure if swc's lint is valid

### Actual behavior

_No response_

### Version

1.2.239

### Additional context

_No response_

Pull request description:
fix(es/parser): Allow expr to follow `ClassExpr`
<!-- 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 #5557 

Patch:
diff --git a/crates/swc_ecma_parser/src/lexer/state.rs b/crates/swc_ecma_parser/src/lexer/state.rs
index 0964ae6094..0f67c78e76 100644
--- a/crates/swc_ecma_parser/src/lexer/state.rs
+++ b/crates/swc_ecma_parser/src/lexer/state.rs
@@ -438,7 +438,10 @@ impl State {
 
                     // let a = function(){}
                     if out == TokenContext::BraceStmt
-                        && context.current() == Some(TokenContext::FnExpr)
+                        && matches!(
+                            context.current(),
+                            Some(TokenContext::FnExpr | TokenContext::ClassExpr)
+                        )
                     {
                         context.pop();
                         return false;
@@ -467,6 +470,15 @@ impl State {
                     false
                 }
 
+                tok!("class") => {
+                    if is_expr_allowed
+                        && !context.is_brace_block(prev, had_line_break, is_expr_allowed)
+                    {
+                        context.push(TokenContext::ClassExpr);
+                    }
+                    false
+                }
+
                 tok!(':') if context.current() == Some(TokenContext::FnExpr) => {
                     // function keyword is object prop: { function: expr }
                     context.pop(); // Remove FnExpr
@@ -726,6 +738,8 @@ pub enum TokenContext {
     },
     #[kind(is_expr)]
     FnExpr,
+    #[kind(is_expr)]
+    ClassExpr,
     JSXOpeningTag,
     JSXClosingTag,
     #[kind(is_expr, preserve_space)]
diff --git a/crates/swc_ecma_parser/src/parser/stmt.rs b/crates/swc_ecma_parser/src/parser/stmt.rs
index 7046241f3f..56fd511968 100644
--- a/crates/swc_ecma_parser/src/parser/stmt.rs
+++ b/crates/swc_ecma_parser/src/parser/stmt.rs
@@ -2426,4 +2426,11 @@ const foo;"#;
             |p| p.parse_script(),
         );
     }
+
+    #[test]
+    fn issue_5557_expr_follow_class() {
+        let src = "foo * class {} / bar;";
+
+        test_parser(src, Default::default(), |p| p.parse_script());
+    }
 }


Test patch:
diff --git a/crates/swc/tests/fixture/issues-5xxx/5557/input/.swcrc b/crates/swc/tests/fixture/issues-5xxx/5557/input/.swcrc
new file mode 100644
index 0000000000..f78f7ff0dc
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-5xxx/5557/input/.swcrc
@@ -0,0 +1,22 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": false
+        },
+        "target": "es5",
+        "loose": false,
+        "minify": {
+            "compress": false,
+            "mangle": false
+        }
+    },
+    "module": {
+        "type": "es6",
+        "strict": false,
+        "strictMode": false,
+        "lazy": false,
+        "noInterop": false
+    },
+    "minify": false
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-5xxx/5557/input/index.js b/crates/swc/tests/fixture/issues-5xxx/5557/input/index.js
new file mode 100644
index 0000000000..d4417716e6
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-5xxx/5557/input/index.js
@@ -0,0 +1,16 @@
+var commands;
+var command;
+function retornaTempoArenaEmMilisegundos(distancia, velocidade) {
+    let conv = distancia * class LogExit {
+        constructor({logger}) {
+            this.logger = logger;
+        }
+        handle(commands) {
+            commands.forEach(command => command.close.subscribe(({exitCode}) => {
+                this.logger.logCommandEvent(`${ command.command } exited with code ${ exitCode }`, command);
+            }));
+            return commands;
+        }
+    } / velocidade * 1000;
+    return Math.round(conv);
+}
\ No newline at end of file
diff --git a/crates/swc/tests/fixture/issues-5xxx/5557/output/index.js b/crates/swc/tests/fixture/issues-5xxx/5557/output/index.js
new file mode 100644
index 0000000000..02b4e9ee6d
--- /dev/null
+++ b/crates/swc/tests/fixture/issues-5xxx/5557/output/index.js
@@ -0,0 +1,31 @@
+import _class_call_check from "@swc/helpers/src/_class_call_check.mjs";
+import _create_class from "@swc/helpers/src/_create_class.mjs";
+var commands;
+var command;
+function retornaTempoArenaEmMilisegundos(distancia, velocidade) {
+    var conv = distancia * function() {
+        "use strict";
+        function LogExit(param) {
+            var logger = param.logger;
+            _class_call_check(this, LogExit);
+            this.logger = logger;
+        }
+        _create_class(LogExit, [
+            {
+                key: "handle",
+                value: function handle(commands) {
+                    var _this = this;
+                    commands.forEach(function(command) {
+                        return command.close.subscribe(function(param) {
+                            var exitCode = param.exitCode;
+                            _this.logger.logCommandEvent("".concat(command.command, " exited with code ").concat(exitCode), command);
+                        });
+                    });
+                    return commands;
+                }
+            }
+        ]);
+        return LogExit;
+    }() / velocidade * 1000;
+    return Math.round(conv);
+}