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.
This commit is contained in:
Joe Savona
2023-09-14 16:22:05 -07:00
parent 88b1a069c0
commit ca1217be6d
3 changed files with 107 additions and 2 deletions
@@ -1280,12 +1280,15 @@ function codegenJsxAttribute(
let value;
switch (innerValue.type) {
case "StringLiteral":
case "JSXElement":
case "JSXFragment": {
case "JSXElement": {
value = innerValue;
break;
}
default: {
// NOTE JSXFragment is technically allowed as an attribute value per the spec
// but many tools do not support this case. We emit fragments wrapped in an
// expression container for compatibility purposes.
// spec: https://github.com/facebook/jsx/blob/main/AST.md#jsx-attributes
value = createJsxExpressionContainer(attribute.place.loc, innerValue);
break;
}
@@ -0,0 +1,80 @@
## Input
```javascript
// @flow
function Component({items}) {
// Per the spec, <Foo value=<>{...}</> /> is valid.
// But many tools don't allow fragments as jsx attribute values,
// so we ensure not to emit them wrapped in an expression container
return items.length > 0
? (
<Foo value={
<>{items.map(item => <Bar key={item.id} item={item} />)}</>
}></Foo>
)
: null;
}
function Foo({item}) {
return <div>{item.name}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{items: [{id: 1, name: 'One!'}]}],
};
```
## Code
```javascript
import { unstable_useMemoCache as useMemoCache } from "react";
function Component(t26) {
const $ = useMemoCache(2);
const { items } = t26;
const c_0 = $[0] !== items;
let t0;
if (c_0) {
t0 =
items.length > 0 ? (
<Foo
value={
<>
{items.map((item) => (
<Bar key={item.id} item={item} />
))}
</>
}
/>
) : null;
$[0] = items;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
function Foo(t7) {
const $ = useMemoCache(2);
const { item } = t7;
const c_0 = $[0] !== item.name;
let t0;
if (c_0) {
t0 = <div>{item.name}</div>;
$[0] = item.name;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ items: [{ id: 1, name: "One!" }] }],
};
```
@@ -0,0 +1,22 @@
// @flow
function Component({items}) {
// Per the spec, <Foo value=<>{...}</> /> is valid.
// But many tools don't allow fragments as jsx attribute values,
// so we ensure not to emit them wrapped in an expression container
return items.length > 0
? (
<Foo value={
<>{items.map(item => <Bar key={item.id} item={item} />)}</>
}></Foo>
)
: null;
}
function Foo({item}) {
return <div>{item.name}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{items: [{id: 1, name: 'One!'}]}],
};