text: /**
   * Copyright 2015-present, Facebook, Inc.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   */
  
  /**
   * class Component extends React.Component {
   *   constructor() { this.onClick = this.onClick.bind(this); }
   *   onClick() { }
   * }
   *
   * -->
   *
   * class Component extends React.Component {
   *   onClick = () => { }
   * }
   */
  
  export default function transformer(file, api, options) {
    const j = api.jscodeshift;
  
    const printOptions = options.printOptions || {};
    var root = j(file.source);
  
    // Helper functions to transform a method declaration to an arrow function
    // By default recast drops comments and jscodeshift doesn't have a way to
    // set the return type in the convenience method. Otherwise we would have
    // inlined all those.
    function withComments(to, from) {
      to.comments = from.comments;
      return to;
    }
  
    function createArrowFunctionExpression(fn) {
      var arrowFunc = j.arrowFunctionExpression(fn.params, fn.body, false);
  
      arrowFunc.returnType = fn.returnType;
      arrowFunc.defaults = fn.defaults;
      arrowFunc.rest = fn.rest;
      arrowFunc.async = fn.async;
  
      return arrowFunc;
    }
  
    function createArrowProperty(prop) {
      return withComments(
        j.classProperty(
          j.identifier(prop.key.name),
          createArrowFunctionExpression(prop.value),
          null,
          false
        ),
        prop
      );
    }
  
    var hasChanged = false;
    var transform = root.find(j.AssignmentExpression).forEach(path => {
      // Check that the englobing function is constructor
      var methodPath = path;
      while (
        methodPath &&
        (methodPath.node.type !== 'MethodDefinition' ||
          methodPath.node.kind !== 'constructor')
      ) {
        methodPath = methodPath.parentPath;
      }
      if (!methodPath) {
        return;
      }
  
      // Check that it looks like
      // this.method = this.method.bind(this);
      // or
      // (this: any).method = this.method.bind(this);
      // or
      // self.method = this.method.bind(this);
      if (
        !(
          path.node.left.type === 'MemberExpression' &&
          // this
          (path.node.left.object.type === 'ThisExpression' ||
            // self
            (path.node.left.object.type === 'Identifier' &&
              path.node.left.object.name === 'self') ||
            // (this: any)
            (path.node.left.object.type === 'TypeCastExpression' &&
              path.node.left.object.expression.type === 'ThisExpression')) &&
          path.node.left.property.type === 'Identifier' &&
          path.node.right.type === 'CallExpression' &&
          path.node.right.callee.type === 'MemberExpression' &&
          path.node.right.callee.property.type === 'Identifier' &&
          path.node.right.callee.property.name === 'bind' &&
          path.node.right.callee.object.type === 'MemberExpression' &&
          path.node.right.callee.object.property.type === 'Identifier' &&
          path.node.right.callee.object.object.type === 'ThisExpression' &&
          path.node.left.property.name ===
            path.node.right.callee.object.property.name &&
          true
        )
      ) {
        return;
      }
  
      // Find the method() declaration and replace it with an arrow function
      var methodName = path.node.left.property.name;
  
      const componentDecl = methodPath.parentPath;
      var methods = j(componentDecl)
        .find(j.MethodDefinition)
        .filter(
          path =>
            path.node.key.type === 'Identifier' &&
            path.node.key.name === methodName
        );
  
      // Do not remove the binding if there's no corresponding method to turn
      // into an arrow function
      if (methods.size() === 0) {
        return;
      }
      methods.replaceWith(path => createArrowProperty(path.node));
      
      // Remove the line
      // this.method = this.method.bind(this);
      j(path.parentPath).remove();
  
      var selfCount = j(methodPath)
        .find(j.Identifier, { name: 'self' })
        .size();
      if (selfCount === 1) {
        // Remove the line
        // const self: any = this;
        // If self is present somewhere else in the method, then it is
        // not safe to do.
        j(methodPath)
          .find(j.VariableDeclaration)
          .filter(
            path =>
              j(path)
                .find(j.Identifier, { name: 'self' })
                .size() === 1
          )
          .remove();
      }
  
      // If we delete everything from the constructor but the super() call,
      // then delete the entire constructor.
      var canDeleteConstructor = true;
      methodPath.node.value.body.body.forEach(node => {
        if (
          !node ||
          (node.type === 'ExpressionStatement' &&
            node.expression.type === 'CallExpression' &&
            // babylon parser
            (node.expression.callee.type === 'Super' ||
              // flow parser
              (node.expression.callee.type === 'Identifier' &&
                node.expression.callee.name === 'super')))
        ) {
          return;
        }
        canDeleteConstructor = false;
      });
      if (canDeleteConstructor) {
        j(methodPath).remove();
      }
  
      hasChanged++;
    });
  
    if (hasChanged) {
      return transform.toSource(printOptions);
    }
    return null;
  }
  
  // module.exports.parser = 'flow';
  