no-unsafe.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /**
  2. * @fileoverview Prevent usage of unsafe lifecycle methods
  3. * @author Sergei Startsev
  4. */
  5. 'use strict';
  6. const astUtil = require('../util/ast');
  7. const componentUtil = require('../util/componentUtil');
  8. const docsUrl = require('../util/docsUrl');
  9. const testReactVersion = require('../util/version').testReactVersion;
  10. const report = require('../util/report');
  11. // ------------------------------------------------------------------------------
  12. // Rule Definition
  13. // ------------------------------------------------------------------------------
  14. const messages = {
  15. unsafeMethod: '{{method}} is unsafe for use in async rendering. Update the component to use {{newMethod}} instead. {{details}}',
  16. };
  17. module.exports = {
  18. meta: {
  19. docs: {
  20. description: 'Disallow usage of unsafe lifecycle methods',
  21. category: 'Best Practices',
  22. recommended: false,
  23. url: docsUrl('no-unsafe'),
  24. },
  25. messages,
  26. schema: [
  27. {
  28. type: 'object',
  29. properties: {
  30. checkAliases: {
  31. default: false,
  32. type: 'boolean',
  33. },
  34. },
  35. additionalProperties: false,
  36. },
  37. ],
  38. },
  39. create(context) {
  40. const config = context.options[0] || {};
  41. const checkAliases = config.checkAliases || false;
  42. const isApplicable = testReactVersion(context, '>= 16.3.0');
  43. if (!isApplicable) {
  44. return {};
  45. }
  46. const unsafe = {
  47. UNSAFE_componentWillMount: {
  48. newMethod: 'componentDidMount',
  49. details:
  50. 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.',
  51. },
  52. UNSAFE_componentWillReceiveProps: {
  53. newMethod: 'getDerivedStateFromProps',
  54. details:
  55. 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.',
  56. },
  57. UNSAFE_componentWillUpdate: {
  58. newMethod: 'componentDidUpdate',
  59. details:
  60. 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.',
  61. },
  62. };
  63. if (checkAliases) {
  64. unsafe.componentWillMount = unsafe.UNSAFE_componentWillMount;
  65. unsafe.componentWillReceiveProps = unsafe.UNSAFE_componentWillReceiveProps;
  66. unsafe.componentWillUpdate = unsafe.UNSAFE_componentWillUpdate;
  67. }
  68. /**
  69. * Returns a list of unsafe methods
  70. * @returns {Array} A list of unsafe methods
  71. */
  72. function getUnsafeMethods() {
  73. return Object.keys(unsafe);
  74. }
  75. /**
  76. * Checks if a passed method is unsafe
  77. * @param {string} method Life cycle method
  78. * @returns {boolean} Returns true for unsafe methods, otherwise returns false
  79. */
  80. function isUnsafe(method) {
  81. const unsafeMethods = getUnsafeMethods();
  82. return unsafeMethods.indexOf(method) !== -1;
  83. }
  84. /**
  85. * Reports the error for an unsafe method
  86. * @param {ASTNode} node The AST node being checked
  87. * @param {string} method Life cycle method
  88. */
  89. function checkUnsafe(node, method) {
  90. if (!isUnsafe(method)) {
  91. return;
  92. }
  93. const meta = unsafe[method];
  94. const newMethod = meta.newMethod;
  95. const details = meta.details;
  96. const propertyNode = astUtil.getComponentProperties(node)
  97. .find((property) => astUtil.getPropertyName(property) === method);
  98. report(context, messages.unsafeMethod, 'unsafeMethod', {
  99. node: propertyNode,
  100. data: {
  101. method,
  102. newMethod,
  103. details,
  104. },
  105. });
  106. }
  107. /**
  108. * Returns life cycle methods if available
  109. * @param {ASTNode} node The AST node being checked.
  110. * @returns {Array} The array of methods.
  111. */
  112. function getLifeCycleMethods(node) {
  113. const properties = astUtil.getComponentProperties(node);
  114. return properties.map((property) => astUtil.getPropertyName(property));
  115. }
  116. /**
  117. * Checks life cycle methods
  118. * @param {ASTNode} node The AST node being checked.
  119. */
  120. function checkLifeCycleMethods(node) {
  121. if (componentUtil.isES5Component(node, context) || componentUtil.isES6Component(node, context)) {
  122. const methods = getLifeCycleMethods(node);
  123. methods
  124. .sort((a, b) => a.localeCompare(b))
  125. .forEach((method) => checkUnsafe(node, method));
  126. }
  127. }
  128. return {
  129. ClassDeclaration: checkLifeCycleMethods,
  130. ClassExpression: checkLifeCycleMethods,
  131. ObjectExpression: checkLifeCycleMethods,
  132. };
  133. },
  134. };