Skip to content

Commit 49a4887

Browse files
committed
[compiler:codegen] Wrap non-ascii characters in JsxExpressionContainer
This PR extends the previous logic added in facebook#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 facebook#29648. ghstack-source-id: ecc61c9f0bece90d18623b3c570fea05fbcd811a Pull Request resolved: facebook#29997
1 parent 0b724e9 commit 49a4887

File tree

4 files changed

+116
-4
lines changed

4 files changed

+116
-4
lines changed

compiler/.eslintrc.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ module.exports = {
4343

4444
"multiline-comment-style": ["error", "starred-block"],
4545

46+
/**
47+
* We sometimes need to check for control characters in regexes for things like preserving input
48+
* strings
49+
*/
50+
"no-control-regex": "off",
51+
4652
"@typescript-eslint/no-empty-function": "off",
4753

4854
/*
@@ -82,7 +88,7 @@ module.exports = {
8288
],
8389
"@typescript-eslint/array-type": ["error", { default: "generic" }],
8490
"@typescript-eslint/triple-slash-reference": "off",
85-
"@typescript-eslint/no-var-requires": "off"
91+
"@typescript-eslint/no-var-requires": "off",
8692
},
8793
parser: "@typescript-eslint/parser",
8894
plugins: ["@typescript-eslint"],

compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -2157,10 +2157,18 @@ function codegenInstructionValue(
21572157
}
21582158

21592159
/**
2160-
* Due to a bug in earlier Babel versions, JSX string attributes with double quotes or with unicode characters
2161-
* may be escaped unnecessarily. To avoid trigger this Babel bug, we use a JsxExpressionContainer for such strings.
2160+
* Due to a bug in earlier Babel versions, JSX string attributes with double quotes, unicode characters, or special
2161+
* control characters such as \n may be escaped unnecessarily. To avoid trigger this Babel bug, we use a
2162+
* JsxExpressionContainer for such strings.
2163+
*
2164+
* u0000 to u001F: C0 control codes
2165+
* u007F : Delete character
2166+
* u0080 to u009F: C1 control codes
2167+
* u00A0 to uFFFF: All non-basic Latin characters
2168+
* https://en.wikipedia.org/wiki/List_of_Unicode_characters#Control_codes
21622169
*/
2163-
const STRING_REQUIRES_EXPR_CONTAINER_PATTERN = /[\u{0080}-\u{FFFF}]|"/u;
2170+
const STRING_REQUIRES_EXPR_CONTAINER_PATTERN =
2171+
/[\u{0000}-\u{001F}|\u{007F}|\u{0080}-\u{FFFF}]|"/u;
21642172
function codegenJsxAttribute(
21652173
cx: Context,
21662174
attribute: JsxAttribute
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
2+
## Input
3+
4+
```javascript
5+
function Component() {
6+
return (
7+
<div>
8+
<Text value={"\n"} />
9+
<Text value={"A\tE"} />
10+
<Text value={"나은"} />
11+
<Text value={"Lauren"} />
12+
<Text value={"சத்யா"} />
13+
<Text value={"Sathya"} />
14+
</div>
15+
);
16+
}
17+
18+
function Text({ value }) {
19+
return <span>{value}</span>;
20+
}
21+
22+
export const FIXTURE_ENTRYPOINT = {
23+
fn: Component,
24+
params: [{}],
25+
};
26+
27+
```
28+
29+
## Code
30+
31+
```javascript
32+
import { c as _c } from "react/compiler-runtime";
33+
function Component() {
34+
const $ = _c(1);
35+
let t0;
36+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
37+
t0 = (
38+
<div>
39+
<Text value={"\n"} />
40+
<Text value={"A\tE"} />
41+
<Text value={"\uB098\uC740"} />
42+
<Text value="Lauren" />
43+
<Text value={"\u0B9A\u0BA4\u0BCD\u0BAF\u0BBE"} />
44+
<Text value="Sathya" />
45+
</div>
46+
);
47+
$[0] = t0;
48+
} else {
49+
t0 = $[0];
50+
}
51+
return t0;
52+
}
53+
54+
function Text(t0) {
55+
const $ = _c(2);
56+
const { value } = t0;
57+
let t1;
58+
if ($[0] !== value) {
59+
t1 = <span>{value}</span>;
60+
$[0] = value;
61+
$[1] = t1;
62+
} else {
63+
t1 = $[1];
64+
}
65+
return t1;
66+
}
67+
68+
export const FIXTURE_ENTRYPOINT = {
69+
fn: Component,
70+
params: [{}],
71+
};
72+
73+
```
74+
75+
### Eval output
76+
(kind: ok) <div><span>
77+
</span><span>A E</span><span>나은</span><span>Lauren</span><span>சத்யா</span><span>Sathya</span></div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
function Component() {
2+
return (
3+
<div>
4+
<Text value={"\n"} />
5+
<Text value={"A\tE"} />
6+
<Text value={"나은"} />
7+
<Text value={"Lauren"} />
8+
<Text value={"சத்யா"} />
9+
<Text value={"Sathya"} />
10+
</div>
11+
);
12+
}
13+
14+
function Text({ value }) {
15+
return <span>{value}</span>;
16+
}
17+
18+
export const FIXTURE_ENTRYPOINT = {
19+
fn: Component,
20+
params: [{}],
21+
};

0 commit comments

Comments
 (0)