Repository: swc-project/swc (rust)

Issue:
Another unicode "double-escaping" bug
We (Framer) have run into this issue recently where unicode chars are double-escaped in JSX attributes, see the PR with a failing test for it https://github.com/swc-project/swc/pull/2012

```tsx
<Bla title="Ú"/>
```
is being transformed to, note the double backslash:
```tsx
React.createElement(Bla, {
    title: "\\xda"
});
```
 

Pull request description:
fix(es/transforms/react): Handle escape correctly
swc_ecma_transforms_react:
 - [x] Handle escapes properly. (Closes #2013)

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 026cf2abbb..28c0d593d7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2744,7 +2744,7 @@ dependencies = [
 
 [[package]]
 name = "swc_ecma_transforms_react"
-version = "0.31.0"
+version = "0.31.1"
 dependencies = [
  "base64 0.13.0",
  "dashmap",
diff --git a/ecmascript/transforms/react/Cargo.toml b/ecmascript/transforms/react/Cargo.toml
index 19c7aae98a..2574b9e224 100644
--- a/ecmascript/transforms/react/Cargo.toml
+++ b/ecmascript/transforms/react/Cargo.toml
@@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs"]
 license = "Apache-2.0/MIT"
 name = "swc_ecma_transforms_react"
 repository = "https://github.com/swc-project/swc.git"
-version = "0.31.0"
+version = "0.31.1"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
diff --git a/ecmascript/transforms/react/src/jsx/mod.rs b/ecmascript/transforms/react/src/jsx/mod.rs
index 57f92e6537..bfe8d1d4f9 100644
--- a/ecmascript/transforms/react/src/jsx/mod.rs
+++ b/ecmascript/transforms/react/src/jsx/mod.rs
@@ -3,7 +3,6 @@ use dashmap::DashMap;
 use once_cell::sync::Lazy;
 use regex::Regex;
 use serde::{Deserialize, Serialize};
-use std::fmt::Write;
 use std::iter::once;
 use std::{iter, mem};
 use string_enum::StringEnum;
@@ -1129,11 +1128,8 @@ fn transform_jsx_attr_str(v: &str) -> String {
             '\'' if single_quote => buf.push_str("\\'"),
             '"' if !single_quote => buf.push_str("\\\""),
 
-            '\x01'..='\x0f' => {
-                let _ = write!(buf, "\\x0{:x}", c as u8);
-            }
-            '\x10'..='\x1f' => {
-                let _ = write!(buf, "\\x{:x}", c as u8);
+            '\x01'..='\x0f' | '\x10'..='\x1f' => {
+                buf.push(c);
             }
 
             '\x20'..='\x7e' => {
@@ -1141,7 +1137,7 @@ fn transform_jsx_attr_str(v: &str) -> String {
                 buf.push(c);
             }
             '\u{7f}'..='\u{ff}' => {
-                let _ = write!(buf, "\\x{:x}", c as u8);
+                buf.push(c);
             }
 
             _ => {


Test patch:
diff --git a/ecmascript/transforms/react/src/jsx/tests.rs b/ecmascript/transforms/react/src/jsx/tests.rs
index ce561780aa..da5e964d11 100644
--- a/ecmascript/transforms/react/src/jsx/tests.rs
+++ b/ecmascript/transforms/react/src/jsx/tests.rs
@@ -706,7 +706,7 @@ test!(
 "#,
     r#"
 React.createElement("div", {
-  id: "w\\xf4w"
+  id: "w\xf4w"
 });
 React.createElement("div", {
   id: "w"
@@ -765,6 +765,19 @@ React.createElement("div", null, "this should not parse as unicode: \\u00a0");
     ok_if_code_eq
 );
 
+test!(
+    ::swc_ecma_parser::Syntax::Es(::swc_ecma_parser::EsConfig {
+        jsx: true,
+        ..Default::default()
+    }),
+    |t| tr(t, Default::default()),
+    react_should_escape_unicode_chars_in_attribute,
+    r#"<Bla title="Ú"/>"#,
+    r#"React.createElement(Bla, {
+    title: "\xda"
+});"#
+);
+
 test!(
     // FIXME
     ignore,
diff --git a/ecmascript/transforms/react/tests/jsx/fixture/react-automatic/should-escape-xhtml-jsxattribute/output.mjs b/ecmascript/transforms/react/tests/jsx/fixture/react-automatic/should-escape-xhtml-jsxattribute/output.mjs
index 048aeb80d2..c15d6ffc23 100644
--- a/ecmascript/transforms/react/tests/jsx/fixture/react-automatic/should-escape-xhtml-jsxattribute/output.mjs
+++ b/ecmascript/transforms/react/tests/jsx/fixture/react-automatic/should-escape-xhtml-jsxattribute/output.mjs
@@ -1,6 +1,6 @@
 import { jsx as _jsx } from "react/jsx-runtime";
 _jsx("div", {
-    id: "w\\xf4w"
+    id: "w\xf4w"
 });
 _jsx("div", {
     id: "w"
diff --git a/ecmascript/transforms/react/tests/jsx/fixture/react/should-escape-xhtml-jsxattribute-babel-7/output.js b/ecmascript/transforms/react/tests/jsx/fixture/react/should-escape-xhtml-jsxattribute-babel-7/output.js
index 851cebb9d3..06ba917695 100644
--- a/ecmascript/transforms/react/tests/jsx/fixture/react/should-escape-xhtml-jsxattribute-babel-7/output.js
+++ b/ecmascript/transforms/react/tests/jsx/fixture/react/should-escape-xhtml-jsxattribute-babel-7/output.js
@@ -1,5 +1,5 @@
 React.createElement("div", {
-    id: "w\\xf4w"
+    id: "w\xf4w"
 });
 React.createElement("div", {
     id: "w"
diff --git a/ecmascript/transforms/react/tests/jsx/fixture/react/should-escape-xhtml-jsxattribute/output.js b/ecmascript/transforms/react/tests/jsx/fixture/react/should-escape-xhtml-jsxattribute/output.js
index 851cebb9d3..06ba917695 100644
--- a/ecmascript/transforms/react/tests/jsx/fixture/react/should-escape-xhtml-jsxattribute/output.js
+++ b/ecmascript/transforms/react/tests/jsx/fixture/react/should-escape-xhtml-jsxattribute/output.js
@@ -1,5 +1,5 @@
 React.createElement("div", {
-    id: "w\\xf4w"
+    id: "w\xf4w"
 });
 React.createElement("div", {
     id: "w"