From 49a48875860e652f2d7c097faf256440676f4d18 Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Thu, 20 Jun 2024 19:09:51 -0400 Subject: [PATCH] [compiler:codegen] Wrap non-ascii characters in JsxExpressionContainer This PR extends the previous logic added in #29141 to also account for other kinds of non-ascii characters such as `\n`. Because these control characters are individual special characters (and not 2 characters `\` and `n`) we match based on unicode which was already being checked for non-Latin characters. This allows control characters to continue to be compiled equivalently to its original source if it was provided in a JsxExpressionContainer. However note that this PR does not convert JSX attributes that are StringLiterals to JsxExpressionContainer, to preserve the original source code as it was written. Alternatively we could always emit a JsxExpressionContainer if it was used in the source and not try to down level it to some other node kind. But since we already do this I opted to keep this behavior. Partially addresses #29648. ghstack-source-id: ecc61c9f0bece90d18623b3c570fea05fbcd811a Pull Request resolved: https://github.com/facebook/react/pull/29997 --- compiler/.eslintrc.js | 8 +- .../ReactiveScopes/CodegenReactiveFunction.ts | 14 +++- ...g-attribute-expression-container.expect.md | 77 +++++++++++++++++++ ...x-string-attribute-expression-container.js | 21 +++++ 4 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-expression-container.expect.md create mode 100644 compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-expression-container.js diff --git a/compiler/.eslintrc.js b/compiler/.eslintrc.js index 996046a130..2f68f5c310 100644 --- a/compiler/.eslintrc.js +++ b/compiler/.eslintrc.js @@ -43,6 +43,12 @@ module.exports = { "multiline-comment-style": ["error", "starred-block"], + /** + * We sometimes need to check for control characters in regexes for things like preserving input + * strings + */ + "no-control-regex": "off", + "@typescript-eslint/no-empty-function": "off", /* @@ -82,7 +88,7 @@ module.exports = { ], "@typescript-eslint/array-type": ["error", { default: "generic" }], "@typescript-eslint/triple-slash-reference": "off", - "@typescript-eslint/no-var-requires": "off" + "@typescript-eslint/no-var-requires": "off", }, parser: "@typescript-eslint/parser", plugins: ["@typescript-eslint"], 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 3886628c80..d6818187bb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts @@ -2157,10 +2157,18 @@ function codegenInstructionValue( } /** - * 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. + * Due to a bug in earlier Babel versions, JSX string attributes with double quotes, unicode characters, or special + * control characters such as \n may be escaped unnecessarily. To avoid trigger this Babel bug, we use a + * JsxExpressionContainer for such strings. + * + * u0000 to u001F: C0 control codes + * u007F : Delete character + * u0080 to u009F: C1 control codes + * u00A0 to uFFFF: All non-basic Latin characters + * https://en.wikipedia.org/wiki/List_of_Unicode_characters#Control_codes */ -const STRING_REQUIRES_EXPR_CONTAINER_PATTERN = /[\u{0080}-\u{FFFF}]|"/u; +const STRING_REQUIRES_EXPR_CONTAINER_PATTERN = + /[\u{0000}-\u{001F}|\u{007F}|\u{0080}-\u{FFFF}]|"/u; function codegenJsxAttribute( cx: Context, attribute: JsxAttribute diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-expression-container.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-expression-container.expect.md new file mode 100644 index 0000000000..ad25964ed8 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-expression-container.expect.md @@ -0,0 +1,77 @@ + +## Input + +```javascript +function Component() { + return ( +
+ + + + + + +
+ ); +} + +function Text({ value }) { + return {value}; +} + +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 Text(t0) { + const $ = _c(2); + const { value } = t0; + let t1; + if ($[0] !== value) { + t1 = {value}; + $[0] = value; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + +``` + +### Eval output +(kind: ok)
+A E나은Laurenசத்யாSathya
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-expression-container.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-expression-container.js new file mode 100644 index 0000000000..042b95f348 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/jsx-string-attribute-expression-container.js @@ -0,0 +1,21 @@ +function Component() { + return ( +
+ + + + + + +
+ ); +} + +function Text({ value }) { + return {value}; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +};