Skip to content

Commit

Permalink
Re-use common JSX element transform for <>...</> (#15047)
Browse files Browse the repository at this point in the history
* Re-use common JSX element transform for `<>...</>`

* Avoid trailing `void 0` params

* Update fixtures (Windows)

Co-authored-by: Babel Bot <babel-bot@users.noreply.github.com>
  • Loading branch information
nicolo-ribaudo and babel-bot committed Dec 13, 2022
1 parent 362451b commit ce37692
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 114 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
/*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/babelHelpers.jsx("span", {}), /*#__PURE__*/babelHelpers.jsx("div", {}));
/*#__PURE__*/babelHelpers.jsx(React.Fragment, {}, void 0, /*#__PURE__*/babelHelpers.jsx("span", {}), /*#__PURE__*/babelHelpers.jsx("div", {}));
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var _jsxFileName = "<CWD>/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/auto-import-dev/input.js";
import { Fragment as _Fragment } from "react/jsx-dev-runtime";
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
import { createElement as _createElement } from "react";
import { Fragment as _Fragment } from "react/jsx-dev-runtime";
var x = /*#__PURE__*/_jsxDEV(_Fragment, {
children: /*#__PURE__*/_jsxDEV("div", {
children: [/*#__PURE__*/_jsxDEV("div", {}, "1", false, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ class B extends A {
fileName: _jsxFileName,
lineNumber: 5,
columnNumber: 5
}, void 0);
});
super( /*#__PURE__*/_reactJsxDevRuntime.jsxDEV("sometag2", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 6,
columnNumber: 11
}, void 0));
}));
/*#__PURE__*/_reactJsxDevRuntime.jsxDEV("sometag3", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 7,
columnNumber: 5
}, void 0);
});
}
}
class C {
Expand Down Expand Up @@ -46,7 +46,7 @@ class E extends A {
fileName: _jsxFileName,
lineNumber: 29,
columnNumber: 20
}, void 0);
});
};
this.y = function () {
return /*#__PURE__*/_reactJsxDevRuntime.jsxDEV("sometag6", {}, void 0, false, {
Expand All @@ -67,7 +67,7 @@ class E extends A {
fileName: _jsxFileName,
lineNumber: 36,
columnNumber: 7
}, void 0);
});
}
super();
}
Expand All @@ -87,6 +87,6 @@ class G extends A {
fileName: _jsxFileName,
lineNumber: 49,
columnNumber: 12
}, void 0);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var _jsxFileName = "<CWD>\\packages\\babel-plugin-transform-react-jsx-development\\test\\fixtures\\windows\\auto-import-dev-windows\\input.js";
import { Fragment as _Fragment } from "react/jsx-dev-runtime";
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";
import { createElement as _createElement } from "react";
import { Fragment as _Fragment } from "react/jsx-dev-runtime";
var x = /*#__PURE__*/_jsxDEV(_Fragment, {
children: /*#__PURE__*/_jsxDEV("div", {
children: [/*#__PURE__*/_jsxDEV("div", {}, "1", false, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ class B extends A {
fileName: _jsxFileName,
lineNumber: 5,
columnNumber: 5
}, void 0);
});
super( /*#__PURE__*/_reactJsxDevRuntime.jsxDEV("sometag2", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 6,
columnNumber: 11
}, void 0));
}));
/*#__PURE__*/_reactJsxDevRuntime.jsxDEV("sometag3", {}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 7,
columnNumber: 5
}, void 0);
});
}
}
class C {
Expand Down Expand Up @@ -46,7 +46,7 @@ class E extends A {
fileName: _jsxFileName,
lineNumber: 29,
columnNumber: 20
}, void 0);
});
};
this.y = function () {
return /*#__PURE__*/_reactJsxDevRuntime.jsxDEV("sometag6", {}, void 0, false, {
Expand All @@ -67,7 +67,7 @@ class E extends A {
fileName: _jsxFileName,
lineNumber: 36,
columnNumber: 7
}, void 0);
});
}
super();
}
Expand All @@ -87,6 +87,6 @@ class G extends A {
fileName: _jsxFileName,
lineNumber: 49,
columnNumber: 12
}, void 0);
});
}
}
148 changes: 53 additions & 95 deletions packages/babel-plugin-transform-react-jsx/src/create-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import jsx from "@babel/plugin-syntax-jsx";
import { declare } from "@babel/helper-plugin-utils";
import { types as t } from "@babel/core";
import { template, types as t } from "@babel/core";
import type { PluginPass } from "@babel/core";
import type { NodePath, Scope, Visitor } from "@babel/traverse";
import { addNamed, addNamespace, isModule } from "@babel/helper-module-imports";
Expand All @@ -12,7 +12,6 @@ import type {
Identifier,
JSXAttribute,
JSXElement,
JSXFragment,
JSXOpeningElement,
JSXSpreadAttribute,
MemberExpression,
Expand Down Expand Up @@ -72,9 +71,6 @@ export default function createPlugin({

throwIfNamespace = true,

// TODO (Babel 8): It should throw if this option is used with the automatic runtime
filter,

runtime: RUNTIME_DEFAULT = process.env.BABEL_8_BREAKING
? "automatic"
: development
Expand Down Expand Up @@ -280,6 +276,26 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
// },
},

JSXFragment(path, file) {
// <>...</> -> <React.Fragment>...</React.Fragment>

const frag = memberExpressionToJSX(get(file, "id/fragment")());

path.replaceWith(
t.inherits(
t.jsxElement(
t.inherits(
t.jsxOpeningElement(frag, []),
path.node.openingFragment,
),
t.jsxClosingElement(t.cloneNode(frag)),
path.node.children,
),
path.node,
),
);
},

JSXElement: {
exit(path, file) {
let callExpr;
Expand All @@ -296,25 +312,12 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
},
},

JSXFragment: {
exit(path, file) {
let callExpr;
if (get(file, "runtime") === "classic") {
callExpr = buildCreateElementFragmentCall(path, file);
} else {
callExpr = buildJSXFragmentCall(path, file);
}

path.replaceWith(t.inherits(callExpr, path.node));
},
},

JSXAttribute(path) {
if (t.isJSXElement(path.node.value)) {
path.node.value = t.jsxExpressionContainer(path.node.value);
}
},
} as Visitor<PluginPass>,
},
};

// Returns whether the class has specified a superclass.
Expand Down Expand Up @@ -569,9 +572,13 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
args.push(
extracted.key ?? path.scope.buildUndefinedNode(),
t.booleanLiteral(children.length > 1),
extracted.__source ?? path.scope.buildUndefinedNode(),
extracted.__self ?? path.scope.buildUndefinedNode(),
);
if (extracted.__source) {
args.push(extracted.__source);
if (extracted.__self) args.push(extracted.__self);
} else if (extracted.__self) {
args.push(path.scope.buildUndefinedNode(), extracted.__self);
}
} else if (extracted.key !== undefined) {
args.push(extracted.key);
}
Expand All @@ -596,56 +603,6 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
return t.objectExpression(props);
}

// Builds JSX Fragment <></> into
// Production: React.jsx(type, arguments)
// Development: React.jsxDEV(type, { children })
function buildJSXFragmentCall(
path: NodePath<JSXFragment>,
file: PluginPass,
) {
const args = [get(file, "id/fragment")()];

const children = t.react.buildChildren(path.node);

args.push(
t.objectExpression(
children.length > 0
? [
buildChildrenProperty(
//@ts-expect-error The children here contains JSXSpreadChild,
// which will be thrown later
children,
),
]
: [],
),
);

if (development) {
args.push(
path.scope.buildUndefinedNode(),
t.booleanLiteral(children.length > 1),
);
}

return call(file, children.length > 1 ? "jsxs" : "jsx", args);
}

// Builds JSX Fragment <></> into
// React.createElement(React.Fragment, null, ...children)
function buildCreateElementFragmentCall(
path: NodePath<JSXFragment>,
file: PluginPass,
) {
if (filter && !filter(path.node, file)) return;

return call(file, "createElement", [
get(file, "id/fragment")(),
t.nullLiteral(),
...t.react.buildChildren(path.node),
]);
}

// Builds JSX into:
// Production: React.createElement(type, arguments, children)
// Development: React.createElement(type, arguments, children, source, self)
Expand Down Expand Up @@ -849,6 +806,22 @@ function toMemberExpression(id: string): Identifier | MemberExpression {
);
}

function memberExpressionToJSX(
expr: t.Node,
): t.JSXMemberExpression | t.JSXIdentifier {
switch (expr.type) {
case "Identifier":
return t.jsxIdentifier(expr.name);
case "MemberExpression":
return t.jsxMemberExpression(
memberExpressionToJSX(expr.object),
memberExpressionToJSX(expr.property) as t.JSXIdentifier,
);
default:
throw new Error("Internal error: unknown member expression type");
}
}

function makeSource(path: NodePath, state: PluginPass) {
const location = path.node.loc;
if (!location) {
Expand All @@ -861,13 +834,10 @@ function makeSource(path: NodePath, state: PluginPass) {
const { filename = "" } = state;

const fileNameIdentifier = path.scope.generateUidIdentifier("_jsxFileName");
const scope = path.hub.getScope();
if (scope) {
scope.push({
id: fileNameIdentifier,
init: t.stringLiteral(filename),
});
}
path.scope.getProgramParent().push({
id: fileNameIdentifier,
init: t.stringLiteral(filename),
});
// @ts-expect-error todo: avoid mutating PluginPass
state.fileNameIdentifier = fileNameIdentifier;
}
Expand All @@ -893,23 +863,11 @@ function makeTrace(
const fileColumnLiteral =
column0Based != null ? t.numericLiteral(column0Based + 1) : t.nullLiteral();

const fileNameProperty = t.objectProperty(
t.identifier("fileName"),
fileNameIdentifier,
);
const lineNumberProperty = t.objectProperty(
t.identifier("lineNumber"),
fileLineLiteral,
);
const columnNumberProperty = t.objectProperty(
t.identifier("columnNumber"),
fileColumnLiteral,
);
return t.objectExpression([
fileNameProperty,
lineNumberProperty,
columnNumberProperty,
]);
return template.expression.ast`{
fileName: ${fileNameIdentifier},
lineNumber: ${fileLineLiteral},
columnNumber: ${fileColumnLiteral},
}`;
}

function sourceSelfError(path: NodePath, name: string) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Fragment as _Fragment } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { createElement as _createElement } from "react";
import { jsxs as _jsxs } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
var x = /*#__PURE__*/_jsx(_Fragment, {
children: /*#__PURE__*/_jsxs("div", {
children: [/*#__PURE__*/_jsx("div", {}, "1"), /*#__PURE__*/_jsx("div", {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Fragment as _Fragment } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { createElement as _createElement } from "react";
import { jsxs as _jsxs } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
var x = /*#__PURE__*/_jsx(_Fragment, {
children: /*#__PURE__*/_jsxs("div", {
children: [/*#__PURE__*/_jsx("div", {}, "1"), /*#__PURE__*/_jsx("div", {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { jsx as _jsx } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
var x = /*#__PURE__*/_jsx(_Fragment, {
children: /*#__PURE__*/_jsx("div", {})
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { jsx as _jsx } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
/*#__PURE__*/_jsx("div", {
children: /*#__PURE__*/_jsxs(_Fragment, {
Expand Down

0 comments on commit ce37692

Please sign in to comment.