mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 00:20:04 +01:00
Remove react-codemod
This commit is contained in:
parent
244dd5da4c
commit
caece8b4b7
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 <codemod-script> <file>`
|
||||
* 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 <file>`
|
||||
|
||||
`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 <file>`
|
||||
* 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 <file>`
|
||||
* If `--mixin-name=<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 <file>`
|
||||
* 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 <file> --printOptions='{"quote":"double"}'`
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
});
|
||||
|
||||
});
|
||||
133
packages/react-codemod/test/class-test.js
vendored
133
packages/react-codemod/test/class-test.js
vendored
|
|
@ -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 <Text text={text} />;
|
||||
},
|
||||
|
||||
_renderImageRange: function(text, range) {
|
||||
var image = range.image;
|
||||
if (image) {
|
||||
return (
|
||||
<Image
|
||||
src={image.uri}
|
||||
height={image.height / image.scale}
|
||||
width={image.width / image.scale}
|
||||
/>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
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 =
|
||||
<Link href={usersURI}>
|
||||
{text}
|
||||
</Link>;
|
||||
} else {
|
||||
text = <span>{text}</span>;
|
||||
}
|
||||
|
||||
return text;
|
||||
},
|
||||
|
||||
/* This is a comment */
|
||||
render: function() {
|
||||
var content = this.props.text;
|
||||
return (
|
||||
<BaseText
|
||||
{...this.props}
|
||||
textRenderer={this._renderText}
|
||||
rangeRenderer={this._renderRange}
|
||||
text={content.text}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var MyComponent4 = React.createClass({
|
||||
foo: callMeMaybe(),
|
||||
render: function() {},
|
||||
});
|
||||
|
||||
module.exports = Relay.createContainer(MyComponent, {
|
||||
queries: {
|
||||
me: Relay.graphql`this is not graphql`,
|
||||
}
|
||||
});
|
||||
|
|
@ -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 <Text text={text} />;
|
||||
}
|
||||
|
||||
_renderImageRange(text, range) {
|
||||
var image = range.image;
|
||||
if (image) {
|
||||
return (
|
||||
<Image
|
||||
src={image.uri}
|
||||
height={image.height / image.scale}
|
||||
width={image.width / image.scale}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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 =
|
||||
<Link href={usersURI}>
|
||||
{text}
|
||||
</Link>;
|
||||
} else {
|
||||
text = <span>{text}</span>;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/* This is a comment */
|
||||
render() {
|
||||
var content = this.props.text;
|
||||
return (
|
||||
<BaseText
|
||||
{...this.props}
|
||||
textRenderer={this._renderText}
|
||||
rangeRenderer={this._renderRange}
|
||||
text={content.text}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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`,
|
||||
}
|
||||
});
|
||||
33
packages/react-codemod/test/class-test2.js
vendored
33
packages/react-codemod/test/class-test2.js
vendored
|
|
@ -1,33 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
var IdontNeedAParent = React.createClass({
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var ButIDo = React.createClass({
|
||||
foo: function() {
|
||||
this.setState({banana: '?'});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var IAccessProps = React.createClass({
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
relayReleaseDate: this.props.soon,
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return
|
||||
}
|
||||
|
||||
});
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
class IdontNeedAParent {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
class ButIDo extends React.Component {
|
||||
foo() {
|
||||
this.setState({banana: '?'});
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
class IAccessProps {
|
||||
constructor(props) {
|
||||
this.state = {
|
||||
relayReleaseDate: props.soon,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return
|
||||
}
|
||||
}
|
||||
20
packages/react-codemod/test/class-test3.js
vendored
20
packages/react-codemod/test/class-test3.js
vendored
|
|
@ -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 <div />;
|
||||
}
|
||||
});
|
||||
|
|
@ -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 <div />;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.propTypes = {
|
||||
foo: React.PropTypes.bool,
|
||||
};
|
||||
|
|
@ -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 <div />;
|
||||
}
|
||||
});
|
||||
|
|
@ -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 <div />;
|
||||
}
|
||||
}
|
||||
34
packages/react-codemod/test/findDOMNode-test.js
vendored
34
packages/react-codemod/test/findDOMNode-test.js
vendored
|
|
@ -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 (
|
||||
<div />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -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 (
|
||||
<div />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
var React = require('react/addons');
|
||||
|
||||
var PureRenderMixin = React.addons.PureRenderMixin;
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var MyMixedComponent = React.createClass({
|
||||
mixins: [PureRenderMixin, SomeOtherMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var MyFooComponent = React.createClass({
|
||||
mixins: [PureRenderMixin, SomeOtherMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
},
|
||||
|
||||
foo: function() {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
var MyStupidComponent = React.createClass({
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
shouldComponentUpdate: function() {
|
||||
return !!'wtf is this doing here?';
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
|
|
@ -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 <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var MyMixedComponent = React.createClass({
|
||||
mixins: [SomeOtherMixin],
|
||||
|
||||
shouldComponentUpdate: function(nextProps, nextState) {
|
||||
return React.addons.shallowCompare(this, nextProps, nextState);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var MyFooComponent = React.createClass({
|
||||
mixins: [SomeOtherMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
},
|
||||
|
||||
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 <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
var React = require('react/addons');
|
||||
|
||||
var PureRenderMixin = React.addons.PureRenderMixin;
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
|
|
@ -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 <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
|
|
@ -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 <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
|
|
@ -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 <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
var React = require('React');
|
||||
var ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin');
|
||||
|
||||
var MyComponent = React.createClass({
|
||||
mixins: [ReactComponentWithPureRenderMixin],
|
||||
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
|
|
@ -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 <div />;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = MyComponent;
|
||||
555
packages/react-codemod/transforms/class.js
vendored
555
packages/react-codemod/transforms/class.js
vendored
|
|
@ -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.<foo>`
|
||||
findAutobindNamesFor(classPath, fnNames, j.thisExpression()).forEach(add);
|
||||
|
||||
// Find `self.<foo>` 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;
|
||||
};
|
||||
144
packages/react-codemod/transforms/findDOMNode.js
vendored
144
packages/react-codemod/transforms/findDOMNode.js
vendored
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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'});
|
||||
};
|
||||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
@ -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];
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user