<commit_before>import {
  checkResolved,
  getComponents,
  getRouteMatches,
  getRouteValues,
  isResolved,
} from 'found/lib/ResolverUtils';
import isPromise from 'is-promise';
import isEqual from 'lodash/isEqual';
import React from 'react';

import QuerySubscription from './QuerySubscription';
import ReadyStateRenderer from './ReadyStateRenderer';
import renderElement from './renderElement';

export default class Resolver {
  constructor(environment) {
    this.environment = environment;

    this.lastQueries = [];
    this.lastRouteVariables = [];
    this.lastQuerySubscriptions = [];
  }

  async *resolveElements(match) {
    const routeMatches = getRouteMatches(match);

    const Components = getComponents(routeMatches);
    const queries = getRouteValues(
      routeMatches,
      route => route.getQuery,
      route => route.query,
    );
    const cacheConfigs = getRouteValues(
      routeMatches,
      route => route.getCacheConfig,
      route => route.cacheConfig,
    );

    const routeVariables = this.getRouteVariables(routeMatches);
    const querySubscriptions = this.updateQuerySubscriptions(
      queries,
      routeVariables,
      cacheConfigs,
    );

    const fetches = querySubscriptions.map(
      querySubscription => querySubscription && querySubscription.fetch(),
    );

    const earlyComponents = Components.some(isPromise)
      ? await Promise.all(Components.map(checkResolved))
      : Components;
    const earlyData = await Promise.all(fetches.map(checkResolved));

    let fetchedComponents;

    if (!earlyComponents.every(isResolved) || !earlyData.every(isResolved)) {
      const pendingElements = this.createElements(
        routeMatches,
        earlyComponents,
        querySubscriptions,
        false,
      );

      yield pendingElements.every(element => element !== undefined)
        ? pendingElements
        : undefined;

      fetchedComponents = await Promise.all(Components);
      await Promise.all(fetches);
    } else {
      fetchedComponents = earlyComponents;
    }

    yield this.createElements(
      routeMatches,
      fetchedComponents,
      querySubscriptions,
      true,
    );
  }

  getRouteVariables(routeMatches) {
    let variables = null;

    return routeMatches.map(routeMatch => {
      const { route } = routeMatch;

      // We need to always run this to make sure we don't miss route params.
      variables = { ...variables, ...routeMatch.routeParams };
      if (route.prepareVariables) {
        variables = route.prepareVariables(variables, routeMatch);
      }

      return variables;
    });
  }

  updateQuerySubscriptions(queries, routeVariables, cacheConfigs) {
    const {
      createOperationSelector,
      getOperation,
    } = this.environment.unstable_internal;

    const querySubscriptions = queries.map((query, i) => {
      if (!query) {
        return null;
      }

      const variables = routeVariables[i];

      if (
        this.lastQueries[i] === query &&
        isEqual(this.lastRouteVariables[i], variables)
      ) {
        // Match the logic in <QueryRenderer> for not refetching.
        const lastQuerySubscription = this.lastQuerySubscriptions[i];
        this.lastQuerySubscriptions[i] = null;
        return lastQuerySubscription;
      }

      return new QuerySubscription(
        this.environment,
        createOperationSelector(getOperation(query), variables),<commit_msg>Support getRequest instead of getOperation from relay<commit_after>import {
  checkResolved,
  getComponents,
  getRouteMatches,
  getRouteValues,
  isResolved,
} from 'found/lib/ResolverUtils';
import isPromise from 'is-promise';
import isEqual from 'lodash/isEqual';
import React from 'react';

import QuerySubscription from './QuerySubscription';
import ReadyStateRenderer from './ReadyStateRenderer';
import renderElement from './renderElement';

export default class Resolver {
  constructor(environment) {
    this.environment = environment;

    this.lastQueries = [];
    this.lastRouteVariables = [];
    this.lastQuerySubscriptions = [];
  }

  async *resolveElements(match) {
    const routeMatches = getRouteMatches(match);

    const Components = getComponents(routeMatches);
    const queries = getRouteValues(
      routeMatches,
      route => route.getQuery,
      route => route.query,
    );
    const cacheConfigs = getRouteValues(
      routeMatches,
      route => route.getCacheConfig,
      route => route.cacheConfig,
    );

    const routeVariables = this.getRouteVariables(routeMatches);
    const querySubscriptions = this.updateQuerySubscriptions(
      queries,
      routeVariables,
      cacheConfigs,
    );

    const fetches = querySubscriptions.map(
      querySubscription => querySubscription && querySubscription.fetch(),
    );

    const earlyComponents = Components.some(isPromise)
      ? await Promise.all(Components.map(checkResolved))
      : Components;
    const earlyData = await Promise.all(fetches.map(checkResolved));

    let fetchedComponents;

    if (!earlyComponents.every(isResolved) || !earlyData.every(isResolved)) {
      const pendingElements = this.createElements(
        routeMatches,
        earlyComponents,
        querySubscriptions,
        false,
      );

      yield pendingElements.every(element => element !== undefined)
        ? pendingElements
        : undefined;

      fetchedComponents = await Promise.all(Components);
      await Promise.all(fetches);
    } else {
      fetchedComponents = earlyComponents;
    }

    yield this.createElements(
      routeMatches,
      fetchedComponents,
      querySubscriptions,
      true,
    );
  }

  getRouteVariables(routeMatches) {
    let variables = null;

    return routeMatches.map(routeMatch => {
      const { route } = routeMatch;

      // We need to always run this to make sure we don't miss route params.
      variables = { ...variables, ...routeMatch.routeParams };
      if (route.prepareVariables) {
        variables = route.prepareVariables(variables, routeMatch);
      }

      return variables;
    });
  }

  updateQuerySubscriptions(queries, routeVariables, cacheConfigs) {
    const {
      createOperationSelector,
      getOperation,
      getRequest,
    } = this.environment.unstable_internal;

    // FIXME: Use getRequest directly when only supporting relay >=1.5.0.
    const getOperationOrRequest = getOperation || getRequest;

    const querySubscriptions = queries.map((query, i) => {
      if (!query) {
        return null;
      }

      const variables = routeVariables[i];

      if (
        this.lastQueries[i] === query &&
        isEqual(this.lastRouteVariables[i], variables)
      ) {
        // Match the logic in <QueryRenderer> for not refetching.
        const lastQuerySubscription = this.lastQuerySubscriptions[i];
        this.lastQuerySubscriptions[i] = null;
        return lastQuerySubscription;
      }

      return new QuerySubscription(
        this.environment,
        createOperationSelector(getOperationOrRequest(query), variables),
{'fraction_non_alphanumeric': 0.09469051675184555, 'fraction_numerical': 0.00042589437819420784, 'mean_word_length': 3.025714285714286, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5073478', 'n_tokens_mistral': 1895, 'n_tokens_neox': 1858, 'n_words': 419}