no-deprecated.js 8.3 KB

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