export default function resolveToValue()

in src/utils/resolveToValue.ts [120:229]


export default function resolveToValue(
  path: NodePath,
  importer: Importer,
): NodePath {
  const node = path.node;
  if (t.VariableDeclarator.check(node)) {
    if (node.init) {
      return resolveToValue(path.get('init'), importer);
    }
  } else if (t.MemberExpression.check(node)) {
    const root = getMemberExpressionRoot(path);
    const resolved = resolveToValue(root, importer);
    if (t.ObjectExpression.check(resolved.node)) {
      let propertyPath: NodePath | null = resolved;
      for (const propertyName of toArray(path, importer).slice(1)) {
        if (propertyPath && t.ObjectExpression.check(propertyPath.node)) {
          propertyPath = getPropertyValuePath(
            propertyPath,
            propertyName,
            importer,
          );
        }
        if (!propertyPath) {
          return path;
        }
        propertyPath = resolveToValue(propertyPath, importer);
      }
      return propertyPath;
    } else if (isSupportedDefinitionType(resolved)) {
      const memberPath = getMemberValuePath(
        resolved,
        path.node.property.name,
        importer,
      );
      if (memberPath) {
        return resolveToValue(memberPath, importer);
      }
    } else if (
      t.ImportDeclaration.check(resolved.node) &&
      resolved.node.specifiers
    ) {
      // Handle references to namespace imports, e.g. import * as foo from 'bar'.
      // Try to find a specifier that matches the root of the member expression, and
      // find the export that matches the property name.
      for (const specifier of resolved.node.specifiers) {
        if (
          t.ImportNamespaceSpecifier.check(specifier) &&
          specifier.local &&
          specifier.local.name === root.node.name
        ) {
          const resolvedPath = importer(
            resolved,
            root.parentPath.node.property.name,
          );
          if (resolvedPath) {
            return resolveToValue(resolvedPath, importer);
          }
        }
      }
    }
  } else if (
    t.ImportDefaultSpecifier.check(node) ||
    t.ImportNamespaceSpecifier.check(node) ||
    t.ImportSpecifier.check(node)
  ) {
    // go up two levels as first level is only the array of specifiers
    return path.parentPath.parentPath;
  } else if (t.AssignmentExpression.check(node)) {
    if (node.operator === '=') {
      return resolveToValue(path.get('right'), importer);
    }
  } else if (
    t.TypeCastExpression.check(node) ||
    t.TSAsExpression.check(node) ||
    t.TSTypeAssertion.check(node)
  ) {
    return resolveToValue(path.get('expression'), importer);
  } else if (t.Identifier.check(node)) {
    if (
      (t.ClassDeclaration.check(path.parentPath.node) ||
        t.ClassExpression.check(path.parentPath.node) ||
        t.Function.check(path.parentPath.node)) &&
      path.parentPath.get('id') === path
    ) {
      return path.parentPath;
    }

    let scope: Scope = path.scope.lookup(node.name);
    let resolvedPath: NodePath | null = null;
    if (scope) {
      // The variable may be assigned a different value after initialization.
      // We are first trying to find all assignments to the variable in the
      // block where it is defined (i.e. we are not traversing into statements)
      resolvedPath = findLastAssignedValue(scope, path, importer);
      if (!resolvedPath) {
        const bindings = scope.getBindings()[node.name];
        resolvedPath = findScopePath(bindings, path, importer);
      }
    } else {
      scope = path.scope.lookupType(node.name);
      if (scope) {
        const typesInScope = scope.getTypes()[node.name];
        resolvedPath = findScopePath(typesInScope, path, importer);
      }
    }
    return resolvedPath || path;
  }

  return path;
}