From e7704e22a146c2d5b00f8c6bfa6255153272f053 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 15 Oct 2019 15:41:09 +0200 Subject: [PATCH] [babel-plugin-react-jsx] Avoid duplicate "children" key in props object (#17094) * [babel-plugin-react-jsx] Avoid duplicate "children" key in props object * Use Object.assign approach --- .../__tests__/TransformJSXToReactJSX-test.js | 6 ++++++ .../TransformJSXToReactJSX-test.js.snap | 8 ++++++++ .../src/TransformJSXToReactBabelPlugin.js | 19 ++++++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-react-jsx/__tests__/TransformJSXToReactJSX-test.js b/packages/babel-plugin-react-jsx/__tests__/TransformJSXToReactJSX-test.js index 86ee84f1fa26..ce8403941725 100644 --- a/packages/babel-plugin-react-jsx/__tests__/TransformJSXToReactJSX-test.js +++ b/packages/babel-plugin-react-jsx/__tests__/TransformJSXToReactJSX-test.js @@ -480,4 +480,10 @@ describe('transform react to jsx', () => { transform(``, {useBuiltIns: false}) ).toMatchSnapshot(); }); + + it('should not contain duplicate children key in props object', () => { + expect( + transform(`2`) + ).toMatchSnapshot(); + }); }); diff --git a/packages/babel-plugin-react-jsx/__tests__/__snapshots__/TransformJSXToReactJSX-test.js.snap b/packages/babel-plugin-react-jsx/__tests__/__snapshots__/TransformJSXToReactJSX-test.js.snap index e5b844b12fc0..a335ef3806b0 100644 --- a/packages/babel-plugin-react-jsx/__tests__/__snapshots__/TransformJSXToReactJSX-test.js.snap +++ b/packages/babel-plugin-react-jsx/__tests__/__snapshots__/TransformJSXToReactJSX-test.js.snap @@ -250,6 +250,14 @@ var e = React.jsx(F, { }); `; +exports[`transform react to jsx should not contain duplicate children key in props object 1`] = ` +React.jsx(Component, Object.assign({ + children: 1 +}, { + children: "2" +})); +`; + exports[`transform react to jsx should not strip nbsp even couple with other whitespace 1`] = ` React.jsx("div", { children: "\\xA0 " diff --git a/packages/babel-plugin-react-jsx/src/TransformJSXToReactBabelPlugin.js b/packages/babel-plugin-react-jsx/src/TransformJSXToReactBabelPlugin.js index 960f75a3a8ec..6d49d825eba2 100644 --- a/packages/babel-plugin-react-jsx/src/TransformJSXToReactBabelPlugin.js +++ b/packages/babel-plugin-react-jsx/src/TransformJSXToReactBabelPlugin.js @@ -268,11 +268,20 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`, ); } + function isChildrenProp(prop) { + return ( + t.isJSXAttribute(prop) && + t.isJSXIdentifier(prop.name) && + prop.name.name === 'children' + ); + } + // 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) { let _props = []; const objs = []; + const hasChildren = children && children.length > 0; const useBuiltIns = file.opts.useBuiltIns || false; if (typeof useBuiltIns !== 'boolean') { @@ -287,6 +296,14 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`, if (t.isJSXSpreadAttribute(prop)) { _props = pushProps(_props, objs); objs.push(prop.argument); + } else if (hasChildren && isChildrenProp(prop)) { + // In order to avoid having duplicate "children" keys, we avoid + // pushing the "children" prop if we have actual children. Instead + // we put the children into a separate object and then rely on + // the Object.assign logic below to ensure the correct object is + // formed. + _props = pushProps(_props, objs); + objs.push(t.objectExpression([convertAttribute(prop)])); } else { _props.push(convertAttribute(prop)); } @@ -294,7 +311,7 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`, // In React.JSX, children is no longer a separate argument, but passed in // through the argument object - if (children && children.length > 0) { + if (hasChildren) { if (children.length === 1) { _props.push(t.objectProperty(t.identifier('children'), children[0])); } else {