Skip to content

Commit

Permalink
fix(es/react): Allow spread children (#6505)
Browse files Browse the repository at this point in the history
**Related issue:**

 - Closes #2037.
  • Loading branch information
kdy1 authored Nov 24, 2022
1 parent bc61b09 commit 90aa6c8
Show file tree
Hide file tree
Showing 18 changed files with 242 additions and 119 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
var A = function() {
return /*#__PURE__*/ React.createElement("div", null);
return /*#__PURE__*/ React.createElement.apply(React, [
"div",
null
]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,106 @@ export { };
//// [renderer2.d.ts]
export { };
//// [component.tsx]
//!
//! x Spread children are not supported in React.
//! ,----
//! 4 | export const MySFC = (props: {x: number, y: number, children?: predom.JSX.Element[]}) => <p>{props.x} + {props.y} = {props.x + props.y}{...this.props.children}</p>;
//! : ^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
//!
//! x Spread children are not supported in React.
//! ,----
//! 12 | {...this.props.children}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
/** @jsx predom */ import _class_call_check from "@swc/helpers/src/_class_call_check.mjs";
import _to_consumable_array from "@swc/helpers/src/_to_consumable_array.mjs";
var _this = this;
import { predom } from "./renderer2";
export var MySFC = function(props) {
return /*#__PURE__*/ predom.apply(void 0, [
"p",
null,
props.x,
" + ",
props.y,
" = ",
props.x + props.y
].concat(_to_consumable_array(_this.props.children)));
};
export var MyClass = /*#__PURE__*/ function() {
"use strict";
function MyClass(props) {
_class_call_check(this, MyClass);
this.props = props;
}
var _proto = MyClass.prototype;
_proto.render = function render() {
return /*#__PURE__*/ predom.apply(void 0, [
"p",
null,
this.props.x,
" + ",
this.props.y,
" = ",
this.props.x + this.props.y
].concat(_to_consumable_array(this.props.children)));
};
return MyClass;
}();
export var tree = /*#__PURE__*/ predom(MySFC, {
x: 1,
y: 2
}, /*#__PURE__*/ predom(MyClass, {
x: 3,
y: 4
}), /*#__PURE__*/ predom(MyClass, {
x: 5,
y: 6
}));
export default /*#__PURE__*/ predom("h", null);
//// [index.tsx]
//!
//! x Spread children are not supported in React.
//! ,----
//! 13 | return <p>{this.props.x} + {this.props.y} = {this.props.x + this.props.y}{...this.props.children}</p>;
//! : ^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
/** @jsx dom */ import _class_call_check from "@swc/helpers/src/_class_call_check.mjs";
import _to_consumable_array from "@swc/helpers/src/_to_consumable_array.mjs";
import { dom } from "./renderer";
import prerendered, { MySFC, MyClass, tree } from "./component";
var elem = prerendered;
elem = /*#__PURE__*/ dom("h", null); // Expect assignability error here
var DOMSFC = function(props) {
return /*#__PURE__*/ dom("p", null, props.x, " + ", props.y, " = ", props.x + props.y, props.children);
};
var DOMClass = /*#__PURE__*/ function() {
"use strict";
function DOMClass(props) {
_class_call_check(this, DOMClass);
this.props = props;
}
var _proto = DOMClass.prototype;
_proto.render = function render() {
return /*#__PURE__*/ dom.apply(void 0, [
"p",
null,
this.props.x,
" + ",
this.props.y,
" = ",
this.props.x + this.props.y
].concat(_to_consumable_array(this.props.children)));
};
return DOMClass;
}();
// Should work, everything is a DOM element
var _tree = /*#__PURE__*/ dom(DOMSFC, {
x: 1,
y: 2
}, /*#__PURE__*/ dom(DOMClass, {
x: 3,
y: 4
}), /*#__PURE__*/ dom(DOMClass, {
x: 5,
y: 6
}));
// Should fail, no dom elements
var _brokenTree = /*#__PURE__*/ dom(MySFC, {
x: 1,
y: 2
}, /*#__PURE__*/ dom(MyClass, {
x: 3,
y: 4
}), /*#__PURE__*/ dom(MyClass, {
x: 5,
y: 6
}));
// Should fail, nondom isn't allowed as children of dom
var _brokenTree2 = /*#__PURE__*/ dom(DOMSFC, {
x: 1,
y: 2
}, tree, tree);
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,51 @@ export { };
//// [renderer2.d.ts]
export { };
//// [component.tsx]
//!
//! x Spread children are not supported in React.
//! ,----
//! 4 | export const MySFC = (props: {x: number, y: number, children?: predom.JSX.Element[]}) => <p>{props.x} + {props.y} = {props.x + props.y}{...this.props.children}</p>;
//! : ^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
//!
//! x Spread children are not supported in React.
//! ,----
//! 12 | {...this.props.children}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
import _class_call_check from "@swc/helpers/src/_class_call_check.mjs";
import _to_consumable_array from "@swc/helpers/src/_to_consumable_array.mjs";
var _this = this;
import { predom } from "./renderer2";
export var MySFC = function(props) {
return predom.apply(void 0, [
"p",
null,
props.x,
" + ",
props.y,
" = ",
props.x + props.y
].concat(_to_consumable_array(_this.props.children)));
};
export var MyClass = function() {
"use strict";
function MyClass(props) {
_class_call_check(this, MyClass), this.props = props;
}
return MyClass.prototype.render = function() {
return predom.apply(void 0, [
"p",
null,
this.props.x,
" + ",
this.props.y,
" = ",
this.props.x + this.props.y
].concat(_to_consumable_array(this.props.children)));
}, MyClass;
}();
export var tree = predom(MySFC, {
x: 1,
y: 2
}, predom(MyClass, {
x: 3,
y: 4
}), predom(MyClass, {
x: 5,
y: 6
}));
export default predom("h", null);
//// [index.tsx]
//!
//! x Spread children are not supported in React.
//! ,----
//! 13 | return <p>{this.props.x} + {this.props.y} = {this.props.x + this.props.y}{...this.props.children}</p>;
//! : ^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
import _class_call_check from "@swc/helpers/src/_class_call_check.mjs";
import _to_consumable_array from "@swc/helpers/src/_to_consumable_array.mjs";
import { dom } from "./renderer";
import prerendered, { MySFC, MyClass, tree } from "./component";
25 changes: 19 additions & 6 deletions crates/swc/tests/tsc-references/tsxSpreadChildren.1.normal.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
//// [tsxSpreadChildren.tsx]
//!
//! x Spread children are not supported in React.
//! ,----
//! 22 | {...todos.map(todo => <Todo key={todo.id} todo={todo.todo}/>)}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
import _extends from "@swc/helpers/src/_extends.mjs";
import _to_consumable_array from "@swc/helpers/src/_to_consumable_array.mjs";
function Todo(prop) {
return /*#__PURE__*/ React.createElement("div", null, prop.key.toString() + prop.todo);
}
function TodoList(param) {
var todos = param.todos;
return /*#__PURE__*/ React.createElement.apply(React, [
"div",
null
].concat(_to_consumable_array(todos.map(function(todo) {
return /*#__PURE__*/ React.createElement(Todo, {
key: todo.id,
todo: todo.todo
});
}))));
}
var x;
/*#__PURE__*/ React.createElement(TodoList, _extends({}, x));
10 changes: 4 additions & 6 deletions crates/swc/tests/tsc-references/tsxSpreadChildren.2.minified.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
//// [tsxSpreadChildren.tsx]
//!
//! x Spread children are not supported in React.
//! ,----
//! 22 | {...todos.map(todo => <Todo key={todo.id} todo={todo.todo}/>)}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
var x;
import _extends from "@swc/helpers/src/_extends.mjs";
import _to_consumable_array from "@swc/helpers/src/_to_consumable_array.mjs";
_extends({}, x);
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
//// [tsxSpreadChildrenInvalidType.tsx]
//!
//! x Spread children are not supported in React.
//! ,----
//! 21 | {...<Todo key={todos[0].id} todo={todos[0].todo} />}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
//!
//! x Spread children are not supported in React.
//! ,----
//! 27 | {...(<Todo key={todos[0].id} todo={todos[0].todo} /> as any)}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
import _extends from "@swc/helpers/src/_extends.mjs";
function Todo(prop) {
return /*#__PURE__*/ React.createElement("div", null, prop.key.toString() + prop.todo);
}
function TodoList({ todos }) {
return /*#__PURE__*/ React.createElement("div", null, .../*#__PURE__*/ React.createElement(Todo, {
key: todos[0].id,
todo: todos[0].todo
}));
}
function TodoListNoError({ todos }) {
// any is not checked
return /*#__PURE__*/ React.createElement("div", null, .../*#__PURE__*/ React.createElement(Todo, {
key: todos[0].id,
todo: todos[0].todo
}));
}
let x;
/*#__PURE__*/ React.createElement(TodoList, _extends({}, x));
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
//// [tsxSpreadChildrenInvalidType.tsx]
//!
//! x Spread children are not supported in React.
//! ,----
//! 21 | {...<Todo key={todos[0].id} todo={todos[0].todo} />}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
//!
//! x Spread children are not supported in React.
//! ,----
//! 27 | {...(<Todo key={todos[0].id} todo={todos[0].todo} /> as any)}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
let x;
import _extends from "@swc/helpers/src/_extends.mjs";
_extends({}, x);
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
//// [tsxSpreadChildrenInvalidType.tsx]
//!
//! x Spread children are not supported in React.
//! ,----
//! 21 | {...<Todo key={todos[0].id} todo={todos[0].todo} />}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
//!
//! x Spread children are not supported in React.
//! ,----
//! 27 | {...(<Todo key={todos[0].id} todo={todos[0].todo} /> as any)}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
import _extends from "@swc/helpers/src/_extends.mjs";
import _to_consumable_array from "@swc/helpers/src/_to_consumable_array.mjs";
function Todo(prop) {
return /*#__PURE__*/ React.createElement("div", null, prop.key.toString() + prop.todo);
}
function TodoList(param) {
var todos = param.todos;
return /*#__PURE__*/ React.createElement.apply(React, [
"div",
null
].concat(_to_consumable_array(/*#__PURE__*/ React.createElement(Todo, {
key: todos[0].id,
todo: todos[0].todo
}))));
}
function TodoListNoError(param) {
var todos = param.todos;
// any is not checked
return /*#__PURE__*/ React.createElement.apply(React, [
"div",
null
].concat(_to_consumable_array(/*#__PURE__*/ React.createElement(Todo, {
key: todos[0].id,
todo: todos[0].todo
}))));
}
var x;
/*#__PURE__*/ React.createElement(TodoList, _extends({}, x));
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
//// [tsxSpreadChildrenInvalidType.tsx]
//!
//! x Spread children are not supported in React.
//! ,----
//! 21 | {...<Todo key={todos[0].id} todo={todos[0].todo} />}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
//!
//! x Spread children are not supported in React.
//! ,----
//! 27 | {...(<Todo key={todos[0].id} todo={todos[0].todo} /> as any)}
//! : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! `----
var x;
import _extends from "@swc/helpers/src/_extends.mjs";
import _to_consumable_array from "@swc/helpers/src/_to_consumable_array.mjs";
_extends({}, x);
12 changes: 4 additions & 8 deletions crates/swc_ecma_transforms_react/src/jsx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,14 +769,10 @@ where
}) => return None,
JSXElementChild::JSXElement(el) => self.jsx_elem_to_expr(*el).as_arg(),
JSXElementChild::JSXFragment(el) => self.jsx_frag_to_expr(el).as_arg(),
JSXElementChild::JSXSpreadChild(JSXSpreadChild { span, .. }) => {
HANDLER.with(|handler| {
handler
.struct_span_err(span, "Spread children are not supported in React.")
.emit();
});
return None;
}
JSXElementChild::JSXSpreadChild(JSXSpreadChild { span, expr, .. }) => ExprOrSpread {
spread: Some(span),
expr,
},
})
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const A = () => {
return <div>{...[]}</div>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const A = ()=>{
return /*#__PURE__*/ React.createElement("div", null, ...[]);
};
Original file line number Diff line number Diff line change
@@ -1 +1 @@
/*#__PURE__*/ React.createElement("div", null);
/*#__PURE__*/ React.createElement("div", null, ...children);

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1 +1 @@
/*#__PURE__*/ React.createElement("div", null);
/*#__PURE__*/ React.createElement("div", null, ...children);

This file was deleted.

1 comment on commit 90aa6c8

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 90aa6c8 Previous: 8d906b4 Ratio
es/full/bugs-1 343725 ns/iter (± 35985) 339249 ns/iter (± 19114) 1.01
es/full/minify/libraries/antd 1920780678 ns/iter (± 19446273) 1926569394 ns/iter (± 34703410) 1.00
es/full/minify/libraries/d3 453425285 ns/iter (± 13467547) 435442912 ns/iter (± 7649321) 1.04
es/full/minify/libraries/echarts 1673549682 ns/iter (± 35740117) 1624471008 ns/iter (± 28803665) 1.03
es/full/minify/libraries/jquery 106195557 ns/iter (± 3979181) 102257399 ns/iter (± 8813358) 1.04
es/full/minify/libraries/lodash 119116099 ns/iter (± 3980688) 130135274 ns/iter (± 4286135) 0.92
es/full/minify/libraries/moment 61056574 ns/iter (± 1940331) 63941126 ns/iter (± 3293122) 0.95
es/full/minify/libraries/react 20392111 ns/iter (± 768066) 21328940 ns/iter (± 1355804) 0.96
es/full/minify/libraries/terser 325606279 ns/iter (± 5716295) 325367259 ns/iter (± 28015923) 1.00
es/full/minify/libraries/three 565290116 ns/iter (± 14133209) 578632415 ns/iter (± 18338390) 0.98
es/full/minify/libraries/typescript 3386248125 ns/iter (± 37649841) 3437266357 ns/iter (± 47791599) 0.99
es/full/minify/libraries/victory 840360756 ns/iter (± 12367009) 856744228 ns/iter (± 28800418) 0.98
es/full/minify/libraries/vue 161071945 ns/iter (± 13825072) 158569815 ns/iter (± 7575003) 1.02
es/full/codegen/es3 33640 ns/iter (± 1652) 34035 ns/iter (± 1014) 0.99
es/full/codegen/es5 33423 ns/iter (± 2292) 34007 ns/iter (± 807) 0.98
es/full/codegen/es2015 33578 ns/iter (± 1310) 34081 ns/iter (± 957) 0.99
es/full/codegen/es2016 33452 ns/iter (± 2461) 34016 ns/iter (± 674) 0.98
es/full/codegen/es2017 33385 ns/iter (± 1658) 33999 ns/iter (± 743) 0.98
es/full/codegen/es2018 33207 ns/iter (± 1268) 34008 ns/iter (± 715) 0.98
es/full/codegen/es2019 33349 ns/iter (± 843) 34020 ns/iter (± 1942) 0.98
es/full/codegen/es2020 32879 ns/iter (± 7416) 33906 ns/iter (± 842) 0.97
es/full/all/es3 191712687 ns/iter (± 6938640) 200541905 ns/iter (± 8865910) 0.96
es/full/all/es5 180181303 ns/iter (± 6780544) 187712132 ns/iter (± 7367374) 0.96
es/full/all/es2015 145227350 ns/iter (± 4693508) 147288770 ns/iter (± 6736358) 0.99
es/full/all/es2016 142588261 ns/iter (± 4052083) 145597155 ns/iter (± 5191199) 0.98
es/full/all/es2017 146923796 ns/iter (± 6740560) 145379414 ns/iter (± 5165232) 1.01
es/full/all/es2018 140361734 ns/iter (± 3504576) 144724573 ns/iter (± 9131420) 0.97
es/full/all/es2019 139163534 ns/iter (± 3577505) 142600788 ns/iter (± 7385079) 0.98
es/full/all/es2020 136694510 ns/iter (± 4134678) 136994947 ns/iter (± 6100882) 1.00
es/full/parser 708445 ns/iter (± 26460) 717720 ns/iter (± 50162) 0.99
es/full/base/fixer 26395 ns/iter (± 1192) 26456 ns/iter (± 818) 1.00
es/full/base/resolver_and_hygiene 92861 ns/iter (± 3734) 92926 ns/iter (± 3163) 1.00
serialization of ast node 204 ns/iter (± 8) 205 ns/iter (± 1) 1.00
serialization of serde 217 ns/iter (± 6) 212 ns/iter (± 4) 1.02

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.