no-deprecated.js 7.6 KB

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