found-relay
Version:
Relay integration for found
165 lines (140 loc) • 5.88 kB
JavaScript
import _awaitAsyncGenerator from "@babel/runtime/helpers/esm/awaitAsyncGenerator";
import _wrapAsyncGenerator from "@babel/runtime/helpers/esm/wrapAsyncGenerator";
import { dequal } from 'dequal';
import { accumulateRouteValues, checkResolved, getComponents, getRouteMatches, getRouteValues, isResolved // @ts-expect-error
} from 'found/ResolverUtils';
import isPromise from 'is-promise';
import React from 'react';
import QuerySubscription from './QuerySubscription';
import ReadyStateRenderer from './ReadyStateRenderer';
import renderElement from './renderElement';
export default class Resolver {
constructor(environment) {
this.environment = environment;
this.lastQuerySubscriptions = [];
}
createQuerySubscription(options) {
return new QuerySubscription(options);
}
resolveElements(match) {
var _this = this;
return _wrapAsyncGenerator(function* () {
const routeMatches = getRouteMatches(match);
const Components = getComponents(routeMatches);
const queries = getRouteValues(routeMatches, route => route.getQuery, route => route.query);
const cacheConfigs = getRouteValues(routeMatches, route => route.getCacheConfig, route => route.cacheConfig);
const fetchPolicies = getRouteValues(routeMatches, route => route.getFetchPolicy, route => route.fetchPolicy);
const routeVariables = _this.getRouteVariables(match, routeMatches);
const querySubscriptions = _this.updateQuerySubscriptions(queries, routeVariables, cacheConfigs, fetchPolicies);
const fetches = querySubscriptions.map(querySubscription => querySubscription == null ? void 0 : querySubscription.fetch());
const earlyComponents = Components.some(isPromise) ? yield _awaitAsyncGenerator(Promise.all(Components.map(checkResolved))) : Components;
const earlyData = yield _awaitAsyncGenerator(Promise.all(fetches.map(checkResolved)));
let fetchedComponents;
if (!earlyComponents.every(isResolved) || !earlyData.every(isResolved)) {
const pendingElements = _this.createElements(routeMatches, earlyComponents, querySubscriptions, false);
yield pendingElements.every(element => element !== undefined) ? pendingElements : undefined;
fetchedComponents = yield _awaitAsyncGenerator(Promise.all(Components));
yield _awaitAsyncGenerator(Promise.all(fetches));
} else {
fetchedComponents = earlyComponents;
}
yield _this.createElements(routeMatches, fetchedComponents, querySubscriptions, true);
})();
}
getRouteVariables(match, routeMatches) {
return accumulateRouteValues(routeMatches, match.routeIndices, (variables, routeMatch) => {
const {
route,
routeParams
} = routeMatch; // We need to always run this to make sure we don't miss route params.
let nextVariables = Object.assign({}, variables, routeParams);
if (route.prepareVariables) {
nextVariables = route.prepareVariables(nextVariables, routeMatch);
}
return nextVariables;
}, null);
}
updateQuerySubscriptions(queries, routeVariables, cacheConfigs, fetchPolicies) {
const querySubscriptions = queries.map((query, i) => {
if (!query) {
return null;
}
const variables = routeVariables[i];
const cacheConfig = cacheConfigs[i];
const fetchPolicy = fetchPolicies[i];
const lastQuerySubscription = this.lastQuerySubscriptions[i]; // Match the logic in <QueryRenderer> for not refetching.
if (lastQuerySubscription && lastQuerySubscription.query === query && dequal(lastQuerySubscription.variables, variables) && dequal(lastQuerySubscription.cacheConfig, cacheConfig)) {
this.lastQuerySubscriptions[i] = null;
lastQuerySubscription.fetchPolicy = fetchPolicy;
return lastQuerySubscription;
}
return this.createQuerySubscription({
environment: this.environment,
query,
variables,
cacheConfig,
fetchPolicy
});
});
this.lastQuerySubscriptions.forEach(querySubscription => {
if (querySubscription) {
querySubscription.dispose();
}
});
this.lastQuerySubscriptions = [...querySubscriptions];
return querySubscriptions;
}
createElements(routeMatches, Components, querySubscriptions, fetched) {
return routeMatches.map((match, i) => {
const {
route,
router
} = match;
const Component = Components[i];
const querySubscription = querySubscriptions[i];
const isComponentResolved = isResolved(Component); // Handle non-Relay routes.
if (!querySubscription) {
if (route.render) {
return route.render({
match,
Component: isComponentResolved ? Component : null,
props: {
match,
router
}
});
}
if (!isComponentResolved) {
return undefined;
}
return Component ? /*#__PURE__*/React.createElement(Component, {
match: match,
router: router
}) : null;
}
const resolvedComponent = isComponentResolved ? Component : null;
const hasComponent = Component != null;
const element = renderElement({
match,
Component: resolvedComponent,
isComponentResolved,
hasComponent,
querySubscription,
resolving: true
});
if (!element) {
return element;
}
return routeChildren => /*#__PURE__*/React.createElement(ReadyStateRenderer, {
match: match,
Component: resolvedComponent,
isComponentResolved: isComponentResolved,
hasComponent: hasComponent,
element: element,
routeChildren: routeChildren,
querySubscription: querySubscription,
fetched: fetched
});
});
}
}