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:
parent
9e2828322e
commit
1a7194a22f
@ -92,3 +92,25 @@ export function JSXClosingElement(node: Object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function JSXEmptyExpression() {}
|
export function JSXEmptyExpression() {}
|
||||||
|
|
||||||
|
export function JSXFragment(node: Object) {
|
||||||
|
this.print(node.openingFragment, node);
|
||||||
|
|
||||||
|
this.indent();
|
||||||
|
for (const child of (node.children: Array<Object>)) {
|
||||||
|
this.print(child, node);
|
||||||
|
}
|
||||||
|
this.dedent();
|
||||||
|
|
||||||
|
this.print(node.closingFragment, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function JSXOpeningFragment() {
|
||||||
|
this.token("<");
|
||||||
|
this.token(">");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function JSXClosingFragment() {
|
||||||
|
this.token("</");
|
||||||
|
this.token(">");
|
||||||
|
}
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
<></>;
|
||||||
|
|
||||||
|
<
|
||||||
|
>
|
||||||
|
text
|
||||||
|
</>;
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
<></>;
|
||||||
|
<>
|
||||||
|
text
|
||||||
|
</>;
|
||||||
@ -0,0 +1 @@
|
|||||||
|
{ "plugins": ["jsx" ] }
|
||||||
@ -8,8 +8,6 @@ type ElementState = {
|
|||||||
tagName: string; // raw string tag name
|
tagName: string; // raw string tag name
|
||||||
args: Array<Object>; // array of call arguments
|
args: Array<Object>; // array of call arguments
|
||||||
call?: Object; // optional call property that can be set to override the call expression returned
|
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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
require("@babel/helper-builder-react-jsx")({
|
require("@babel/helper-builder-react-jsx")({
|
||||||
@ -18,11 +16,13 @@ require("@babel/helper-builder-react-jsx")({
|
|||||||
},
|
},
|
||||||
|
|
||||||
pre: function (state: ElementState) {
|
pre: function (state: ElementState) {
|
||||||
// called before building the element
|
// function called with (state: ElementState) before building attribs
|
||||||
},
|
},
|
||||||
|
|
||||||
post: function (state: ElementState) {
|
post: function (state: ElementState) {
|
||||||
// called after building the element
|
// function called with (state: ElementState) after building attribs
|
||||||
}
|
},
|
||||||
|
|
||||||
|
compat?: boolean // true if React is in compat mode
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|||||||
@ -3,11 +3,9 @@ import * as t from "@babel/types";
|
|||||||
|
|
||||||
type ElementState = {
|
type ElementState = {
|
||||||
tagExpr: Object, // tag node
|
tagExpr: Object, // tag node
|
||||||
tagName: string, // raw string tag name
|
tagName: ?string, // raw string tag name
|
||||||
args: Array<Object>, // array of call arguments
|
args: Array<Object>, // array of call arguments
|
||||||
call?: Object, // optional call property that can be set to override the call expression returned
|
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) {
|
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;
|
return visitor;
|
||||||
|
|
||||||
function convertJSXIdentifier(node, parent) {
|
function convertJSXIdentifier(node, parent) {
|
||||||
@ -188,4 +200,35 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
|
|||||||
|
|
||||||
return attribs;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export default function({ types: t }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
compat: true,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,12 +46,76 @@ var profile = <div>
|
|||||||
|
|
||||||
var dom = require("deku").dom;
|
var dom = require("deku").dom;
|
||||||
|
|
||||||
var profile = dom( "div", null,
|
var profile = dom("div", null,
|
||||||
dom("img", { src: "avatar.png", className: "profile" }),
|
dom("img", { src: "avatar.png", className: "profile" }),
|
||||||
dom("h3", null, [user.firstName, user.lastName].join(" "))
|
dom("h3", null, [user.firstName, user.lastName].join(" "))
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Fragments
|
||||||
|
|
||||||
|
Fragments are a feature available in React 16.2.0+.
|
||||||
|
|
||||||
|
#### React
|
||||||
|
|
||||||
|
**In**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var descriptions = items.map(item => (
|
||||||
|
<>
|
||||||
|
<dt>{item.name}</dt>
|
||||||
|
<dd>{item.value}</dd>
|
||||||
|
</>
|
||||||
|
));
|
||||||
|
```
|
||||||
|
|
||||||
|
**Out**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var descriptions = items.map(item => React.createElement(
|
||||||
|
React.Fragment,
|
||||||
|
null,
|
||||||
|
React.createElement("dt", null, item.name),
|
||||||
|
React.createElement("dd", null, item.value)
|
||||||
|
));
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom
|
||||||
|
|
||||||
|
**In**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/** @jsx dom */
|
||||||
|
/** @jsxFrag DomFrag */
|
||||||
|
|
||||||
|
var { dom, DomFrag } = require("deku"); // DomFrag is fictional!
|
||||||
|
|
||||||
|
var descriptions = items.map(item => (
|
||||||
|
<>
|
||||||
|
<dt>{item.name}</dt>
|
||||||
|
<dd>{item.value}</dd>
|
||||||
|
</>
|
||||||
|
));
|
||||||
|
```
|
||||||
|
|
||||||
|
**Out**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/** @jsx dom */
|
||||||
|
/** @jsxFrag DomFrag */
|
||||||
|
|
||||||
|
var { dom, DomFrag } = require("deku"); // DomFrag is fictional!
|
||||||
|
|
||||||
|
var descriptions = items.map(item => dom(
|
||||||
|
DomFrag,
|
||||||
|
null,
|
||||||
|
dom("dt", null, item.name),
|
||||||
|
dom("dd", null, item.value)
|
||||||
|
));
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that if a custom pragma is specified, then a custom fragment pragma must also be specified if the `<></>` is used. Otherwise, an error will be thrown.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@ -79,6 +143,7 @@ With options:
|
|||||||
"plugins": [
|
"plugins": [
|
||||||
["@babel/transform-react-jsx", {
|
["@babel/transform-react-jsx", {
|
||||||
"pragma": "dom", // default pragma is React.createElement
|
"pragma": "dom", // default pragma is React.createElement
|
||||||
|
"pragmaFrag": "DomFrag", // default is React.Fragment
|
||||||
"throwIfNamespace": false // defaults to true
|
"throwIfNamespace": false // defaults to true
|
||||||
}]
|
}]
|
||||||
]
|
]
|
||||||
@ -109,6 +174,12 @@ Replace the function used when compiling JSX expressions.
|
|||||||
|
|
||||||
Note that the `@jsx React.DOM` pragma has been deprecated as of React v0.12
|
Note that the `@jsx React.DOM` pragma has been deprecated as of React v0.12
|
||||||
|
|
||||||
|
### `pragmaFrag`
|
||||||
|
|
||||||
|
`string`, defaults to `React.Fragment`.
|
||||||
|
|
||||||
|
Replace the component used when compiling JSX fragments.
|
||||||
|
|
||||||
### `useBuiltIns`
|
### `useBuiltIns`
|
||||||
|
|
||||||
`boolean`, defaults to `false`.
|
`boolean`, defaults to `false`.
|
||||||
|
|||||||
@ -2,11 +2,23 @@ import jsx from "@babel/plugin-syntax-jsx";
|
|||||||
import helper from "@babel/helper-builder-react-jsx";
|
import helper from "@babel/helper-builder-react-jsx";
|
||||||
|
|
||||||
export default function({ types: t }, options) {
|
export default function({ types: t }, options) {
|
||||||
const pragma = options.pragma || "React.createElement";
|
const THROW_IF_NAMESPACE =
|
||||||
const throwIfNamespace =
|
|
||||||
options.throwIfNamespace === undefined ? true : !!options.throwIfNamespace;
|
options.throwIfNamespace === undefined ? true : !!options.throwIfNamespace;
|
||||||
|
|
||||||
|
const PRAGMA_DEFAULT = options.pragma || "React.createElement";
|
||||||
|
const PRAGMA_FRAG_DEFAULT = options.pragmaFrag || "React.Fragment";
|
||||||
|
|
||||||
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/;
|
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/;
|
||||||
|
const JSX_FRAG_ANNOTATION_REGEX = /\*?\s*@jsxFrag\s+([^\s]+)/;
|
||||||
|
|
||||||
|
// returns a closure that returns an identifier or memberExpression node
|
||||||
|
// based on the given id
|
||||||
|
const createIdentifierParser = (id: string) => () => {
|
||||||
|
return id
|
||||||
|
.split(".")
|
||||||
|
.map(name => t.identifier(name))
|
||||||
|
.reduce((object, property) => t.memberExpression(object, property));
|
||||||
|
};
|
||||||
|
|
||||||
const visitor = helper({
|
const visitor = helper({
|
||||||
pre(state) {
|
pre(state) {
|
||||||
@ -23,27 +35,49 @@ export default function({ types: t }, options) {
|
|||||||
state.callee = pass.get("jsxIdentifier")();
|
state.callee = pass.get("jsxIdentifier")();
|
||||||
},
|
},
|
||||||
|
|
||||||
throwIfNamespace,
|
throwIfNamespace: THROW_IF_NAMESPACE,
|
||||||
});
|
});
|
||||||
|
|
||||||
visitor.Program = function(path, state) {
|
visitor.Program = {
|
||||||
const { file } = state;
|
enter(path, state) {
|
||||||
|
const { file } = state;
|
||||||
|
|
||||||
let id = pragma;
|
let pragma = PRAGMA_DEFAULT;
|
||||||
for (const comment of (file.ast.comments: Array<Object>)) {
|
let pragmaFrag = PRAGMA_FRAG_DEFAULT;
|
||||||
const matches = JSX_ANNOTATION_REGEX.exec(comment.value);
|
let pragmaSet = !!options.pragma;
|
||||||
if (matches) {
|
let pragmaFragSet = !!options.pragmaFrag;
|
||||||
id = matches[1];
|
|
||||||
break;
|
for (const comment of (file.ast.comments: Array<Object>)) {
|
||||||
|
const jsxMatches = JSX_ANNOTATION_REGEX.exec(comment.value);
|
||||||
|
if (jsxMatches) {
|
||||||
|
pragma = jsxMatches[1];
|
||||||
|
pragmaSet = true;
|
||||||
|
}
|
||||||
|
const jsxFragMatches = JSX_FRAG_ANNOTATION_REGEX.exec(comment.value);
|
||||||
|
if (jsxFragMatches) {
|
||||||
|
pragmaFrag = jsxFragMatches[1];
|
||||||
|
pragmaFragSet = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
state.set("jsxIdentifier", () =>
|
state.set("jsxIdentifier", createIdentifierParser(pragma));
|
||||||
id
|
state.set("jsxFragIdentifier", createIdentifierParser(pragmaFrag));
|
||||||
.split(".")
|
state.set("usedFragment", false);
|
||||||
.map(name => t.identifier(name))
|
state.set("pragmaSet", pragmaSet);
|
||||||
.reduce((object, property) => t.memberExpression(object, property)),
|
state.set("pragmaFragSet", pragmaFragSet);
|
||||||
);
|
},
|
||||||
|
exit(path, state) {
|
||||||
|
if (
|
||||||
|
state.get("pragmaSet") &&
|
||||||
|
state.get("usedFragment") &&
|
||||||
|
!state.get("pragmaFragSet")
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
"transform-react-jsx: pragma has been set but " +
|
||||||
|
"pragmafrag has not been set",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
visitor.JSXAttribute = function(path) {
|
visitor.JSXAttribute = function(path) {
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
<div>
|
||||||
|
< >
|
||||||
|
<>
|
||||||
|
<span>Hello</span>
|
||||||
|
<span>world</span>
|
||||||
|
</>
|
||||||
|
<>
|
||||||
|
<span>Goodbye</span>
|
||||||
|
<span>world</span>
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
@ -0,0 +1 @@
|
|||||||
|
React.createElement("div", null, React.createElement(React.Fragment, null, React.createElement(React.Fragment, null, React.createElement("span", null, "Hello"), React.createElement("span", null, "world")), React.createElement(React.Fragment, null, React.createElement("span", null, "Goodbye"), React.createElement("span", null, "world"))));
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
/** @jsx dom */
|
||||||
|
|
||||||
|
<div>no fragment is used</div>
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
/** @jsx dom */
|
||||||
|
dom("div", null, "no fragment is used");
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
/** @jsx dom */
|
||||||
|
/** @jsxFrag DomFrag */
|
||||||
|
|
||||||
|
<></>
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
/** @jsx dom */
|
||||||
|
|
||||||
|
/** @jsxFrag DomFrag */
|
||||||
|
dom(DomFrag, null);
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
/** @jsx dom */
|
||||||
|
|
||||||
|
<></>
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
/** @jsx dom */
|
||||||
|
dom(React.Fragment, null);
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"throws": "transform-react-jsx: pragma has been set but pragmafrag has not been set"
|
||||||
|
}
|
||||||
@ -1059,7 +1059,7 @@ See also `t.isJSXAttribute(node, opts)` and `t.assertJSXAttribute(node, opts)`.
|
|||||||
Aliases: `JSX`, `Immutable`
|
Aliases: `JSX`, `Immutable`
|
||||||
|
|
||||||
- `name`: `JSXIdentifier | JSXNamespacedName` (required)
|
- `name`: `JSXIdentifier | JSXNamespacedName` (required)
|
||||||
- `value`: `JSXElement | StringLiteral | JSXExpressionContainer` (default: `null`)
|
- `value`: `JSXElement | JSXFragment | StringLiteral | JSXExpressionContainer` (default: `null`)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -1074,6 +1074,18 @@ Aliases: `JSX`, `Immutable`
|
|||||||
|
|
||||||
- `name`: `JSXIdentifier | JSXMemberExpression` (required)
|
- `name`: `JSXIdentifier | JSXMemberExpression` (required)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### jSXClosingFragment
|
||||||
|
```javascript
|
||||||
|
t.jSXClosingFragment()
|
||||||
|
```
|
||||||
|
|
||||||
|
See also `t.isJSXClosingFragment(node, opts)` and `t.assertJSXClosingFragment(node, opts)`.
|
||||||
|
|
||||||
|
Aliases: `JSX`, `Immutable`
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### jSXElement
|
### jSXElement
|
||||||
@ -1087,7 +1099,7 @@ Aliases: `JSX`, `Immutable`, `Expression`
|
|||||||
|
|
||||||
- `openingElement`: `JSXOpeningElement` (required)
|
- `openingElement`: `JSXOpeningElement` (required)
|
||||||
- `closingElement`: `JSXClosingElement` (default: `null`)
|
- `closingElement`: `JSXClosingElement` (default: `null`)
|
||||||
- `children`: `Array<JSXText | JSXExpressionContainer | JSXSpreadChild | JSXElement>` (required)
|
- `children`: `Array<JSXText | JSXExpressionContainer | JSXSpreadChild | JSXElement | JSXFragment>` (required)
|
||||||
- `selfClosing` (required)
|
- `selfClosing` (required)
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -1117,6 +1129,21 @@ Aliases: `JSX`, `Immutable`
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### jSXFragment
|
||||||
|
```javascript
|
||||||
|
t.jSXFragment(openingFragment, closingFragment, children)
|
||||||
|
```
|
||||||
|
|
||||||
|
See also `t.isJSXFragment(node, opts)` and `t.assertJSXFragment(node, opts)`.
|
||||||
|
|
||||||
|
Aliases: `JSX`, `Immutable`, `Expression`
|
||||||
|
|
||||||
|
- `openingFragment`: `JSXOpeningFragment` (required)
|
||||||
|
- `closingFragment`: `JSXClosingFragment` (required)
|
||||||
|
- `children`: `Array<JSXText | JSXExpressionContainer | JSXSpreadChild | JSXElement | JSXFragment>` (required)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### jSXIdentifier
|
### jSXIdentifier
|
||||||
```javascript
|
```javascript
|
||||||
t.jSXIdentifier(name)
|
t.jSXIdentifier(name)
|
||||||
@ -1171,6 +1198,18 @@ Aliases: `JSX`, `Immutable`
|
|||||||
- `attributes`: `Array<JSXAttribute | JSXSpreadAttribute>` (required)
|
- `attributes`: `Array<JSXAttribute | JSXSpreadAttribute>` (required)
|
||||||
- `selfClosing`: `boolean` (default: `false`)
|
- `selfClosing`: `boolean` (default: `false`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### jSXOpeningFragment
|
||||||
|
```javascript
|
||||||
|
t.jSXOpeningFragment()
|
||||||
|
```
|
||||||
|
|
||||||
|
See also `t.isJSXOpeningFragment(node, opts)` and `t.assertJSXOpeningFragment(node, opts)`.
|
||||||
|
|
||||||
|
Aliases: `JSX`, `Immutable`
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### jSXSpreadAttribute
|
### jSXSpreadAttribute
|
||||||
|
|||||||
@ -16,6 +16,7 @@ defineType("JSXAttribute", {
|
|||||||
optional: true,
|
optional: true,
|
||||||
validate: assertNodeType(
|
validate: assertNodeType(
|
||||||
"JSXElement",
|
"JSXElement",
|
||||||
|
"JSXFragment",
|
||||||
"StringLiteral",
|
"StringLiteral",
|
||||||
"JSXExpressionContainer",
|
"JSXExpressionContainer",
|
||||||
),
|
),
|
||||||
@ -54,6 +55,7 @@ defineType("JSXElement", {
|
|||||||
"JSXExpressionContainer",
|
"JSXExpressionContainer",
|
||||||
"JSXSpreadChild",
|
"JSXSpreadChild",
|
||||||
"JSXElement",
|
"JSXElement",
|
||||||
|
"JSXFragment",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -161,3 +163,39 @@ defineType("JSXText", {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defineType("JSXFragment", {
|
||||||
|
builder: ["openingFragment", "closingFragment", "children"],
|
||||||
|
visitor: ["openingFragment", "children", "closingFragment"],
|
||||||
|
aliases: ["JSX", "Immutable", "Expression"],
|
||||||
|
fields: {
|
||||||
|
openingFragment: {
|
||||||
|
validate: assertNodeType("JSXOpeningFragment"),
|
||||||
|
},
|
||||||
|
closingFragment: {
|
||||||
|
validate: assertNodeType("JSXClosingFragment"),
|
||||||
|
},
|
||||||
|
children: {
|
||||||
|
validate: chain(
|
||||||
|
assertValueType("array"),
|
||||||
|
assertEach(
|
||||||
|
assertNodeType(
|
||||||
|
"JSXText",
|
||||||
|
"JSXExpressionContainer",
|
||||||
|
"JSXSpreadChild",
|
||||||
|
"JSXElement",
|
||||||
|
"JSXFragment",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
defineType("JSXOpeningFragment", {
|
||||||
|
aliases: ["JSX", "Immutable"],
|
||||||
|
});
|
||||||
|
|
||||||
|
defineType("JSXClosingFragment", {
|
||||||
|
aliases: ["JSX", "Immutable"],
|
||||||
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user