UNPKG

react-diode

Version:

Endpoint agnostic, unidirectional data fetching for React applications

1,351 lines (1,119 loc) 39 kB
'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;