diff --git a/.eslintignore b/.eslintignore index c20a6aa20e..c8354bc782 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,9 +11,4 @@ docs/vendor/bundle/ examples/ # Ignore built files. build/ -# react-codemod -packages/react-codemod/test/ -packages/react-codemod/scripts/ -packages/react-codemod/build/ -packages/react-codemod/node_modules/ vendor/react-dom.js diff --git a/docs/_posts/2015-09-10-react-v0.14-rc1.md b/docs/_posts/2015-09-10-react-v0.14-rc1.md index a1ac0ef44e..426ee70e3c 100644 --- a/docs/_posts/2015-09-10-react-v0.14-rc1.md +++ b/docs/_posts/2015-09-10-react-v0.14-rc1.md @@ -57,7 +57,7 @@ These builds are also available in the `react` package on bower. ReactDOM.render(, node); ``` - We’ve published the [automated codemod script](https://github.com/facebook/react/blob/master/packages/react-codemod/README.md) we used at Facebook to help you with this transition. + We’ve published the [automated codemod script](https://github.com/reactjs/react-codemod/blob/master/README.md) we used at Facebook to help you with this transition. The add-ons have moved to separate packages as well: `react-addons-clone-with-props`, `react-addons-create-fragment`, `react-addons-css-transition-group`, `react-addons-linked-state-mixin`, `react-addons-perf`, `react-addons-pure-render-mixin`, `react-addons-shallow-compare`, `react-addons-test-utils`, `react-addons-transition-group`, and `react-addons-update`, plus `ReactDOM.unstable_batchedUpdates` in `react-dom`. diff --git a/docs/_posts/2015-10-07-react-v0.14.md b/docs/_posts/2015-10-07-react-v0.14.md index e95330bcf6..3a8827495c 100644 --- a/docs/_posts/2015-10-07-react-v0.14.md +++ b/docs/_posts/2015-10-07-react-v0.14.md @@ -15,7 +15,7 @@ Like always, we have a few breaking changes in this release. We know changes can If your code is free of warnings when running under React 0.13, upgrading should be easy. We have two new small breaking changes that didn't give a warning in 0.13 (see below). Every new change in 0.14, including the major changes below, is introduced with a runtime warning and will work as before until 0.15, so you don't have to worry about your app breaking with this upgrade. -For the two major changes which require significant code changes, we've included [codemod scripts](https://github.com/facebook/react/blob/master/packages/react-codemod/README.md) to help you upgrade your code automatically. +For the two major changes which require significant code changes, we've included [codemod scripts](https://github.com/reactjs/react-codemod/blob/master/README.md) to help you upgrade your code automatically. See the changelog below for more details. @@ -66,7 +66,7 @@ If you can’t use `npm` yet, we provide pre-built browser builds for your conve ReactDOM.render(, node); ``` - The old names will continue to work with a warning until 0.15 is released, and we’ve published the [automated codemod script](https://github.com/facebook/react/blob/master/packages/react-codemod/README.md) we used at Facebook to help you with this transition. + The old names will continue to work with a warning until 0.15 is released, and we’ve published the [automated codemod script](https://github.com/reactjs/react-codemod/blob/master/README.md) we used at Facebook to help you with this transition. The add-ons have moved to separate packages as well: - `react-addons-clone-with-props` @@ -161,7 +161,7 @@ Each of these changes will continue to work as before with a new warning until t - Due to the DOM node refs change mentioned above, `this.getDOMNode()` is now deprecated and `ReactDOM.findDOMNode(this)` can be used instead. Note that in most cases, calling `findDOMNode` is now unnecessary – see the example above in the “DOM node refs” section. - With each returned DOM node, we've added a `getDOMNode` method for backwards compatibility that will work with a warning until 0.15. If you have a large codebase, you can use our [automated codemod script](https://github.com/facebook/react/blob/master/packages/react-codemod/README.md) to change your code automatically. + With each returned DOM node, we've added a `getDOMNode` method for backwards compatibility that will work with a warning until 0.15. If you have a large codebase, you can use our [automated codemod script](https://github.com/reactjs/react-codemod/blob/master/README.md) to change your code automatically. - `setProps` and `replaceProps` are now deprecated. Instead, call ReactDOM.render again at the top level with the new props. - ES6 component classes must now extend `React.Component` in order to enable stateless function components. The [ES3 module pattern](/react/blog/2015/01/27/react-v0.13.0-beta-1.html#other-languages) will continue to work. diff --git a/packages/react-codemod/README.md b/packages/react-codemod/README.md deleted file mode 100644 index 71cb97903f..0000000000 --- a/packages/react-codemod/README.md +++ /dev/null @@ -1,111 +0,0 @@ -## react-codemod - -This repository contains a collection of codemod scripts based for use with -[JSCodeshift](https://github.com/facebook/jscodeshift) that help update React -APIs. - -### Setup & Run - - * `npm install -g jscodeshift` - * `git clone https://github.com/facebook/react.git` or download a zip file - from `https://github.com/facebook/react/archive/master.zip` - * `jscodeshift -t ` - * Use the `-d` option for a dry-run and use `-p` to print the output - for comparison - -### Included Scripts - -`findDOMNode` updates `this.getDOMNode()` or `this.refs.foo.getDOMNode()` -calls inside of `React.createClass` components to `React.findDOMNode(foo)`. Note -that it will only look at code inside of `React.createClass` calls and only -update calls on the component instance or its refs. You can use this script to -update most calls to `getDOMNode` and then manually go through the remaining -calls. - - * `jscodeshift -t react/packages/react-codemod/transforms/findDOMNode.js ` - -`react-to-react-dom` updates code for the split of the `react` and `react-dom` -packages (e.g., `React.render` to `ReactDOM.render`). It looks for -`require('react')` and replaces the appropriate property accesses using -`require('react-dom')`. It does not support ES6 modules or other non-CommonJS -systems. We recommend performing the `findDOMNode` conversion first. - - * `jscodeshift -t react/packages/react-codemod/transforms/react-to-react-dom.js ` - * After running the automated codemod, you may want to run a regex-based find-and-replace to remove extra whitespace between the added requires, such as `codemod.py -m -d src --extensions js '(var React\s*=\s*require\(.react.\);)\n\n(\s*var ReactDOM)' '\1\n\2'` using https://github.com/facebook/codemod. - -`pure-render-mixin` removes `PureRenderMixin` and inlines -`shouldComponentUpdate` so that the ES2015 class transform can pick up the React -component and turn it into an ES2015 class. NOTE: This currently only works if you -are using the master version (>0.13.1) of React as it is using -`React.addons.shallowCompare` - - * `jscodeshift -t react/packages/react-codemod/transforms/pure-render-mixin.js ` - * If `--mixin-name=` is specified it will look for the specified name - instead of `PureRenderMixin`. Note that it is not possible to use a - namespaced name for the mixin. `mixins: [React.addons.PureRenderMixin]` will - not currently work. - -`class` transforms `React.createClass` calls into ES2015 classes. - - * `jscodeshift -t react/packages/react-codemod/transforms/class.js ` - * If `--no-super-class` is specified it will not extend - `React.Component` if `setState` and `forceUpdate` aren't being called in a - class. We do recommend always extending from `React.Component`, especially - if you are using or planning to use [Flow](http://flowtype.org/). Also make - sure you are not calling `setState` anywhere outside of your component. - -These three scripts take an option `--no-explicit-require` if you don't have a -`require('React')` statement in your code files and if you access React as a -global. - -### Explanation of the ES2015 class transform - - * Ignore components with calls to deprecated APIs. This is very defensive, if - the script finds any identifiers called `isMounted`, `getDOMNode`, - `replaceProps`, `replaceState` or `setProps` it will skip the component. - * Replaces `var A = React.createClass(spec)` with - `class A (extends React.Component) {spec}`. - * Pulls out all statics defined on `statics` plus the few special cased - statics like `propTypes`, `childContextTypes`, `contextTypes` and - `displayName` and assigns them after the class is created. - `class A {}; A.foo = bar;` - * Takes `getDefaultProps` and inlines it as a static `defaultProps`. - If `getDefaultProps` is defined as a function with a single statement that - returns an object, it optimizes and transforms - `getDefaultProps() { return {foo: 'bar'}; }` into - `A.defaultProps = {foo: 'bar'};`. If `getDefaultProps` contains more than - one statement it will transform into a self-invoking function like this: - `A.defaultProps = function() {…}();`. Note that this means that the function - will be executed only a single time per app-lifetime. In practice this - hasn't caused any issues – `getDefaultProps` should not contain any - side-effects. - * Binds class methods to the instance if methods are referenced without being - called directly. It checks for `this.foo` but also traces variable - assignments like `var self = this; self.foo`. It does not bind functions - from the React API and ignores functions that are being called directly - (unless it is both called directly and passed around to somewhere else) - * Creates a constructor if necessary. This is necessary if either - `getInitialState` exists in the `React.createClass` spec OR if functions - need to be bound to the instance. - * When `--no-super-class` is passed it only optionally extends - `React.Component` when `setState` or `forceUpdate` are used within the - class. - -The constructor logic is as follows: - * Call `super(props, context)` if the base class needs to be extended. - * Bind all functions that are passed around, - like `this.foo = this.foo.bind(this)` - * Inline `getInitialState` (and remove `getInitialState` from the spec). It - also updates access of `this.props.foo` to `props.foo` and adds `props` as - argument to the constructor. This is necessary in the case when the base - class does not need to be extended where `this.props` will only be set by - React after the constructor has been run. - * Changes `return StateObject` from `getInitialState` to assign `this.state` - directly. - -### Recast Options - -Options to [recast](https://github.com/benjamn/recast)'s printer can be provided -through the `printOptions` command line argument - - * `jscodeshift -t transform.js --printOptions='{"quote":"double"}'` diff --git a/packages/react-codemod/package.json b/packages/react-codemod/package.json deleted file mode 100644 index 069768edee..0000000000 --- a/packages/react-codemod/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "react-codemod", - "version": "3.0.0", - "description": "React codemod scripts", - "license": "BSD-3-Clause", - "repository": { - "type": "git", - "url": "https://github.com/facebook/react" - }, - "scripts": { - "test": "jest" - }, - "dependencies": { - "jscodeshift": "^0.3.7" - }, - "devDependencies": { - "babel-jest": "^5.3.0", - "jest-cli": "^0.5.1" - }, - "jest": { - "scriptPreprocessor": "./node_modules/babel-jest", - "testPathDirs": [ - "test" - ] - } -} diff --git a/packages/react-codemod/test/__tests__/transform-tests.js b/packages/react-codemod/test/__tests__/transform-tests.js deleted file mode 100644 index cc5c90753b..0000000000 --- a/packages/react-codemod/test/__tests__/transform-tests.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -"use strict"; - -jest.autoMockOff(); - -var fs = require('fs'); -var jscodeshift = require('jscodeshift'); - -function read(fileName) { - return fs.readFileSync(__dirname + '/../' + fileName, 'utf8'); -} - -function test(transformName, testFileName, options) { - var path = testFileName + '.js'; - var source = read(testFileName + '.js'); - var output = read(testFileName + '.output.js'); - - var transform = require('../../transforms/' + transformName); - expect( - (transform({path, source}, {jscodeshift}, options || {}) || '').trim() - ).toEqual( - output.trim() - ); -} - -describe('Transform Tests', () => { - - it('transforms the "findDOMNode" tests correctly', () => { - test('findDOMNode', 'findDOMNode-test'); - }); - - it('transforms the "pure-render-mixin" tests correctly', () => { - test('pure-render-mixin', 'pure-render-mixin-test'); - - test('pure-render-mixin', 'pure-render-mixin-test2'); - - test('pure-render-mixin', 'pure-render-mixin-test3'); - - test('pure-render-mixin', 'pure-render-mixin-test4', { - 'mixin-name': 'ReactComponentWithPureRenderMixin', - }); - }); - - it('transforms the "class" tests correctly', () => { - test('class', 'class-test'); - - test('class', 'class-test2', { - 'super-class': false - }); - - test('class', 'class-test3'); - - }); - - it('transforms exports class', () => { - test('class', 'export-default-class-test'); - }); - -}); diff --git a/packages/react-codemod/test/class-test.js b/packages/react-codemod/test/class-test.js deleted file mode 100644 index fd9533c15f..0000000000 --- a/packages/react-codemod/test/class-test.js +++ /dev/null @@ -1,133 +0,0 @@ -'use strict'; - -var React = require('React'); -var Relay = require('Relay'); - -var Image = require('Image.react'); - -/* - * Multiline - */ -var MyComponent = React.createClass({ - getInitialState: function() { - var x = this.props.foo; - return { - heyoo: 23, - }; - }, - - foo: function() { - this.setState({heyoo: 24}); - } -}); - -// Class comment -var MyComponent2 = React.createClass({ - getDefaultProps: function() { - return {a: 1}; - }, - foo: function() { - pass(this.foo); - this.forceUpdate(); - } -}); - -var MyComponent3 = React.createClass({ - statics: { - someThing: 10, - foo: function() {}, - }, - propTypes: { - highlightEntities: React.PropTypes.bool, - linkifyEntities: React.PropTypes.bool, - text: React.PropTypes.shape({ - text: React.PropTypes.string, - ranges: React.PropTypes.array - }).isRequired - }, - - getDefaultProps: function() { - foo(); - return { - linkifyEntities: true, - highlightEntities: false - }; - }, - - getInitialState: function() { - this.props.foo(); - return { - heyoo: 23, - }; - }, - - _renderText: function(text) { - return ; - }, - - _renderImageRange: function(text, range) { - var image = range.image; - if (image) { - return ( - - ); - } - }, - - autobindMe: function() {}, - dontAutobindMe: function() {}, - - // Function comment - _renderRange: function(text, range) { - var self = this; - - self.dontAutobindMe(); - call(self.autobindMe); - - var type = rage.type; - var {highlightEntities} = this.props; - - if (type === 'ImageAtRange') { - return this._renderImageRange(text, range); - } - - if (this.props.linkifyEntities) { - text = - - {text} - ; - } else { - text = {text}; - } - - return text; - }, - - /* This is a comment */ - render: function() { - var content = this.props.text; - return ( - - ); - } -}); - -var MyComponent4 = React.createClass({ - foo: callMeMaybe(), - render: function() {}, -}); - -module.exports = Relay.createContainer(MyComponent, { - queries: { - me: Relay.graphql`this is not graphql`, - } -}); diff --git a/packages/react-codemod/test/class-test.output.js b/packages/react-codemod/test/class-test.output.js deleted file mode 100644 index 5862308127..0000000000 --- a/packages/react-codemod/test/class-test.output.js +++ /dev/null @@ -1,144 +0,0 @@ -'use strict'; - -var React = require('React'); -var Relay = require('Relay'); - -var Image = require('Image.react'); - -/* - * Multiline - */ -class MyComponent extends React.Component { - constructor(props, context) { - super(props, context); - var x = props.foo; - - this.state = { - heyoo: 23, - }; - } - - foo() { - this.setState({heyoo: 24}); - } -} - -// Class comment -class MyComponent2 extends React.Component { - constructor(props, context) { - super(props, context); - this.foo = this.foo.bind(this); - } - - foo() { - pass(this.foo); - this.forceUpdate(); - } -} - -MyComponent2.defaultProps = {a: 1}; - -class MyComponent3 extends React.Component { - constructor(props, context) { - super(props, context); - this._renderRange = this._renderRange.bind(this); - this._renderText = this._renderText.bind(this); - this.autobindMe = this.autobindMe.bind(this); - props.foo(); - - this.state = { - heyoo: 23, - }; - } - - _renderText(text) { - return ; - } - - _renderImageRange(text, range) { - var image = range.image; - if (image) { - return ( - - ); - } - } - - autobindMe() {} - dontAutobindMe() {} - - // Function comment - _renderRange(text, range) { - var self = this; - - self.dontAutobindMe(); - call(self.autobindMe); - - var type = rage.type; - var {highlightEntities} = this.props; - - if (type === 'ImageAtRange') { - return this._renderImageRange(text, range); - } - - if (this.props.linkifyEntities) { - text = - - {text} - ; - } else { - text = {text}; - } - - return text; - } - - /* This is a comment */ - render() { - var content = this.props.text; - return ( - - ); - } -} - -MyComponent3.defaultProps = function() { - foo(); - return { - linkifyEntities: true, - highlightEntities: false - }; -}(); - -MyComponent3.foo = function() {}; - -MyComponent3.propTypes = { - highlightEntities: React.PropTypes.bool, - linkifyEntities: React.PropTypes.bool, - text: React.PropTypes.shape({ - text: React.PropTypes.string, - ranges: React.PropTypes.array - }).isRequired -}; - -MyComponent3.someThing = 10; - -var MyComponent4 = React.createClass({ - foo: callMeMaybe(), - render: function() {}, -}); - -module.exports = Relay.createContainer(MyComponent, { - queries: { - me: Relay.graphql`this is not graphql`, - } -}); diff --git a/packages/react-codemod/test/class-test2.js b/packages/react-codemod/test/class-test2.js deleted file mode 100644 index 7773f3cd29..0000000000 --- a/packages/react-codemod/test/class-test2.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -var React = require('React'); - -var IdontNeedAParent = React.createClass({ - render: function() { - return
; - } -}); - -var ButIDo = React.createClass({ - foo: function() { - this.setState({banana: '?'}); - }, - - render: function() { - return
; - } -}); - -var IAccessProps = React.createClass({ - - getInitialState: function() { - return { - relayReleaseDate: this.props.soon, - }; - }, - - render: function() { - return - } - -}); diff --git a/packages/react-codemod/test/class-test2.output.js b/packages/react-codemod/test/class-test2.output.js deleted file mode 100644 index 9021d6ffa7..0000000000 --- a/packages/react-codemod/test/class-test2.output.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -var React = require('React'); - -class IdontNeedAParent { - render() { - return
; - } -} - -class ButIDo extends React.Component { - foo() { - this.setState({banana: '?'}); - } - - render() { - return
; - } -} - -class IAccessProps { - constructor(props) { - this.state = { - relayReleaseDate: props.soon, - }; - } - - render() { - return - } -} diff --git a/packages/react-codemod/test/class-test3.js b/packages/react-codemod/test/class-test3.js deleted file mode 100644 index ba03cf8dbd..0000000000 --- a/packages/react-codemod/test/class-test3.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -var React = require('React'); - -// Comment -module.exports = React.createClass({ - propTypes: { - foo: React.PropTypes.bool, - }, - - getInitialState: function() { - return { - foo: 'bar', - }; - }, - - render: function() { - return
; - } -}); diff --git a/packages/react-codemod/test/class-test3.output.js b/packages/react-codemod/test/class-test3.output.js deleted file mode 100644 index 68803e4196..0000000000 --- a/packages/react-codemod/test/class-test3.output.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -var React = require('React'); - -// Comment -module.exports = class extends React.Component { - constructor(props, context) { - super(props, context); - - this.state = { - foo: 'bar', - }; - } - - render() { - return
; - } -}; - -module.exports.propTypes = { - foo: React.PropTypes.bool, -}; diff --git a/packages/react-codemod/test/export-default-class-test.js b/packages/react-codemod/test/export-default-class-test.js deleted file mode 100644 index 676bfc51c7..0000000000 --- a/packages/react-codemod/test/export-default-class-test.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -import React from 'React'; - -export default React.createClass({ - getInitialState: function() { - return { - foo: 'bar', - }; - }, - - propTypes: { - foo: React.PropTypes.string, - }, - - render: function() { - return
; - } -}); diff --git a/packages/react-codemod/test/export-default-class-test.output.js b/packages/react-codemod/test/export-default-class-test.output.js deleted file mode 100644 index 26e4b8107a..0000000000 --- a/packages/react-codemod/test/export-default-class-test.output.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -import React from 'React'; - -export default class extends React.Component { - constructor(props, context) { - super(props, context); - - this.state = { - foo: 'bar', - }; - } - - static propTypes = { - foo: React.PropTypes.string, - }; - - render() { - return
; - } -} diff --git a/packages/react-codemod/test/findDOMNode-test.js b/packages/react-codemod/test/findDOMNode-test.js deleted file mode 100644 index fbdb0a95f6..0000000000 --- a/packages/react-codemod/test/findDOMNode-test.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -var React = require('React'); - -var Composer = React.createClass({ - componentWillReceiveProps: function(nextProps) { - this.getDOMNode(); - return foo(this.refs.input.getDOMNode()); - }, - - foo: function() { - var ref = 'foo'; - var element = this.refs[ref]; - var domNode = element.getDOMNode(); - }, - - bar: function() { - var thing = this.refs.foo; - thing.getDOMNode(); - }, - - foobar: function() { - passThisOn(this.refs.main.refs.list.getDOMNode()); - } -}); - -var SomeDialog = React.createClass({ - render: function() { - call(this.refs.SomeThing); - return ( -
- ); - } -}); diff --git a/packages/react-codemod/test/findDOMNode-test.output.js b/packages/react-codemod/test/findDOMNode-test.output.js deleted file mode 100644 index 45bfca5bbb..0000000000 --- a/packages/react-codemod/test/findDOMNode-test.output.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -var React = require('React'); - -var Composer = React.createClass({ - componentWillReceiveProps: function(nextProps) { - React.findDOMNode(this); - return foo(React.findDOMNode(this.refs.input)); - }, - - foo: function() { - var ref = 'foo'; - var element = this.refs[ref]; - var domNode = React.findDOMNode(element); - }, - - bar: function() { - var thing = this.refs.foo; - React.findDOMNode(thing); - }, - - foobar: function() { - passThisOn(React.findDOMNode(this.refs.main.refs.list)); - } -}); - -var SomeDialog = React.createClass({ - render: function() { - call(this.refs.SomeThing); - return ( -
- ); - } -}); diff --git a/packages/react-codemod/test/pure-render-mixin-test.js b/packages/react-codemod/test/pure-render-mixin-test.js deleted file mode 100644 index dcb374328f..0000000000 --- a/packages/react-codemod/test/pure-render-mixin-test.js +++ /dev/null @@ -1,45 +0,0 @@ -var React = require('react/addons'); - -var PureRenderMixin = React.addons.PureRenderMixin; - -var MyComponent = React.createClass({ - mixins: [PureRenderMixin], - - render: function() { - return
; - } -}); - -var MyMixedComponent = React.createClass({ - mixins: [PureRenderMixin, SomeOtherMixin], - - render: function() { - return
; - } -}); - -var MyFooComponent = React.createClass({ - mixins: [PureRenderMixin, SomeOtherMixin], - - render: function() { - return
; - }, - - foo: function() { - - } -}); - -var MyStupidComponent = React.createClass({ - mixins: [PureRenderMixin], - - shouldComponentUpdate: function() { - return !!'wtf is this doing here?'; - }, - - render: function() { - return
; - } -}); - -module.exports = MyComponent; diff --git a/packages/react-codemod/test/pure-render-mixin-test.output.js b/packages/react-codemod/test/pure-render-mixin-test.output.js deleted file mode 100644 index 31fdec6c87..0000000000 --- a/packages/react-codemod/test/pure-render-mixin-test.output.js +++ /dev/null @@ -1,55 +0,0 @@ -var React = require('react/addons'); - -var PureRenderMixin = React.addons.PureRenderMixin; - -var MyComponent = React.createClass({ - shouldComponentUpdate: function(nextProps, nextState) { - return React.addons.shallowCompare(this, nextProps, nextState); - }, - - render: function() { - return
; - } -}); - -var MyMixedComponent = React.createClass({ - mixins: [SomeOtherMixin], - - shouldComponentUpdate: function(nextProps, nextState) { - return React.addons.shallowCompare(this, nextProps, nextState); - }, - - render: function() { - return
; - } -}); - -var MyFooComponent = React.createClass({ - mixins: [SomeOtherMixin], - - render: function() { - return
; - }, - - foo: function() { - - }, - - shouldComponentUpdate: function(nextProps, nextState) { - return React.addons.shallowCompare(this, nextProps, nextState); - } -}); - -var MyStupidComponent = React.createClass({ - mixins: [PureRenderMixin], - - shouldComponentUpdate: function() { - return !!'wtf is this doing here?'; - }, - - render: function() { - return
; - } -}); - -module.exports = MyComponent; diff --git a/packages/react-codemod/test/pure-render-mixin-test2.js b/packages/react-codemod/test/pure-render-mixin-test2.js deleted file mode 100644 index e5acfdabe5..0000000000 --- a/packages/react-codemod/test/pure-render-mixin-test2.js +++ /dev/null @@ -1,13 +0,0 @@ -var React = require('react/addons'); - -var PureRenderMixin = React.addons.PureRenderMixin; - -var MyComponent = React.createClass({ - mixins: [PureRenderMixin], - - render: function() { - return
; - } -}); - -module.exports = MyComponent; diff --git a/packages/react-codemod/test/pure-render-mixin-test2.output.js b/packages/react-codemod/test/pure-render-mixin-test2.output.js deleted file mode 100644 index 90d0a78208..0000000000 --- a/packages/react-codemod/test/pure-render-mixin-test2.output.js +++ /dev/null @@ -1,13 +0,0 @@ -var React = require('react/addons'); - -var MyComponent = React.createClass({ - shouldComponentUpdate: function(nextProps, nextState) { - return React.addons.shallowCompare(this, nextProps, nextState); - }, - - render: function() { - return
; - } -}); - -module.exports = MyComponent; diff --git a/packages/react-codemod/test/pure-render-mixin-test3.js b/packages/react-codemod/test/pure-render-mixin-test3.js deleted file mode 100644 index 4044f4779c..0000000000 --- a/packages/react-codemod/test/pure-render-mixin-test3.js +++ /dev/null @@ -1,14 +0,0 @@ -var React = require('react/addons'); - -var Foo = 'Foo'; -var PureRenderMixin = React.addons.PureRenderMixin; - -var MyComponent = React.createClass({ - mixins: [PureRenderMixin], - - render: function() { - return
; - } -}); - -module.exports = MyComponent; diff --git a/packages/react-codemod/test/pure-render-mixin-test3.output.js b/packages/react-codemod/test/pure-render-mixin-test3.output.js deleted file mode 100644 index 37cc2fe1d3..0000000000 --- a/packages/react-codemod/test/pure-render-mixin-test3.output.js +++ /dev/null @@ -1,15 +0,0 @@ -var React = require('react/addons'); - -var Foo = 'Foo'; - -var MyComponent = React.createClass({ - shouldComponentUpdate: function(nextProps, nextState) { - return React.addons.shallowCompare(this, nextProps, nextState); - }, - - render: function() { - return
; - } -}); - -module.exports = MyComponent; diff --git a/packages/react-codemod/test/pure-render-mixin-test4.js b/packages/react-codemod/test/pure-render-mixin-test4.js deleted file mode 100644 index cbf384e0f4..0000000000 --- a/packages/react-codemod/test/pure-render-mixin-test4.js +++ /dev/null @@ -1,12 +0,0 @@ -var React = require('React'); -var ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin'); - -var MyComponent = React.createClass({ - mixins: [ReactComponentWithPureRenderMixin], - - render: function() { - return
; - } -}); - -module.exports = MyComponent; diff --git a/packages/react-codemod/test/pure-render-mixin-test4.output.js b/packages/react-codemod/test/pure-render-mixin-test4.output.js deleted file mode 100644 index 14c9cf2f94..0000000000 --- a/packages/react-codemod/test/pure-render-mixin-test4.output.js +++ /dev/null @@ -1,13 +0,0 @@ -var React = require('React'); - -var MyComponent = React.createClass({ - shouldComponentUpdate: function(nextProps, nextState) { - return React.addons.shallowCompare(this, nextProps, nextState); - }, - - render: function() { - return
; - } -}); - -module.exports = MyComponent; diff --git a/packages/react-codemod/transforms/class.js b/packages/react-codemod/transforms/class.js deleted file mode 100644 index 782fff5cac..0000000000 --- a/packages/react-codemod/transforms/class.js +++ /dev/null @@ -1,555 +0,0 @@ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -'use strict'; - -module.exports = (file, api, options) => { - const j = api.jscodeshift; - - require('./utils/array-polyfills'); - const ReactUtils = require('./utils/ReactUtils')(j); - - const printOptions = - options.printOptions || {quote: 'single', trailingComma: true}; - const root = j(file.source); - - const AUTOBIND_IGNORE_KEYS = { - componentDidMount: true, - componentDidUpdate: true, - componentWillReceiveProps: true, - componentWillMount: true, - componentWillUpdate: true, - componentWillUnmount: true, - getDefaultProps: true, - getInitialState: true, - render: true, - shouldComponentUpdate: true, - }; - - const BASE_COMPONENT_METHODS = ['setState', 'forceUpdate']; - - const DEFAULT_PROPS_FIELD = 'getDefaultProps'; - const DEFAULT_PROPS_KEY = 'defaultProps'; - const GET_INITIAL_STATE_FIELD = 'getInitialState'; - - const DEPRECATED_APIS = [ - 'getDOMNode', - 'isMounted', - 'replaceProps', - 'replaceState', - 'setProps', - ]; - - const STATIC_KEY = 'statics'; - - const STATIC_KEYS = { - childContextTypes: true, - contextTypes: true, - displayName: true, - propTypes: true, - }; - - // --------------------------------------------------------------------------- - // Checks if the module uses mixins or accesses deprecated APIs. - const checkDeprecatedAPICalls = classPath => - DEPRECATED_APIS.reduce( - (acc, name) => - acc + j(classPath) - .find(j.Identifier, {name}) - .size(), - 0 - ) > 0; - - const callsDeprecatedAPIs = classPath => { - if (checkDeprecatedAPICalls(classPath)) { - console.log( - file.path + ': `' + ReactUtils.getComponentName(classPath) + '` ' + - 'was skipped because of deprecated API calls. Remove calls to ' + - DEPRECATED_APIS.join(', ') + ' in your React component and re-run ' + - 'this script.' - ); - return false; - } - return true; - }; - - const canConvertToClass = classPath => { - const specPath = ReactUtils.getReactCreateClassSpec(classPath); - const invalidProperties = specPath.properties.filter(prop => ( - !prop.key.name || ( - !STATIC_KEYS[prop.key.name] && - STATIC_KEY != prop.key.name && - !filterDefaultPropsField(prop) && - !filterGetInitialStateField(prop) && - !isFunctionExpression(prop) - ) - )); - - if (invalidProperties.length) { - const invalidText = invalidProperties - .map(prop => prop.key.name ? prop.key.name : prop.key) - .join(', '); - console.log( - file.path + ': `' + ReactUtils.getComponentName(classPath) + '` ' + - 'was skipped because of invalid field(s) `' + invalidText + '` on ' + - 'the React component. Remove any right-hand-side expressions that ' + - 'are not simple, like: `componentWillUpdate: createWillUpdate()` or ' + - '`render: foo ? renderA : renderB`.' - ); - } - return !invalidProperties.length; - }; - - const hasMixins = classPath => { - if (ReactUtils.hasMixins(classPath)) { - console.log( - file.path + ': `' + ReactUtils.getComponentName(classPath) + '` ' + - 'was skipped because of mixins.' - ); - return false; - } - return true; - }; - - // --------------------------------------------------------------------------- - // Helpers - const createFindPropFn = prop => property => ( - property.key && - property.key.type === 'Identifier' && - property.key.name === prop - ); - - const filterDefaultPropsField = node => - createFindPropFn(DEFAULT_PROPS_FIELD)(node); - - const filterGetInitialStateField = node => - createFindPropFn(GET_INITIAL_STATE_FIELD)(node); - - const findGetDefaultProps = specPath => - specPath.properties.find(createFindPropFn(DEFAULT_PROPS_FIELD)); - - const findGetInitialState = specPath => - specPath.properties.find(createFindPropFn(GET_INITIAL_STATE_FIELD)); - - // This is conservative; only check for `setState` and `forceUpdate` literals - // instead of also checking which objects they are called on. - const shouldExtendReactComponent = classPath => - BASE_COMPONENT_METHODS.some(name => ( - j(classPath) - .find(j.Identifier, {name}) - .size() > 0 - )); - - const withComments = (to, from) => { - to.comments = from.comments; - return to; - }; - - // --------------------------------------------------------------------------- - // Collectors - const isFunctionExpression = node => ( - node.key && - node.key.type === 'Identifier' && - node.value && - node.value.type === 'FunctionExpression' - ); - - const collectStatics = specPath => { - const statics = specPath.properties.find(createFindPropFn('statics')); - const staticsObject = - (statics && statics.value && statics.value.properties) || []; - - const getDefaultProps = findGetDefaultProps(specPath); - if (getDefaultProps) { - staticsObject.push(createDefaultProps(getDefaultProps)); - } - - return ( - staticsObject.concat(specPath.properties.filter(property => - property.key && STATIC_KEYS[property.key.name] - )) - .sort((a, b) => a.key.name < b.key.name) - ); - }; - - const collectFunctions = specPath => specPath.properties - .filter(prop => - !(filterDefaultPropsField(prop) || filterGetInitialStateField(prop)) - ) - .filter(isFunctionExpression); - - const findAutobindNamesFor = (subtree, fnNames, literalOrIdentifier) => { - const node = literalOrIdentifier; - const autobindNames = {}; - - j(subtree) - .find(j.MemberExpression, { - object: node.name ? { - type: node.type, - name: node.name, - } : {type: node.type}, - property: { - type: 'Identifier', - }, - }) - .filter(path => path.value.property && fnNames[path.value.property.name]) - .filter(path => { - const call = path.parent.value; - return !( - call && - call.type === 'CallExpression' && - call.callee.type === 'MemberExpression' && - call.callee.object.type === node.type && - call.callee.object.name === node.name && - call.callee.property.type === 'Identifier' && - call.callee.property.name === path.value.property.name - ); - }) - .forEach(path => autobindNames[path.value.property.name] = true); - - return Object.keys(autobindNames); - }; - - const collectAutoBindFunctions = (functions, classPath) => { - const fnNames = {}; - functions - .filter(fn => !AUTOBIND_IGNORE_KEYS[fn.key.name]) - .forEach(fn => fnNames[fn.key.name] = true); - - const autobindNames = {}; - const add = name => autobindNames[name] = true; - - // Find `this.` - findAutobindNamesFor(classPath, fnNames, j.thisExpression()).forEach(add); - - // Find `self.` if `self = this` - j(classPath) - .findVariableDeclarators() - .filter(path => ( - path.value.id.type === 'Identifier' && - path.value.init && - path.value.init.type === 'ThisExpression' - )) - .forEach(path => - findAutobindNamesFor( - j(path).closest(j.FunctionExpression).get(), - fnNames, - path.value.id - ).forEach(add) - ); - - return Object.keys(autobindNames).sort(); - }; - - // --------------------------------------------------------------------------- - // Boom! - const createMethodDefinition = fn => - withComments(j.methodDefinition( - 'method', - fn.key, - fn.value - ), fn); - - const createBindAssignment = name => - j.expressionStatement( - j.assignmentExpression( - '=', - j.memberExpression( - j.thisExpression(), - j.identifier(name), - false - ), - j.callExpression( - j.memberExpression( - j.memberExpression( - j.thisExpression(), - j.identifier(name), - false - ), - j.identifier('bind'), - false - ), - [j.thisExpression()] - ) - ) - ); - - const createSuperCall = shouldAddSuperCall => - !shouldAddSuperCall ? - [] : - [ - j.expressionStatement( - j.callExpression( - j.identifier('super'), - [j.identifier('props'), j.identifier('context')] - ) - ), - ]; - - const updatePropsAccess = getInitialState => - getInitialState ? - j(getInitialState) - .find(j.MemberExpression, { - object: { - type: 'ThisExpression', - }, - property: { - type: 'Identifier', - name: 'props', - }, - }) - .forEach(path => j(path).replaceWith(j.identifier('props'))) - .size() > 0 : - false; - - const inlineGetInitialState = getInitialState => { - if (!getInitialState) { - return []; - } - - return getInitialState.value.body.body.map(statement => { - if (statement.type === 'ReturnStatement') { - return j.expressionStatement( - j.assignmentExpression( - '=', - j.memberExpression( - j.thisExpression(), - j.identifier('state'), - false - ), - statement.argument - ) - ); - } - - return statement; - }); - }; - - const createConstructorArgs = (shouldAddSuperClass, hasPropsAccess) => { - if (shouldAddSuperClass) { - return [j.identifier('props'), j.identifier('context')]; - } else if (hasPropsAccess) { - return [j.identifier('props')]; - } else { - return []; - } - }; - - const createConstructor = ( - getInitialState, - autobindFunctions, - shouldAddSuperClass - ) => { - if (!getInitialState && !autobindFunctions.length) { - return []; - } - - const hasPropsAccess = updatePropsAccess(getInitialState); - return [ - createMethodDefinition({ - key: j.identifier('constructor'), - value: j.functionExpression( - null, - createConstructorArgs(shouldAddSuperClass, hasPropsAccess), - j.blockStatement( - [].concat( - createSuperCall(shouldAddSuperClass), - autobindFunctions.map(createBindAssignment), - inlineGetInitialState(getInitialState) - ) - ) - ), - }), - ]; - }; - - const createESClass = ( - name, - properties, - getInitialState, - autobindFunctions, - comments, - shouldAddSuperClass - ) => - withComments(j.classDeclaration( - name ? j.identifier(name) : null, - j.classBody( - [].concat( - createConstructor( - getInitialState, - autobindFunctions, - shouldAddSuperClass - ), - properties - ) - ), - shouldAddSuperClass ? j.memberExpression( - j.identifier('React'), - j.identifier('Component'), - false - ) : null - ), {comments}); - - const createStaticAssignment = (name, staticProperty) => - withComments(j.expressionStatement( - j.assignmentExpression( - '=', - j.memberExpression( - name, - j.identifier(staticProperty.key.name), - false - ), - staticProperty.value - ) - ), staticProperty); - - const createStaticAssignmentExpressions = (name, statics) => - statics.map(staticProperty => createStaticAssignment(name, staticProperty)); - - const createStaticClassProperty = staticProperty => - withComments(j.classProperty( - j.identifier(staticProperty.key.name), - staticProperty.value, - null, - true - ), staticProperty); - - const createStaticClassProperties = statics => - statics.map(createStaticClassProperty); - - const hasSingleReturnStatement = value => ( - value.type === 'FunctionExpression' && - value.body && - value.body.type === 'BlockStatement' && - value.body.body && - value.body.body.length === 1 && - value.body.body[0].type === 'ReturnStatement' && - value.body.body[0].argument && - value.body.body[0].argument.type === 'ObjectExpression' - ); - - const createDefaultPropsValue = value => { - if (hasSingleReturnStatement(value)) { - return value.body.body[0].argument; - } else { - return j.callExpression( - value, - [] - ); - } - }; - - const createDefaultProps = prop => - withComments( - j.property( - 'init', - j.identifier(DEFAULT_PROPS_KEY), - createDefaultPropsValue(prop.value) - ), - prop - ); - - const getComments = classPath => { - if (classPath.value.comments) { - return classPath.value.comments; - } - const declaration = j(classPath).closest(j.VariableDeclaration); - if (declaration.size()) { - return declaration.get().value.comments; - } - return null; - }; - - const createModuleExportsMemberExpression = () => - j.memberExpression( - j.identifier('module'), - j.identifier('exports'), - false - ); - - const updateToClass = (classPath, shouldExtend, type) => { - const specPath = ReactUtils.getReactCreateClassSpec(classPath); - const name = ReactUtils.getComponentName(classPath); - const statics = collectStatics(specPath); - const functions = collectFunctions(specPath); - const comments = getComments(classPath); - - const autobindFunctions = collectAutoBindFunctions(functions, classPath); - const getInitialState = findGetInitialState(specPath); - - const staticName = - name ? j.identifier(name) : createModuleExportsMemberExpression(); - - var path; - if (type == 'moduleExports' || type == 'exportDefault') { - path = ReactUtils.findReactCreateClassCallExpression(classPath); - } else { - path = j(classPath).closest(j.VariableDeclaration); - } - - const properties = - (type == 'exportDefault') ? createStaticClassProperties(statics) : []; - - path.replaceWith( - createESClass( - name, - properties.concat(functions.map(createMethodDefinition)), - getInitialState, - autobindFunctions, - comments, - shouldExtend || shouldExtendReactComponent(classPath) - ) - ); - - if (type == 'moduleExports' || type == 'var') { - const staticAssignments = createStaticAssignmentExpressions( - staticName, - statics - ); - if (type == 'moduleExports') { - root.get().value.program.body.push(...staticAssignments); - } else { - path.insertAfter(staticAssignments.reverse()); - } - } - }; - - if ( - !options['explicit-require'] || ReactUtils.hasReact(root) - ) { - const apply = (path, type) => - path - .filter(hasMixins) - .filter(callsDeprecatedAPIs) - .filter(canConvertToClass) - .forEach(classPath => updateToClass( - classPath, - !options['super-class'], - type - )); - - const didTransform = ( - apply(ReactUtils.findReactCreateClass(root), 'var') - .size() + - apply(ReactUtils.findReactCreateClassModuleExports(root), 'moduleExports') - .size() + - apply(ReactUtils.findReactCreateClassExportDefault(root), 'exportDefault') - .size() - ) > 0; - - if (didTransform) { - return root.toSource(printOptions); - } - - } - - return null; -}; diff --git a/packages/react-codemod/transforms/findDOMNode.js b/packages/react-codemod/transforms/findDOMNode.js deleted file mode 100644 index 3c925b226c..0000000000 --- a/packages/react-codemod/transforms/findDOMNode.js +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -'use strict'; - -function getDOMNodeToFindDOMNode(file, api, options) { - const j = api.jscodeshift; - - require('./utils/array-polyfills'); - const ReactUtils = require('./utils/ReactUtils')(j); - - const printOptions = - options.printOptions || {quote: 'single', trailingComma: true}; - const root = j(file.source); - - const createReactFindDOMNodeCall = arg => j.callExpression( - j.memberExpression( - j.identifier('React'), - j.identifier('findDOMNode'), - false - ), - [arg] - ); - - const updateRefCall = (path, refName) => { - j(path) - .find(j.CallExpression, { - callee: { - object: { - type: 'Identifier', - name: refName, - }, - property: { - type: 'Identifier', - name: 'getDOMNode', - }, - }, - }) - .forEach(callPath => j(callPath).replaceWith( - createReactFindDOMNodeCall(j.identifier(refName)) - )); - }; - - const updateToFindDOMNode = classPath => { - var sum = 0; - - // this.getDOMNode() - sum += j(classPath) - .find(j.CallExpression, { - callee: { - object: { - type: 'ThisExpression', - }, - property: { - type: 'Identifier', - name: 'getDOMNode', - }, - }, - }) - .forEach(path => j(path).replaceWith( - createReactFindDOMNodeCall(j.thisExpression()) - )) - .size(); - - // this.refs.xxx.getDOMNode() or this.refs.xxx.refs.yyy.getDOMNode() - sum += j(classPath) - .find(j.MemberExpression, { - object: { - type: 'MemberExpression', - object: { - type: 'MemberExpression', - object: { - type: 'ThisExpression', - }, - property: { - type: 'Identifier', - name: 'refs', - }, - }, - }, - }) - .closest(j.CallExpression) - .filter(path => ( - path.value.callee.property && - path.value.callee.property.type === 'Identifier' && - path.value.callee.property.name === 'getDOMNode' - )) - .forEach(path => j(path).replaceWith( - createReactFindDOMNodeCall(path.value.callee.object) - )) - .size(); - - // someVariable.getDOMNode() wherre `someVariable = this.refs.xxx` - sum += j(classPath) - .findVariableDeclarators() - .filter(path => { - const init = path.value.init; - const value = init && init.object; - return ( - value && - value.type === 'MemberExpression' && - value.object && - value.object.type === 'ThisExpression' && - value.property && - value.property.type === 'Identifier' && - value.property.name === 'refs' && - init.property && - init.property.type === 'Identifier' - ); - }) - .forEach(path => j(path) - .closest(j.FunctionExpression) - .forEach(fnPath => updateRefCall(fnPath, path.value.id.name)) - ) - .size(); - - return sum > 0; - }; - - if ( - !options['explicit-require'] || - ReactUtils.hasReact(root) - ) { - const didTransform = ReactUtils - .findReactCreateClass(root) - .filter(updateToFindDOMNode) - .size() > 0; - - if (didTransform) { - return root.toSource(printOptions); - } - } - - return null; -} - -module.exports = getDOMNodeToFindDOMNode; diff --git a/packages/react-codemod/transforms/pure-render-mixin.js b/packages/react-codemod/transforms/pure-render-mixin.js deleted file mode 100644 index f5bc29d214..0000000000 --- a/packages/react-codemod/transforms/pure-render-mixin.js +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -'use strict'; - -function removePureRenderMixin(file, api, options) { - const j = api.jscodeshift; - - require('./utils/array-polyfills'); - const ReactUtils = require('./utils/ReactUtils')(j); - - const printOptions = - options.printOptions || {quote: 'single', trailingComma: true}; - const root = j(file.source); - - const PURE_RENDER_MIXIN = options['mixin-name'] || 'PureRenderMixin'; - const SHOULD_COMPONENT_UPDATE = 'shouldComponentUpdate'; - const NEXT_PROPS = 'nextProps'; - const NEXT_STATE = 'nextState'; - - // --------------------------------------------------------------------------- - // shouldComponentUpdate - const createShouldComponentUpdateFunction = () => - j.functionExpression( - null, - [j.identifier(NEXT_PROPS), j.identifier(NEXT_STATE)], - j.blockStatement([ - j.returnStatement( - j.callExpression( - j.memberExpression( - j.identifier('React'), - j.memberExpression( - j.identifier('addons'), - j.identifier('shallowCompare'), - false - ), - false - ), - [ - j.thisExpression(), - j.identifier(NEXT_PROPS), - j.identifier(NEXT_STATE), - ] - ) - ), - ]) - ); - - const createShouldComponentUpdateProperty = () => - j.property( - 'init', - j.identifier(SHOULD_COMPONENT_UPDATE), - createShouldComponentUpdateFunction() - ); - - const hasShouldComponentUpdate = classPath => - ReactUtils.getReactCreateClassSpec(classPath) - .properties.every(property => - property.key.name !== SHOULD_COMPONENT_UPDATE - ); - - // --------------------------------------------------------------------------- - // Mixin related code - const isPureRenderMixin = node => ( - node.type === 'Identifier' && - node.name === PURE_RENDER_MIXIN - ); - - const hasPureRenderMixin = classPath => { - const spec = ReactUtils.getReactCreateClassSpec(classPath); - const mixin = spec && spec.properties.find(ReactUtils.isMixinProperty); - return mixin && mixin.value.elements.some(isPureRenderMixin); - }; - - const removeMixin = elements => - j.property( - 'init', - j.identifier('mixins'), - j.arrayExpression( - elements.filter(element => !isPureRenderMixin(element)) - ) - ); - - // --------------------------------------------------------------------------- - // Boom! - const insertShouldComponentUpdate = properties => { - const length = properties.length; - const lastProp = properties[length - 1]; - // I wouldn't dare insert at the bottom if the last function is render - if ( - lastProp.key.type === 'Identifier' && - lastProp.key.name === 'render' - ) { - properties.splice( - length - 1, - 1, - createShouldComponentUpdateProperty(), - lastProp - ); - } else { - properties.push(createShouldComponentUpdateProperty()); - } - return properties; - }; - - const cleanupReactComponent = classPath => { - const spec = ReactUtils.getReactCreateClassSpec(classPath); - const properties = spec.properties - .map(property => { - if (ReactUtils.isMixinProperty(property)) { - const elements = property.value.elements; - return (elements.length !== 1) ? removeMixin(elements) : null; - } - return property; - }) - .filter(property => !!property); - - ReactUtils.findReactCreateClassCallExpression(classPath).replaceWith( - ReactUtils.createCreateReactClassCallExpression( - insertShouldComponentUpdate(properties) - ) - ); - }; - - // Remove it if only two or fewer are left: - // var PureRenderMixin = React.addons.PureRenderMixin; - const hasPureRenderIdentifiers = path => - path.find(j.Identifier, { - name: PURE_RENDER_MIXIN, - }).size() > 2; - - const deletePureRenderMixin = path => { - if (hasPureRenderIdentifiers(path)) { - return; - } - - const declaration = path - .findVariableDeclarators(PURE_RENDER_MIXIN) - .closest(j.VariableDeclaration); - - if (declaration.size > 1) { - declaration.forEach(p => - j(p).replaceWith( - j.variableDeclaration( - 'var', - p.value.declarations.filter(isPureRenderMixin) - ) - ) - ); - } else { - // Let's assume the variable declaration happens at the top level - const program = declaration.closest(j.Program).get(); - const body = program.value.body; - const index = body.indexOf(declaration.get().value); - if (index !== -1) { - body.splice(index, 1); - } - } - }; - - if ( - !options['explicit-require'] || - ReactUtils.hasReact(root) - ) { - const didTransform = ReactUtils - .findReactCreateClass(root) - .filter(hasPureRenderMixin) - .filter(hasShouldComponentUpdate) - .forEach(cleanupReactComponent) - .size() > 0; - - if (didTransform) { - deletePureRenderMixin(root); - return root.toSource(printOptions); - } - } - - return null; -} - -module.exports = removePureRenderMixin; diff --git a/packages/react-codemod/transforms/react-to-react-dom.js b/packages/react-codemod/transforms/react-to-react-dom.js deleted file mode 100644 index 0c9ce96c03..0000000000 --- a/packages/react-codemod/transforms/react-to-react-dom.js +++ /dev/null @@ -1,321 +0,0 @@ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -'use strict'; - -var CORE_PROPERTIES = [ - 'Children', - 'Component', - 'createElement', - 'cloneElement', - 'isValidElement', - 'PropTypes', - 'createClass', - 'createFactory', - 'createMixin', - 'DOM', - '__spread', -]; - -var DOM_PROPERTIES = [ - 'findDOMNode', - 'render', - 'unmountComponentAtNode', - 'unstable_batchedUpdates', - 'unstable_renderSubtreeIntoContainer', -]; - -var DOM_SERVER_PROPERTIES = [ - 'renderToString', - 'renderToStaticMarkup', -]; - -function reportError(node, error) { - throw new Error( - `At ${node.loc.start.line}:${node.loc.start.column}: ${error}` - ); -} - -function isRequire(path, moduleName) { - return ( - path.value.type === 'CallExpression' && - path.value.callee.type === 'Identifier' && - path.value.callee.name === 'require' && - path.value.arguments.length === 1 && - path.value.arguments[0].type === 'Literal' && - path.value.arguments[0].value === moduleName - ); -} - -module.exports = function(file, api) { - var j = api.jscodeshift; - var root = j(file.source); - - [ - ['React', 'ReactDOM', 'ReactDOMServer'], - ['react', 'react-dom', 'react-dom/server'], - ].forEach(function(pair) { - var coreModuleName = pair[0]; - var domModuleName = pair[1]; - var domServerModuleName = pair[2]; - - var domAlreadyDeclared = false; - var domServerAlreadyDeclared = false; - - var coreRequireDeclarator; - root - .find(j.CallExpression) - .filter(p => isRequire(p, coreModuleName)) - .forEach(p => { - var name, scope; - if (p.parent.value.type === 'VariableDeclarator') { - if (p.parent.value.id.type === 'ObjectPattern') { - var pattern = p.parent.value.id; - var all = pattern.properties.every(function(prop) { - if (prop.key.type === 'Identifier') { - name = prop.key.name; - return CORE_PROPERTIES.indexOf(name) !== -1; - } - return false; - }); - if (all) { - // var {PropTypes} = require('React'); so leave alone - return; - } - } - if (coreRequireDeclarator) { - reportError( - p.value, - 'Multiple declarations of React' - ); - } - if (p.parent.value.id.type !== 'Identifier') { - reportError( - p.value, - 'Unexpected destructuring in require of ' + coreModuleName - ); - } - name = p.parent.value.id.name; - scope = p.scope.lookup(name); - if (scope.declares('ReactDOM')) { - console.log('Using existing ReactDOM var in ' + file.path); - domAlreadyDeclared = true; - } - if (scope.declares('ReactDOMServer')) { - console.log('Using existing ReactDOMServer var in ' + file.path); - domServerAlreadyDeclared = true; - } - coreRequireDeclarator = p.parent; - } else if (p.parent.value.type === 'AssignmentExpression') { - if (p.parent.value.left.type !== 'Identifier') { - reportError( - p.value, - 'Unexpected destructuring in require of ' + coreModuleName - ); - } - name = p.parent.value.left.name; - scope = p.scope.lookup(name); - var reactBindings = scope.getBindings()[name]; - if (reactBindings.length !== 1) { - throw new Error( - 'Unexpected number of bindings: ' + reactBindings.length - ); - } - coreRequireDeclarator = reactBindings[0].parent; - if (coreRequireDeclarator.value.init && - !isRequire(coreRequireDeclarator.get('init'), coreModuleName)) { - reportError( - coreRequireDeclarator.value, - 'Unexpected initialization of ' + coreModuleName - ); - } - if (scope.declares('ReactDOM')) { - console.log('Using existing ReactDOM var in ' + file.path); - domAlreadyDeclared = true; - } - if (scope.declares('ReactDOMServer')) { - console.log('Using existing ReactDOMServer var in ' + file.path); - domServerAlreadyDeclared = true; - } - } - }); - if (!coreRequireDeclarator) { - return; - } - - if (!domAlreadyDeclared && - root.find(j.Identifier, {name: 'ReactDOM'}).size() > 0) { - throw new Error( - 'ReactDOM is already defined in a different scope than React' - ); - } - if (!domServerAlreadyDeclared && - root.find(j.Identifier, {name: 'ReactDOMServer'}).size() > 0) { - throw new Error( - 'ReactDOMServer is already defined in a different scope than React' - ); - } - - var coreName = coreRequireDeclarator.value.id.name; - - var processed = new Set(); - var requireAssignments = []; - var coreUses = 0; - var domUses = 0; - var domServerUses = 0; - - root - .find(j.Identifier, {name: coreName}) - .forEach(p => { - if (processed.has(p.value)) { - // https://github.com/facebook/jscodeshift/issues/36 - return; - } - processed.add(p.value); - if (p.parent.value.type === 'MemberExpression' || - p.parent.value.type === 'QualifiedTypeIdentifier') { - var left; - var right; - if (p.parent.value.type === 'MemberExpression') { - left = p.parent.value.object; - right = p.parent.value.property; - } else { - left = p.parent.value.qualification; - right = p.parent.value.id; - } - if (left === p.value) { - // React.foo (or React[foo]) - if (right.type === 'Identifier') { - var name = right.name; - if (CORE_PROPERTIES.indexOf(name) !== -1) { - coreUses++; - } else if (DOM_PROPERTIES.indexOf(name) !== -1) { - domUses++; - j(p).replaceWith(j.identifier('ReactDOM')); - } else if (DOM_SERVER_PROPERTIES.indexOf(name) !== -1) { - domServerUses++; - j(p).replaceWith(j.identifier('ReactDOMServer')); - } else { - throw new Error('Unknown property React.' + name); - } - } - } else if (right === p.value) { - // foo.React, no need to transform - } else { - throw new Error('unimplemented'); - } - } else if (p.parent.value.type === 'VariableDeclarator') { - if (p.parent.value.id === p.value) { - // var React = ...; - } else if (p.parent.value.init === p.value) { - // var ... = React; - var pattern = p.parent.value.id; - if (pattern.type === 'ObjectPattern') { - // var {PropTypes} = React; - // Most of these cases will just be looking at {PropTypes} so this - // is usually a no-op. - var coreProperties = []; - var domProperties = []; - pattern.properties.forEach(function(prop) { - if (prop.key.type === 'Identifier') { - var key = prop.key.name; - if (CORE_PROPERTIES.indexOf(key) !== -1) { - coreProperties.push(prop); - } else if (DOM_PROPERTIES.indexOf(key) !== -1) { - domProperties.push(prop); - } else { - throw new Error( - 'Unknown property React.' + key + ' while destructuring' - ); - } - } else { - throw new Error('unimplemented'); - } - }); - var domDeclarator = j.variableDeclarator( - j.objectPattern(domProperties), - j.identifier('ReactDOM') - ); - if (coreProperties.length && !domProperties.length) { - // nothing to do - coreUses++; - } else if (domProperties.length && !coreProperties.length) { - domUses++; - j(p.parent).replaceWith(domDeclarator); - } else { - coreUses++; - domUses++; - var decl = j(p).closest(j.VariableDeclaration); - decl.insertAfter(j.variableDeclaration( - decl.get().value.kind, - [domDeclarator] - )); - } - } else { - throw new Error('unimplemented'); - } - } else { - throw new Error('unimplemented'); - } - } else if (p.parent.value.type === 'AssignmentExpression') { - if (p.parent.value.left === p.value) { - if (isRequire(p.parent.get('right'), coreModuleName)) { - requireAssignments.push(p.parent); - } else { - reportError( - p.parent.value, - 'Unexpected assignment to ' + coreModuleName - ); - } - } else { - throw new Error('unimplemented'); - } - } else { - reportError(p.value, 'unimplemented ' + p.parent.value.type); - } - }); - - coreUses += root.find(j.JSXElement).size(); - - function insertRequire(name, path) { - var req = j.callExpression( - j.identifier('require'), - [j.literal(path)] - ); - requireAssignments.forEach(function(requireAssignment) { - requireAssignment.parent.insertAfter( - j.expressionStatement( - j.assignmentExpression('=', j.identifier(name), req) - ) - ); - }); - coreRequireDeclarator.parent.insertAfter(j.variableDeclaration( - coreRequireDeclarator.parent.value.kind, - [j.variableDeclarator( - j.identifier(name), - coreRequireDeclarator.value.init ? req : null - )] - )); - } - - if (domServerUses > 0 && !domServerAlreadyDeclared) { - insertRequire('ReactDOMServer', domServerModuleName); - } - if (domUses > 0 && !domAlreadyDeclared) { - insertRequire('ReactDOM', domModuleName); - } - if ((domUses > 0 || domServerUses > 0) && coreUses === 0) { - j(coreRequireDeclarator).remove(); - requireAssignments.forEach(r => j(r).remove()); - } - }); - - return root.toSource({quote: 'single'}); -}; diff --git a/packages/react-codemod/transforms/utils/ReactUtils.js b/packages/react-codemod/transforms/utils/ReactUtils.js deleted file mode 100644 index 7dab7ca4e7..0000000000 --- a/packages/react-codemod/transforms/utils/ReactUtils.js +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -'use strict'; - -module.exports = function(j) { - const REACT_CREATE_CLASS_MEMBER_EXPRESSION = { - type: 'MemberExpression', - object: { - name: 'React', - }, - property: { - name: 'createClass', - }, - }; - - // --------------------------------------------------------------------------- - // Checks if the file requires a certain module - const hasModule = (path, module) => - path - .findVariableDeclarators() - .filter(j.filters.VariableDeclarator.requiresModule(module)) - .size() === 1 || - path - .find(j.ImportDeclaration, { - type: 'ImportDeclaration', - source: { - type: 'Literal', - }, - }) - .filter(declarator => declarator.value.source.value === module) - .size() === 1; - - const hasReact = path => ( - hasModule(path, 'React') || - hasModule(path, 'react') || - hasModule(path, 'react/addons') - ); - - // --------------------------------------------------------------------------- - // Finds all variable declarations that call React.createClass - const findReactCreateClassCallExpression = path => - j(path).find(j.CallExpression, { - callee: REACT_CREATE_CLASS_MEMBER_EXPRESSION, - }); - - const findReactCreateClass = path => - path - .findVariableDeclarators() - .filter(decl => findReactCreateClassCallExpression(decl).size() > 0); - - const findReactCreateClassExportDefault = path => - path.find(j.ExportDefaultDeclaration, { - declaration: { - type: 'CallExpression', - callee: REACT_CREATE_CLASS_MEMBER_EXPRESSION, - }, - }); - - const findReactCreateClassModuleExports = path => - path - .find(j.AssignmentExpression, { - left: { - type: 'MemberExpression', - object: { - type: 'Identifier', - name: 'module', - }, - property: { - type: 'Identifier', - name: 'exports', - }, - }, - right: { - type: 'CallExpression', - callee: REACT_CREATE_CLASS_MEMBER_EXPRESSION, - }, - }); - - // --------------------------------------------------------------------------- - // Finds all classes that extend React.Component - const findReactES6ClassDeclaration = path => - path - .find(j.ClassDeclaration, { - superClass: { - type: 'MemberExpression', - object: { - type: 'Identifier', - name: 'React', - }, - property: { - type: 'Identifier', - name: 'Component', - }, - }, - }); - - // --------------------------------------------------------------------------- - // Checks if the React class has mixins - const isMixinProperty = property => { - const key = property.key; - const value = property.value; - return ( - key.name === 'mixins' && - value.type === 'ArrayExpression' && - Array.isArray(value.elements) && - value.elements.length - ); - }; - - const hasMixins = classPath => { - const spec = getReactCreateClassSpec(classPath); - return spec && spec.properties.some(isMixinProperty); - }; - - // --------------------------------------------------------------------------- - // Others - const getReactCreateClassSpec = classPath => { - var {value} = classPath; - const spec = (value.init || value.right || value.declaration).arguments[0]; - if (spec.type === 'ObjectExpression' && Array.isArray(spec.properties)) { - return spec; - } - }; - - const createCreateReactClassCallExpression = properties => - j.callExpression( - j.memberExpression( - j.identifier('React'), - j.identifier('createClass'), - false - ), - [j.objectExpression(properties)] - ); - - const getComponentName = - classPath => classPath.node.id && classPath.node.id.name; - - return { - createCreateReactClassCallExpression, - findReactES6ClassDeclaration, - findReactCreateClass, - findReactCreateClassCallExpression, - findReactCreateClassModuleExports, - findReactCreateClassExportDefault, - getComponentName, - getReactCreateClassSpec, - hasMixins, - hasModule, - hasReact, - isMixinProperty, - }; -}; diff --git a/packages/react-codemod/transforms/utils/array-polyfills.js b/packages/react-codemod/transforms/utils/array-polyfills.js deleted file mode 100644 index 377de52d6c..0000000000 --- a/packages/react-codemod/transforms/utils/array-polyfills.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - - /*eslint-disable no-extend-native*/ - -'use strict'; - -function findIndex(predicate, context) { - if (this == null) { - throw new TypeError( - 'Array.prototype.findIndex called on null or undefined' - ); - } - if (typeof predicate !== 'function') { - throw new TypeError('predicate must be a function'); - } - var list = Object(this); - var length = list.length >>> 0; - for (var i = 0; i < length; i++) { - if (predicate.call(context, list[i], i, list)) { - return i; - } - } - return -1; -} - -if (!Array.prototype.findIndex) { - Array.prototype.findIndex = findIndex; -} - -if (!Array.prototype.find) { - Array.prototype.find = function(predicate, context) { - if (this == null) { - throw new TypeError('Array.prototype.find called on null or undefined'); - } - var index = findIndex.call(this, predicate, context); - return index === -1 ? undefined : this[index]; - }; -}