jsx-sort-default-props.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /**
  2. * @fileoverview Enforce default props alphabetical sorting
  3. * @author Vladimir Kattsov
  4. * @deprecated
  5. */
  6. 'use strict';
  7. const variableUtil = require('../util/variable');
  8. const docsUrl = require('../util/docsUrl');
  9. const report = require('../util/report');
  10. const log = require('../util/log');
  11. const eslintUtil = require('../util/eslint');
  12. const getFirstTokens = eslintUtil.getFirstTokens;
  13. const getText = eslintUtil.getText;
  14. let isWarnedForDeprecation = false;
  15. // ------------------------------------------------------------------------------
  16. // Rule Definition
  17. // ------------------------------------------------------------------------------
  18. const messages = {
  19. propsNotSorted: 'Default prop types declarations should be sorted alphabetically',
  20. };
  21. /** @type {import('eslint').Rule.RuleModule} */
  22. module.exports = {
  23. meta: {
  24. deprecated: true,
  25. replacedBy: ['sort-default-props'],
  26. docs: {
  27. description: 'Enforce defaultProps declarations alphabetical sorting',
  28. category: 'Stylistic Issues',
  29. recommended: false,
  30. url: docsUrl('jsx-sort-default-props'),
  31. },
  32. // fixable: 'code',
  33. messages,
  34. schema: [{
  35. type: 'object',
  36. properties: {
  37. ignoreCase: {
  38. type: 'boolean',
  39. },
  40. },
  41. additionalProperties: false,
  42. }],
  43. },
  44. create(context) {
  45. const configuration = context.options[0] || {};
  46. const ignoreCase = configuration.ignoreCase || false;
  47. /**
  48. * Get properties name
  49. * @param {Object} node - Property.
  50. * @returns {string} Property name.
  51. */
  52. function getPropertyName(node) {
  53. if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) {
  54. return node.key.name;
  55. }
  56. if (node.type === 'MemberExpression') {
  57. return node.property.name;
  58. // Special case for class properties
  59. // (babel-eslint@5 does not expose property name so we have to rely on tokens)
  60. }
  61. if (node.type === 'ClassProperty') {
  62. const tokens = getFirstTokens(context, node, 2);
  63. return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value;
  64. }
  65. return '';
  66. }
  67. /**
  68. * Checks if the Identifier node passed in looks like a defaultProps declaration.
  69. * @param {ASTNode} node The node to check. Must be an Identifier node.
  70. * @returns {boolean} `true` if the node is a defaultProps declaration, `false` if not
  71. */
  72. function isDefaultPropsDeclaration(node) {
  73. const propName = getPropertyName(node);
  74. return (propName === 'defaultProps' || propName === 'getDefaultProps');
  75. }
  76. function getKey(node) {
  77. return getText(context, node.key || node.argument);
  78. }
  79. /**
  80. * Find a variable by name in the current scope.
  81. * @param {ASTNode} node The node to look for.
  82. * @param {string} name Name of the variable to look for.
  83. * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
  84. */
  85. function findVariableByName(node, name) {
  86. const variable = variableUtil
  87. .getVariableFromContext(context, node, name);
  88. if (!variable || !variable.defs[0] || !variable.defs[0].node) {
  89. return null;
  90. }
  91. if (variable.defs[0].node.type === 'TypeAlias') {
  92. return variable.defs[0].node.right;
  93. }
  94. return variable.defs[0].node.init;
  95. }
  96. /**
  97. * Checks if defaultProps declarations are sorted
  98. * @param {Array} declarations The array of AST nodes being checked.
  99. * @returns {void}
  100. */
  101. function checkSorted(declarations) {
  102. // function fix(fixer) {
  103. // return propTypesSortUtil.fixPropTypesSort(context, fixer, declarations, ignoreCase);
  104. // }
  105. declarations.reduce((prev, curr, idx, decls) => {
  106. if (/Spread(?:Property|Element)$/.test(curr.type)) {
  107. return decls[idx + 1];
  108. }
  109. let prevPropName = getKey(prev);
  110. let currentPropName = getKey(curr);
  111. if (ignoreCase) {
  112. prevPropName = prevPropName.toLowerCase();
  113. currentPropName = currentPropName.toLowerCase();
  114. }
  115. if (currentPropName < prevPropName) {
  116. report(context, messages.propsNotSorted, 'propsNotSorted', {
  117. node: curr,
  118. // fix
  119. });
  120. return prev;
  121. }
  122. return curr;
  123. }, declarations[0]);
  124. }
  125. function checkNode(node) {
  126. if (!node) {
  127. return;
  128. }
  129. if (node.type === 'ObjectExpression') {
  130. checkSorted(node.properties);
  131. } else if (node.type === 'Identifier') {
  132. const propTypesObject = findVariableByName(node, node.name);
  133. if (propTypesObject && propTypesObject.properties) {
  134. checkSorted(propTypesObject.properties);
  135. }
  136. }
  137. }
  138. // --------------------------------------------------------------------------
  139. // Public API
  140. // --------------------------------------------------------------------------
  141. return {
  142. 'ClassProperty, PropertyDefinition'(node) {
  143. if (!isDefaultPropsDeclaration(node)) {
  144. return;
  145. }
  146. checkNode(node.value);
  147. },
  148. MemberExpression(node) {
  149. if (!isDefaultPropsDeclaration(node)) {
  150. return;
  151. }
  152. checkNode('right' in node.parent && node.parent.right);
  153. },
  154. Program() {
  155. if (isWarnedForDeprecation) {
  156. return;
  157. }
  158. log('The react/jsx-sort-default-props rule is deprecated. It has been renamed to `react/sort-default-props`.');
  159. isWarnedForDeprecation = true;
  160. },
  161. };
  162. },
  163. };