Skip to content

Commit

Permalink
Stop using old JSX transform
Browse files Browse the repository at this point in the history
  • Loading branch information
Andarist committed Nov 22, 2020
1 parent b36f988 commit 3abc2c0
Show file tree
Hide file tree
Showing 15 changed files with 183 additions and 251 deletions.
111 changes: 89 additions & 22 deletions packages/babel-helper-builder-react-jsx-experimental/src/index.js
Expand Up @@ -10,6 +10,29 @@ const DEFAULT = {
};

export function helper(babel, options) {
const { useSpread = true, useBuiltIns = false } = options;

if (typeof useSpread !== "boolean") {
throw new Error(
"transform-react-jsx currently only accepts a boolean option for " +
"useSpread (defaults to true)",
);
}

if (typeof useBuiltIns !== "boolean") {
throw new Error(
"transform-react-jsx currently only accepts a boolean option for " +
"useBuiltIns (defaults to false)",
);
}

if (useSpread && useBuiltIns) {
throw new Error(
"transform-react-jsx currently only accepts useBuiltIns or useSpread " +
"but not both",
);
}

const FILE_NAME_VAR = "_jsxFileName";

const JSX_SOURCE_ANNOTATION_REGEX = /\*?\s*@jsxImportSource\s+([^\s]+)/;
Expand Down Expand Up @@ -43,16 +66,16 @@ export function helper(babel, options) {
}
}

const source = t.jsxAttribute(
t.jsxIdentifier("__source"),
t.jsxExpressionContainer(makeSource(path, state)),
);
const self = t.jsxAttribute(
t.jsxIdentifier("__self"),
t.jsxExpressionContainer(t.thisExpression()),
);
const source = t.jsxAttribute(
t.jsxIdentifier("__source"),
t.jsxExpressionContainer(makeSource(path, state)),
);

path.pushContainer("attributes", [source, self]);
path.pushContainer("attributes", [self, source]);
},
};

Expand Down Expand Up @@ -246,19 +269,22 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
}
},

exit(path, state) {
if (
state.get("@babel/plugin-react-jsx/runtime") === "classic" &&
state.get("@babel/plugin-react-jsx/pragmaSet") &&
state.get("@babel/plugin-react-jsx/usedFragment") &&
!state.get("@babel/plugin-react-jsx/pragmaFragSet")
) {
throw new Error(
"transform-react-jsx: pragma has been set but " +
"pragmaFrag has not been set",
);
}
},
// TODO (Babel 8): Decide if this should be removed or brought back.
// see: https://github.com/babel/babel/pull/12253#discussion_r513086528
//
// exit(path, state) {
// if (
// state.get("@babel/plugin-react-jsx/runtime") === "classic" &&
// state.get("@babel/plugin-react-jsx/pragmaSet") &&
// state.get("@babel/plugin-react-jsx/usedFragment") &&
// !state.get("@babel/plugin-react-jsx/pragmaFragSet")
// ) {
// throw new Error(
// "transform-react-jsx: pragma has been set but " +
// "pragmaFrag has not been set",
// );
// }
// },
},
};

Expand Down Expand Up @@ -781,6 +807,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
}

const attribs = buildCreateElementOpeningElementAttributes(
file,
path,
openingPath.node.attributes,
);
Expand All @@ -804,8 +831,9 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
* breaking on spreads, we then push a new object containing
* all prior attributes to an array for later processing.
*/
function buildCreateElementOpeningElementAttributes(path, attribs) {
const props = [];
function buildCreateElementOpeningElementAttributes(file, path, attribs) {
let props = [];
const objs = [];
const found = Object.create(null);

for (const attr of attribs) {
Expand All @@ -820,10 +848,49 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
if (!options.development) continue;
}

props.push(convertAttribute(attr));
if (
RUNTIME_DEFAULT === "automatic" ||
useSpread ||
!t.isJSXSpreadAttribute(attr)
) {
props.push(convertAttribute(attr));
} else {
if (props.length) {
objs.push(t.objectExpression(props));
props = [];
}
objs.push(attr.argument);
}
}

if (!props.length && !objs.length) {
return t.nullLiteral();
}

return props.length > 0 ? t.objectExpression(props) : t.nullLiteral();
if (RUNTIME_DEFAULT === "automatic" || useSpread) {
return props.length > 0 ? t.objectExpression(props) : t.nullLiteral();
}

if (props.length) {
objs.push(t.objectExpression(props));
props = [];
}

if (objs.length === 1) {
return objs[0];
}

// looks like we have multiple objects
if (!t.isObjectExpression(objs[0])) {
objs.unshift(t.objectExpression([]));
}

const helper = useBuiltIns
? t.memberExpression(t.identifier("Object"), t.identifier("assign"))
: file.addHelper("extends");

// spread it
return t.callExpression(helper, objs);
}

function sourceSelfError(path, name) {
Expand Down
Expand Up @@ -21,12 +21,12 @@ var x = /*#__PURE__*/_jsxDEV(_Fragment, {
columnNumber: 7
}, this), /*#__PURE__*/_createElement("div", { ...props,
key: "4",
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 7,
columnNumber: 7
},
__self: this
}
})]
}, void 0, true, {
fileName: _jsxFileName,
Expand Down
@@ -1,42 +1,42 @@
var _jsxFileName = "<CWD>/packages/babel-plugin-transform-react-jsx-development/test/fixtures/linux/classic-runtime/input.js";
var x = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 3,
columnNumber: 5
},
__self: this
}
}, /*#__PURE__*/React.createElement("div", {
key: "1",
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 4,
columnNumber: 9
},
__self: this
}
}), /*#__PURE__*/React.createElement("div", {
key: "2",
meow: "wolf",
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 5,
columnNumber: 9
},
__self: this
}
}), /*#__PURE__*/React.createElement("div", {
key: "3",
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 6,
columnNumber: 9
},
__self: this
}
}), /*#__PURE__*/React.createElement("div", { ...props,
key: "4",
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 7,
columnNumber: 9
},
__self: this
}
})));
Expand Up @@ -21,12 +21,12 @@ var x = /*#__PURE__*/_jsxDEV(_Fragment, {
columnNumber: 7
}, this), /*#__PURE__*/_createElement("div", { ...props,
key: "4",
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 7,
columnNumber: 7
},
__self: this
}
})]
}, void 0, true, {
fileName: _jsxFileName,
Expand Down
@@ -1,42 +1,42 @@
var _jsxFileName = "<CWD>\\packages\\babel-plugin-transform-react-jsx-development\\test\\fixtures\\windows\\classic-runtime-windows\\input.js";
var x = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 3,
columnNumber: 5
},
__self: this
}
}, /*#__PURE__*/React.createElement("div", {
key: "1",
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 4,
columnNumber: 7
},
__self: this
}
}), /*#__PURE__*/React.createElement("div", {
key: "2",
meow: "wolf",
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 5,
columnNumber: 7
},
__self: this
}
}), /*#__PURE__*/React.createElement("div", {
key: "3",
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 6,
columnNumber: 7
},
__self: this
}
}), /*#__PURE__*/React.createElement("div", { ...props,
key: "4",
__self: this,
__source: {
fileName: _jsxFileName,
lineNumber: 7,
columnNumber: 7
},
__self: this
}
})));
73 changes: 62 additions & 11 deletions packages/babel-plugin-transform-react-jsx/src/index.js
@@ -1,18 +1,69 @@
/* eslint-disable-next-line @babel/development/plugin-name */
import transformClassic from "./transform-classic";
/* eslint-disable-next-line @babel/development/plugin-name */
import transformAutomatic from "./transform-automatic";
import jsx from "@babel/plugin-syntax-jsx";
import { helper } from "@babel/helper-builder-react-jsx-experimental";
import { declare } from "@babel/helper-plugin-utils";
import { types as t } from "@babel/core";

export default declare((api, options) => {
const { runtime = "classic" } = options;
const PURE_ANNOTATION = options.pure;

// we throw a warning in helper-builder-react-jsx-experimental if runtime
// is neither automatic or classic because we will remove this file
// in v8.0.0
if (runtime === "classic") {
return transformClassic(api, options);
} else {
return transformAutomatic(api, options);
// TODO (Babel 8): remove support for `useSpread` entirely
if (
runtime === "classic" &&
options.useSpread !== undefined &&
typeof options.useSpread !== "boolean"
) {
throw new Error(
"transform-react-jsx currently only accepts a boolean option for " +
"useSpread (defaults to false)",
);
}

const visitor = helper(api, {
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) {
if (pass.get("@babel/plugin-react-jsx/runtime") === "classic") {
state.createElementCallee = pass.get(
"@babel/plugin-react-jsx/createElementIdentifier",
)();

state.pure =
PURE_ANNOTATION ?? !pass.get("@babel/plugin-react-jsx/pragmaSet");
} else {
state.jsxCallee = pass.get("@babel/plugin-react-jsx/jsxIdentifier")();
state.jsxStaticCallee = pass.get(
"@babel/plugin-react-jsx/jsxStaticIdentifier",
)();
state.createElementCallee = pass.get(
"@babel/plugin-react-jsx/createElementIdentifier",
)();

state.pure =
PURE_ANNOTATION ??
!pass.get("@babel/plugin-react-jsx/importSourceSet");
}
},

...options,
development: false,
runtime,
// TODO (Babel 8): remove support for `useSpread` entirely
useSpread:
"useSpread" in options ? options.useSpread : runtime !== "classic",
});

return {
name: "transform-react-jsx",
inherits: jsx,
visitor,
};
});

0 comments on commit 3abc2c0

Please sign in to comment.