From ed90f179788fd942a68a84408070bcd5b2fc4db0 Mon Sep 17 00:00:00 2001 From: Martin Packman Date: Tue, 5 Jan 2021 22:04:26 +0000 Subject: [PATCH] Optimize jsx spreads of object expressions (#12557) --- .../src/create-plugin.js | 60 ++++++++++--------- .../react-automatic/duplicate-props/input.js | 7 +++ .../duplicate-props/output.mjs | 29 +++++++++ .../react-automatic/flattens-spread/input.js | 7 +++ .../flattens-spread/output.mjs | 24 ++++++++ .../fixtures/react/avoids-spread/input.js | 11 ++++ .../fixtures/react/avoids-spread/output.js | 32 ++++++++++ .../fixtures/react/duplicate-props/input.js | 7 +++ .../fixtures/react/duplicate-props/output.js | 23 +++++++ .../fixtures/react/flattens-spread/input.js | 7 +++ .../fixtures/react/flattens-spread/output.js | 17 ++++++ 11 files changed, 197 insertions(+), 27 deletions(-) create mode 100644 packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/duplicate-props/input.js create mode 100644 packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/duplicate-props/output.mjs create mode 100644 packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/flattens-spread/input.js create mode 100644 packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/flattens-spread/output.mjs create mode 100644 packages/babel-plugin-transform-react-jsx/test/fixtures/react/avoids-spread/input.js create mode 100644 packages/babel-plugin-transform-react-jsx/test/fixtures/react/avoids-spread/output.js create mode 100644 packages/babel-plugin-transform-react-jsx/test/fixtures/react/duplicate-props/input.js create mode 100644 packages/babel-plugin-transform-react-jsx/test/fixtures/react/duplicate-props/output.js create mode 100644 packages/babel-plugin-transform-react-jsx/test/fixtures/react/flattens-spread/input.js create mode 100644 packages/babel-plugin-transform-react-jsx/test/fixtures/react/flattens-spread/output.js diff --git a/packages/babel-plugin-transform-react-jsx/src/create-plugin.js b/packages/babel-plugin-transform-react-jsx/src/create-plugin.js index 77134bb4d4e9..ab074f851793 100644 --- a/packages/babel-plugin-transform-react-jsx/src/create-plugin.js +++ b/packages/babel-plugin-transform-react-jsx/src/create-plugin.js @@ -316,13 +316,20 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, } } - function convertAttribute(node) { - const value = convertAttributeValue(node.value || t.booleanLiteral(true)); - + function accumulateAttribute(array, node) { if (t.isJSXSpreadAttribute(node)) { - return t.spreadElement(node.argument); + const arg = node.argument; + // Collect properties into props array if spreading object expression + if (t.isObjectExpression(arg)) { + array.push(...arg.properties); + } else { + array.push(t.spreadElement(arg)); + } + return array; } + const value = convertAttributeValue(node.value || t.booleanLiteral(true)); + if (t.isStringLiteral(value) && !t.isJSXExpressionContainer(node.value)) { value.value = value.value.replace(/\n\s+/g, " "); @@ -340,7 +347,8 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, node.name = t.stringLiteral(node.name.name); } - return t.inherits(t.objectProperty(node.name, value), node); + array.push(t.inherits(t.objectProperty(node.name, value), node)); + return array; } function buildChildrenProperty(children) { @@ -420,7 +428,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, // Builds props for React.jsx. This function adds children into the props // and ensures that props is always an object function buildJSXOpeningElementAttributes(attribs, file, children) { - const props = attribs.map(convertAttribute); + const props = attribs.reduce(accumulateAttribute, []); // In React.jsx, children is no longer a separate argument, but passed in // through the argument object @@ -530,40 +538,38 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, found[name] = true; } - props.push(convertAttribute(attr)); + accumulateAttribute(props, attr); } return props.length > 0 ? t.objectExpression(props) : t.nullLiteral(); } - let props = []; const objs = []; - - for (const attr of attribs) { - if (useSpread || !t.isJSXSpreadAttribute(attr)) { - props.push(convertAttribute(attr)); - } else { - if (props.length) { - objs.push(t.objectExpression(props)); - props = []; + const props = attribs.reduce(accumulateAttribute, []); + + if (!useSpread) { + // Convert syntax to use multiple objects instead of spread + let start = 0; + props.forEach((prop, i) => { + if (t.isSpreadElement(prop)) { + if (i > start) { + objs.push(t.objectExpression(props.slice(start, i))); + } + objs.push(prop.argument); + start = i + 1; } - objs.push(attr.argument); + }); + if (props.length > start) { + objs.push(t.objectExpression(props.slice(start))); } + } else if (props.length) { + objs.push(t.objectExpression(props)); } - if (!props.length && !objs.length) { + if (!objs.length) { return t.nullLiteral(); } - if (useSpread) { - return props.length > 0 ? t.objectExpression(props) : t.nullLiteral(); - } - - if (props.length) { - objs.push(t.objectExpression(props)); - props = []; - } - if (objs.length === 1) { return objs[0]; } diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/duplicate-props/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/duplicate-props/input.js new file mode 100644 index 000000000000..8741892b4cef --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/duplicate-props/input.js @@ -0,0 +1,7 @@ +

text

; + +

text

; + +

text

; + +

text

; diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/duplicate-props/output.mjs b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/duplicate-props/output.mjs new file mode 100644 index 000000000000..f968ee0b3cb9 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/duplicate-props/output.mjs @@ -0,0 +1,29 @@ +import { jsx as _jsx } from "react/jsx-runtime"; + +/*#__PURE__*/ +_jsx("p", { + prop: true, + prop: true, + children: "text" +}); + +/*#__PURE__*/ +_jsx("p", { + prop, + prop, + children: "text" +}); + +/*#__PURE__*/ +_jsx("p", { + prop: true, + prop, + children: "text" +}); + +/*#__PURE__*/ +_jsx("p", { + prop, + prop: true, + children: "text" +}); diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/flattens-spread/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/flattens-spread/input.js new file mode 100644 index 000000000000..7cf92a69c605 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/flattens-spread/input.js @@ -0,0 +1,7 @@ +

text

; + +
{contents}
; + +; + +
{items}
; diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/flattens-spread/output.mjs b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/flattens-spread/output.mjs new file mode 100644 index 000000000000..738dcc4e9e95 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/flattens-spread/output.mjs @@ -0,0 +1,24 @@ +import { jsx as _jsx } from "react/jsx-runtime"; + +/*#__PURE__*/ +_jsx("p", { ...props, + children: "text" +}); + +/*#__PURE__*/ +_jsx("div", { ...props, + children: contents +}); + +/*#__PURE__*/ +_jsx("img", { + alt: "", + src, + title +}); + +/*#__PURE__*/ +_jsx("blockquote", { + cite, + children: items +}); diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/avoids-spread/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/avoids-spread/input.js new file mode 100644 index 000000000000..6dbb876ac43d --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/avoids-spread/input.js @@ -0,0 +1,11 @@ +; + +; + +; + +; + +; + +; diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/avoids-spread/output.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/avoids-spread/output.js new file mode 100644 index 000000000000..8d4d99fab9c0 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/avoids-spread/output.js @@ -0,0 +1,32 @@ +/*#__PURE__*/ +React.createElement(E, babelHelpers.extends({}, props, { + last: true +})); + +/*#__PURE__*/ +React.createElement(E, babelHelpers.extends({ + first: true +}, props)); + +/*#__PURE__*/ +React.createElement(E, babelHelpers.extends({}, pre, suf)); + +/*#__PURE__*/ +React.createElement(E, babelHelpers.extends({ + first: true +}, pre, { + mid: true +}, suf)); + +/*#__PURE__*/ +React.createElement(E, babelHelpers.extends({}, pre, { + mid: true +}, suf, { + last: true +})); + +/*#__PURE__*/ +React.createElement(E, babelHelpers.extends({}, pre, { + mid1: true, + mid2: true +}, suf)); diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/duplicate-props/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/duplicate-props/input.js new file mode 100644 index 000000000000..8741892b4cef --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/duplicate-props/input.js @@ -0,0 +1,7 @@ +

text

; + +

text

; + +

text

; + +

text

; diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/duplicate-props/output.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/duplicate-props/output.js new file mode 100644 index 000000000000..341892d567dd --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/duplicate-props/output.js @@ -0,0 +1,23 @@ +/*#__PURE__*/ +React.createElement("p", { + prop: true, + prop: true +}, "text"); + +/*#__PURE__*/ +React.createElement("p", { + prop, + prop +}, "text"); + +/*#__PURE__*/ +React.createElement("p", { + prop: true, + prop +}, "text"); + +/*#__PURE__*/ +React.createElement("p", { + prop, + prop: true +}, "text"); diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/flattens-spread/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/flattens-spread/input.js new file mode 100644 index 000000000000..7cf92a69c605 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/flattens-spread/input.js @@ -0,0 +1,7 @@ +

text

; + +
{contents}
; + +; + +
{items}
; diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/flattens-spread/output.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/flattens-spread/output.js new file mode 100644 index 000000000000..25b234ea3b58 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/flattens-spread/output.js @@ -0,0 +1,17 @@ +/*#__PURE__*/ +React.createElement("p", props, "text"); + +/*#__PURE__*/ +React.createElement("div", props, contents); + +/*#__PURE__*/ +React.createElement("img", { + alt: "", + src, + title +}); + +/*#__PURE__*/ +React.createElement("blockquote", { + cite +}, items);