no-unsafe.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /**
  2. * @fileoverview Prevent usage of unsafe lifecycle methods
  3. * @author Sergei Startsev
  4. */
  5. 'use strict';
  6. const Components = require('../util/Components');
  7. const astUtil = require('../util/ast');
  8. const docsUrl = require('../util/docsUrl');
  9. const versionUtil = require('../util/version');
  10. // ------------------------------------------------------------------------------
  11. // Rule Definition
  12. // ------------------------------------------------------------------------------
  13. module.exports = {
  14. meta: {
  15. docs: {
  16. description: 'Prevent usage of unsafe lifecycle methods',
  17. category: 'Best Practices',
  18. recommended: false,
  19. url: docsUrl('no-unsafe')
  20. },
  21. messages: {
  22. unsafeMethod: '{{method}} is unsafe for use in async rendering. Update the component to use {{newMethod}} instead. {{details}}'
  23. },
  24. schema: [
  25. {
  26. type: 'object',
  27. properties: {
  28. checkAliases: {
  29. default: false,
  30. type: 'boolean'
  31. }
  32. },
  33. additionalProperties: false
  34. }
  35. ]
  36. },
  37. create: Components.detect((context, components, utils) => {
  38. const config = context.options[0] || {};
  39. const checkAliases = config.checkAliases || false;
  40. const isApplicable = versionUtil.testReactVersion(context, '16.3.0');
  41. if (!isApplicable) {
  42. return {};
  43. }
  44. const unsafe = {
  45. UNSAFE_componentWillMount: {
  46. newMethod: 'componentDidMount',
  47. details:
  48. 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.'
  49. },
  50. UNSAFE_componentWillReceiveProps: {
  51. newMethod: 'getDerivedStateFromProps',
  52. details:
  53. 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.'
  54. },
  55. UNSAFE_componentWillUpdate: {
  56. newMethod: 'componentDidUpdate',
  57. details:
  58. '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. context.report({
  95. node,
  96. messageId: 'unsafeMethod',
  97. data: {
  98. method,
  99. newMethod,
  100. details
  101. }
  102. });
  103. }
  104. /**
  105. * Returns life cycle methods if available
  106. * @param {ASTNode} node The AST node being checked.
  107. * @returns {Array} The array of methods.
  108. */
  109. function getLifeCycleMethods(node) {
  110. const properties = astUtil.getComponentProperties(node);
  111. return properties.map((property) => astUtil.getPropertyName(property));
  112. }
  113. /**
  114. * Checks life cycle methods
  115. * @param {ASTNode} node The AST node being checked.
  116. */
  117. function checkLifeCycleMethods(node) {
  118. if (utils.isES5Component(node) || utils.isES6Component(node)) {
  119. const methods = getLifeCycleMethods(node);
  120. methods.forEach((method) => checkUnsafe(node, method));
  121. }
  122. }
  123. return {
  124. ClassDeclaration: checkLifeCycleMethods,
  125. ClassExpression: checkLifeCycleMethods,
  126. ObjectExpression: checkLifeCycleMethods
  127. };
  128. })
  129. };