UNPKG

react-relay

Version:

A framework for building data-driven React applications.

234 lines (202 loc) 7.18 kB
/** * Copyright 2013-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. * * @providesModule RelayFragmentReference * @typechecks * */ 'use strict'; var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default']; var _extends = require('babel-runtime/helpers/extends')['default']; var GraphQL = require('./GraphQL'); var forEachObject = require('fbjs/lib/forEachObject'); var getWeakIdForObject = require('./getWeakIdForObject'); var invariant = require('fbjs/lib/invariant'); /** * @internal * * RelayFragmentReference is the return type of fragment composition: * * fragment on Foo { * ${Child.getFragment('bar', {baz: variables.qux})} * } * * Whereas a fragment defines a sub-query's structure, a fragment reference is * a particular instantiation of the fragment as it is composed within a query * or another fragment. It encodes the source fragment, initial variables, and * a mapping from variables in the composing query's (or fragment's) scope to * variables in the fragment's scope. * * The variable mapping is represented by `variableMapping`, a dictionary that * maps from names of variables in the parent scope to variables that exist in * the fragment. Example: * * ``` * // Fragment: * var Container = Relay.createContainer(..., { * initialVariables: { * private: 'foo', * public: 'bar', * variable: null, * }, * fragments: { * foo: ... * } * }); * * // Reference: * ${Container.getQuery( * 'foo', * // Variable Mapping: * { * public: 'BAR', * variable: variables.source, * } * )} * ``` * * When evaluating the referenced fragment, `$public` will be overridden with * `'Bar'`. The value of `$variable` will become the value of `$source` in the * outer scope. This is analagous to: * * ``` * function inner(private = 'foo', public = 'bar', variable) {} * function outer(source) { * inner(public = 'BAR', variable = source); * } * ``` * * Where the value of the inner `variable` depends on how `outer` is called. * * The `prepareVariables` function allows for variables to be modified based on * the runtime environment or route name. */ var RelayFragmentReference = (function () { function RelayFragmentReference(fragmentGetter, initialVariables, variableMapping, prepareVariables) { _classCallCheck(this, RelayFragmentReference); this._initialVariables = initialVariables || {}; this._fragment = undefined; this._fragmentGetter = fragmentGetter; this._isDeferred = false; this._isTypeConditional = false; this._variableMapping = variableMapping; this._prepareVariables = prepareVariables; // Help find `getFragment` calls with undefined variable values. // For example, `${Child.getFragment('foo', {variable: undefined})}`. if (process.env.NODE_ENV !== 'production') { if (variableMapping) { forEachObject(variableMapping, function (variableValue, variableName) { if (variableValue === undefined) { console.error('RelayFragmentReference: Variable `%s` cannot be undefined.', variableName); } }); } } } /** * Mark this usage of the fragment as deferred. */ RelayFragmentReference.prototype.defer = function defer() { this._isDeferred = true; return this; }; /** * Mark this usage of the fragment as conditional on its type. */ RelayFragmentReference.prototype.conditionOnType = function conditionOnType() { this._isTypeConditional = true; return this; }; /** * Mark this fragment for inclusion only if the given variable is truthy. */ RelayFragmentReference.prototype['if'] = function _if(callVariable) { !GraphQL.isCallVariable(callVariable) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayFragmentReference: Invalid value `%s` supplied to `if()`. ' + 'Expected a variable.', callVariable) : invariant(false) : undefined; this._addCondition(function (variables) { return !!variables[callVariable.callVariableName]; }); return this; }; /** * Mark this fragment for inclusion only if the given variable is falsy. */ RelayFragmentReference.prototype.unless = function unless(callVariable) { !GraphQL.isCallVariable(callVariable) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'RelayFragmentReference: Invalid value `%s` supplied to `unless()`. ' + 'Expected a variable.', callVariable) : invariant(false) : undefined; this._addCondition(function (variables) { return !variables[callVariable.callVariableName]; }); return this; }; /** * @private * * Memoize the fragment so it has the same `getWeakIdForObject`. */ RelayFragmentReference.prototype._getFragment = function _getFragment() { if (this._fragment == null) { this._fragment = this._fragmentGetter(); } return this._fragment; }; /** * Get the referenced fragment if all conditions are met. */ RelayFragmentReference.prototype.getFragment = function getFragment(variables) { // determine if the variables match the supplied if/unless conditions var conditions = this._conditions; if (conditions && !conditions.every(function (cb) { return cb(variables); })) { return null; } return this._getFragment(); }; /** * Get the variables to pass to the referenced fragment, accounting for * initial values, overrides, and route-specific variables. */ RelayFragmentReference.prototype.getVariables = function getVariables(route, variables) { var innerVariables = _extends({}, this._initialVariables); // map variables from outer -> inner scope var variableMapping = this._variableMapping; if (variableMapping) { forEachObject(variableMapping, function (value, name) { if (GraphQL.isCallVariable(value)) { value = variables[value.callVariableName]; } if (value !== undefined) { innerVariables[name] = value; } }); } var prepareVariables = this._prepareVariables; if (prepareVariables) { innerVariables = prepareVariables(innerVariables, route); } return innerVariables; }; RelayFragmentReference.prototype.getFragmentName = function getFragmentName() { return getWeakIdForObject(this._getFragment()); }; RelayFragmentReference.prototype.isTypeConditional = function isTypeConditional() { return this._isTypeConditional; }; RelayFragmentReference.prototype.isDeferred = function isDeferred() { return this._isDeferred; }; RelayFragmentReference.prototype._addCondition = function _addCondition(condition) { var conditions = this._conditions; if (!conditions) { conditions = []; this._conditions = conditions; } conditions.push(condition); }; return RelayFragmentReference; })(); module.exports = RelayFragmentReference;