/** * 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'; var _objectSpread2 = require("@babel/runtime/helpers/interopRequireDefault")(require("@babel/runtime/helpers/objectSpread")); var _defineProperty2 = require("@babel/runtime/helpers/interopRequireDefault")(require("@babel/runtime/helpers/defineProperty")); /** * A utility for resolving and subscribing to the results of a fragment spec * (key -> fragment mapping) given some "props" that determine the root ID * and variables to use when reading each fragment. When props are changed via * `setProps()`, the resolver will update its results and subscriptions * accordingly. Internally, the resolver: * - Converts the fragment map & props map into a map of `Selector`s. * - Removes any resolvers for any props that became null. * - Creates resolvers for any props that became non-null. * - Updates resolvers with the latest props. * * This utility is implemented as an imperative, stateful API for performance * reasons: reusing previous resolvers, callback functions, and subscriptions * all helps to reduce object allocation and thereby decrease GC time. * * The `resolve()` function is also lazy and memoized: changes in the store mark * the resolver as stale and notify the caller, and the actual results are * recomputed the first time `resolve()` is called. */ var RelayModernFragmentSpecResolver = /*#__PURE__*/ function () { function RelayModernFragmentSpecResolver(context, fragments, props, callback) { var _this = this; (0, _defineProperty2["default"])(this, "_onChange", function () { _this._stale = true; if (typeof _this._callback === 'function') { _this._callback(); } }); this._callback = callback; this._context = context; this._data = {}; this._fragments = fragments; this._props = props; this._resolvers = {}; this._stale = false; this.setProps(props); } var _proto = RelayModernFragmentSpecResolver.prototype; _proto.dispose = function dispose() { for (var _key in this._resolvers) { if (this._resolvers.hasOwnProperty(_key)) { disposeCallback(this._resolvers[_key]); } } }; _proto.resolve = function resolve() { if (this._stale) { // Avoid mapping the object multiple times, which could occur if data for // multiple keys changes in the same event loop. var prevData = this._data; var nextData; for (var _key2 in this._resolvers) { if (this._resolvers.hasOwnProperty(_key2)) { var resolver = this._resolvers[_key2]; var prevItem = prevData[_key2]; if (resolver) { var nextItem = resolver.resolve(); if (nextData || nextItem !== prevItem) { nextData = nextData || (0, _objectSpread2["default"])({}, prevData); nextData[_key2] = nextItem; } } else { var prop = this._props[_key2]; var _nextItem = prop !== undefined ? prop : null; if (nextData || !require("./isScalarAndEqual")(_nextItem, prevItem)) { nextData = nextData || (0, _objectSpread2["default"])({}, prevData); nextData[_key2] = _nextItem; } } } } this._data = nextData || prevData; this._stale = false; } return this._data; }; _proto.setCallback = function setCallback(callback) { this._callback = callback; }; _proto.setProps = function setProps(props) { var ownedSelectors = require("./RelayModernSelector").getSelectorsFromObject(this._context.variables, this._fragments, props); for (var _key3 in ownedSelectors) { if (ownedSelectors.hasOwnProperty(_key3)) { var ownedSelector = ownedSelectors[_key3]; var resolver = this._resolvers[_key3]; if (ownedSelector == null) { if (resolver != null) { resolver.dispose(); } resolver = null; } else if (Array.isArray(ownedSelector)) { if (resolver == null) { resolver = new SelectorListResolver(this._context.environment, ownedSelector, this._onChange); } else { !(resolver instanceof SelectorListResolver) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernFragmentSpecResolver: Expected prop `%s` to always be an array.', _key3) : require("fbjs/lib/invariant")(false) : void 0; resolver.setSelectors(ownedSelector); } } else { if (resolver == null) { resolver = new SelectorResolver(this._context.environment, ownedSelector, this._onChange); } else { !(resolver instanceof SelectorResolver) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernFragmentSpecResolver: Expected prop `%s` to always be an object.', _key3) : require("fbjs/lib/invariant")(false) : void 0; resolver.setSelector(ownedSelector); } } this._resolvers[_key3] = resolver; } } this._props = props; this._stale = true; }; _proto.setVariables = function setVariables(variables) { for (var _key4 in this._resolvers) { if (this._resolvers.hasOwnProperty(_key4)) { var resolver = this._resolvers[_key4]; if (resolver) { resolver.setVariables(variables); } } } this._stale = true; }; return RelayModernFragmentSpecResolver; }(); /** * A resolver for a single Selector. */ var SelectorResolver = /*#__PURE__*/ function () { function SelectorResolver(environment, ownedSelector, callback) { var _this2 = this; (0, _defineProperty2["default"])(this, "_onChange", function (snapshot) { _this2._data = snapshot.data; _this2._callback(); }); var _snapshot = environment.lookup(ownedSelector.selector); this._callback = callback; this._data = _snapshot.data; this._environment = environment; this._ownedSelector = ownedSelector; this._subscription = environment.subscribe(_snapshot, this._onChange); } var _proto2 = SelectorResolver.prototype; _proto2.dispose = function dispose() { if (this._subscription) { this._subscription.dispose(); this._subscription = null; } }; _proto2.resolve = function resolve() { return this._data; }; _proto2.setSelector = function setSelector(ownedSelector) { if (this._subscription != null && require("./RelayModernSelector").areEqualSelectors(ownedSelector, this._ownedSelector)) { return; } this.dispose(); var snapshot = this._environment.lookup(ownedSelector.selector); this._data = snapshot.data; this._ownedSelector = ownedSelector; this._subscription = this._environment.subscribe(snapshot, this._onChange); }; _proto2.setVariables = function setVariables(variables) { var ownedSelector = { owner: null, selector: (0, _objectSpread2["default"])({}, this._ownedSelector.selector, { variables: variables }) }; this.setSelector(ownedSelector); }; return SelectorResolver; }(); /** * A resolver for an array of Selectors. */ var SelectorListResolver = /*#__PURE__*/ function () { function SelectorListResolver(environment, selectors, callback) { var _this3 = this; (0, _defineProperty2["default"])(this, "_onChange", function (data) { _this3._stale = true; _this3._callback(); }); this._callback = callback; this._data = []; this._environment = environment; this._resolvers = []; this._stale = true; this.setSelectors(selectors); } var _proto3 = SelectorListResolver.prototype; _proto3.dispose = function dispose() { this._resolvers.forEach(disposeCallback); }; _proto3.resolve = function resolve() { if (this._stale) { // Avoid mapping the array multiple times, which could occur if data for // multiple indices changes in the same event loop. var prevData = this._data; var nextData; for (var ii = 0; ii < this._resolvers.length; ii++) { var prevItem = prevData[ii]; var nextItem = this._resolvers[ii].resolve(); if (nextData || nextItem !== prevItem) { nextData = nextData || prevData.slice(0, ii); nextData.push(nextItem); } } if (!nextData && this._resolvers.length !== prevData.length) { nextData = prevData.slice(0, this._resolvers.length); } this._data = nextData || prevData; this._stale = false; } return this._data; }; _proto3.setSelectors = function setSelectors(selectors) { while (this._resolvers.length > selectors.length) { var resolver = this._resolvers.pop(); resolver.dispose(); } for (var ii = 0; ii < selectors.length; ii++) { if (ii < this._resolvers.length) { this._resolvers[ii].setSelector(selectors[ii]); } else { this._resolvers[ii] = new SelectorResolver(this._environment, selectors[ii], this._onChange); } } this._stale = true; }; _proto3.setVariables = function setVariables(variables) { this._resolvers.forEach(function (resolver) { return resolver.setVariables(variables); }); this._stale = true; }; return SelectorListResolver; }(); function disposeCallback(disposable) { disposable && disposable.dispose(); } module.exports = RelayModernFragmentSpecResolver;