diff --git a/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts b/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts index 3e3a93963030..1b23ae5c769a 100644 --- a/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts +++ b/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts @@ -39,6 +39,15 @@ const get = (pass: PluginPass, name: string) => const set = (pass: PluginPass, name: string, v: any) => pass.set(`@babel/plugin-react-jsx/${name}`, v); +function hasProto(node: t.ObjectExpression) { + return node.properties.some( + value => + t.isObjectProperty(value, { computed: false, shorthand: false }) && + (t.isIdentifier(value.key, { name: "__proto__" }) || + t.isStringLiteral(value.key, { value: "__proto__" })), + ); +} + export interface Options { filter?: (node: t.Node, pass: PluginPass) => boolean; importSource?: string; @@ -422,7 +431,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, if (t.isJSXSpreadAttribute(attribute.node)) { const arg = attribute.node.argument; // Collect properties into props array if spreading object expression - if (t.isObjectExpression(arg)) { + if (t.isObjectExpression(arg) && !hasProto(arg)) { array.push(...arg.properties); } else { array.push(t.spreadElement(arg)); @@ -718,7 +727,17 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, } if (objs.length === 1) { - return objs[0]; + if ( + !( + t.isSpreadElement(props[0]) && + // If an object expression is spread element's argument + // it is very likely to contain __proto__ and we should stop + // optimizing spread element + t.isObjectExpression(props[0].argument) + ) + ) { + return objs[0]; + } } // looks like we have multiple objects @@ -755,7 +774,12 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`, accumulateAttribute(props, attr); } - return props.length === 1 && t.isSpreadElement(props[0]) + return props.length === 1 && + t.isSpreadElement(props[0]) && + // If an object expression is spread element's argument + // it is very likely to contain __proto__ and we should stop + // optimizing spread element + !t.isObjectExpression(props[0].argument) ? props[0].argument : props.length > 0 ? t.objectExpression(props) 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 index 7cf92a69c605..24d4257e21b4 100644 --- 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 @@ -2,6 +2,10 @@
{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
index 738dcc4e9e95..1612ddc01cf0 100644
--- 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
@@ -14,7 +14,8 @@ _jsx("div", { ...props,
 _jsx("img", {
   alt: "",
   src,
-  title
+  title,
+  __proto__
 });
 
 /*#__PURE__*/
@@ -22,3 +23,13 @@ _jsx("blockquote", {
   cite,
   children: items
 });
+
+/*#__PURE__*/
+_jsx("pre", {
+  ["__proto__"]: null
+});
+
+/*#__PURE__*/
+_jsx("code", {
+  [__proto__]: null
+});
diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-spread-with-proto/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-spread-with-proto/input.js
new file mode 100644
index 000000000000..4d745e96c31f
--- /dev/null
+++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-spread-with-proto/input.js
@@ -0,0 +1,3 @@
+

text

; + +
{contents}
; diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-spread-with-proto/output.mjs b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-spread-with-proto/output.mjs new file mode 100644 index 000000000000..31d60b3878f3 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react-automatic/handle-spread-with-proto/output.mjs @@ -0,0 +1,15 @@ +import { jsx as _jsx } from "react/jsx-runtime"; + +/*#__PURE__*/ +_jsx("p", { ...{ + __proto__: null + }, + children: "text" +}); + +/*#__PURE__*/ +_jsx("div", { ...{ + "__proto__": null + }, + children: contents +}); 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 index 7cf92a69c605..24d4257e21b4 100644 --- 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 @@ -2,6 +2,10 @@
{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
index 25b234ea3b58..59b3fe96530b 100644
--- 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
@@ -8,10 +8,21 @@ React.createElement("div", props, contents);
 React.createElement("img", {
   alt: "",
   src,
-  title
+  title,
+  __proto__
 });
 
 /*#__PURE__*/
 React.createElement("blockquote", {
   cite
 }, items);
+
+/*#__PURE__*/
+React.createElement("pre", {
+  ["__proto__"]: null
+});
+
+/*#__PURE__*/
+React.createElement("code", {
+  [__proto__]: null
+});
diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto-babel-7/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto-babel-7/input.js
new file mode 100644
index 000000000000..4d745e96c31f
--- /dev/null
+++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto-babel-7/input.js
@@ -0,0 +1,3 @@
+

text

; + +
{contents}
; diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto-babel-7/options.json b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto-babel-7/options.json new file mode 100644 index 000000000000..29a3f0e84167 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto-babel-7/options.json @@ -0,0 +1,3 @@ +{ + "BABEL_8_BREAKING": false +} diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto-babel-7/output.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto-babel-7/output.js new file mode 100644 index 000000000000..3e1b64acf38a --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto-babel-7/output.js @@ -0,0 +1,9 @@ +/*#__PURE__*/ +React.createElement("p", babelHelpers.extends({ + __proto__: null +}), "text"); + +/*#__PURE__*/ +React.createElement("div", babelHelpers.extends({ + "__proto__": null +}), contents); diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto/input.js new file mode 100644 index 000000000000..4d745e96c31f --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto/input.js @@ -0,0 +1,3 @@ +

text

; + +
{contents}
; diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto/options.json b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto/options.json new file mode 100644 index 000000000000..cbf6d1595427 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto/options.json @@ -0,0 +1,3 @@ +{ + "BABEL_8_BREAKING": true +} diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto/output.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto/output.js new file mode 100644 index 000000000000..98dcdecf6585 --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/handle-spread-with-proto/output.js @@ -0,0 +1,11 @@ +/*#__PURE__*/ +React.createElement("p", { ...{ + __proto__: null + } +}, "text"); + +/*#__PURE__*/ +React.createElement("div", { ...{ + "__proto__": null + } +}, contents); diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/proto-in-jsx-attribute/input.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/proto-in-jsx-attribute/input.js new file mode 100644 index 000000000000..15228285e30e --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/proto-in-jsx-attribute/input.js @@ -0,0 +1 @@ +

text

; diff --git a/packages/babel-plugin-transform-react-jsx/test/fixtures/react/proto-in-jsx-attribute/output.js b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/proto-in-jsx-attribute/output.js new file mode 100644 index 000000000000..a975dc74f39e --- /dev/null +++ b/packages/babel-plugin-transform-react-jsx/test/fixtures/react/proto-in-jsx-attribute/output.js @@ -0,0 +1,5 @@ +/*#__PURE__*/ +React.createElement("p", { + __proto__: null, + class: "bar" +}, "text");