1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | 'use strict';
|
9 |
|
10 | const values = require('object.values');
|
11 |
|
12 | const Components = require('../util/Components');
|
13 | const astUtil = require('../util/ast');
|
14 | const docsUrl = require('../util/docsUrl');
|
15 | const pragmaUtil = require('../util/pragma');
|
16 | const versionUtil = require('../util/version');
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 | const MODULES = {
|
23 | react: ['React'],
|
24 | 'react-addons-perf': ['ReactPerf', 'Perf']
|
25 | };
|
26 |
|
27 | const DEPRECATED_MESSAGE = '{{oldMethod}} is deprecated since React {{version}}{{newMethod}}{{refs}}';
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | module.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 |
|
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 |
|
59 | deprecated[`${pragma}.addons.classSet`] = ['0.13.0', 'the npm module classnames'];
|
60 | deprecated[`${pragma}.addons.cloneWithProps`] = ['0.13.0', `${pragma}.cloneElement`];
|
61 |
|
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 |
|
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 |
|
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 |
|
78 | deprecated[`${pragma}.DOM`] = ['15.6.0', 'the npm module react-dom-factories'];
|
79 |
|
80 |
|
81 |
|
82 |
|
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 |
|
151 |
|
152 |
|
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 |
|
164 |
|
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 |
|
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 | };
|