UNPKG

8.46 kBJavaScriptView Raw
1/**
2 * @fileoverview Prevent usage of deprecated methods
3 * @author Yannick Croissant
4 * @author Scott Feeney
5 * @author Sergei Startsev
6 */
7
8'use strict';
9
10const values = require('object.values');
11
12const Components = require('../util/Components');
13const astUtil = require('../util/ast');
14const docsUrl = require('../util/docsUrl');
15const pragmaUtil = require('../util/pragma');
16const versionUtil = require('../util/version');
17
18// ------------------------------------------------------------------------------
19// Constants
20// ------------------------------------------------------------------------------
21
22const MODULES = {
23 react: ['React'],
24 'react-addons-perf': ['ReactPerf', 'Perf']
25};
26
27const DEPRECATED_MESSAGE = '{{oldMethod}} is deprecated since React {{version}}{{newMethod}}{{refs}}';
28
29// ------------------------------------------------------------------------------
30// Rule Definition
31// ------------------------------------------------------------------------------
32
33module.exports = {
34 meta: {
35 docs: {
36 description: 'Prevent usage of deprecated methods',
37 category: 'Best Practices',
38 recommended: true,
39 url: docsUrl('no-deprecated')
40 },
41 schema: []
42 },
43
44 create: Components.detect((context, components, utils) => {
45 const pragma = pragmaUtil.getFromContext(context);
46
47 function getDeprecated() {
48 const deprecated = {};
49 // 0.12.0
50 deprecated[`${pragma}.renderComponent`] = ['0.12.0', `${pragma}.render`];
51 deprecated[`${pragma}.renderComponentToString`] = ['0.12.0', `${pragma}.renderToString`];
52 deprecated[`${pragma}.renderComponentToStaticMarkup`] = ['0.12.0', `${pragma}.renderToStaticMarkup`];
53 deprecated[`${pragma}.isValidComponent`] = ['0.12.0', `${pragma}.isValidElement`];
54 deprecated[`${pragma}.PropTypes.component`] = ['0.12.0', `${pragma}.PropTypes.element`];
55 deprecated[`${pragma}.PropTypes.renderable`] = ['0.12.0', `${pragma}.PropTypes.node`];
56 deprecated[`${pragma}.isValidClass`] = ['0.12.0'];
57 deprecated['this.transferPropsTo'] = ['0.12.0', 'spread operator ({...})'];
58 // 0.13.0
59 deprecated[`${pragma}.addons.classSet`] = ['0.13.0', 'the npm module classnames'];
60 deprecated[`${pragma}.addons.cloneWithProps`] = ['0.13.0', `${pragma}.cloneElement`];
61 // 0.14.0
62 deprecated[`${pragma}.render`] = ['0.14.0', 'ReactDOM.render'];
63 deprecated[`${pragma}.unmountComponentAtNode`] = ['0.14.0', 'ReactDOM.unmountComponentAtNode'];
64 deprecated[`${pragma}.findDOMNode`] = ['0.14.0', 'ReactDOM.findDOMNode'];
65 deprecated[`${pragma}.renderToString`] = ['0.14.0', 'ReactDOMServer.renderToString'];
66 deprecated[`${pragma}.renderToStaticMarkup`] = ['0.14.0', 'ReactDOMServer.renderToStaticMarkup'];
67 // 15.0.0
68 deprecated[`${pragma}.addons.LinkedStateMixin`] = ['15.0.0'];
69 deprecated['ReactPerf.printDOM'] = ['15.0.0', 'ReactPerf.printOperations'];
70 deprecated['Perf.printDOM'] = ['15.0.0', 'Perf.printOperations'];
71 deprecated['ReactPerf.getMeasurementsSummaryMap'] = ['15.0.0', 'ReactPerf.getWasted'];
72 deprecated['Perf.getMeasurementsSummaryMap'] = ['15.0.0', 'Perf.getWasted'];
73 // 15.5.0
74 deprecated[`${pragma}.createClass`] = ['15.5.0', 'the npm module create-react-class'];
75 deprecated[`${pragma}.addons.TestUtils`] = ['15.5.0', 'ReactDOM.TestUtils'];
76 deprecated[`${pragma}.PropTypes`] = ['15.5.0', 'the npm module prop-types'];
77 // 15.6.0
78 deprecated[`${pragma}.DOM`] = ['15.6.0', 'the npm module react-dom-factories'];
79 // 16.9.0
80 // For now the following life-cycle methods are just legacy, not deprecated:
81 // `componentWillMount`, `componentWillReceiveProps`, `componentWillUpdate`
82 // https://github.com/yannickcr/eslint-plugin-react/pull/1750#issuecomment-425975934
83 deprecated.componentWillMount = [
84 '16.9.0',
85 'UNSAFE_componentWillMount',
86 'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount. '
87 + 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.'
88 ];
89 deprecated.componentWillReceiveProps = [
90 '16.9.0',
91 'UNSAFE_componentWillReceiveProps',
92 'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops. '
93 + 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.'
94 ];
95 deprecated.componentWillUpdate = [
96 '16.9.0',
97 'UNSAFE_componentWillUpdate',
98 'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate. '
99 + 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.'
100 ];
101 return deprecated;
102 }
103
104 function isDeprecated(method) {
105 const deprecated = getDeprecated();
106
107 return (
108 deprecated
109 && deprecated[method]
110 && deprecated[method][0]
111 && versionUtil.testReactVersion(context, deprecated[method][0])
112 );
113 }
114
115 function checkDeprecation(node, methodName, methodNode) {
116 if (!isDeprecated(methodName)) {
117 return;
118 }
119 const deprecated = getDeprecated();
120 const version = deprecated[methodName][0];
121 const newMethod = deprecated[methodName][1];
122 const refs = deprecated[methodName][2];
123 context.report({
124 node: methodNode || node,
125 message: DEPRECATED_MESSAGE,
126 data: {
127 oldMethod: methodName,
128 version,
129 newMethod: newMethod ? `, use ${newMethod} instead` : '',
130 refs: refs ? `, see ${refs}` : ''
131 }
132 });
133 }
134
135 function getReactModuleName(node) {
136 let moduleName = false;
137 if (!node.init) {
138 return moduleName;
139 }
140
141 values(MODULES).some((moduleNames) => {
142 moduleName = moduleNames.find((name) => name === node.init.name);
143 return moduleName;
144 });
145
146 return moduleName;
147 }
148
149 /**
150 * Returns life cycle methods if available
151 * @param {ASTNode} node The AST node being checked.
152 * @returns {Array} The array of methods.
153 */
154 function getLifeCycleMethods(node) {
155 const properties = astUtil.getComponentProperties(node);
156 return properties.map((property) => ({
157 name: astUtil.getPropertyName(property),
158 node: astUtil.getPropertyNameNode(property)
159 }));
160 }
161
162 /**
163 * Checks life cycle methods
164 * @param {ASTNode} node The AST node being checked.
165 */
166 function checkLifeCycleMethods(node) {
167 if (utils.isES5Component(node) || utils.isES6Component(node)) {
168 const methods = getLifeCycleMethods(node);
169 methods.forEach((method) => checkDeprecation(node, method.name, method.node));
170 }
171 }
172
173 // --------------------------------------------------------------------------
174 // Public
175 // --------------------------------------------------------------------------
176
177 return {
178 MemberExpression(node) {
179 checkDeprecation(node, context.getSourceCode().getText(node));
180 },
181
182 ImportDeclaration(node) {
183 const isReactImport = typeof MODULES[node.source.value] !== 'undefined';
184 if (!isReactImport) {
185 return;
186 }
187 node.specifiers.forEach((specifier) => {
188 if (!specifier.imported) {
189 return;
190 }
191 checkDeprecation(node, `${MODULES[node.source.value][0]}.${specifier.imported.name}`);
192 });
193 },
194
195 VariableDeclarator(node) {
196 const reactModuleName = getReactModuleName(node);
197 const isRequire = node.init && node.init.callee && node.init.callee.name === 'require';
198 const isReactRequire = node.init
199 && node.init.arguments
200 && node.init.arguments.length
201 && typeof MODULES[node.init.arguments[0].value] !== 'undefined';
202 const isDestructuring = node.id && node.id.type === 'ObjectPattern';
203
204 if (
205 !(isDestructuring && reactModuleName)
206 && !(isDestructuring && isRequire && isReactRequire)
207 ) {
208 return;
209 }
210 node.id.properties.forEach((property) => {
211 checkDeprecation(node, `${reactModuleName || pragma}.${property.key.name}`);
212 });
213 },
214
215 ClassDeclaration: checkLifeCycleMethods,
216 ClassExpression: checkLifeCycleMethods,
217 ObjectExpression: checkLifeCycleMethods
218 };
219 })
220};