From be6712f72ccfe3e3b9388e169f6f9e261d78637b Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Fri, 17 May 2024 10:38:56 -0700 Subject: [PATCH] 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 --- .../ReactiveScopes/CodegenReactiveFunction.ts | 9 +- .../jsx-string-attribute-non-ascii.expect.md | 97 +++++++++++++++++++ .../jsx-string-attribute-non-ascii.js | 22 +++++ 3 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts index 9ff169346b..81d0569639 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -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; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.expect.md new file mode 100644 index 0000000000..1cb36945e9 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.expect.md @@ -0,0 +1,97 @@ + +## Input + +```javascript +function Component() { + return ( + + ); +} + +function Post({ author, text }) { + return ( +
+

{author}

+ {text} +
+ ); +} + +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 = ( + + ); + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +function Post(t0) { + const $ = _c(7); + const { author, text } = t0; + let t1; + if ($[0] !== author) { + t1 =

{author}

; + $[0] = author; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== text) { + t2 = {text}; + $[2] = text; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] !== t1 || $[5] !== t2) { + t3 = ( +
+ {t1} + {t2} +
+ ); + $[4] = t1; + $[5] = t2; + $[6] = t3; + } else { + t3 = $[6]; + } + return t3; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + +``` + +### Eval output +(kind: ok)

potetotes

in addition to understanding JavaScript semantics and the rules of React, the compiler team also understands தமிழ், 中文, 日本語, 한국어 and i think that’s pretty cool
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.js new file mode 100644 index 0000000000..9618332b06 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-non-ascii.js @@ -0,0 +1,22 @@ +function Component() { + return ( + + ); +} + +function Post({ author, text }) { + return ( +
+

{author}

+ {text} +
+ ); +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +};