compiler: Workaround Babel bug with unicode in jsx string attrs
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 ghstack-source-id: 065440d4fb97e164beb8a8f15f252f372a59c5a0 Pull Request resolved: https://github.com/facebook/react/pull/29141
Joe Savona committed
May 17, 2024 at 10:38 UTC
be6712f72ccfe3e3b9388e169f6f9e261d78637b
3 files changed
+126
-2
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+7
-2
@@ -6,6 +6,7 @@
6
*/
7
8
import * as t from "@babel/types";
9
+import { createHmac } from "crypto";
10
import { pruneHoistedContexts, pruneUnusedLValues, pruneUnusedLabels } from ".";
11
import { CompilerError, ErrorSeverity } from "../CompilerError";
12
import { Environment, EnvironmentConfig, ExternalFunction } from "../HIR";
@@ -43,7 +44,6 @@ import { assertExhaustive } from "../Utils/utils";
44
import { buildReactiveFunction } from "./BuildReactiveFunction";
45
import { SINGLE_CHILD_FBT_TAGS } from "./MemoizeFbtOperandsInSameScope";
46
import { ReactiveFunctionVisitor, visitReactiveFunction } from "./visitors";
46
-import { createHmac } from "crypto";
47
48
export const MEMO_CACHE_SENTINEL = "react.memo_cache_sentinel";
49
export const EARLY_RETURN_SENTINEL = "react.early_return_sentinel";
@@ -2018,6 +2018,11 @@ function codegenInstructionValue(
2018
return value;
2019
}
2020
2021
+/**
2022
+ * Due to a bug in earlier Babel versions, JSX string attributes with double quotes or with unicode characters
2023
+ * may be escaped unnecessarily. To avoid trigger this Babel bug, we use a JsxExpressionContainer for such strings.
2024
+ */
2025
+const STRING_REQUIRES_EXPR_CONTAINER_PATTERN = /[\u{0080}-\u{FFFF}]|"/u;
2026
function codegenJsxAttribute(
2027
cx: Context,
2028
attribute: JsxAttribute
@@ -2040,7 +2045,7 @@ function codegenJsxAttribute(
2045
switch (innerValue.type) {
2046
case "StringLiteral": {
2047
value = innerValue;
2043
- if (value.value.indexOf('"') !== -1) {
2048
+ if (STRING_REQUIRES_EXPR_CONTAINER_PATTERN.test(value.value)) {
2049
value = createJsxExpressionContainer(value.loc, value);
2050
}
2051
break;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.expect.md
new
+97
@@ -0,0 +1,97 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component() {
6
+ return (
7
+ <Post
8
+ author="potetotes"
9
+ text="in addition to understanding JavaScript semantics and the rules of React, the compiler team also understands தமிழ், 中文, 日本語, 한국어 and i think that’s pretty cool"
10
+ />
11
+ );
12
+}
13
+
14
+function Post({ author, text }) {
15
+ return (
16
+ <div>
17
+ <h1>{author}</h1>
18
+ <span>{text}</span>
19
+ </div>
20
+ );
21
+}
22
+
23
+export const FIXTURE_ENTRYPOINT = {
24
+ fn: Component,
25
+ params: [{}],
26
+};
27
+
28
+```
29
+
30
+## Code
31
+
32
+```javascript
33
+import { c as _c } from "react/compiler-runtime";
34
+function Component() {
35
+ const $ = _c(1);
36
+ let t0;
37
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
38
+ t0 = (
39
+ <Post
40
+ author="potetotes"
41
+ text={
42
+ "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"
43
+ }
44
+ />
45
+ );
46
+ $[0] = t0;
47
+ } else {
48
+ t0 = $[0];
49
+ }
50
+ return t0;
51
+}
52
+
53
+function Post(t0) {
54
+ const $ = _c(7);
55
+ const { author, text } = t0;
56
+ let t1;
57
+ if ($[0] !== author) {
58
+ t1 = <h1>{author}</h1>;
59
+ $[0] = author;
60
+ $[1] = t1;
61
+ } else {
62
+ t1 = $[1];
63
+ }
64
+ let t2;
65
+ if ($[2] !== text) {
66
+ t2 = <span>{text}</span>;
67
+ $[2] = text;
68
+ $[3] = t2;
69
+ } else {
70
+ t2 = $[3];
71
+ }
72
+ let t3;
73
+ if ($[4] !== t1 || $[5] !== t2) {
74
+ t3 = (
75
+ <div>
76
+ {t1}
77
+ {t2}
78
+ </div>
79
+ );
80
+ $[4] = t1;
81
+ $[5] = t2;
82
+ $[6] = t3;
83
+ } else {
84
+ t3 = $[6];
85
+ }
86
+ return t3;
87
+}
88
+
89
+export const FIXTURE_ENTRYPOINT = {
90
+ fn: Component,
91
+ params: [{}],
92
+};
93
+
94
+```
95
+
96
+### Eval output
97
+(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
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.js
new
+22
@@ -0,0 +1,22 @@
1
+function Component() {
2
+ return (
3
+ <Post
4
+ author="potetotes"
5
+ text="in addition to understanding JavaScript semantics and the rules of React, the compiler team also understands தமிழ், 中文, 日本語, 한국어 and i think that’s pretty cool"
6
+ />
7
+ );
8
+}
9
+
10
+function Post({ author, text }) {
11
+ return (
12
+ <div>
13
+ <h1>{author}</h1>
14
+ <span>{text}</span>
15
+ </div>
16
+ );
17
+}
18
+
19
+export const FIXTURE_ENTRYPOINT = {
20
+ fn: Component,
21
+ params: [{}],
22
+};