Add JSX Fragment syntax support (#6552)

* Add JSX Fragments to babel-types

* Support JSX fragments in the transform-react-jsx plugin

* Add tests JSX fragments

* Update helper-builder and transform plugin documentations for jsx fragment

* Add generator for jsx fragments

* Add test for jsx fragment generator

* Split jsx transform example into normal and fragment examples

* Remove unnecessary fields from ElementState in babel-helper-builder-react-jsx

* inline [skip ci]
This commit is contained in:
Clement Hoang
2017-11-03 07:43:48 -07:00
committed by Henry Zhu
parent 9e2828322e
commit 1a7194a22f
20 changed files with 322 additions and 29 deletions

View File

@@ -3,11 +3,9 @@ import * as t from "@babel/types";
type ElementState = {
tagExpr: Object, // tag node
tagName: string, // raw string tag name
tagName: ?string, // raw string tag name
args: Array<Object>, // array of call arguments
call?: Object, // optional call property that can be set to override the call expression returned
pre?: Function, // function called with (state: ElementState) before building attribs
post?: Function, // function called with (state: ElementState) after building attribs
};
export default function(opts) {
@@ -30,6 +28,20 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
},
};
visitor.JSXFragment = {
exit(path, file) {
if (opts.compat) {
throw path.buildCodeFrameError(
"Fragment tags are only supported in React 16 and up.",
);
}
const callExpr = buildFragmentCall(path, file);
if (callExpr) {
path.replaceWith(t.inherits(callExpr, path.node));
}
},
};
return visitor;
function convertJSXIdentifier(node, parent) {
@@ -188,4 +200,35 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
return attribs;
}
function buildFragmentCall(path, file) {
if (opts.filter && !opts.filter(path.node, file)) return;
const openingPath = path.get("openingElement");
openingPath.parent.children = t.react.buildChildren(openingPath.parent);
const args = [];
const tagName = null;
const tagExpr = file.get("jsxFragIdentifier")();
const state: ElementState = {
tagExpr: tagExpr,
tagName: tagName,
args: args,
};
if (opts.pre) {
opts.pre(state, file);
}
// no attributes are allowed with <> syntax
args.push(t.nullLiteral(), ...path.node.children);
if (opts.post) {
opts.post(state, file);
}
file.set("usedFragment", true);
return state.call || t.callExpression(state.callee, args);
}
}