Home Reference Source Repository

src/container/getRelayQueries.js

/**
 * Copyright (c) 2013-present, 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.
 *
 * @providesModule getRelayQueries
 * @flow
 */

'use strict';

const Map = require('Map');
import type {RelayLazyContainer} from 'RelayContainer';
import type {RelayQuerySet} from 'RelayInternalTypes';
const RelayMetaRoute = require('RelayMetaRoute');
const RelayProfiler = require('RelayProfiler');
const RelayQuery = require('RelayQuery');
const RelayQueryCaching = require('RelayQueryCaching');
import type {RelayQueryConfigInterface} from 'RelayQueryConfig';

const buildRQL = require('buildRQL');
const invariant = require('invariant');
const stableStringify = require('stableStringify');
const warning = require('warning');

const queryCache = new Map();

/**
 * @internal
 *
 * `getRelayQueries` retrieves all queries for a component given a route.
 */
function getRelayQueries(
  Component: RelayLazyContainer,
  route: RelayQueryConfigInterface
): RelayQuerySet {
  const queryCachingEnabled = RelayQueryCaching.getEnabled();
  if (!queryCachingEnabled) {
    return buildQuerySet(Component, route);
  }
  let cache = queryCache.get(Component);
  if (!cache) {
    cache = {};
    queryCache.set(Component, cache);
  }
  const cacheKey = route.name + ':' + stableStringify(route.params);
  if (cache.hasOwnProperty(cacheKey)) {
    return cache[cacheKey];
  }
  const querySet = buildQuerySet(Component, route);
  cache[cacheKey] = querySet;
  return querySet;
}

/**
 * @internal
 */
function buildQuerySet(
  Component: RelayLazyContainer,
  route: RelayQueryConfigInterface
): RelayQuerySet {
  const querySet = {};
  Component.getFragmentNames().forEach(fragmentName => {
    querySet[fragmentName] = null;
  });
  Object.keys(route.queries).forEach(queryName => {
    if (!Component.hasFragment(queryName)) {
      warning(
        false,
        'Relay.QL: query `%s.queries.%s` is invalid, expected fragment ' +
        '`%s.fragments.%s` to be defined.',
        route.name,
        queryName,
        Component.displayName,
        queryName
      );
      return;
    }
    const queryBuilder = route.queries[queryName];
    if (queryBuilder) {
      const concreteQuery = buildRQL.Query(
        queryBuilder,
        Component,
        queryName,
        route.params
      );
      invariant(
        concreteQuery !== undefined,
        'Relay.QL: query `%s.queries.%s` is invalid, a typical query is ' +
        'defined using: () => Relay.QL`query { ... }`.',
        route.name,
        queryName
      );
      if (concreteQuery) {
        const rootQuery = RelayQuery.Root.create(
          concreteQuery,
          RelayMetaRoute.get(route.name),
          route.params
        );
        const identifyingArg = rootQuery.getIdentifyingArg();
        if (!identifyingArg || identifyingArg.value !== undefined) {
          querySet[queryName] = rootQuery;
          return;
        }
      }
    }
    querySet[queryName] = null;
  });
  return querySet;
}

module.exports = RelayProfiler.instrument('Relay.getQueries', getRelayQueries);