From efa1cc24279c97164eaabe7cbb9ee6241240d69c Mon Sep 17 00:00:00 2001 From: Sergii Diak Date: Wed, 13 Mar 2019 19:45:38 +0200 Subject: [PATCH] Add support for es5 transpiled react components --- src/index.js | 20 +++- src/isInheritedComponent.js | 26 +++++ src/isStatelessComponent.js | 11 +-- test/fixtures/es5-class-inheritance/actual.js | 62 ++++++++++++ .../expected-remove-es5.js | 96 +++++++++++++++++++ .../stateless-functional-components/actual.js | 8 ++ .../expected-remove-es5.js | 4 + .../expected-remove-es6.js | 4 + .../expected-wrap-es5.js | 8 ++ .../expected-wrap-es6.js | 8 ++ 10 files changed, 237 insertions(+), 10 deletions(-) create mode 100644 src/isInheritedComponent.js create mode 100644 test/fixtures/es5-class-inheritance/actual.js create mode 100644 test/fixtures/es5-class-inheritance/expected-remove-es5.js diff --git a/src/index.js b/src/index.js index 935310d..8412ae4 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ // console.log(generate(node).code); import isAnnotatedForRemoval from './isAnnotatedForRemoval' import isStatelessComponent from './isStatelessComponent' +import isInheritedComponent from './isInheritedComponent' import remove from './remove' function isPathReactClass(path, globalOptions) { @@ -284,15 +285,28 @@ export default function(api) { return } - if (binding.path.isClassDeclaration()) { - const superClass = binding.path.get('superClass') + let bindingPath = binding.path + + if (binding.value === null) { + bindingPath = + binding.constantViolations.find(p => { + return types.isAssignmentExpression(p) && !types.isNullLiteral(p.node.right) + }) || bindingPath + } + + if (bindingPath.isClassDeclaration()) { + const superClass = bindingPath.get('superClass') if (isReactClass(superClass, scope, globalOptions)) { path.traverse(collectNestedIdentifiers) removedPaths.add(path) remove(path, globalOptions, { type: 'assign' }) } - } else if (isStatelessComponent(binding.path)) { + } else if (isStatelessComponent(bindingPath)) { + path.traverse(collectNestedIdentifiers) + removedPaths.add(path) + remove(path, globalOptions, { type: 'assign' }) + } else if (isInheritedComponent(bindingPath, globalOptions)) { path.traverse(collectNestedIdentifiers) removedPaths.add(path) remove(path, globalOptions, { type: 'assign' }) diff --git a/src/isInheritedComponent.js b/src/isInheritedComponent.js new file mode 100644 index 0000000..b588c9d --- /dev/null +++ b/src/isInheritedComponent.js @@ -0,0 +1,26 @@ +const VALID_POSSIBLE_INHERITED_COMPONENT_TYPES = ['VariableDeclarator', 'AssignmentExpression'] + +export default function isInheritedComponent(path, { types }) { + if (VALID_POSSIBLE_INHERITED_COMPONENT_TYPES.indexOf(path.node.type) === -1) { + return false + } + let visited = false + path.traverse({ + CallExpression(path2) { + if (visited) { + return + } + if (types.isVariableDeclarator(path2.parent) || types.isAssignmentExpression(path2.parent)) { + const args = path2.node && path2.node.arguments + if (args) { + visited = args.some( + node => + node.property && + (node.property.name === 'Component' || node.property.name === 'PureComponent') + ) + } + } + }, + }) + return visited +} diff --git a/src/isStatelessComponent.js b/src/isStatelessComponent.js index ba376b8..9e54435 100644 --- a/src/isStatelessComponent.js +++ b/src/isStatelessComponent.js @@ -6,11 +6,12 @@ function isJSXElementOrReactCreateElement(path) { path.traverse({ CallExpression(path2) { const callee = path2.get('callee') - + const name = callee.node.name || (callee.node.property && callee.node.property.name) if ( callee.matchesPattern('React.createElement') || callee.matchesPattern('React.cloneElement') || - callee.node.name === 'cloneElement' + name === 'cloneElement' || + name === 'createElement' ) { visited = true } @@ -87,9 +88,5 @@ export default function isStatelessComponent(path) { return false } - if (isReturningJSXElement(path)) { - return true - } - - return false + return !!isReturningJSXElement(path) } diff --git a/test/fixtures/es5-class-inheritance/actual.js b/test/fixtures/es5-class-inheritance/actual.js new file mode 100644 index 0000000..4938b57 --- /dev/null +++ b/test/fixtures/es5-class-inheritance/actual.js @@ -0,0 +1,62 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _class, _temp; + +var _propTypes = require('prop-types'); + +var _propTypes2 = _interopRequireDefault(_propTypes); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } + +var SomeComponent = (_temp = _class = function (_React$Component) { + _inherits(SomeComponent, _React$Component); + + function SomeComponent(props) { + _classCallCheck(this, SomeComponent); + + var _this = _possibleConstructorReturn(this, (SomeComponent.__proto__ || Object.getPrototypeOf(SomeComponent)).call(this, props)); + + _this.state = {}; + + return _this; + } + + _createClass(SomeComponent, [{ + key: 'render', + value: function render() { + var el; + if (this.state.show) { + el = _react2.default.createElement( + 'div', + null, + null + ); + } + return el; + } + }]); + + return SomeComponent; +}(_react2.default.Component), _class.propTypes = { + prop: _propTypes2.default.bool +}, _class.defaultProps = { + prop: false +}, _temp); + +exports.default = SomeComponent; diff --git a/test/fixtures/es5-class-inheritance/expected-remove-es5.js b/test/fixtures/es5-class-inheritance/expected-remove-es5.js new file mode 100644 index 0000000..fa406db --- /dev/null +++ b/test/fixtures/es5-class-inheritance/expected-remove-es5.js @@ -0,0 +1,96 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _class, _temp; + +function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; +} + +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +} + +function _possibleConstructorReturn(self, call) { + if (!self) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + + return call && (babelHelpers.typeof(call) === "object" || typeof call === "function") ? call : self; +} + +function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function, not " + babelHelpers.typeof(superClass)); + } + + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; +} + +var SomeComponent = (_temp = _class = function (_React$Component) { + _inherits(SomeComponent, _React$Component); + + function SomeComponent(props) { + _classCallCheck(this, SomeComponent); + + var _this = _possibleConstructorReturn(this, (SomeComponent.__proto__ || Object.getPrototypeOf(SomeComponent)).call(this, props)); + + _this.state = {}; + return _this; + } + + _createClass(SomeComponent, [{ + key: 'render', + value: function render() { + var el; + + if (this.state.show) { + el = _react2.default.createElement('div', null, null); + } + + return el; + } + }]); + + return SomeComponent; +}(_react2.default.Component), _class.defaultProps = { + prop: false +}, _temp); +exports.default = SomeComponent; diff --git a/test/fixtures/stateless-functional-components/actual.js b/test/fixtures/stateless-functional-components/actual.js index 4fb46a9..b51fd14 100644 --- a/test/fixtures/stateless-functional-components/actual.js +++ b/test/fixtures/stateless-functional-components/actual.js @@ -123,3 +123,11 @@ function Foo12(props) { Foo12.propTypes = { foo: PropTypes.string, }; + +function Foo13(props) { + return _react2.default.createElement(props.children); +} + +Foo13.propTypes = { + foo: PropTypes.string +} diff --git a/test/fixtures/stateless-functional-components/expected-remove-es5.js b/test/fixtures/stateless-functional-components/expected-remove-es5.js index 85495ab..f75201c 100644 --- a/test/fixtures/stateless-functional-components/expected-remove-es5.js +++ b/test/fixtures/stateless-functional-components/expected-remove-es5.js @@ -77,3 +77,7 @@ var Foo11 = function Foo11() { function Foo12(props) { return React.cloneElement(props.children); } + +function Foo13(props) { + return _react2.default.createElement(props.children); +} diff --git a/test/fixtures/stateless-functional-components/expected-remove-es6.js b/test/fixtures/stateless-functional-components/expected-remove-es6.js index 6d5b0ca..d73016a 100644 --- a/test/fixtures/stateless-functional-components/expected-remove-es6.js +++ b/test/fixtures/stateless-functional-components/expected-remove-es6.js @@ -71,3 +71,7 @@ const Foo11 = () => true &&
; function Foo12(props) { return React.cloneElement(props.children); } + +function Foo13(props) { + return _react2.default.createElement(props.children); +} diff --git a/test/fixtures/stateless-functional-components/expected-wrap-es5.js b/test/fixtures/stateless-functional-components/expected-wrap-es5.js index 703786c..44f3275 100644 --- a/test/fixtures/stateless-functional-components/expected-wrap-es5.js +++ b/test/fixtures/stateless-functional-components/expected-wrap-es5.js @@ -125,3 +125,11 @@ function Foo12(props) { Foo12.propTypes = process.env.NODE_ENV !== "production" ? { foo: PropTypes.string } : {}; + +function Foo13(props) { + return _react2.default.createElement(props.children); +} + +Foo13.propTypes = process.env.NODE_ENV !== "production" ? { + foo: PropTypes.string +} : {}; diff --git a/test/fixtures/stateless-functional-components/expected-wrap-es6.js b/test/fixtures/stateless-functional-components/expected-wrap-es6.js index 36d7da9..7e94ff0 100644 --- a/test/fixtures/stateless-functional-components/expected-wrap-es6.js +++ b/test/fixtures/stateless-functional-components/expected-wrap-es6.js @@ -119,3 +119,11 @@ function Foo12(props) { Foo12.propTypes = process.env.NODE_ENV !== "production" ? { foo: PropTypes.string } : {}; + +function Foo13(props) { + return _react2.default.createElement(props.children); +} + +Foo13.propTypes = process.env.NODE_ENV !== "production" ? { + foo: PropTypes.string +} : {};