292 lines
11 KiB
JavaScript
292 lines
11 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
*
|
|
* @format
|
|
*/
|
|
'use strict';
|
|
|
|
/**
|
|
* @public
|
|
*
|
|
* Given the result `item` from a parent that fetched `fragment`, creates a
|
|
* selector that can be used to read the results of that fragment for that item.
|
|
*
|
|
* Example:
|
|
*
|
|
* Given two fragments as follows:
|
|
*
|
|
* ```
|
|
* fragment Parent on User {
|
|
* id
|
|
* ...Child
|
|
* }
|
|
* fragment Child on User {
|
|
* name
|
|
* }
|
|
* ```
|
|
*
|
|
* And given some object `parent` that is the results of `Parent` for id "4",
|
|
* the results of `Child` can be accessed by first getting a selector and then
|
|
* using that selector to `lookup()` the results against the environment:
|
|
*
|
|
* ```
|
|
* const childSelector = getSelector(queryVariables, Child, parent);
|
|
* const childData = environment.lookup(childSelector).data;
|
|
* ```
|
|
*/
|
|
function getSelector(operationVariables, fragment, item) {
|
|
!(typeof item === 'object' && item !== null && !Array.isArray(item)) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernSelector: Expected value for fragment `%s` to be an object, got ' + '`%s`.', fragment.name, JSON.stringify(item)) : require("fbjs/lib/invariant")(false) : void 0;
|
|
|
|
var dataID = item[require("./RelayStoreUtils").ID_KEY];
|
|
|
|
var fragments = item[require("./RelayStoreUtils").FRAGMENTS_KEY];
|
|
|
|
var owner = item[require("./RelayStoreUtils").FRAGMENT_OWNER_KEY];
|
|
|
|
if (typeof dataID === 'string' && typeof fragments === 'object' && fragments !== null && typeof fragments[fragment.name] === 'object' && fragments[fragment.name] !== null) {
|
|
var argumentVariables = fragments[fragment.name];
|
|
|
|
if (owner != null && typeof owner === 'object') {
|
|
// $FlowFixMe - TODO T39154660
|
|
var typedOwner = owner;
|
|
var ownerOperationVariables = typedOwner.variables;
|
|
|
|
var _fragmentVariables = require("./RelayConcreteVariables").getFragmentVariables(fragment, ownerOperationVariables, argumentVariables);
|
|
|
|
return {
|
|
owner: typedOwner,
|
|
selector: {
|
|
dataID: dataID,
|
|
node: fragment,
|
|
variables: _fragmentVariables
|
|
}
|
|
};
|
|
}
|
|
|
|
var fragmentVariables = require("./RelayConcreteVariables").getFragmentVariables(fragment, operationVariables, argumentVariables);
|
|
|
|
return {
|
|
owner: null,
|
|
selector: {
|
|
dataID: dataID,
|
|
node: fragment,
|
|
variables: fragmentVariables
|
|
}
|
|
};
|
|
}
|
|
|
|
process.env.NODE_ENV !== "production" ? require("fbjs/lib/warning")(false, 'RelayModernSelector: Expected object to contain data for fragment `%s`, got ' + '`%s`. Make sure that the parent operation/fragment included fragment ' + '`...%s` without `@relay(mask: false)`.', fragment.name, JSON.stringify(item), fragment.name) : void 0;
|
|
return null;
|
|
}
|
|
/**
|
|
* @public
|
|
*
|
|
* Given the result `items` from a parent that fetched `fragment`, creates a
|
|
* selector that can be used to read the results of that fragment on those
|
|
* items. This is similar to `getSelector` but for "plural" fragments that
|
|
* expect an array of results and therefore return an array of selectors.
|
|
*/
|
|
|
|
|
|
function getSelectorList(operationVariables, fragment, items) {
|
|
var selectors = null;
|
|
items.forEach(function (item) {
|
|
var selector = item != null ? getSelector(operationVariables, fragment, item) : null;
|
|
|
|
if (selector != null) {
|
|
selectors = selectors || [];
|
|
selectors.push(selector);
|
|
}
|
|
});
|
|
return selectors;
|
|
}
|
|
/**
|
|
* @public
|
|
*
|
|
* Given a mapping of keys -> results and a mapping of keys -> fragments,
|
|
* extracts the selectors for those fragments from the results.
|
|
*
|
|
* The canonical use-case for this function is ReactRelayFragmentContainer, which
|
|
* uses this function to convert (props, fragments) into selectors so that it
|
|
* can read the results to pass to the inner component.
|
|
*/
|
|
|
|
|
|
function getSelectorsFromObject(operationVariables, fragments, object) {
|
|
var selectors = {};
|
|
|
|
for (var _key in fragments) {
|
|
if (fragments.hasOwnProperty(_key)) {
|
|
var fragment = fragments[_key];
|
|
var item = object[_key];
|
|
|
|
if (item == null) {
|
|
selectors[_key] = item;
|
|
} else if (fragment.metadata && fragment.metadata.plural === true) {
|
|
!Array.isArray(item) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernSelector: Expected value for key `%s` to be an array, got `%s`. ' + 'Remove `@relay(plural: true)` from fragment `%s` to allow the prop to be an object.', _key, JSON.stringify(item), fragment.name) : require("fbjs/lib/invariant")(false) : void 0;
|
|
selectors[_key] = getSelectorList(operationVariables, fragment, item);
|
|
} else {
|
|
!!Array.isArray(item) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernFragmentSpecResolver: Expected value for key `%s` to be an object, got `%s`. ' + 'Add `@relay(plural: true)` to fragment `%s` to allow the prop to be an array of items.', _key, JSON.stringify(item), fragment.name) : require("fbjs/lib/invariant")(false) : void 0;
|
|
selectors[_key] = getSelector(operationVariables, fragment, item);
|
|
}
|
|
}
|
|
}
|
|
|
|
return selectors;
|
|
}
|
|
/**
|
|
* @public
|
|
*
|
|
* Given a mapping of keys -> results and a mapping of keys -> fragments,
|
|
* extracts a mapping of keys -> id(s) of the results.
|
|
*
|
|
* Similar to `getSelectorsFromObject()`, this function can be useful in
|
|
* determining the "identity" of the props passed to a component.
|
|
*/
|
|
|
|
|
|
function getDataIDsFromObject(fragments, object) {
|
|
var ids = {};
|
|
|
|
for (var _key2 in fragments) {
|
|
if (fragments.hasOwnProperty(_key2)) {
|
|
var fragment = fragments[_key2];
|
|
var item = object[_key2];
|
|
|
|
if (item == null) {
|
|
ids[_key2] = item;
|
|
} else if (fragment.metadata && fragment.metadata.plural === true) {
|
|
!Array.isArray(item) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernSelector: Expected value for key `%s` to be an array, got `%s`. ' + 'Remove `@relay(plural: true)` from fragment `%s` to allow the prop to be an object.', _key2, JSON.stringify(item), fragment.name) : require("fbjs/lib/invariant")(false) : void 0;
|
|
ids[_key2] = getDataIDs(fragment, item);
|
|
} else {
|
|
!!Array.isArray(item) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernFragmentSpecResolver: Expected value for key `%s` to be an object, got `%s`. ' + 'Add `@relay(plural: true)` to fragment `%s` to allow the prop to be an array of items.', _key2, JSON.stringify(item), fragment.name) : require("fbjs/lib/invariant")(false) : void 0;
|
|
ids[_key2] = getDataID(fragment, item);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ids;
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
|
|
|
|
function getDataIDs(fragment, items) {
|
|
var ids;
|
|
items.forEach(function (item) {
|
|
var id = item != null ? getDataID(fragment, item) : null;
|
|
|
|
if (id != null) {
|
|
ids = ids || [];
|
|
ids.push(id);
|
|
}
|
|
});
|
|
return ids || null;
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
|
|
|
|
function getDataID(fragment, item) {
|
|
!(typeof item === 'object' && item !== null && !Array.isArray(item)) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernSelector: Expected value for fragment `%s` to be an object, got ' + '`%s`.', fragment.name, JSON.stringify(item)) : require("fbjs/lib/invariant")(false) : void 0;
|
|
|
|
var dataID = item[require("./RelayStoreUtils").ID_KEY];
|
|
|
|
if (typeof dataID === 'string') {
|
|
return dataID;
|
|
}
|
|
|
|
process.env.NODE_ENV !== "production" ? require("fbjs/lib/warning")(false, 'RelayModernSelector: Expected object to contain data for fragment `%s`, got ' + '`%s`. Make sure that the parent operation/fragment included fragment ' + '`...%s` without `@relay(mask: false)`.', fragment.name, JSON.stringify(item), fragment.name) : void 0;
|
|
return null;
|
|
}
|
|
/**
|
|
* @public
|
|
*
|
|
* Given a mapping of keys -> results and a mapping of keys -> fragments,
|
|
* extracts the merged variables that would be in scope for those
|
|
* fragments/results.
|
|
*
|
|
* This can be useful in determing what varaibles were used to fetch the data
|
|
* for a Relay container, for example.
|
|
*/
|
|
|
|
|
|
function getVariablesFromObject(operationVariables, fragments, object) {
|
|
var variables = {};
|
|
|
|
for (var _key3 in fragments) {
|
|
if (fragments.hasOwnProperty(_key3)) {
|
|
var _ret = function () {
|
|
var fragment = fragments[_key3];
|
|
var item = object[_key3];
|
|
|
|
if (item == null) {
|
|
return "continue";
|
|
} else if (fragment.metadata && fragment.metadata.plural === true) {
|
|
!Array.isArray(item) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernSelector: Expected value for key `%s` to be an array, got `%s`. ' + 'Remove `@relay(plural: true)` from fragment `%s` to allow the prop to be an object.', _key3, JSON.stringify(item), fragment.name) : require("fbjs/lib/invariant")(false) : void 0;
|
|
item.forEach(function (value) {
|
|
if (value != null) {
|
|
var itemVariables = getVariables(operationVariables, fragment, value);
|
|
|
|
if (itemVariables) {
|
|
Object.assign(variables, itemVariables);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
!!Array.isArray(item) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernFragmentSpecResolver: Expected value for key `%s` to be an object, got `%s`. ' + 'Add `@relay(plural: true)` to fragment `%s` to allow the prop to be an array of items.', _key3, JSON.stringify(item), fragment.name) : require("fbjs/lib/invariant")(false) : void 0;
|
|
var itemVariables = getVariables(operationVariables, fragment, item);
|
|
|
|
if (itemVariables) {
|
|
Object.assign(variables, itemVariables);
|
|
}
|
|
}
|
|
}();
|
|
|
|
if (_ret === "continue") continue;
|
|
}
|
|
}
|
|
|
|
return variables;
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
|
|
|
|
function getVariables(operationVariables, fragment, item) {
|
|
var ownedSelector = getSelector(operationVariables, fragment, item);
|
|
|
|
if (!ownedSelector) {
|
|
return null;
|
|
}
|
|
|
|
return ownedSelector.selector.variables;
|
|
}
|
|
/**
|
|
* @public
|
|
*
|
|
* Determine if two selectors are equal (represent the same selection). Note
|
|
* that this function returns `false` when the two queries/fragments are
|
|
* different objects, even if they select the same fields.
|
|
*/
|
|
|
|
|
|
function areEqualSelectors(thisSelector, thatSelector) {
|
|
return thisSelector.selector.dataID === thatSelector.selector.dataID && thisSelector.selector.node === thatSelector.selector.node && require("fbjs/lib/areEqual")(thisSelector.selector.variables, thatSelector.selector.variables);
|
|
}
|
|
|
|
module.exports = {
|
|
areEqualSelectors: areEqualSelectors,
|
|
getDataIDsFromObject: getDataIDsFromObject,
|
|
getSelector: getSelector,
|
|
getSelectorList: getSelectorList,
|
|
getSelectorsFromObject: getSelectorsFromObject,
|
|
getVariablesFromObject: getVariablesFromObject
|
|
}; |