react-diode
Version:
Endpoint agnostic, unidirectional data fetching for React applications
1,351 lines (1,119 loc) • 39 kB
JavaScript
'use strict';
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = _interopDefault(require('react'));
var mergeWith = _interopDefault(require('lodash.mergewith'));
var objectAssign = _interopDefault(require('object-assign'));
var find = _interopDefault(require('lodash.find'));
var reactIs = require('react-is');
var deepExtend = _interopDefault(require('deep-extend'));
var hoistStatics = _interopDefault(require('hoist-non-react-statics'));
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === 'function') {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
}
ownKeys.forEach(function (key) {
_defineProperty(target, key, source[key]);
});
}
return target;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
}
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance");
}
/*
Possible Errors:
1. Queries key specified in Diode Container is not the same as query type in Diode Query
*/
function resolveContainerProps(response, RootContainer) {
// Convert diode response as root container props
var _RootContainer$query$ = RootContainer.query.getContainerQuery(),
queryMap = _RootContainer$query$.map;
var containerProps = Object.keys(queryMap).reduce(function (props, key) {
props[key] = response[key];
if (!props[key]) {
throw new Error("Queries key ".concat(key, " does not match any type specified in Diode query."));
}
return props;
}, {});
var __additional = response.__additional;
if (__additional) {
return objectAssign(containerProps, {
__additional: __additional
});
} else {
return containerProps;
}
}
var DiodeNetworkLayer =
/*#__PURE__*/
function () {
function DiodeNetworkLayer() {
_classCallCheck(this, DiodeNetworkLayer);
_defineProperty(this, "_injectedNetworkLayer", void 0);
_defineProperty(this, "_queryMockEnabled", void 0);
this._queryMockEnabled = false;
}
/**
* @internal
*
* Supply your own network layer
*/
_createClass(DiodeNetworkLayer, [{
key: "injectNetworkLayer",
value: function injectNetworkLayer(networkLayer) {
this._injectedNetworkLayer = networkLayer;
}
}, {
key: "injectQueryMockResolver",
value: function injectQueryMockResolver(queryMockResolver) {
this._queryMockEnabled = true;
this._queryMockResolver = queryMockResolver;
}
/**
* @internal
*
* Send diode query via injected network layer
*/
}, {
key: "sendQueries",
value: function sendQueries(queries, options) {
var _this = this;
var queryRequests = queries;
var mockedQueryResponse = {};
var networkLayer = this._getCurrentNetworkLayer();
if (this._queryMockEnabled) {
queryRequests = queries.filter(function (query) {
// check whether a query as a mocked response
var queryMockResolver = _this._queryMockResolver[query.type];
if (queryMockResolver && typeof queryMockResolver === "function") {
var mockedResponse = queryMockResolver(query);
if (mockedResponse) {
mockedQueryResponse[query.type] = mockedResponse;
return false;
}
return true;
} else {
return true;
}
});
}
return networkLayer.sendQueries(queryRequests, options).then(function (response) {
return objectAssign(mockedQueryResponse, response);
});
}
}, {
key: "_getCurrentNetworkLayer",
value: function _getCurrentNetworkLayer() {
return this._injectedNetworkLayer;
}
}]);
return DiodeNetworkLayer;
}();
/**
* Represent complete query in Diode.RootContainer
*
*/
var DiodeRootQuery =
/*#__PURE__*/
function () {
function DiodeRootQuery(containerQuery) {
_classCallCheck(this, DiodeRootQuery);
_defineProperty(this, "_variables", {});
_defineProperty(this, "_containerQuery", void 0);
this._containerQuery = containerQuery;
}
/**
* @internal
*
*/
_createClass(DiodeRootQuery, [{
key: "getContainerQuery",
value: function getContainerQuery() {
return this._containerQuery;
}
/**
* @public
*
*/
}, {
key: "setVariables",
value: function setVariables(variables) {
mergeWith(this._variables, variables);
}
/**
* @public
*
*/
}, {
key: "getVariables",
value: function getVariables() {
return this._variables;
}
/**
* @public
*
* Compile query map into an array of actionable query and apply fragment
* values from .setVariables() method (if any)
*/
}, {
key: "compile",
value: function compile() {
var _this = this;
var queryTypeMap = this._containerQuery.getQueryTypeMap();
return Object.keys(queryTypeMap).map(function (queryType) {
var query = queryTypeMap[queryType];
query.fragment = _this._compileFragmentKeys(query.fragmentStructure);
query.params = _this._compileFragmentKeys(query.paramsStructure);
return query;
});
}
/**
* Iterate over fragment structure keys and compile the value.
* If an object is found, recursively iterate the object keys
*/
}, {
key: "_compileFragmentKeys",
value: function _compileFragmentKeys(rawFragment) {
var _this2 = this;
return Object.keys(rawFragment).reduce(function (fragment, key) {
var parsedKey = _this2._compileFragmentValue(key); // typeof null === 'object'
if (_typeof(rawFragment[key]) === "object" && rawFragment[key] !== null) {
fragment[parsedKey] = _this2._compileFragmentKeys(rawFragment[key]);
} else {
fragment[parsedKey] = _this2._compileFragmentValue(rawFragment[key]);
}
return fragment;
}, {});
}
/**
* Replace value format with actual value from stored value map
*/
}, {
key: "_compileFragmentValue",
value: function _compileFragmentValue(rawFragment) {
if (typeof rawFragment === "string" && rawFragment.charAt(0) === "$") {
var key = rawFragment.slice(1);
var value = this._variables[key]; // If value not found from value map, return as is.
// Do not use boolean coercion here as it's possible we have
// falsy value like 0 and ''
if (typeof value === "undefined" || value === null) {
return rawFragment;
}
return value;
}
return rawFragment;
}
}]);
return DiodeRootQuery;
}();
/**
* @public
*
*/
function createPendingQueryRequest(QueryDependencies, callback) {
if (!(QueryDependencies instanceof Array)) {
QueryDependencies = [QueryDependencies];
}
return {
pending: true,
dependencies: QueryDependencies,
callback: callback
};
}
/**
* @public
*
*/
function createQueryRequest(url, method, payload) {
var headers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
return {
pending: false,
url: url,
method: method,
payload: payload,
headers: headers
};
}
/**
* @internal
*
*/
function getQueryRequests(RootContainer, options) {
var queries = RootContainer.query.compile();
return queries.map(function (query) {
var fragment = query.fragment,
params = query.params;
var queryRequestInfo = query.request(fragment, params, options);
return generateQueryRequest(query, queryRequestInfo);
});
}
/**
* @internal
*
* Generate query request to be sent via sendQueries
*/
function generateQueryRequest(query, queryRequestInfo) {
var type = query.type,
fragment = query.fragment,
resolve = query.resolve;
return _objectSpread({
type: type,
fragment: fragment,
resolve: resolve
}, queryRequestInfo);
}
function resolvePendingQuery(pendingQuery, response) {
var callback = pendingQuery.callback,
dependencies = pendingQuery.dependencies;
var resolvedDependencies = dependencies.map(function (query) {
return response[query.type];
}).filter(function (response) {
return Boolean(response);
});
if (resolvedDependencies.length === dependencies.length) {
// all dependencies resolvedDependencies
// eslint-disable-next-line callback-return
var queryRequestInfo = callback.apply(void 0, _toConsumableArray(resolvedDependencies));
var queryRequest = generateQueryRequest(pendingQuery, queryRequestInfo); // After resolving initial query dependencies, it's possible that the query
// still return pending query which can be resolved using initial response.
if (queryRequest.dependencies) {
return resolvePendingQuery(queryRequest, response);
}
return queryRequest;
} else {
return pendingQuery;
}
}
function resolvePendingQueries(pendingQueries, response) {
return pendingQueries.map(function (pendingQuery) {
return resolvePendingQuery(pendingQuery, response);
});
}
var DiodeQueryTypes = {
BATCH: "batch-query"
};
/**
* Parse batch query response and assign the response to each
* initial queries before filtered as batch queryType
*/
function resolveBatchQuery(batchResponse, initialQueries, options) {
var responseMap = {};
for (var queryType in batchResponse) {
/* istanbul ignore else */
if (batchResponse.hasOwnProperty(queryType)) {
var queryResponse = batchResponse[queryType];
var query = find(initialQueries, {
type: queryType
}); // Special treatment for __additional property in batch query response.
// This is used internally so only remove this if statement when you
// know what you're doing
if (queryType === "__additional") {
var specialResponse = {
__additional: queryResponse
};
objectAssign(responseMap, specialResponse);
} else if (queryResponse && query) {
var resolve = query.resolve,
fragment = query.fragment;
responseMap[queryType] = resolve(queryResponse, fragment, options);
}
}
}
return responseMap;
}
/*
Possible Errors:
1. Injected network layer did not properly map query type and response
(different type specified in query)
*/
function resolveQueryResponse(queries, initialQueries, queryResponseMap, options) {
return Object.keys(queryResponseMap).reduce(function (map, type) {
try {
var queryResponse = queryResponseMap[type];
var query = find(queries, {
type: type
});
if (type === DiodeQueryTypes.BATCH) {
// BatchQuery returns multiple response at once as single object,
// we need to merge initial response with response generated by
// batch query response resolution
var batchQueryResponseMap = resolveBatchQuery(query.resolve(queryResponse, options), initialQueries, options);
Object.assign(map, batchQueryResponseMap);
} else {
map[type] = query.resolve(queryResponse, query.fragment, options);
}
return map;
} catch (err) {
throw new Error("Query type ".concat(type, " from network layer does not match any query types specified in Diode queries."));
}
}, {});
}
function createBatchQueryRequest(query, queryList, options) {
var resolve = query.resolve,
type = query.type;
return _objectSpread({
type: type,
resolve: resolve
}, query.request(queryList, options));
}
/**
* Given a set of query requests, filter and transform some queries
* as BatchQuery (if applicable).
*
* If BatchQuery types is incomplete, send the queries as normal query
*/
function filterBatchQuery(queries, batchQuery, options) {
var filteredQueries = [];
var queryTypes = batchQuery.queryTypes,
forceMerge = batchQuery.forceMerge;
var batchQueryList = queryTypes.map(function (type) {
return find(queries, {
type: type
});
}).filter(function (query) {
return Boolean(query);
}); // We need to make sure the queries listed has same length as
// query requirements in queryTypes. We can use forceMerge to bypass
// this checking but be warned that by using forceMerge, you can no
// longer rely on query requirements order in request() method
if (batchQueryList.length === queryTypes.length || // if forceMerge is enabled, make sure at least one query satisfied
// the requirement to prevent zero query length
forceMerge && batchQueryList.length > 0) {
var batchQueryRequest = createBatchQueryRequest(batchQuery, batchQueryList, options);
filteredQueries.push(batchQueryRequest);
} else {
// incomplete batch query, send each query individually
filteredQueries = filteredQueries.concat(batchQueryList);
}
var normalQueryList = queries.filter(function (query) {
return batchQueryList.indexOf(query) === -1;
});
filteredQueries = filteredQueries.concat(normalQueryList);
return filteredQueries;
}
var FETCH_ALL_CACHE = "__fac__";
function markFetchAllCache(response, queries) {
// mark special fetch-all case so our cache is aware
queries.forEach(function (query) {
try {
Object.keys(query.fragment).forEach(function (fragmentKey) {
var fragmentResponse = response[query.type][fragmentKey];
if (Object.keys(query.fragment[fragmentKey]).length === 0 && fragmentResponse) {
if (Array.isArray(response[query.type][FETCH_ALL_CACHE]) === false) {
response[query.type][FETCH_ALL_CACHE] = [];
}
response[query.type][FETCH_ALL_CACHE].push(fragmentKey);
}
});
} catch (err) {}
});
return response;
}
var DiodeStore =
/*#__PURE__*/
function () {
function DiodeStore() {
_classCallCheck(this, DiodeStore);
_defineProperty(this, "_networkLayer", void 0);
_defineProperty(this, "_batchQueryEnabled", void 0);
_defineProperty(this, "_batchQuery", void 0);
this._batchQueriesEnabled = false;
this._networkLayer = new DiodeNetworkLayer();
}
/**
* @public
*/
_createClass(DiodeStore, [{
key: "injectNetworkLayer",
value: function injectNetworkLayer(networkLayer) {
this._networkLayer.injectNetworkLayer(networkLayer);
}
/**
* @public
*/
}, {
key: "useMockQueries",
value: function useMockQueries(queryMockResolver) {
this._networkLayer.injectQueryMockResolver(queryMockResolver);
}
/**
* @public
*/
}, {
key: "useBatchQuery",
value: function useBatchQuery(batchQuery) {
/* istanbul ignore else */
if (batchQuery) {
this._batchQueryEnabled = true;
this._batchQuery = batchQuery;
}
}
/**
* @public
*
* Send queries via network layer bypassing internal cache. Note that the
* server may respond with 304 status but it's not actually an internal
* cached response
*/
}, {
key: "forceFetch",
value: function forceFetch(RootContainer, options) {
var queries = getQueryRequests(RootContainer, options);
return this._fetchQueries(queries, options).then(function (response) {
var result = resolveContainerProps(response, RootContainer);
return markFetchAllCache(result, queries);
});
}
/**
* @private
*
* Remove fragments that have already been cached from query; cached fragments
* should not be fetched. Returned fragment are fragments that are required but
* have not been fetched.
*/
}, {
key: "filterCachedFragments",
value: function filterCachedFragments(query) {
var cache = this.cache[query.type];
if (!cache) {
return query;
}
var filteredFragments = {};
Object.keys(query.fragment).forEach(function (fragmentKey) {
var cachedFragment = cache[fragmentKey];
var innerFragment = query.fragment[fragmentKey];
if (!innerFragment) {
return;
}
var innerFragmentKeys = Object.keys(innerFragment);
if (innerFragmentKeys.length === 0) {
// If fragment is already cached and all data in the fragment is
// already fetched, remove all fragments. Otherwise, fetch all
// fragments.
if (Array.isArray(cache[FETCH_ALL_CACHE]) && cache[FETCH_ALL_CACHE].includes(fragmentKey)) {
return;
}
filteredFragments[fragmentKey] = {};
return;
} // this query type might be partially cached
// check specific fragment
innerFragmentKeys.forEach(function (innerKey) {
if (cachedFragment && cachedFragment[innerKey]) {
return;
}
if (!filteredFragments[fragmentKey]) {
filteredFragments[fragmentKey] = {};
}
filteredFragments[fragmentKey][innerKey] = innerFragment[innerKey];
});
});
return _objectSpread({}, query, {
fragment: filteredFragments
});
}
}, {
key: "fetch",
value: function fetch(rawQuery, options) {
function customizer(objValue, srcValue) {
if (Array.isArray(objValue)) {
return objValue.concat(srcValue);
}
}
var _this,
rootQuery,
queries,
response,
_this3 = this;
return Promise.resolve().then(function () {
_this = _this3;
rootQuery = new DiodeRootQuery(rawQuery);
queries = rootQuery.compile().map(function (query) {
var _this$filterCachedFra = _this.filterCachedFragments(query),
fragment = _this$filterCachedFra.fragment,
params = _this$filterCachedFra.params;
var queryRequestInfo = query.request(fragment, params, options);
return generateQueryRequest(query, queryRequestInfo);
}).filter(Boolean);
return _this3._fetchQueries(queries, options);
}).then(function (_resp) {
response = _resp; // customizer function is required. Otherwise, __fac__ array in cache will be
// replaced, not concat when updating cache
mergeWith(_this3.cache, markFetchAllCache(response, queries), customizer);
});
}
/**
* Recursively fetch over query dependency, starting with query with no
* dependency, and build response moving up
*
* TODO:
* - All DiodeQueryRequest should contain pending property for filtering
* instead of using query.dependencies which is not available in normal
* QueryRequest
*/
}, {
key: "_fetchQueries",
value: function _fetchQueries(allQueries) {
var _this2 = this;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// We need to store unmodified initial queries before filtered
// into batch query so we can use their resolve function when resolving
// response from batch query
var pendingQueries = allQueries.filter(function (query) {
return query.dependencies;
});
var initialQueries = allQueries.filter(function (query) {
return !query.dependencies;
});
var queries = initialQueries;
if (this._batchQueryEnabled) {
queries = filterBatchQuery(queries, this._batchQuery, options);
}
return this._networkLayer.sendQueries(queries, options).then(function (queryResponseMap) {
var responseMap = resolveQueryResponse(queries, initialQueries, queryResponseMap, options);
if (pendingQueries.length > 0) {
var nextQueries = resolvePendingQueries(pendingQueries, responseMap);
return _this2._fetchQueries(nextQueries, options).then(function (nextResponseMap) {
return Object.assign(responseMap, nextResponseMap);
});
}
return responseMap;
});
}
}]);
return DiodeStore;
}();
var DiodeStore$1 = new DiodeStore();
var CacheContext = React.createContext(null);
var DiodeCache =
/*#__PURE__*/
function () {
function DiodeCache(cache, options) {
_classCallCheck(this, DiodeCache);
DiodeStore$1.cache = cache;
DiodeStore$1.options = options;
}
_createClass(DiodeCache, [{
key: "getContents",
value: function getContents() {
return DiodeStore$1.cache;
} // TODO memo
}, {
key: "getUnresolvedQueries",
value: function getUnresolvedQueries(containerQuery) {
return Object.keys(containerQuery.map).filter(function (type) {
var cache = DiodeStore$1.cache[type];
if (!cache) {
// have no matching cache
return true;
} // check for unresolved fragment
var query = containerQuery.map[type];
return Object.keys(query.fragmentStructure).some(function (fragment) {
var cachedFragment = cache[fragment];
if (Array.isArray(cachedFragment)) {
return false;
}
var innerFragment = query.fragmentStructure[fragment];
if (!innerFragment || _typeof(innerFragment) !== "object") {
return false;
}
var innerFragmentKeys = Object.keys(innerFragment);
if (innerFragmentKeys.length === 0) {
if (Array.isArray(cache[FETCH_ALL_CACHE])) {
// might already cache fetch-all
return !cache[FETCH_ALL_CACHE].includes(fragment);
}
return true;
}
return innerFragmentKeys.some(function (key) {
// API can return null
if (cachedFragment) {
return cachedFragment[key] === undefined;
}
return cachedFragment === undefined;
});
});
});
}
}, {
key: "hasResolved",
value: function hasResolved(containerQuery) {
return this.getUnresolvedQueries(containerQuery).length === 0;
}
}, {
key: "resolve",
value: function resolve(containerQuery) {
return Promise.resolve().then(function () {
return DiodeStore$1.fetch(containerQuery, DiodeStore$1.options);
}).then(function () {});
}
}]);
return DiodeCache;
}();
function createCache(initialCache, options) {
return new DiodeCache(initialCache, options);
}
/**
* Partially-complete query in given DiodeContainer
*
*/
var DiodeContainerQuery =
/*#__PURE__*/
function () {
// store final query shape
// store distinct query type
function DiodeContainerQuery(componentName, queries, children) {
_classCallCheck(this, DiodeContainerQuery);
_defineProperty(this, "map", {});
_defineProperty(this, "_queryTypeMap", {});
_defineProperty(this, "_queries", void 0);
_defineProperty(this, "_children", void 0);
this._componentName = componentName;
this._queries = queries;
this._children = children || [];
this._parseQueryTypeMap(queries);
this._mergeChildQueryTypeMap(children);
this._buildFinalQueryMap(queries, children);
}
_createClass(DiodeContainerQuery, [{
key: "getQueryTypeMap",
value: function getQueryTypeMap() {
return this._queryTypeMap;
}
/**
* @internal
* @unstable
*
* Add child container dynamicly and rebuilt the query map
*/
}, {
key: "injectChildren",
value: function injectChildren(children) {
this._children = this._children.concat(children);
this._mergeChildQueryTypeMap(children);
this._buildFinalQueryMap(this._queries, this._children);
}
/**
* Group distinct query type into single query shape, this is to make sure
* there is no same query type with different key.
*/
}, {
key: "_parseQueryTypeMap",
value: function _parseQueryTypeMap(queries) {
var _this = this;
if (queries === null || queries === undefined) {
return;
}
Object.keys(queries).forEach(function (key) {
var query = queries[key];
var existingQueryType = _this._queryTypeMap[query.type];
if (existingQueryType) {
var existingFragment = existingQueryType.fragmentStructure;
var newFragment = query.fragmentStructure;
mergeWith(existingFragment, newFragment);
mergeWith(existingQueryType.params, query.params);
} else if (query.type) {
_this._queryTypeMap[query.type] = query;
} else {
throw new Error("Invalid query type in query key ".concat(key, " at component ").concat(_this._componentName));
}
});
}
/**
* Merge all child query map with existing query map, also merge all queries
* with same type as we can have different key between parent-child that
* represent same query type
*/
}, {
key: "_mergeChildQueryTypeMap",
value: function _mergeChildQueryTypeMap(children) {
var _this2 = this;
if (!children || !children.length) {
return;
}
children.forEach(function (child) {
var childQueryMap = child.query.map;
/* istanbul ignore if */
if (childQueryMap === null || childQueryMap === undefined) {
return;
}
Object.keys(childQueryMap).forEach(function (key) {
var childQuery = childQueryMap[key];
var existingQueryType = _this2._queryTypeMap[childQuery.type];
if (existingQueryType) {
var existingFragment = existingQueryType.fragmentStructure;
var newFragment = childQuery.fragmentStructure;
mergeWith(existingFragment, newFragment);
} else {
_this2._queryTypeMap[childQuery.type] = childQuery;
}
});
});
}
/**
* Given complete query type map from current container and child container,
* re-generate query map from initial queries with the complete query
*/
}, {
key: "_buildFinalQueryMap",
value: function _buildFinalQueryMap(queries, children) {
var _this3 = this;
if (queries) {
// Create initial query map from parent container via query type map.
// For the most part, this is enough as we already compile the complete
// fragment and usually parent and child use same query key
this.map = Object.keys(queries).reduce(function (queryMap, key) {
var query = queries[key];
queryMap[key] = _this3._queryTypeMap[query.type];
return queryMap;
}, {});
}
if (!children || !children.length) {
return;
}
children.forEach(function (child) {
var queryMap = child.query.map;
Object.keys(queryMap).forEach(function (key) {
var childQuery = queryMap[key];
var existingQuery = _this3.map[key];
if (existingQuery && existingQuery.type !== childQuery.type) {
return console.warn( // eslint-disable-line no-console
"Different query type for same query key %s: %s (%s) and %s (%s)", key, _this3._componentName, existingQuery.type, child.componentName, childQuery.type);
}
_this3.map[key] = _this3._queryTypeMap[childQuery.type];
});
});
}
}]);
return DiodeContainerQuery;
}();
var DiodeQueryFetcher =
/*#__PURE__*/
function (_React$Component) {
_inherits(DiodeQueryFetcher, _React$Component);
function DiodeQueryFetcher() {
var _getPrototypeOf2;
var _this;
_classCallCheck(this, DiodeQueryFetcher);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _possibleConstructorReturn(this, (_getPrototypeOf2 = _getPrototypeOf(DiodeQueryFetcher)).call.apply(_getPrototypeOf2, [this].concat(args)));
_defineProperty(_assertThisInitialized(_this), "state", {
error: null,
// set default isLoading state to true
// only if cache is provided & hasn't resolved
isLoading: _this.props.cache instanceof DiodeCache && !_this.props.cache.hasResolved(_this.props.query)
});
return _this;
}
_createClass(DiodeQueryFetcher, [{
key: "componentDidMount",
value: function componentDidMount() {
var _this2,
_this$props,
cache,
query,
_this5 = this;
return Promise.resolve().then(function () {
_this2 = _this5;
_this$props = _this5.props;
cache = _this$props.cache;
query = _this$props.query;
if (!(_this5.state.isLoading === false)) {
return Promise.resolve().then(function () {
return cache.resolve(query);
}).then(function () {
return new Promise(function (resolve) {
if (typeof _this2.props.afterFetchCallback === "function") {
_this2.props.afterFetchCallback(resolve, cache.getContents());
} else {
resolve();
}
});
}).then(function () {
_this5.setState({
isLoading: false
});
}).catch(function (error) {
console.error("error", error);
_this5.setState({
error: error,
isLoading: false
});
});
}
}).then(function () {});
}
}, {
key: "render",
value: function render() {
var _this$props2 = this.props,
Component = _this$props2.Component,
wrapper = _this$props2.wrapper,
cache = _this$props2.cache,
query = _this$props2.query,
LoadingComponent = _this$props2.loading,
ErrorComponent = _this$props2.error,
props = _objectWithoutProperties(_this$props2, ["Component", "wrapper", "cache", "query", "loading", "error"]);
var isLoading = this.state.isLoading;
if (this.state.error !== null) {
return ErrorComponent && reactIs.isValidElementType(ErrorComponent) ? React.createElement(ErrorComponent, props) : React.createElement("span", null, this.state.error.message);
}
var component;
if (isLoading) {
component = LoadingComponent && reactIs.isValidElementType(LoadingComponent) ? React.createElement(LoadingComponent, props) : null;
} else {
component = React.createElement(Component, _extends({}, props, cache instanceof DiodeCache && cache.getContents()));
}
if (wrapper) {
return React.createElement("div", wrapper, component);
}
return component;
}
}]);
return DiodeQueryFetcher;
}(React.Component);
function createContainerComponent(Component, spec, query) {
/* istanbul ignore next */
var componentName = Component.displayName || Component.name;
var containerName = "Diode(".concat(componentName, ")");
var DiodeContainer =
/*#__PURE__*/
function (_React$Component2) {
_inherits(DiodeContainer, _React$Component2);
function DiodeContainer(props) {
var _this3;
_classCallCheck(this, DiodeContainer);
_this3 = _possibleConstructorReturn(this, _getPrototypeOf(DiodeContainer).call(this, props));
_this3.wrapperInfo = spec.wrapperInfo;
return _this3;
}
_createClass(DiodeContainer, [{
key: "render",
value: function render() {
var _this4 = this;
var props = this.props,
wrapperInfo = this.wrapperInfo;
var wrapper = props.wrapperInfo ? props.wrapperInfo : wrapperInfo;
return React.createElement(CacheContext.Consumer, null, function (cache) {
return React.createElement(DiodeQueryFetcher, _extends({}, _this4.props, {
Component: Component,
wrapper: wrapper,
query: query,
cache: cache,
loading: spec.loading,
error: spec.error
}));
});
}
}]);
return DiodeContainer;
}(React.Component);
DiodeContainer.displayName = containerName;
return hoistStatics(DiodeContainer, Component);
}
function createContainer(Component) {
var spec = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
/* istanbul ignore next */
var componentName = Component.displayName || Component.name;
var query = new DiodeContainerQuery(componentName, spec.queries, spec.children);
var ContainerConstructor = createContainerComponent(Component, spec, query);
ContainerConstructor.setWrapperInfo = function setWrapperInfo(wrapperInfo) {
objectAssign(spec.wrapperInfo, wrapperInfo);
};
ContainerConstructor.getWrapperInfo = function getWrapperInfo(key) {
return spec.wrapperInfo[key];
};
ContainerConstructor.getComponent = function getComponent() {
return Component;
};
ContainerConstructor.getChildren = function getChildren() {
if (spec.children && spec.children.length) {
return spec.children;
} else {
return [];
}
};
ContainerConstructor.query = deepExtend(query, Component.query);
ContainerConstructor.componentName = componentName;
return hoistStatics(ContainerConstructor, Component, {
query: true
});
}
function createRootContainer(Component, spec) {
var Container = createContainer(Component, spec);
Container.query = new DiodeRootQuery(Container.query);
return Container;
}
function createDiodeQuery(Query, fragmentStructure) {
var paramsStructure = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (typeof Query.type !== "string") {
throw new TypeError("Expected query definition to have query type");
}
if (typeof Query.request !== "function") {
throw new TypeError("Expected query definition to have .request() method");
}
if (typeof Query.resolve !== "function") {
throw new TypeError("Expected query definition to have .resolve() method");
}
var query = Object.create(Query);
query.fragmentStructure = fragmentStructure;
query.paramsStructure = paramsStructure;
return query;
}
var DiodePublic = {
Store: DiodeStore$1,
CacheProvider: CacheContext.Provider,
QueryTypes: DiodeQueryTypes,
RootQuery: DiodeRootQuery,
createCache: createCache,
createContainer: createContainer,
createRootContainer: createRootContainer,
createQuery: createDiodeQuery,
queryRequest: createQueryRequest,
waitForQuery: createPendingQueryRequest,
useBatchQuery: DiodeStore$1.useBatchQuery.bind(DiodeStore$1),
useMockQueries: DiodeStore$1.useMockQueries.bind(DiodeStore$1),
injectNetworkLayer: DiodeStore$1.injectNetworkLayer.bind(DiodeStore$1)
};
module.exports = DiodePublic;