diff --git a/packages/babel-helper-builder-react-jsx/src/index.js b/packages/babel-helper-builder-react-jsx/src/index.js
index fa68384f1b..64b136923f 100644
--- a/packages/babel-helper-builder-react-jsx/src/index.js
+++ b/packages/babel-helper-builder-react-jsx/src/index.js
@@ -87,6 +87,10 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
function convertAttribute(node) {
const value = convertAttributeValue(node.value || t.booleanLiteral(true));
+ if (t.isJSXSpreadAttribute(node)) {
+ return t.spreadElement(node.argument);
+ }
+
if (t.isStringLiteral(value) && !t.isJSXExpressionContainer(node.value)) {
value.value = value.value.replace(/\n\s+/g, " ");
@@ -170,6 +174,14 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
let _props = [];
const objs = [];
+ const { useSpread = false } = file.opts;
+ if (typeof useSpread !== "boolean") {
+ throw new Error(
+ "transform-react-jsx currently only accepts a boolean option for " +
+ "useSpread (defaults to false)",
+ );
+ }
+
const useBuiltIns = file.opts.useBuiltIns || false;
if (typeof useBuiltIns !== "boolean") {
throw new Error(
@@ -178,6 +190,18 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
);
}
+ if (useSpread && useBuiltIns) {
+ throw new Error(
+ "transform-react-jsx currently only accepts useBuiltIns or useSpread " +
+ "but not both",
+ );
+ }
+
+ if (useSpread) {
+ const props = attribs.map(convertAttribute);
+ return t.objectExpression(props);
+ }
+
while (attribs.length) {
const prop = attribs.shift();
if (t.isJSXSpreadAttribute(prop)) {
diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-invalid-option/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-invalid-option/input.js
new file mode 100644
index 0000000000..4caacb6aa1
--- /dev/null
+++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-invalid-option/input.js
@@ -0,0 +1 @@
+var div =
diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-invalid-option/options.json b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-invalid-option/options.json
new file mode 100644
index 0000000000..ff6406c9a4
--- /dev/null
+++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-invalid-option/options.json
@@ -0,0 +1,4 @@
+{
+ "plugins": [["transform-react-jsx", { "useSpread": 0 }]],
+ "throws": "transform-react-jsx currently only accepts a boolean option for useSpread (defaults to false)"
+}
diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-use-builtin/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-use-builtin/input.js
new file mode 100644
index 0000000000..4caacb6aa1
--- /dev/null
+++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-use-builtin/input.js
@@ -0,0 +1 @@
+var div =
diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-use-builtin/options.json b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-use-builtin/options.json
new file mode 100644
index 0000000000..eab6051daa
--- /dev/null
+++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment-use-builtin/options.json
@@ -0,0 +1,6 @@
+{
+ "plugins": [
+ ["transform-react-jsx", { "useSpread": true, "useBuiltIns": true }]
+ ],
+ "throws": "transform-react-jsx currently only accepts useBuiltIns or useSpread but not both"
+}
diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment/input.js
new file mode 100644
index 0000000000..4caacb6aa1
--- /dev/null
+++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment/input.js
@@ -0,0 +1 @@
+var div =
diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment/output.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment/output.js
new file mode 100644
index 0000000000..6d3c491321
--- /dev/null
+++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/assignment/output.js
@@ -0,0 +1,3 @@
+var div = React.createElement(Component, { ...props,
+ foo: "bar"
+});
diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/options.json b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/options.json
new file mode 100644
index 0000000000..7e0d5fcba0
--- /dev/null
+++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/useSpread/options.json
@@ -0,0 +1,3 @@
+{
+ "plugins": [["transform-react-jsx", { "useSpread": true }]]
+}
diff --git a/packages/babel-preset-react/src/index.js b/packages/babel-preset-react/src/index.js
index f99fe36e0b..aaf358ea59 100644
--- a/packages/babel-preset-react/src/index.js
+++ b/packages/babel-preset-react/src/index.js
@@ -13,6 +13,7 @@ export default declare((api, opts) => {
opts.throwIfNamespace === undefined ? true : !!opts.throwIfNamespace;
const development = !!opts.development;
const useBuiltIns = !!opts.useBuiltIns;
+ const { useSpread } = opts;
if (typeof development !== "boolean") {
throw new Error(
@@ -24,7 +25,7 @@ export default declare((api, opts) => {
plugins: [
[
transformReactJSX,
- { pragma, pragmaFrag, throwIfNamespace, useBuiltIns },
+ { pragma, pragmaFrag, throwIfNamespace, useBuiltIns, useSpread },
],
transformReactDisplayName,