Files
30-seconds-of-code/node_modules/relay-runtime/lib/RelayModernFragmentSpecResolver.js
2019-08-20 15:52:05 +02:00

323 lines
9.4 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';
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;