no-unsafe.js 4.3 KB

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