Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark transpiled JSX elements as pure #11126

Merged
merged 7 commits into from Mar 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/babel-generator/src/printer.js
Expand Up @@ -262,6 +262,8 @@ export default class Printer {
if (i + 1 === str.length) return;
const chaPost = str[i + 1];
if (chaPost !== "/" && chaPost !== "*") return;
// We don't print newlines aroung /*#__PURE__*/ annotations
if (PURE_ANNOTATION_RE.test(str.slice(i + 2, str.length - 2))) return;
}
this.token("(");
this.indent();
Expand Down
Expand Up @@ -9,6 +9,7 @@
},
"main": "lib/index.js",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.8.3",
"@babel/helper-module-imports": "^7.8.3",
"@babel/types": "^7.8.3",
"esutils": "^2.0.0"
Expand Down
64 changes: 50 additions & 14 deletions packages/babel-helper-builder-react-jsx-experimental/src/index.js
@@ -1,6 +1,14 @@
import esutils from "esutils";
import * as t from "@babel/types";
import { addNamed, addNamespace, isModule } from "@babel/helper-module-imports";
import annotateAsPure from "@babel/helper-annotate-as-pure";

const DEFAULT = {
importSource: "react",
runtime: "automatic",
pragma: "React.createElement",
pragmaFrag: "React.Fragment",
};

export function helper(babel, options) {
const FILE_NAME_VAR = "_jsxFileName";
Expand All @@ -17,10 +25,10 @@ export function helper(babel, options) {
const IMPORT_NAME_SIZE = options.development ? 3 : 4;

const {
importSource: IMPORT_SOURCE_DEFAULT = "react",
runtime: RUNTIME_DEFAULT = "automatic",
pragma: PRAGMA_DEFAULT = "React.createElement",
pragmaFrag: PRAGMA_FRAG_DEFAULT = "React.Fragment",
importSource: IMPORT_SOURCE_DEFAULT = DEFAULT.importSource,
runtime: RUNTIME_DEFAULT = DEFAULT.runtime,
pragma: PRAGMA_DEFAULT = DEFAULT.pragma,
pragmaFrag: PRAGMA_FRAG_DEFAULT = DEFAULT.pragmaFrag,
} = options;

return {
Expand Down Expand Up @@ -142,8 +150,14 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
createIdentifierParser(pragmaFrag),
);
state.set("@babel/plugin-react-jsx/usedFragment", false);
state.set("@babel/plugin-react-jsx/pragmaSet", pragmaSet);
state.set("@babel/plugin-react-jsx/pragmaFragSet", pragmaFragSet);
state.set(
"@babel/plugin-react-jsx/pragmaSet",
pragma !== DEFAULT.pragma,
);
state.set(
"@babel/plugin-react-jsx/pragmaFragSet",
pragmaFrag !== DEFAULT.pragmaFrag,
);
} else if (runtime === "automatic") {
if (pragmaSet || pragmaFragSet) {
throw path.buildCodeFrameError(
Expand Down Expand Up @@ -190,6 +204,11 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
createIdentifierName(path, "Fragment", importName),
),
);

state.set(
"@babel/plugin-react-jsx/importSourceSet",
source !== DEFAULT.importSource,
);
} else {
throw path.buildCodeFrameError(
`Runtime must be either "classic" or "automatic".`,
Expand Down Expand Up @@ -505,6 +524,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
tagExpr: tagExpr,
tagName: tagName,
args: args,
pure: false,
};

if (options.pre) {
Expand Down Expand Up @@ -577,13 +597,15 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
options.post(state, file);
}

return (
const call =
state.call ||
t.callExpression(
path.node.children.length > 1 ? state.jsxStaticCallee : state.jsxCallee,
args,
)
);
);
if (state.pure) annotateAsPure(call);

return call;
}

// Builds props for React.jsx. This function adds children into the props
Expand Down Expand Up @@ -633,6 +655,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
tagExpr: tagExpr,
tagName: tagName,
args: args,
pure: false,
};

if (options.pre) {
Expand Down Expand Up @@ -667,13 +690,15 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
options.post(state, file);
}

return (
const call =
state.call ||
t.callExpression(
path.node.children.length > 1 ? state.jsxStaticCallee : state.jsxCallee,
args,
)
);
);
if (state.pure) annotateAsPure(call);

return call;
}

function buildCreateElementFragmentCall(path, file) {
Expand All @@ -692,6 +717,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
tagExpr: tagExpr,
tagName: tagName,
args: args,
pure: false,
};

if (options.pre) {
Expand All @@ -706,7 +732,12 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
}

file.set("@babel/plugin-react-jsx/usedFragment", true);
return state.call || t.callExpression(state.createElementCallee, args);

const call =
state.call || t.callExpression(state.createElementCallee, args);
if (state.pure) annotateAsPure(call);

return call;
}

// Builds JSX into:
Expand All @@ -733,6 +764,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
tagExpr: tagExpr,
tagName: tagName,
args: args,
pure: false,
};

if (options.pre) {
Expand All @@ -751,7 +783,11 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
options.post(state, file);
}

return state.call || t.callExpression(state.createElementCallee, args);
const call =
state.call || t.callExpression(state.createElementCallee, args);
if (state.pure) annotateAsPure(call);

return call;
}

/**
Expand Down
1 change: 1 addition & 0 deletions packages/babel-helper-builder-react-jsx/package.json
Expand Up @@ -9,6 +9,7 @@
},
"main": "lib/index.js",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.8.3",
"@babel/types": "^7.8.3",
"esutils": "^2.0.0"
}
Expand Down
15 changes: 13 additions & 2 deletions packages/babel-helper-builder-react-jsx/src/index.js
@@ -1,11 +1,13 @@
import esutils from "esutils";
import * as t from "@babel/types";
import annotateAsPure from "@babel/helper-annotate-as-pure";

type ElementState = {
tagExpr: Object, // tag node
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
pure: boolean, // true if the element can be marked with a #__PURE__ annotation
};

export default function(opts) {
Expand Down Expand Up @@ -134,6 +136,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
tagExpr: tagExpr,
tagName: tagName,
args: args,
pure: false,
};

if (opts.pre) {
Expand All @@ -153,7 +156,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
opts.post(state, file);
}

return state.call || t.callExpression(state.callee, args);
const call = state.call || t.callExpression(state.callee, args);
if (state.pure) annotateAsPure(call);

return call;
}

function pushProps(_props, objs) {
Expand Down Expand Up @@ -248,6 +254,7 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
tagExpr: tagExpr,
tagName: tagName,
args: args,
pure: false,
};

if (opts.pre) {
Expand All @@ -262,6 +269,10 @@ You can set \`throwIfNamespace: false\` to bypass this warning.`,
}

file.set("usedFragment", true);
return state.call || t.callExpression(state.callee, args);

const call = state.call || t.callExpression(state.callee, args);
if (state.pure) annotateAsPure(call);

return call;
}
}
Expand Up @@ -2,5 +2,5 @@
var arr = [];

for (var i = 0; i < 4; ++i) {
arr.push(React.createElement("i", null));
arr.push( /*#__PURE__*/React.createElement("i", null));
}
Expand Up @@ -24,9 +24,9 @@ var RandomComponent = /*#__PURE__*/function (_Component) {
babelHelpers.createClass(RandomComponent, [{
key: "render",
value: function render() {
return _react["default"].createElement("div", {
return /*#__PURE__*/_react["default"].createElement("div", {
className: "sui-RandomComponent"
}, _react["default"].createElement("h2", null, "Hi there!"));
}, /*#__PURE__*/_react["default"].createElement("h2", null, "Hi there!"));
}
}]);
return RandomComponent;
Expand Down
Expand Up @@ -6,16 +6,15 @@ Object.defineProperty(exports, "__esModule", {
exports["default"] = _default;

function _default() {
return (/*#__PURE__*/function () {
function Select() {
babelHelpers.classCallCheck(this, Select);
}
return /*#__PURE__*/function () {
function Select() {
babelHelpers.classCallCheck(this, Select);
}

babelHelpers.createClass(Select, [{
key: "query",
value: function query(_query) {}
}]);
return Select;
}()
);
babelHelpers.createClass(Select, [{
key: "query",
value: function query(_query) {}
}]);
return Select;
}();
}
Expand Up @@ -7,7 +7,7 @@ exports["default"] = void 0;

var _default = function _default(_ref) {
var _onClick = _ref.onClick;
return React.createElement("div", {
return /*#__PURE__*/React.createElement("div", {
onClick: function onClick() {
return _onClick();
}
Expand Down
Expand Up @@ -12,7 +12,6 @@
"babel-plugin"
],
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.8.3",
"@babel/helper-plugin-utils": "^7.8.3"
},
"peerDependencies": {
Expand Down
@@ -1,6 +1,5 @@
import { declare } from "@babel/helper-plugin-utils";
import { types as t } from "@babel/core";
import annotateAsPure from "@babel/helper-annotate-as-pure";
existentialism marked this conversation as resolved.
Show resolved Hide resolved

export default declare((api, options) => {
api.assertVersion(7);
Expand Down Expand Up @@ -107,13 +106,7 @@ export default declare((api, options) => {
// Traverse all props passed to this element for immutability.
path.traverse(immutabilityVisitor, state);

if (state.isImmutable) {
const hoisted = path.hoist();

if (hoisted) {
annotateAsPure(hoisted);
}
}
if (state.isImmutable) path.hoist();
},
},
};
Expand Down
@@ -1,10 +1,10 @@
var _ref = /*#__PURE__*/<div>child</div>;
var _ref = <div>child</div>;

const AppItem = () => {
return _ref;
};

var _ref2 = /*#__PURE__*/<div>
var _ref2 = <div>
<p>Parent</p>
<AppItem />
</div>;
Expand Down
@@ -1,6 +1,6 @@
var _ref2 = /*#__PURE__*/<div>child</div>;
var _ref2 = <div>child</div>;

var _ref3 = /*#__PURE__*/<p>Parent</p>;
var _ref3 = <p>Parent</p>;

(function () {
class App extends React.Component {
Expand All @@ -13,7 +13,7 @@ var _ref3 = /*#__PURE__*/<p>Parent</p>;
const AppItem = () => {
return _ref2;
},
_ref = /*#__PURE__*/<div>
_ref = <div>
{_ref3}
<AppItem />
</div>;
Expand Down
@@ -1,13 +1,13 @@
var _ref = /*#__PURE__*/<div>child</div>;
var _ref = <div>child</div>;

var _ref3 = /*#__PURE__*/<p>Parent</p>;
var _ref3 = <p>Parent</p>;

(function () {
const AppItem = () => {
return _ref;
};

var _ref2 = /*#__PURE__*/<div>
var _ref2 = <div>
{_ref3}
<AppItem />
</div>;
Expand Down
Expand Up @@ -5,12 +5,12 @@ export default class App extends React.Component {

}

var _ref2 = /*#__PURE__*/<div>child</div>;
var _ref2 = <div>child</div>;

const AppItem = () => {
return _ref2;
},
_ref = /*#__PURE__*/<div>
_ref = <div>
<p>Parent</p>
<AppItem />
</div>;
@@ -1,4 +1,4 @@
var _ref = /*#__PURE__*/<span />;
var _ref = <span />;

var Foo = React.createClass({
render: function () {
Expand Down
@@ -1,6 +1,6 @@
import React from 'react'; // Regression test for https://github.com/babel/babel/issues/5552

var _ref = /*#__PURE__*/<div />;
var _ref = <div />;

class BugReport extends React.Component {
constructor(...args) {
Expand Down