Wrap JSXFragment in expr container for attributes
@mofeiZ noticed that some configurations of prettier don't support JSXFragment appearing as a JSXAttribute value, in violation of the spec. Coincidentally I noticed that our internal build system also fails on this as i was trying to roll out on more surfaces. This PR makes sure we wrap fragments in an expressioncontainer if they appear as jsxattribute values.
Joe Savona committed
Sep 14, 2023 at 16:22 UTC
ca1217be6db94d6441bb68043a32ec7f3e2118c9
3 files changed
+107
-2
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+5
-2
@@ -1280,12 +1280,15 @@ function codegenJsxAttribute(
1280
let value;
1281
switch (innerValue.type) {
1282
case "StringLiteral":
1283
- case "JSXElement":
1284
- case "JSXFragment": {
1283
+ case "JSXElement": {
1284
value = innerValue;
1285
break;
1286
}
1287
default: {
1288
+ // NOTE JSXFragment is technically allowed as an attribute value per the spec
1289
+ // but many tools do not support this case. We emit fragments wrapped in an
1290
+ // expression container for compatibility purposes.
1291
+ // spec: https://github.com/facebook/jsx/blob/main/AST.md#jsx-attributes
1292
value = createJsxExpressionContainer(attribute.place.loc, innerValue);
1293
break;
1294
}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/jsx-attribute-with-jsx-fragment-value.flow.expect.md
new
+80
@@ -0,0 +1,80 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @flow
6
+function Component({items}) {
7
+ // Per the spec, <Foo value=<>{...}</> /> is valid.
8
+ // But many tools don't allow fragments as jsx attribute values,
9
+ // so we ensure not to emit them wrapped in an expression container
10
+ return items.length > 0
11
+ ? (
12
+ <Foo value={
13
+ <>{items.map(item => <Bar key={item.id} item={item} />)}</>
14
+ }></Foo>
15
+ )
16
+ : null;
17
+}
18
+
19
+function Foo({item}) {
20
+ return <div>{item.name}</div>;
21
+}
22
+
23
+export const FIXTURE_ENTRYPOINT = {
24
+ fn: Component,
25
+ params: [{items: [{id: 1, name: 'One!'}]}],
26
+};
27
+```
28
+
29
+## Code
30
+
31
+```javascript
32
+import { unstable_useMemoCache as useMemoCache } from "react";
33
+function Component(t26) {
34
+ const $ = useMemoCache(2);
35
+ const { items } = t26;
36
+ const c_0 = $[0] !== items;
37
+ let t0;
38
+ if (c_0) {
39
+ t0 =
40
+ items.length > 0 ? (
41
+ <Foo
42
+ value={
43
+ <>
44
+ {items.map((item) => (
45
+ <Bar key={item.id} item={item} />
46
+ ))}
47
+ </>
48
+ }
49
+ />
50
+ ) : null;
51
+ $[0] = items;
52
+ $[1] = t0;
53
+ } else {
54
+ t0 = $[1];
55
+ }
56
+ return t0;
57
+}
58
+
59
+function Foo(t7) {
60
+ const $ = useMemoCache(2);
61
+ const { item } = t7;
62
+ const c_0 = $[0] !== item.name;
63
+ let t0;
64
+ if (c_0) {
65
+ t0 = <div>{item.name}</div>;
66
+ $[0] = item.name;
67
+ $[1] = t0;
68
+ } else {
69
+ t0 = $[1];
70
+ }
71
+ return t0;
72
+}
73
+
74
+export const FIXTURE_ENTRYPOINT = {
75
+ fn: Component,
76
+ params: [{ items: [{ id: 1, name: "One!" }] }],
77
+};
78
+
79
+```
80
+
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/jsx-attribute-with-jsx-fragment-value.flow.js
new
+22
@@ -0,0 +1,22 @@
1
+// @flow
2
+function Component({items}) {
3
+ // Per the spec, <Foo value=<>{...}</> /> is valid.
4
+ // But many tools don't allow fragments as jsx attribute values,
5
+ // so we ensure not to emit them wrapped in an expression container
6
+ return items.length > 0
7
+ ? (
8
+ <Foo value={
9
+ <>{items.map(item => <Bar key={item.id} item={item} />)}</>
10
+ }></Foo>
11
+ )
12
+ : null;
13
+}
14
+
15
+function Foo({item}) {
16
+ return <div>{item.name}</div>;
17
+}
18
+
19
+export const FIXTURE_ENTRYPOINT = {
20
+ fn: Component,
21
+ params: [{items: [{id: 1, name: 'One!'}]}],
22
+};
\ No newline at end of file