All files / src/utils normalizeClassDefinition.js

100% Statements 29/29
92.31% Branches 24/26
100% Functions 3/3
100% Lines 28/28
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96                              5x 132x                                                 80x 41x 39x 39x 39x   39x   37x 37x 2x   1x 1x   1x       80x 1x     79x 79x             32x 31x 31x   7x 7x 7x           7x 7x     24x   1x          
/*
 * Copyright (c) 2015, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @flow
 */
 
import getMemberExpressionRoot from '../utils/getMemberExpressionRoot';
import getMembers from '../utils/getMembers';
import recast from 'recast';
 
var {types: {namedTypes: types, builders}} = recast;
var ignore = () => false;
 
/**
 * Given a class definition (i.e. `class` declaration or expression), this
 * function "normalizes" the definition, by looking for assignments of static
 * properties and converting them to ClassProperties.
 *
 * Example:
 *
 * class MyComponent extends React.Component {
 *   // ...
 * }
 * MyComponent.propTypes = { ... };
 *
 * is converted to
 *
 * class MyComponent extends React.Component {
 *   // ...
 *   static propTypes = { ... };
 * }
 */
export default function normalizeClassDefinition(
  classDefinition: NodePath
): void {
  var variableName;
  if (types.ClassDeclaration.check(classDefinition.node)) {
    variableName = classDefinition.node.id.name;
  } else Eif (types.ClassExpression.check(classDefinition.node)) {
    var {parentPath} = classDefinition;
    while (parentPath.node !== classDefinition.scope.node &&
        !types.BlockStatement.check(parentPath.node)) {
      if (types.VariableDeclarator.check(parentPath.node) &&
        types.Identifier.check(parentPath.node.id)) {
        variableName = parentPath.node.id.name;
        break;
      } else if (types.AssignmentExpression.check(parentPath.node) &&
        types.Identifier.check(parentPath.node.left)) {
        variableName = parentPath.node.left.name;
        break;
      }
      parentPath = parentPath.parentPath;
    }
  }
 
  if (!variableName) {
    return;
  }
 
  var scopeRoot = classDefinition.scope;
  recast.visit(scopeRoot.node, {
    visitFunction: ignore,
    visitClassDeclaration: ignore,
    visitClassExpression: ignore,
    visitForInStatement: ignore,
    visitForStatement: ignore,
    visitAssignmentExpression: function(path) {
      if (types.MemberExpression.check(path.node.left)) {
        var first = getMemberExpressionRoot(path.get('left'));
        if (types.Identifier.check(first.node) &&
          first.node.name === variableName) {
          var [member] = getMembers(path.get('left'));
          Eif (member && !member.path.node.computed) {
            var classProperty = builders.classProperty(
              member.path.node,
              path.node.right,
              null,
              true
            );
            classDefinition.get('body', 'body').value.push(classProperty);
            return false;
          }
        }
        this.traverse(path);
      } else {
        return false;
      }
    },
  });
}