Repository: swc-project/swc (rust)

Issue:
Issue parsing JSX attribute with single back slash
**Input code**

```ts
function test() {
  return (
      <>
          <A b="\" />
      </>
  );
}
```

**Expected behavior**

Should parse because back slashes are valid like that in JSX.

Ex. https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wG4BYAKABMk0AbFIuNCAOwGd4BBALjhRsAnhUpVMAVzYZg7ODCRcAFAEo4Abypw4RGBKhs4SrdtMAeAHwnTNuGe5wARgF58AHXxwA9Fcq27PiYqogC+VEA (even though syntax highlighting breaks)

**Version**

swc_ecmascript 0.57.0

Pull request:
fix(ecma/lexer/jsx): jsx back slash escape parse
attempt to fix #2162 
maybe relate to #299
I think babel only escape `\` to `\\` [babel playground](https://babeljs.io/repl#?browsers=IE%208&build=&builtIns=false&corejs=false&spec=true&loose=true&code_lz=MYewdgzgLgBAhjAvDAPAPgFAoIIwEaIDkAOoTAPRoDcWuBJArgKwAsApgGxmU075HEAHgCYAjN2q1-JUhUl96xDDAm9KQA&debug=false&forceAllTransforms=true&shippedProposals=true&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=false&presets=env%2Creact%2Ctypescript&prettier=true&targets=&version=7.16.3&externalPlugins=&assumptions=%7B%7D)

Patch:
diff --git a/crates/swc_ecma_parser/src/lexer/jsx.rs b/crates/swc_ecma_parser/src/lexer/jsx.rs
index b90248604a..9f30e1d2da 100644
--- a/crates/swc_ecma_parser/src/lexer/jsx.rs
+++ b/crates/swc_ecma_parser/src/lexer/jsx.rs
@@ -157,9 +157,8 @@ impl<'a, I: Input> Lexer<'a, I> {
             if ch == '\\' {
                 has_escape = true;
                 out.push_str(self.input.slice(chunk_start, cur_pos));
-
-                let chars = self.read_jsx_escaped_char(&mut Raw(None))?;
-                out.extend(IntoIterator::into_iter(chars).flatten().flatten());
+                out.push_str("\\");
+                self.bump();
 
                 chunk_start = self.input.cur_pos();
                 continue;
@@ -192,65 +191,6 @@ impl<'a, I: Input> Lexer<'a, I> {
         })
     }
 
-    fn read_jsx_escaped_char(&mut self, raw: &mut Raw) -> LexResult<[Option<Char>; 2]> {
-        debug_assert_eq!(self.cur(), Some('\\'));
-        let start = self.cur_pos();
-        self.bump(); // '\'
-
-        let c = match self.cur() {
-            Some(c) => c,
-            None => self.error_span(pos_span(start), SyntaxError::InvalidStrEscape)?,
-        };
-
-        macro_rules! push_c_and_ret {
-            ($c:expr) => {{
-                raw.push(c);
-                $c
-            }};
-        }
-
-        let c = match c {
-            '\\' => push_c_and_ret!('\\'),
-            '\r' => {
-                raw.push_str("\r");
-                self.bump(); // remove '\r'
-
-                if self.eat(b'\n') {
-                    raw.push_str("\n");
-                }
-                return Ok(Default::default());
-            }
-            '\n' | '\u{2028}' | '\u{2029}' => {
-                match c {
-                    '\n' => raw.push_str("\n"),
-                    '\u{2028}' => raw.push_str("\u{2028}"),
-                    '\u{2029}' => raw.push_str("\u{2029}"),
-                    _ => unreachable!(),
-                }
-                self.bump();
-                return Ok(Default::default());
-            }
-
-            // read hexadecimal escape sequences
-            'x' => {
-                raw.push_str("0x");
-                self.bump(); // 'x'
-                return self.read_hex_char(start, 2, raw).map(|v| [Some(v), None]);
-            }
-
-            // read unicode escape sequences
-            'u' => {
-                return self
-                    .read_unicode_escape(start, raw)
-                    .map(|v| [Some(v), None]);
-            }
-            _ => c,
-        };
-        self.input.bump();
-
-        Ok([Some('\\'.into()), Some(c.into())])
-    }
-
     /// Read a JSX identifier (valid tag or attribute name).
     ///
     /// Optimized version since JSX identifiers can"t contain


Test patch:
diff --git a/crates/swc/tests/fixture/issue-2162/case1/input/.swcrc b/crates/swc/tests/fixture/issue-2162/case1/input/.swcrc
new file mode 100644
index 0000000000..17a31498a1
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case1/input/.swcrc
@@ -0,0 +1,8 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": true
+        }
+    }
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case1/input/index.js b/crates/swc/tests/fixture/issue-2162/case1/input/index.js
new file mode 100644
index 0000000000..81b630c85b
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case1/input/index.js
@@ -0,0 +1,7 @@
+function test() {
+    return (
+        <>
+            <A b='\' />
+        </>
+    );
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case1/output/index.js b/crates/swc/tests/fixture/issue-2162/case1/output/index.js
new file mode 100644
index 0000000000..085acab27f
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case1/output/index.js
@@ -0,0 +1,5 @@
+function test() {
+    return(/*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(A, {
+        b: "\\"
+    })));
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case2/input/.swcrc b/crates/swc/tests/fixture/issue-2162/case2/input/.swcrc
new file mode 100644
index 0000000000..17a31498a1
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case2/input/.swcrc
@@ -0,0 +1,8 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": true
+        }
+    }
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case2/input/index.js b/crates/swc/tests/fixture/issue-2162/case2/input/index.js
new file mode 100644
index 0000000000..1c5689950f
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case2/input/index.js
@@ -0,0 +1,7 @@
+function test() {
+    return (
+        <>
+            <A b="\" />
+        </>
+    );
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case2/output/index.js b/crates/swc/tests/fixture/issue-2162/case2/output/index.js
new file mode 100644
index 0000000000..085acab27f
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case2/output/index.js
@@ -0,0 +1,5 @@
+function test() {
+    return(/*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(A, {
+        b: "\\"
+    })));
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case3/input/.swcrc b/crates/swc/tests/fixture/issue-2162/case3/input/.swcrc
new file mode 100644
index 0000000000..17a31498a1
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case3/input/.swcrc
@@ -0,0 +1,8 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": true
+        }
+    }
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case3/input/index.js b/crates/swc/tests/fixture/issue-2162/case3/input/index.js
new file mode 100644
index 0000000000..7de07b8a56
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case3/input/index.js
@@ -0,0 +1,7 @@
+function test() {
+    return (
+        <>
+            <A b="\x21" />
+        </>
+    );
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case3/output/index.js b/crates/swc/tests/fixture/issue-2162/case3/output/index.js
new file mode 100644
index 0000000000..43607ef117
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case3/output/index.js
@@ -0,0 +1,5 @@
+function test() {
+    return(/*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(A, {
+        b: "\\x21"
+    })));
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case4/input/.swcrc b/crates/swc/tests/fixture/issue-2162/case4/input/.swcrc
new file mode 100644
index 0000000000..17a31498a1
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case4/input/.swcrc
@@ -0,0 +1,8 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": true
+        }
+    }
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case4/input/index.js b/crates/swc/tests/fixture/issue-2162/case4/input/index.js
new file mode 100644
index 0000000000..6dc20a69f8
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case4/input/index.js
@@ -0,0 +1,8 @@
+function test() {
+    return (
+        <>
+            <A b="\
+            " />
+        </>
+    );
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case4/output/index.js b/crates/swc/tests/fixture/issue-2162/case4/output/index.js
new file mode 100644
index 0000000000..2f37af3a08
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case4/output/index.js
@@ -0,0 +1,5 @@
+function test() {
+    return(/*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(A, {
+        b: "\\ "
+    })));
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case5/input/.swcrc b/crates/swc/tests/fixture/issue-2162/case5/input/.swcrc
new file mode 100644
index 0000000000..17a31498a1
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case5/input/.swcrc
@@ -0,0 +1,8 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": true
+        }
+    }
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case5/input/index.js b/crates/swc/tests/fixture/issue-2162/case5/input/index.js
new file mode 100644
index 0000000000..7c55c28149
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case5/input/index.js
@@ -0,0 +1,7 @@
+function test() {
+    return (
+        <>
+            <A b="\u54e6"/>
+        </>
+    );
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case5/output/index.js b/crates/swc/tests/fixture/issue-2162/case5/output/index.js
new file mode 100644
index 0000000000..f3cf574ac3
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case5/output/index.js
@@ -0,0 +1,5 @@
+function test() {
+    return(/*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(A, {
+        b: "\\u54e6"
+    })));
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case6/input/.swcrc b/crates/swc/tests/fixture/issue-2162/case6/input/.swcrc
new file mode 100644
index 0000000000..17a31498a1
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case6/input/.swcrc
@@ -0,0 +1,8 @@
+{
+    "jsc": {
+        "parser": {
+            "syntax": "ecmascript",
+            "jsx": true
+        }
+    }
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case6/input/index.js b/crates/swc/tests/fixture/issue-2162/case6/input/index.js
new file mode 100644
index 0000000000..b1b4543bad
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case6/input/index.js
@@ -0,0 +1,7 @@
+function test() {
+    return (
+        <>
+            <A b="\'"/>
+        </>
+    );
+}
diff --git a/crates/swc/tests/fixture/issue-2162/case6/output/index.js b/crates/swc/tests/fixture/issue-2162/case6/output/index.js
new file mode 100644
index 0000000000..a8e8aa225a
--- /dev/null
+++ b/crates/swc/tests/fixture/issue-2162/case6/output/index.js
@@ -0,0 +1,5 @@
+function test() {
+    return(/*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(A, {
+        b: "\\'"
+    })));
+}