Repository: facebook/react
Pull request: compiler: Workaround Babel bug with unicode in jsx string attrs

Stack from [ghstack](https://github.com/ezyang/ghstack) (oldest at bottom):
* __->__ #29141

Workaround for a bug in older versions of Babel, where strings with unicode are incorrectly escaped when emitted as JSX attributes, causing double-escaping by later processing.

Closes #29120
Closes #29124

Changed files:
filename: compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
patch: @@ -6,6 +6,7 @@
  */
 
 import * as t from "@babel/types";
+import { createHmac } from "crypto";
 import { pruneHoistedContexts, pruneUnusedLValues, pruneUnusedLabels } from ".";
 import { CompilerError, ErrorSeverity } from "../CompilerError";
 import { Environment, EnvironmentConfig, ExternalFunction } from "../HIR";
@@ -43,7 +44,6 @@ import { assertExhaustive } from "../Utils/utils";
 import { buildReactiveFunction } from "./BuildReactiveFunction";
 import { SINGLE_CHILD_FBT_TAGS } from "./MemoizeFbtOperandsInSameScope";
 import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
-import { createHmac } from "crypto";
 
 export const MEMO_CACHE_SENTINEL = "react.memo_cache_sentinel";
 export const EARLY_RETURN_SENTINEL = "react.early_return_sentinel";
@@ -2018,6 +2018,11 @@ function codegenInstructionValue(
   return value;
 }
 
+/**
+ * Due to a bug in earlier Babel versions, JSX string attributes with double quotes or with unicode characters
+ * may be escaped unnecessarily. To avoid trigger this Babel bug, we use a JsxExpressionContainer for such strings.
+ */
+const STRING_REQUIRES_EXPR_CONTAINER_PATTERN = /[\u{0080}-\u{FFFF}]|"/u;
 function codegenJsxAttribute(
   cx: Context,
   attribute: JsxAttribute
@@ -2040,7 +2045,7 @@ function codegenJsxAttribute(
       switch (innerValue.type) {
         case "StringLiteral": {
           value = innerValue;
-          if (value.value.indexOf('"') !== -1) {
+          if (STRING_REQUIRES_EXPR_CONTAINER_PATTERN.test(value.value)) {
             value = createJsxExpressionContainer(value.loc, value);
           }
           break;

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.expect.md
patch: @@ -0,0 +1,97 @@
+
+## Input
+
+```javascript
+function Component() {
+  return (
+    <Post
+      author="potetotes"
+      text="in addition to understanding JavaScript semantics and the rules of React, the compiler team also understands தமிழ், 中文, 日本語, 한국어 and i think that’s pretty cool"
+    />
+  );
+}
+
+function Post({ author, text }) {
+  return (
+    <div>
+      <h1>{author}</h1>
+      <span>{text}</span>
+    </div>
+  );
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  params: [{}],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime";
+function Component() {
+  const $ = _c(1);
+  let t0;
+  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
+    t0 = (
+      <Post
+        author="potetotes"
+        text={
+          "in addition to understanding JavaScript semantics and the rules of React, the compiler team also understands \u0BA4\u0BAE\u0BBF\u0BB4\u0BCD, \u4E2D\u6587, \u65E5\u672C\u8A9E, \uD55C\uAD6D\uC5B4 and i think that\u2019s pretty cool"
+        }
+      />
+    );
+    $[0] = t0;
+  } else {
+    t0 = $[0];
+  }
+  return t0;
+}
+
+function Post(t0) {
+  const $ = _c(7);
+  const { author, text } = t0;
+  let t1;
+  if ($[0] !== author) {
+    t1 = <h1>{author}</h1>;
+    $[0] = author;
+    $[1] = t1;
+  } else {
+    t1 = $[1];
+  }
+  let t2;
+  if ($[2] !== text) {
+    t2 = <span>{text}</span>;
+    $[2] = text;
+    $[3] = t2;
+  } else {
+    t2 = $[3];
+  }
+  let t3;
+  if ($[4] !== t1 || $[5] !== t2) {
+    t3 = (
+      <div>
+        {t1}
+        {t2}
+      </div>
+    );
+    $[4] = t1;
+    $[5] = t2;
+    $[6] = t3;
+  } else {
+    t3 = $[6];
+  }
+  return t3;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  params: [{}],
+};
+
+```
+      
+### Eval output
+(kind: ok) <div><h1>potetotes</h1><span>in addition to understanding JavaScript semantics and the rules of React, the compiler team also understands தமிழ், 中文, 日本語, 한국어 and i think that’s pretty cool</span></div>
\ No newline at end of file

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.js
patch: @@ -0,0 +1,22 @@
+function Component() {
+  return (
+    <Post
+      author="potetotes"
+      text="in addition to understanding JavaScript semantics and the rules of React, the compiler team also understands தமிழ், 中文, 日本語, 한국어 and i think that’s pretty cool"
+    />
+  );
+}
+
+function Post({ author, text }) {
+  return (
+    <div>
+      <h1>{author}</h1>
+      <span>{text}</span>
+    </div>
+  );
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: Component,
+  params: [{}],
+};