Skip to content

Commit 77723c4

Browse files
committed
Merge pull request jsx-eslint#360 from yannickcr/rule-no-deprecated
Add no-deprecated rule
2 parents ad9d78e + c3e3be7 commit 77723c4

File tree

5 files changed

+230
-10
lines changed

5 files changed

+230
-10
lines changed

README.md

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Finally, enable all of the rules that you would like to use.
6666
"react/jsx-uses-react": 1,
6767
"react/jsx-uses-vars": 1,
6868
"react/no-danger": 1,
69+
"react/no-deprecated": 1,
6970
"react/no-did-mount-set-state": 1,
7071
"react/no-did-update-set-state": 1,
7172
"react/no-direct-mutation-state": 1,
@@ -105,6 +106,7 @@ Finally, enable all of the rules that you would like to use.
105106
* [jsx-uses-react](docs/rules/jsx-uses-react.md): Prevent React to be incorrectly marked as unused
106107
* [jsx-uses-vars](docs/rules/jsx-uses-vars.md): Prevent variables used in JSX to be incorrectly marked as unused
107108
* [no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties
109+
* [no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods
108110
* [no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount`
109111
* [no-did-update-set-state](docs/rules/no-did-update-set-state.md): Prevent usage of `setState` in `componentDidUpdate`
110112
* [no-direct-mutation-state](docs/rules/no-direct-mutation-state.md): Prevent direct mutation of `this.state`
@@ -119,16 +121,6 @@ Finally, enable all of the rules that you would like to use.
119121
* [sort-comp](docs/rules/sort-comp.md): Enforce component methods order
120122
* [wrap-multilines](docs/rules/wrap-multilines.md): Prevent missing parentheses around multilines JSX
121123

122-
## To Do
123-
124-
* no-deprecated: Prevent usage of deprecated methods ([React 0.12 Updated API](http://facebook.github.io/react/blog/2014/10/28/react-v0.12.html#new-terminology-amp-updated-apis))
125-
* no-classic: Prevent usage of "classic" methods ([#2700](https://github.com/facebook/react/pull/2700))
126-
* [Implement relevant rules from David Chang's React Style Guide](https://reactjsnews.com/react-style-guide-patterns-i-like)
127-
* [Implement relevant rules from John Cobb's best practices and conventions](http://web-design-weekly.com/2015/01/29/opinionated-guide-react-js-best-practices-conventions/)
128-
* [Implement relevant rules from Alexander Early's tips and best practices](http://aeflash.com/2015-02/react-tips-and-best-practices.html)
129-
130-
[Any rule idea is welcome !](https://github.com/yannickcr/eslint-plugin-react/issues)
131-
132124
# License
133125

134126
ESLint-plugin-React is licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php).

docs/rules/no-deprecated.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Prevent usage of deprecated methods (no-deprecated)
2+
3+
Several methods are deprecated between React versions. This rule will warn you if you try to use a deprecated method.
4+
5+
## Rule Details
6+
7+
The following patterns are considered warnings:
8+
9+
```js
10+
React.render(<MyComponent />, root);
11+
12+
React.unmountComponentAtNode(root);
13+
14+
React.findDOMNode(this.refs.foo);
15+
16+
React.renderToString(<MyComponent />);
17+
18+
React.renderToStaticMarkup(<MyComponent />);
19+
```
20+
21+
The following patterns are not considered warnings:
22+
23+
```js
24+
ReactDOM.render(<MyComponent />, root);
25+
26+
// When [1, {"react": "0.13.0"}]
27+
ReactDOM.findDOMNode(this.refs.foo);
28+
```
29+
30+
## Rule Options
31+
32+
By default this rule will warn to every methods marked as deprecated. You can limit it to an older React version if you are not using the latest one:
33+
34+
```js
35+
"rules": {
36+
"react/no-deprecated": [1, {"react": "0.12.0"}] // Will warn for every deprecated methods in React 0.12.0 and below
37+
}
38+
```

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
'self-closing-comp': require('./lib/rules/self-closing-comp'),
1111
'no-danger': require('./lib/rules/no-danger'),
1212
'no-set-state': require('./lib/rules/no-set-state'),
13+
'no-deprecated': require('./lib/rules/no-deprecated'),
1314
'no-did-mount-set-state': require('./lib/rules/no-did-mount-set-state'),
1415
'no-did-update-set-state': require('./lib/rules/no-did-update-set-state'),
1516
'react-in-jsx-scope': require('./lib/rules/react-in-jsx-scope'),
@@ -43,6 +44,7 @@ module.exports = {
4344
'display-name': 0,
4445
'wrap-multilines': 0,
4546
'self-closing-comp': 0,
47+
'no-deprecated': 0,
4648
'no-danger': 0,
4749
'no-set-state': 0,
4850
'no-did-mount-set-state': 0,

lib/rules/no-deprecated.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/**
2+
* @fileoverview Prevent usage of deprecated methods
3+
* @author Yannick Croissant
4+
* @author Scott Feeney
5+
*/
6+
'use strict';
7+
8+
// ------------------------------------------------------------------------------
9+
// Constants
10+
// ------------------------------------------------------------------------------
11+
12+
var DEPRECATED_MESSAGE = '{{oldMethod}} is deprecated since React {{version}}{{newMethod}}';
13+
14+
var DEPRECATED = {
15+
MemberExpression: {
16+
// 0.12.0
17+
'React.renderComponent': ['0.12.0', 'React.render'],
18+
'React.renderComponentToString': ['0.12.0', 'React.renderToString'],
19+
'React.renderComponentToStaticMarkup': ['0.12.0', 'React.renderToStaticMarkup'],
20+
'React.isValidComponent': ['0.12.0', 'React.isValidElement'],
21+
'React.PropTypes.component': ['0.12.0', 'React.PropTypes.element'],
22+
'React.PropTypes.renderable': ['0.12.0', 'React.PropTypes.node'],
23+
'React.isValidClass': ['0.12.0'],
24+
'this.transferPropsTo': ['0.12.0', 'spread operator ({...})'],
25+
// 0.13.0
26+
'React.addons.classSet': ['0.13.0', 'the npm module classnames'],
27+
'React.addons.cloneWithProps': ['0.13.0', 'React.cloneElement'],
28+
// 0.14.0
29+
'React.render': ['0.14.0', 'ReactDOM.render'],
30+
'React.unmountComponentAtNode': ['0.14.0', 'ReactDOM.unmountComponentAtNode'],
31+
'React.findDOMNode': ['0.14.0', 'ReactDOM.findDOMNode'],
32+
'React.renderToString': ['0.14.0', 'ReactDOMServer.renderToString'],
33+
'React.renderToStaticMarkup': ['0.14.0', 'ReactDOMServer.renderToStaticMarkup']
34+
}
35+
};
36+
37+
// ------------------------------------------------------------------------------
38+
// Rule Definition
39+
// ------------------------------------------------------------------------------
40+
41+
module.exports = function(context) {
42+
43+
// Validate React version passed in options
44+
// (append the patch version if missing, allowing shorthands like 0.12 for React 0.12.0)
45+
var optVer = context.options[0] ? context.options[0].react : '999.999.999';
46+
optVer = /^[0-9]+\.[0-9]+$/.test(optVer) ? optVer + '.0' : optVer;
47+
optVer = optVer.split('.').map(function(part) {
48+
return Number(part);
49+
});
50+
51+
function checkVersion(methodVer) {
52+
methodVer = methodVer.split('.').map(function(part) {
53+
return Number(part);
54+
});
55+
var higherMajor = methodVer[0] < optVer[0];
56+
var higherMinor = methodVer[0] === optVer[0] && methodVer[1] < optVer[1];
57+
var higherOrEqualPatch = methodVer[0] === optVer[0] && methodVer[1] === optVer[1] && methodVer[2] <= optVer[2];
58+
59+
return higherMajor || higherMinor || higherOrEqualPatch;
60+
}
61+
62+
function isDeprecated(type, method) {
63+
return (
64+
DEPRECATED[type] &&
65+
DEPRECATED[type][method] &&
66+
checkVersion(DEPRECATED[type][method][0])
67+
);
68+
}
69+
70+
// --------------------------------------------------------------------------
71+
// Public
72+
// --------------------------------------------------------------------------
73+
74+
return {
75+
76+
MemberExpression: function(node) {
77+
var method = context.getSource(node);
78+
if (!isDeprecated(node.type, method)) {
79+
return;
80+
}
81+
context.report(node, DEPRECATED_MESSAGE, {
82+
oldMethod: method,
83+
version: DEPRECATED[node.type][method][0],
84+
newMethod: DEPRECATED[node.type][method][1] ? ', use ' + DEPRECATED[node.type][method][1] + ' instead' : ''
85+
});
86+
}
87+
88+
};
89+
90+
};
91+
92+
module.exports.schema = [{
93+
type: 'object',
94+
properties: {
95+
react: {
96+
type: 'string',
97+
pattern: '^[0-9]+\.[0-9]+(\.[0-9]+)?$'
98+
}
99+
},
100+
additionalProperties: false
101+
}];

tests/lib/rules/no-deprecated.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @fileoverview Prevent usage of deprecated methods
3+
* @author Yannick Croissant
4+
* @author Scott Feeney
5+
*/
6+
'use strict';
7+
8+
// ------------------------------------------------------------------------------
9+
// Requirements
10+
// ------------------------------------------------------------------------------
11+
12+
var rule = require('../../../lib/rules/no-deprecated');
13+
var RuleTester = require('eslint').RuleTester;
14+
15+
require('babel-eslint');
16+
17+
// ------------------------------------------------------------------------------
18+
// Tests
19+
// ------------------------------------------------------------------------------
20+
21+
var ruleTester = new RuleTester();
22+
ruleTester.run('no-deprecated', rule, {
23+
24+
valid: [
25+
// Not deprecated
26+
'var MyClass = React.createClass({});',
27+
'var element = React.createElement(\'p\', {}, null);',
28+
'var clone = React.cloneElement(element);',
29+
'ReactDOM.render(element, container);',
30+
'ReactDOM.unmountComponentAtNode(container);',
31+
'ReactDOM.findDOMNode(instance);',
32+
'ReactDOMServer.renderToString(element);',
33+
'ReactDOMServer.renderToStaticMarkup(element);',
34+
// Deprecated in a later version
35+
{code: 'React.renderComponent()', options: [{react: '0.11.0'}]}
36+
],
37+
38+
invalid: [{
39+
code: 'React.renderComponent()',
40+
options: [{react: '0.12.0'}],
41+
errors: [{
42+
message: 'React.renderComponent is deprecated since React 0.12.0, use React.render instead'
43+
}]
44+
}, {
45+
code: 'this.transferPropsTo()',
46+
errors: [{
47+
message: 'this.transferPropsTo is deprecated since React 0.12.0, use spread operator ({...}) instead'
48+
}]
49+
}, {
50+
code: 'React.addons.classSet()',
51+
errors: [{
52+
message: 'React.addons.classSet is deprecated since React 0.13.0, use the npm module classnames instead'
53+
}]
54+
}, {
55+
code: 'React.render(element, container);',
56+
errors: [{
57+
message: 'React.render is deprecated since React 0.14.0, use ReactDOM.render instead'
58+
}]
59+
}, {
60+
code: 'React.unmountComponentAtNode(container);',
61+
errors: [{
62+
message: (
63+
'React.unmountComponentAtNode is deprecated since React 0.14.0, ' +
64+
'use ReactDOM.unmountComponentAtNode instead'
65+
)
66+
}]
67+
}, {
68+
code: 'React.findDOMNode(instance);',
69+
errors: [{
70+
message: 'React.findDOMNode is deprecated since React 0.14.0, use ReactDOM.findDOMNode instead'
71+
}]
72+
}, {
73+
code: 'React.renderToString(element);',
74+
errors: [{
75+
message: 'React.renderToString is deprecated since React 0.14.0, use ReactDOMServer.renderToString instead'
76+
}]
77+
}, {
78+
code: 'React.renderToStaticMarkup(element);',
79+
errors: [{
80+
message: (
81+
'React.renderToStaticMarkup is deprecated since React 0.14.0, ' +
82+
'use ReactDOMServer.renderToStaticMarkup instead'
83+
)
84+
}]
85+
}]
86+
87+
});

0 commit comments

Comments
 (0)