Implement @babel/plugin-transform-react-pure-annotations (#11428)
The new plugin is also enabled in `@babel/preset-react`
This commit is contained in:
parent
93a50056ca
commit
6ba1f0dd22
@ -0,0 +1,3 @@
|
||||
src
|
||||
test
|
||||
*.log
|
||||
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@babel/plugin-transform-react-pure-annotations",
|
||||
"version": "7.9.4",
|
||||
"description": "Mark top-level React method calls as pure for tree shaking",
|
||||
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-pure",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"keywords": [
|
||||
"babel-plugin"
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.8.3",
|
||||
"@babel/helper-plugin-utils": "^7.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/helper-plugin-test-runner": "^7.8.3"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
import { declare } from "@babel/helper-plugin-utils";
|
||||
import annotateAsPure from "@babel/helper-annotate-as-pure";
|
||||
import { types as t } from "@babel/core";
|
||||
|
||||
// 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([
|
||||
[
|
||||
"react",
|
||||
[
|
||||
"cloneElement",
|
||||
"createElement",
|
||||
"createFactory",
|
||||
"createRef",
|
||||
"forwardRef",
|
||||
"isValidElement",
|
||||
"memo",
|
||||
"lazy",
|
||||
],
|
||||
],
|
||||
["react-dom", ["createPortal"]],
|
||||
]);
|
||||
|
||||
export default declare(api => {
|
||||
api.assertVersion(7);
|
||||
|
||||
return {
|
||||
name: "transform-react-pure-annotations",
|
||||
visitor: {
|
||||
CallExpression(path) {
|
||||
if (isReactCall(path)) {
|
||||
annotateAsPure(path);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
function isReactCall(path) {
|
||||
// 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");
|
||||
for (const [module, methods] of PURE_CALLS) {
|
||||
for (const method of methods) {
|
||||
if (callee.referencesImport(module, method)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, check if the member expression's object matches
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
const Portal = ReactDOM.createPortal(React.createElement('div'), document.getElementById('test'));
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"plugins": ["transform-react-pure-annotations"]
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
import * as React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
const Portal = /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement('div'), document.getElementById('test'));
|
||||
@ -0,0 +1,3 @@
|
||||
import React from 'react';
|
||||
|
||||
React.cloneElement(React.createElement('div'));
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"plugins": ["transform-react-pure-annotations"]
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
import React from 'react';
|
||||
|
||||
/*#__PURE__*/
|
||||
React.cloneElement( /*#__PURE__*/React.createElement('div'));
|
||||
@ -0,0 +1,3 @@
|
||||
import React from 'react';
|
||||
|
||||
React.createElement('div');
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"plugins": ["transform-react-pure-annotations"]
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
import React from 'react';
|
||||
|
||||
/*#__PURE__*/
|
||||
React.createElement('div');
|
||||
@ -0,0 +1,3 @@
|
||||
import {createFactory} from 'react';
|
||||
|
||||
const div = createFactory('div');
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"plugins": ["transform-react-pure-annotations"]
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
import { createFactory } from 'react';
|
||||
const div = /*#__PURE__*/createFactory('div');
|
||||
@ -0,0 +1,3 @@
|
||||
import React from 'react';
|
||||
|
||||
React.createRef();
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"plugins": ["transform-react-pure-annotations"]
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
import React from 'react';
|
||||
|
||||
/*#__PURE__*/
|
||||
React.createRef();
|
||||
@ -0,0 +1,3 @@
|
||||
import {forwardRef} from 'react';
|
||||
|
||||
const Comp = forwardRef((props, ref) => null);
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"plugins": ["transform-react-pure-annotations"]
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
import { forwardRef } from 'react';
|
||||
const Comp = /*#__PURE__*/forwardRef((props, ref) => null);
|
||||
@ -0,0 +1,3 @@
|
||||
import React from 'react';
|
||||
|
||||
const isElement = React.isValidElement(React.createElement('div'));
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"plugins": ["transform-react-pure-annotations"]
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
import React from 'react';
|
||||
const isElement = /*#__PURE__*/React.isValidElement( /*#__PURE__*/React.createElement('div'));
|
||||
3
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/lazy/input.js
vendored
Normal file
3
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/lazy/input.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import React from 'react';
|
||||
|
||||
const SomeComponent = React.lazy(() => import('./SomeComponent'));
|
||||
4
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/lazy/options.json
vendored
Normal file
4
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/lazy/options.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"plugins": ["transform-react-pure-annotations"]
|
||||
}
|
||||
2
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/lazy/output.mjs
vendored
Normal file
2
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/lazy/output.mjs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import React from 'react';
|
||||
const SomeComponent = /*#__PURE__*/React.lazy(() => import('./SomeComponent'));
|
||||
3
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/memo/input.js
vendored
Normal file
3
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/memo/input.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import React from 'react';
|
||||
|
||||
const Comp = React.memo((props) => null);
|
||||
4
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/memo/options.json
vendored
Normal file
4
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/memo/options.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"plugins": ["transform-react-pure-annotations"]
|
||||
}
|
||||
2
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/memo/output.mjs
vendored
Normal file
2
packages/babel-plugin-transform-react-pure-annotations/test/fixtures/react/memo/output.mjs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import React from 'react';
|
||||
const Comp = /*#__PURE__*/React.memo(props => null);
|
||||
@ -0,0 +1,3 @@
|
||||
import runner from "@babel/helper-plugin-test-runner";
|
||||
|
||||
runner(__dirname);
|
||||
@ -16,7 +16,8 @@
|
||||
"@babel/plugin-transform-react-jsx": "^7.9.4",
|
||||
"@babel/plugin-transform-react-jsx-development": "^7.9.0",
|
||||
"@babel/plugin-transform-react-jsx-self": "^7.9.0",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.9.0"
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.9.0",
|
||||
"@babel/plugin-transform-react-pure-annotations": "^7.9.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
|
||||
@ -4,6 +4,7 @@ import transformReactJSXDevelopment from "@babel/plugin-transform-react-jsx-deve
|
||||
import transformReactDisplayName from "@babel/plugin-transform-react-display-name";
|
||||
import transformReactJSXSource from "@babel/plugin-transform-react-jsx-source";
|
||||
import transformReactJSXSelf from "@babel/plugin-transform-react-jsx-self";
|
||||
import transformReactPure from "@babel/plugin-transform-react-pure-annotations";
|
||||
|
||||
export default declare((api, opts) => {
|
||||
api.assertVersion(7);
|
||||
@ -55,6 +56,7 @@ export default declare((api, opts) => {
|
||||
},
|
||||
],
|
||||
transformReactDisplayName,
|
||||
pure !== false && transformReactPure,
|
||||
|
||||
development && runtime === "classic" && transformReactJSXSource,
|
||||
development && runtime === "classic" && transformReactJSXSelf,
|
||||
|
||||
3
packages/babel-preset-react/test/fixtures/preset-options/pure-false/input.js
vendored
Normal file
3
packages/babel-preset-react/test/fixtures/preset-options/pure-false/input.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import {forwardRef} from 'react';
|
||||
|
||||
const Comp = forwardRef((props, ref) => null);
|
||||
4
packages/babel-preset-react/test/fixtures/preset-options/pure-false/options.json
vendored
Normal file
4
packages/babel-preset-react/test/fixtures/preset-options/pure-false/options.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"presets": [["react", {"pure": false}]]
|
||||
}
|
||||
2
packages/babel-preset-react/test/fixtures/preset-options/pure-false/output.mjs
vendored
Normal file
2
packages/babel-preset-react/test/fixtures/preset-options/pure-false/output.mjs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import { forwardRef } from 'react';
|
||||
const Comp = forwardRef((props, ref) => null);
|
||||
3
packages/babel-preset-react/test/fixtures/preset-options/pure/input.js
vendored
Normal file
3
packages/babel-preset-react/test/fixtures/preset-options/pure/input.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
import {forwardRef} from 'react';
|
||||
|
||||
const Comp = forwardRef((props, ref) => null);
|
||||
4
packages/babel-preset-react/test/fixtures/preset-options/pure/options.json
vendored
Normal file
4
packages/babel-preset-react/test/fixtures/preset-options/pure/options.json
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"sourceType": "module",
|
||||
"presets": ["react"]
|
||||
}
|
||||
2
packages/babel-preset-react/test/fixtures/preset-options/pure/output.mjs
vendored
Normal file
2
packages/babel-preset-react/test/fixtures/preset-options/pure/output.mjs
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import { forwardRef } from 'react';
|
||||
const Comp = /*#__PURE__*/forwardRef((props, ref) => null);
|
||||
Loading…
x
Reference in New Issue
Block a user