@samitouri / QOS-React-2 / commits / ca1217be6d

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