diff --git a/packages/babel-helper-builder-react-jsx/README.md b/packages/babel-helper-builder-react-jsx/README.md index 895857ec4c..fb48db3ef2 100644 --- a/packages/babel-helper-builder-react-jsx/README.md +++ b/packages/babel-helper-builder-react-jsx/README.md @@ -13,6 +13,10 @@ type ElementState = { }; require("babel-helper-builder-react-jsx")({ + filter: function (element: JSXElement) { + // if returns false, the element isn't transformed + }, + pre: function (state: ElementState) { // called before building the element }, diff --git a/packages/babel-helper-builder-react-jsx/src/index.js b/packages/babel-helper-builder-react-jsx/src/index.js index 65cc0fc6ee..c3dc851b06 100644 --- a/packages/babel-helper-builder-react-jsx/src/index.js +++ b/packages/babel-helper-builder-react-jsx/src/index.js @@ -21,10 +21,10 @@ export default function(opts) { visitor.JSXElement = { exit(path, file) { - const callExpr = buildElementCall(path.get("openingElement"), file); - - callExpr.arguments = callExpr.arguments.concat(path.node.children); - path.replaceWith(t.inherits(callExpr, path.node)); + const callExpr = buildElementCall(path, file); + if (callExpr) { + path.replaceWith(t.inherits(callExpr, path.node)); + } }, }; @@ -79,9 +79,15 @@ export default function(opts) { } function buildElementCall(path, file) { - path.parent.children = t.react.buildChildren(path.parent); + if (opts.filter && !opts.filter(path.node, file)) return; - const tagExpr = convertJSXIdentifier(path.node.name, path.node); + const openingPath = path.get("openingElement"); + openingPath.parent.children = t.react.buildChildren(openingPath.parent); + + const tagExpr = convertJSXIdentifier( + openingPath.node.name, + openingPath.node, + ); const args = []; let tagName; @@ -101,14 +107,14 @@ export default function(opts) { opts.pre(state, file); } - let attribs = path.node.attributes; + let attribs = openingPath.node.attributes; if (attribs.length) { attribs = buildOpeningElementAttributes(attribs, file); } else { attribs = t.nullLiteral(); } - args.push(attribs); + args.push(attribs, ...path.node.children); if (opts.post) { opts.post(state, file); diff --git a/packages/babel-plugin-transform-react-inline-elements/package.json b/packages/babel-plugin-transform-react-inline-elements/package.json index c33a14ce78..5d0b4ca7f0 100644 --- a/packages/babel-plugin-transform-react-inline-elements/package.json +++ b/packages/babel-plugin-transform-react-inline-elements/package.json @@ -8,6 +8,9 @@ "keywords": [ "babel-plugin" ], + "dependencies": { + "babel-helper-builder-react-jsx": "7.0.0-beta.1" + }, "devDependencies": { "babel-helper-plugin-test-runner": "7.0.0-beta.1" } diff --git a/packages/babel-plugin-transform-react-inline-elements/src/index.js b/packages/babel-plugin-transform-react-inline-elements/src/index.js index a84b09e80e..ee706c0b85 100644 --- a/packages/babel-plugin-transform-react-inline-elements/src/index.js +++ b/packages/babel-plugin-transform-react-inline-elements/src/index.js @@ -1,3 +1,5 @@ +import helper from "babel-helper-builder-react-jsx"; + export default function({ types: t }) { function hasRefOrSpread(attrs) { for (let i = 0; i < attrs.length; i++) { @@ -14,60 +16,45 @@ export default function({ types: t }) { ); } - function getAttributeValue(attr) { - let value = attr.value; - if (!value) return t.booleanLiteral(true); - if (t.isJSXExpressionContainer(value)) value = value.expression; - return value; - } - - return { - visitor: { - JSXElement(path, file) { - const { node } = path; - - // filter - const open = node.openingElement; - if (hasRefOrSpread(open.attributes)) return; - - // init - const props = t.objectExpression([]); - let key = null; - let type = open.name; - - if (t.isJSXIdentifier(type) && t.react.isCompatTag(type.name)) { - type = t.stringLiteral(type.name); - } - - function pushProp(objProps, key, value) { - objProps.push(t.objectProperty(key, value)); - } - - // props - for (const attr of (open.attributes: Array)) { - if (isJSXAttributeOfName(attr, "key")) { - key = getAttributeValue(attr); - } else { - const name = attr.name.name; - const propertyKey = t.isValidIdentifier(name) - ? t.identifier(name) - : t.stringLiteral(name); - pushProp(props.properties, propertyKey, getAttributeValue(attr)); - } - } - - const args = [type, props]; - if (key || node.children.length) { - const children = t.react.buildChildren(node); - args.push( - key || t.unaryExpression("void", t.numericLiteral(0), true), - ...children, - ); - } - - const el = t.callExpression(file.addHelper("jsx"), args); - path.replaceWith(el); - }, + const visitor = helper({ + filter(node) { + return !hasRefOrSpread(node.openingElement.attributes); }, - }; + pre(state) { + const tagName = state.tagName; + const args = state.args; + if (t.react.isCompatTag(tagName)) { + args.push(t.stringLiteral(tagName)); + } else { + args.push(state.tagExpr); + } + }, + post(state, pass) { + state.callee = pass.addHelper("jsx"); + // NOTE: The arguments passed to the "jsx" helper are: + // (element, props, key, ...children) or (element, props) + // The argument generated by the helper are: + // (element, { ...props, key }, ...children) + + const props = state.args[1]; + let hasKey = false; + if (t.isObjectExpression(props)) { + const keyIndex = props.properties.findIndex(prop => + t.isIdentifier(prop.key, { name: "key" }), + ); + if (keyIndex > -1) { + state.args.splice(2, 0, props.properties[keyIndex].value); + props.properties.splice(keyIndex, 1); + hasKey = true; + } + } else if (t.isNullLiteral(props)) { + state.args.splice(1, 1, t.objectExpression([])); + } + + if (!hasKey && state.args.length > 2) { + state.args.splice(2, 0, t.unaryExpression("void", t.numericLiteral(0))); + } + }, + }); + return { visitor }; } diff --git a/packages/babel-plugin-transform-react-inline-elements/test/fixtures/inline-elements/multiline/expected.js b/packages/babel-plugin-transform-react-inline-elements/test/fixtures/inline-elements/multiline/expected.js index 2516786750..0e606e15ed 100644 --- a/packages/babel-plugin-transform-react-inline-elements/test/fixtures/inline-elements/multiline/expected.js +++ b/packages/babel-plugin-transform-react-inline-elements/test/fixtures/inline-elements/multiline/expected.js @@ -1 +1 @@ -babelHelpers.jsx(Baz, {}, void 0); +babelHelpers.jsx(Baz, {}); diff --git a/packages/babel-plugin-transform-react-inline-elements/test/fixtures/inline-elements/options.json b/packages/babel-plugin-transform-react-inline-elements/test/fixtures/options.json similarity index 100% rename from packages/babel-plugin-transform-react-inline-elements/test/fixtures/inline-elements/options.json rename to packages/babel-plugin-transform-react-inline-elements/test/fixtures/options.json diff --git a/packages/babel-plugin-transform-react-inline-elements/test/fixtures/regressions/6276/actual.js b/packages/babel-plugin-transform-react-inline-elements/test/fixtures/regressions/6276/actual.js new file mode 100644 index 0000000000..437e4bacb3 --- /dev/null +++ b/packages/babel-plugin-transform-react-inline-elements/test/fixtures/regressions/6276/actual.js @@ -0,0 +1,3 @@ +var test = ; diff --git a/packages/babel-plugin-transform-react-inline-elements/test/fixtures/regressions/6276/expected.js b/packages/babel-plugin-transform-react-inline-elements/test/fixtures/regressions/6276/expected.js new file mode 100644 index 0000000000..c2f317566e --- /dev/null +++ b/packages/babel-plugin-transform-react-inline-elements/test/fixtures/regressions/6276/expected.js @@ -0,0 +1,3 @@ +var test = babelHelpers.jsx(T, { + "default": " some string " +});