mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Support JsxSpreadAttribute
Changes the representation of JsxElement props to be an array of attributes, each of which can be a named attribute or spread attribute.
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
IfTerminal,
|
||||
InstructionKind,
|
||||
InstructionValue,
|
||||
JsxAttribute,
|
||||
makeInstructionId,
|
||||
Place,
|
||||
ReturnTerminal,
|
||||
@@ -1207,9 +1208,17 @@ function lowerExpression(
|
||||
const children = expr
|
||||
.get("children")
|
||||
.map((child) => lowerJsxElement(builder, child));
|
||||
const props: Map<string, Place> = new Map();
|
||||
const props: Array<JsxAttribute> = [];
|
||||
let hasError = false;
|
||||
for (const attribute of opening.get("attributes")) {
|
||||
if (attribute.isJSXSpreadAttribute()) {
|
||||
const argument = lowerExpressionToPlace(
|
||||
builder,
|
||||
attribute.get("argument")
|
||||
);
|
||||
props.push({ kind: "JsxSpreadAttribute", argument });
|
||||
continue;
|
||||
}
|
||||
if (!attribute.isJSXAttribute()) {
|
||||
builder.errors.push({
|
||||
reason: `(BuildHIR::lowerExpression) Handle ${attribute.type} attributes in JSXElement`,
|
||||
@@ -1256,7 +1265,7 @@ function lowerExpression(
|
||||
value = lowerExpressionToPlace(builder, expression);
|
||||
}
|
||||
const prop: string = name.node.name;
|
||||
props.set(prop, value);
|
||||
props.push({ kind: "JsxAttribute", name: prop, place: value });
|
||||
}
|
||||
return hasError
|
||||
? { kind: "UnsupportedNode", node: exprNode, loc: exprLoc }
|
||||
|
||||
@@ -419,7 +419,7 @@ export type InstructionData =
|
||||
| {
|
||||
kind: "JsxExpression";
|
||||
tag: Place;
|
||||
props: Map<string, Place>;
|
||||
props: Array<JsxAttribute>;
|
||||
children: Array<Place> | null; // null === no children
|
||||
}
|
||||
| {
|
||||
@@ -465,6 +465,10 @@ export type InstructionData =
|
||||
node: t.Node;
|
||||
};
|
||||
|
||||
export type JsxAttribute =
|
||||
| { kind: "JsxSpreadAttribute"; argument: Place }
|
||||
| { kind: "JsxAttribute"; name: string; place: Place };
|
||||
|
||||
/**
|
||||
* A place where data may be read from / written to:
|
||||
* - a variable (identifier)
|
||||
|
||||
@@ -261,8 +261,12 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
|
||||
}
|
||||
case "JsxExpression": {
|
||||
const propItems = [];
|
||||
for (const [prop, value] of instrValue.props) {
|
||||
propItems.push(`${prop}={${printPlace(value)}}`);
|
||||
for (const attribute of instrValue.props) {
|
||||
if (attribute.kind === "JsxAttribute") {
|
||||
propItems.push(`${attribute.name}={${printPlace(attribute.place)}}`);
|
||||
} else {
|
||||
propItems.push(`...${printPlace(attribute.argument)}`);
|
||||
}
|
||||
}
|
||||
const props = propItems.length !== 0 ? " " + propItems.join(" ") : "";
|
||||
if (instrValue.children !== null) {
|
||||
|
||||
@@ -78,7 +78,24 @@ export function* eachInstructionValueOperand(
|
||||
}
|
||||
case "JsxExpression": {
|
||||
yield instrValue.tag;
|
||||
yield* instrValue.props.values();
|
||||
for (const attribute of instrValue.props) {
|
||||
switch (attribute.kind) {
|
||||
case "JsxAttribute": {
|
||||
yield attribute.place;
|
||||
break;
|
||||
}
|
||||
case "JsxSpreadAttribute": {
|
||||
yield attribute.argument;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
attribute,
|
||||
`Unexpected attribute kind '${(attribute as any).kind}'`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (instrValue.children) {
|
||||
yield* instrValue.children;
|
||||
}
|
||||
@@ -178,8 +195,23 @@ export function mapInstructionOperands(
|
||||
}
|
||||
case "JsxExpression": {
|
||||
instrValue.tag = fn(instrValue.tag);
|
||||
for (const [prop, place] of instrValue.props) {
|
||||
instrValue.props.set(prop, fn(place));
|
||||
for (const attribute of instrValue.props) {
|
||||
switch (attribute.kind) {
|
||||
case "JsxAttribute": {
|
||||
attribute.place = fn(attribute.place);
|
||||
break;
|
||||
}
|
||||
case "JsxSpreadAttribute": {
|
||||
attribute.argument = fn(attribute.argument);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
attribute,
|
||||
`Unexpected attribute kind '${(attribute as any).kind}'`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (instrValue.children) {
|
||||
instrValue.children = instrValue.children.map((p) => fn(p));
|
||||
|
||||
@@ -535,14 +535,31 @@ function codegenInstructionValue(
|
||||
break;
|
||||
}
|
||||
case "JsxExpression": {
|
||||
const attributes: Array<t.JSXAttribute> = [];
|
||||
for (const [prop, value] of instrValue.props) {
|
||||
attributes.push(
|
||||
t.jsxAttribute(
|
||||
t.jsxIdentifier(prop),
|
||||
t.jsxExpressionContainer(codegenPlace(cx, value))
|
||||
)
|
||||
);
|
||||
const attributes: Array<t.JSXAttribute | t.JSXSpreadAttribute> = [];
|
||||
for (const attribute of instrValue.props) {
|
||||
switch (attribute.kind) {
|
||||
case "JsxAttribute": {
|
||||
attributes.push(
|
||||
t.jsxAttribute(
|
||||
t.jsxIdentifier(attribute.name),
|
||||
t.jsxExpressionContainer(codegenPlace(cx, attribute.place))
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "JsxSpreadAttribute": {
|
||||
attributes.push(
|
||||
t.jsxSpreadAttribute(codegenPlace(cx, attribute.argument))
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
attribute,
|
||||
`Unexpected attribute kind '${(attribute as any).kind}'`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
let tagValue = codegenPlace(cx, instrValue.tag);
|
||||
let tag: string;
|
||||
|
||||
@@ -187,15 +187,6 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
|
||||
20 | <Button {...args}></Button>;
|
||||
21 | <Button xlink:href="localhost:3000"></Button>;
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle JSXSpreadAttribute attributes in JSXElement
|
||||
18 | const { z, aa = "aa", ...zz } = useCustom();
|
||||
19 |
|
||||
> 20 | <Button {...args}></Button>;
|
||||
| ^^^^^^^^^
|
||||
21 | <Button xlink:href="localhost:3000"></Button>;
|
||||
22 | <Button haha={1}></Button>;
|
||||
23 | <Button>{/** empty */}</Button>;
|
||||
|
||||
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle JSXNamespacedName attribute names in JSXElement
|
||||
19 |
|
||||
20 | <Button {...args}></Button>;
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
return (
|
||||
<Component {...props} {...{ bar: props.cond ? props.foo : props.bar }} />
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const $ = React.useMemoCache();
|
||||
const c_0 = $[0] !== props;
|
||||
let t1;
|
||||
if (c_0) {
|
||||
t1 = props.cond ? props.foo : props.bar;
|
||||
$[0] = props;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const c_2 = $[2] !== t1;
|
||||
let t3;
|
||||
if (c_2) {
|
||||
t3 = { bar: t1 };
|
||||
$[2] = t1;
|
||||
$[3] = t3;
|
||||
} else {
|
||||
t3 = $[3];
|
||||
}
|
||||
const c_4 = $[4] !== props;
|
||||
const c_5 = $[5] !== t3;
|
||||
let t6;
|
||||
if (c_4 || c_5) {
|
||||
t6 = <Component {...props} {...t3}></Component>;
|
||||
$[4] = props;
|
||||
$[5] = t3;
|
||||
$[6] = t6;
|
||||
} else {
|
||||
t6 = $[6];
|
||||
}
|
||||
return t6;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
function Component(props) {
|
||||
return (
|
||||
<Component {...props} {...{ bar: props.cond ? props.foo : props.bar }} />
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user