Skip to content

Commit

Permalink
fix: do not mark pure for computed React methods
Browse files Browse the repository at this point in the history
  • Loading branch information
JLHwung committed May 5, 2022
1 parent 97d1967 commit 4ea92cf
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 21 deletions.
43 changes: 22 additions & 21 deletions packages/babel-plugin-transform-react-pure-annotations/src/index.ts
@@ -1,16 +1,17 @@
import { declare } from "@babel/helper-plugin-utils";
import annotateAsPure from "@babel/helper-annotate-as-pure";
import { types as t } from "@babel/core";
import type { NodePath } from "@babel/traverse";

// Mapping of React top-level methods that are pure.
// This plugin adds a /*#__PURE__#/ annotation to calls to these methods,
// so that terser and other minifiers can safely remove them during dead
// code elimination.
// See https://reactjs.org/docs/react-api.html
const PURE_CALLS = new Map([
const PURE_CALLS: [string, Set<string>][] = [
[
"react",
[
new Set([
"cloneElement",
"createContext",
"createElement",
Expand All @@ -20,10 +21,10 @@ const PURE_CALLS = new Map([
"isValidElement",
"memo",
"lazy",
],
]),
],
["react-dom", ["createPortal"]],
]);
["react-dom", new Set(["createPortal"])],
];

export default declare(api => {
api.assertVersion(7);
Expand All @@ -40,14 +41,15 @@ export default declare(api => {
};
});

function isReactCall(path) {
function isReactCall(path: NodePath<t.CallExpression>) {
// If the callee is not a member expression, then check if it matches
// a named import, e.g. `import {forwardRef} from 'react'`.
if (!t.isMemberExpression(path.node.callee)) {
const callee = path.get("callee");
const calleePath = path.get("callee");
const callee = calleePath.node;
if (!t.isMemberExpression(callee)) {
for (const [module, methods] of PURE_CALLS) {
for (const method of methods) {
if (callee.referencesImport(module, method)) {
if (calleePath.referencesImport(module, method)) {
return true;
}
}
Expand All @@ -60,19 +62,18 @@ function isReactCall(path) {
// a default import (`import React from 'react'`) or namespace
// import (`import * as React from 'react'), and check if the
// property matches one of the pure methods.
for (const [module, methods] of PURE_CALLS) {
const object = path.get("callee.object");
if (
object.referencesImport(module, "default") ||
object.referencesImport(module, "*")
) {
for (const method of methods) {
if (t.isIdentifier(path.node.callee.property, { name: method })) {
return true;
}
const object = calleePath.get("object") as NodePath<
t.MemberExpression["object"]
>;
if (!callee.computed && t.isIdentifier(callee.property)) {
const propertyName = callee.property.name;
for (const [module, methods] of PURE_CALLS) {
if (
object.referencesImport(module, "default") ||
object.referencesImport(module, "*")
) {
return methods.has(propertyName);
}

return false;
}
}

Expand Down
@@ -0,0 +1,4 @@
import React from 'react';

var cloneElement, createElement;
React[cloneElement](React[createElement]('div'));
@@ -0,0 +1,4 @@
{
"sourceType": "module",
"plugins": ["transform-react-pure-annotations"]
}
@@ -0,0 +1,3 @@
import React from 'react';
var cloneElement, createElement;
React[cloneElement](React[createElement]('div'));

0 comments on commit 4ea92cf

Please sign in to comment.