All files / src/utils getFlowTypeFromReactComponent.js

100% Statements 34/34
97.06% Branches 33/34
100% Functions 5/5
100% Lines 34/34
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 97 98 99 100 101 102 103 104 105 106                                        3x                   50x 14x   14x 6x   8x 8x 2x     6x   36x 24x   24x     48x 30x 30x         8x     22x     40x             28x 21x 39x   7x 2x 5x   5x     3x 3x 1x               3x 3x 3x         2x     1x     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 getTypeAnnotation from '../utils/getTypeAnnotation';
import getMemberValuePath from '../utils/getMemberValuePath';
import isReactComponentClass from '../utils/isReactComponentClass';
import isStatelessComponent from '../utils/isStatelessComponent';
import isUnreachableFlowType from '../utils/isUnreachableFlowType';
import recast from 'recast';
import resolveToValue from '../utils/resolveToValue';
 
var {types: {namedTypes: types}} = recast;
 
/**
 * Given an React component (stateless or class) tries to find the
 * flow type for the props. If not found or not one of the supported
 * component types returns null.
 */
export default (path: NodePath): ?NodePath  => {
  let typePath: ?NodePath;
 
  if (isReactComponentClass(path)) {
    const superTypes = path.get('superTypeParameters');
 
    if (superTypes.value) {
      typePath = superTypes.get('params').get(1);
    } else {
      const propsMemberPath = getMemberValuePath(path, 'props');
      if (!propsMemberPath) {
        return null;
      }
 
      typePath = getTypeAnnotation(propsMemberPath.parentPath);
    }
  } else if (isStatelessComponent(path)) {
    const param = path.get('params').get(0);
 
    typePath = getTypeAnnotation(param);
  }
 
  if (typePath && types.GenericTypeAnnotation.check(typePath.node)) {
    typePath = resolveToValue(typePath.get('id'));
    if (
      !typePath ||
      types.Identifier.check(typePath.node) ||
      isUnreachableFlowType(typePath)
    ) {
      return;
    }
 
    typePath = typePath.get('right');
  }
 
  return typePath;
}
 
export function applyToFlowTypeProperties(
  path: NodePath,
  callback: (propertyPath: NodePath) => void
) {
  if (path.node.properties) {
    path.get('properties').each(
      propertyPath => callback(propertyPath)
    );
  } else if (path.node.type === 'IntersectionTypeAnnotation') {
    path.get('types').each(
      typesPath => applyToFlowTypeProperties(typesPath, callback)
    );
  } else if (path.node.type !== 'UnionTypeAnnotation') {
    // The react-docgen output format does not currently allow
    // for the expression of union types
    let typePath = resolveGenericTypeAnnotation(path);
    if (typePath) {
      applyToFlowTypeProperties(typePath, callback);
    }
  }
}
 
function resolveGenericTypeAnnotation(path: NodePath): ?NodePath {
  // If the node doesn't have types or properties, try to get the type.
  let typePath: ?NodePath;
  Eif (path && types.GenericTypeAnnotation.check(path.node)) {
    typePath = resolveToValue(path.get('id'));
    if (
      !typePath ||
      types.Identifier.check(typePath.node) ||
      isUnreachableFlowType(typePath)
    ) {
      return;
    }
 
    typePath = typePath.get('right');
  }
 
  return typePath;
}