mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
Remove react-codemod
This commit is contained in:
parent
244dd5da4c
commit
caece8b4b7
|
|
@ -11,9 +11,4 @@ docs/vendor/bundle/
|
||||||
examples/
|
examples/
|
||||||
# Ignore built files.
|
# Ignore built files.
|
||||||
build/
|
build/
|
||||||
# react-codemod
|
|
||||||
packages/react-codemod/test/
|
|
||||||
packages/react-codemod/scripts/
|
|
||||||
packages/react-codemod/build/
|
|
||||||
packages/react-codemod/node_modules/
|
|
||||||
vendor/react-dom.js
|
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