text: /**
   * @fileoverview Enforce default props alphabetical sorting
   * @author Vladimir Kattsov
   */
  
  'use strict';
  
  const variableUtil = require('../util/variable');
  const docsUrl = require('../util/docsUrl');
  const propWrapperUtil = require('../util/propWrapper');
  // const propTypesSortUtil = require('../util/propTypesSort');
  const report = require('../util/report');
  
  // ------------------------------------------------------------------------------
  // Rule Definition
  // ------------------------------------------------------------------------------
  
  const messages = {
    propsNotSorted: 'Default prop types declarations should be sorted alphabetically'
  };
  
  module.exports = {
    meta: {
      docs: {
        description: 'Enforce default props alphabetical sorting',
        category: 'Stylistic Issues',
        recommended: false,
        url: docsUrl('jsx-sort-default-props')
      },
      // fixable: 'code',
  
      messages,
  
      schema: [{
        type: 'object',
        properties: {
          ignoreCase: {
            type: 'boolean'
          }
        },
        additionalProperties: false
      }]
    },
  
    create(context) {
      const configuration = context.options[0] || {};
      const ignoreCase = configuration.ignoreCase || false;
  
      /**
       * Get properties name
       * @param {Object} node - Property.
       * @returns {String} Property name.
       */
      function getPropertyName(node) {
        if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) {
          return node.key.name;
        }
        if (node.type === 'MemberExpression') {
          return node.property.name;
        // Special case for class properties
        // (babel-eslint@5 does not expose property name so we have to rely on tokens)
        }
        if (node.type === 'ClassProperty') {
          const tokens = context.getFirstTokens(node, 2);
          return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value;
        }
        return '';
      }
  
      /**
       * Checks if the Identifier node passed in looks like a defaultProps declaration.
       * @param   {ASTNode}  node The node to check. Must be an Identifier node.
       * @returns {Boolean}       `true` if the node is a defaultProps declaration, `false` if not
       */
      function isDefaultPropsDeclaration(node) {
        const propName = getPropertyName(node);
        return (propName === 'defaultProps' || propName === 'getDefaultProps');
      }
  
      function getKey(node) {
        return context.getSourceCode().getText(node.key || node.argument);
      }
  
      /**
       * Find a variable by name in the current scope.
       * @param  {string} name Name of the variable to look for.
       * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise.
       */
      function findVariableByName(name) {
        const variable = variableUtil.variablesInScope(context).find((item) => item.name === name);
  
        if (!variable || !variable.defs[0] || !variable.defs[0].node) {
          return null;
        }
  
        if (variable.defs[0].node.type === 'TypeAlias') {
          return variable.defs[0].node.right;
        }
  
        return variable.defs[0].node.init;
      }
  
      /**
       * Checks if defaultProps declarations are sorted
       * @param {Array} declarations The array of AST nodes being checked.
       * @returns {void}
       */
      function checkSorted(declarations) {
        // function fix(fixer) {
        //   return propTypesSortUtil.fixPropTypesSort(fixer, context, declarations, ignoreCase);
        // }
  
        declarations.reduce((prev, curr, idx, decls) => {
          if (/Spread(?:Property|Element)$/.test(curr.type)) {
            return decls[idx + 1];
          }
  
          let prevPropName = getKey(prev);
          let currentPropName = getKey(curr);
  
          if (ignoreCase) {
            prevPropName = prevPropName.toLowerCase();
            currentPropName = currentPropName.toLowerCase();
          }
  
          if (currentPropName < prevPropName) {
            report(context, messages.propsNotSorted, 'propsNotSorted', {
              node: curr
              // fix
            });
  
            return prev;
          }
  
          return curr;
        }, declarations[0]);
      }
  
      function checkNode(node) {
        switch (node && node.type) {
          case 'ObjectExpression':
            checkSorted(node.properties);
            break;
          case 'Identifier': {
            const propTypesObject = findVariableByName(node.name);
            if (propTypesObject && propTypesObject.properties) {
              checkSorted(propTypesObject.properties);
            }
            break;
          }
          case 'CallExpression': {
            const innerNode = node.arguments && node.arguments[0];
            if (propWrapperUtil.isPropWrapperFunction(context, node.callee.name) && innerNode) {
              checkNode(innerNode);
            }
            break;
          }
          default:
            break;
        }
      }
  
      // --------------------------------------------------------------------------
      // Public API
      // --------------------------------------------------------------------------
  
      return {
        ClassProperty(node) {
          if (!isDefaultPropsDeclaration(node)) {
            return;
          }
  
          checkNode(node.value);
        },
  
        MemberExpression(node) {
          if (!isDefaultPropsDeclaration(node)) {
            return;
          }
  
          checkNode(node.parent.right);
        }
      };
    }
  };
  