265 lines
14 KiB
JavaScript
265 lines
14 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
handleExternalUrl: null,
|
|
navigateReducer: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
handleExternalUrl: function() {
|
|
return handleExternalUrl;
|
|
},
|
|
navigateReducer: function() {
|
|
return navigateReducer;
|
|
}
|
|
});
|
|
const _approutercontextsharedruntime = require("../../../../shared/lib/app-router-context.shared-runtime");
|
|
const _fetchserverresponse = require("../fetch-server-response");
|
|
const _createrecordfromthenable = require("../create-record-from-thenable");
|
|
const _readrecordvalue = require("../read-record-value");
|
|
const _createhreffromurl = require("../create-href-from-url");
|
|
const _invalidatecachebelowflightsegmentpath = require("../invalidate-cache-below-flight-segmentpath");
|
|
const _fillcachewithdataproperty = require("../fill-cache-with-data-property");
|
|
const _createoptimistictree = require("../create-optimistic-tree");
|
|
const _applyrouterstatepatchtotree = require("../apply-router-state-patch-to-tree");
|
|
const _shouldhardnavigate = require("../should-hard-navigate");
|
|
const _isnavigatingtonewrootlayout = require("../is-navigating-to-new-root-layout");
|
|
const _routerreducertypes = require("../router-reducer-types");
|
|
const _handlemutable = require("../handle-mutable");
|
|
const _applyflightdata = require("../apply-flight-data");
|
|
const _getprefetchcacheentrystatus = require("../get-prefetch-cache-entry-status");
|
|
const _pruneprefetchcache = require("./prune-prefetch-cache");
|
|
const _prefetchreducer = require("./prefetch-reducer");
|
|
function handleExternalUrl(state, mutable, url, pendingPush) {
|
|
mutable.previousTree = state.tree;
|
|
mutable.mpaNavigation = true;
|
|
mutable.canonicalUrl = url;
|
|
mutable.pendingPush = pendingPush;
|
|
mutable.scrollableSegments = undefined;
|
|
return (0, _handlemutable.handleMutable)(state, mutable);
|
|
}
|
|
function generateSegmentsFromPatch(flightRouterPatch) {
|
|
const segments = [];
|
|
const [segment, parallelRoutes] = flightRouterPatch;
|
|
if (Object.keys(parallelRoutes).length === 0) {
|
|
return [
|
|
[
|
|
segment
|
|
]
|
|
];
|
|
}
|
|
for (const [parallelRouteKey, parallelRoute] of Object.entries(parallelRoutes)){
|
|
for (const childSegment of generateSegmentsFromPatch(parallelRoute)){
|
|
// If the segment is empty, it means we are at the root of the tree
|
|
if (segment === "") {
|
|
segments.push([
|
|
parallelRouteKey,
|
|
...childSegment
|
|
]);
|
|
} else {
|
|
segments.push([
|
|
segment,
|
|
parallelRouteKey,
|
|
...childSegment
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
return segments;
|
|
}
|
|
function addRefetchToLeafSegments(newCache, currentCache, flightSegmentPath, treePatch, data) {
|
|
let appliedPatch = false;
|
|
newCache.status = _approutercontextsharedruntime.CacheStates.READY;
|
|
newCache.subTreeData = currentCache.subTreeData;
|
|
newCache.parallelRoutes = new Map(currentCache.parallelRoutes);
|
|
const segmentPathsToFill = generateSegmentsFromPatch(treePatch).map((segment)=>[
|
|
...flightSegmentPath,
|
|
...segment
|
|
]);
|
|
for (const segmentPaths of segmentPathsToFill){
|
|
const res = (0, _fillcachewithdataproperty.fillCacheWithDataProperty)(newCache, currentCache, segmentPaths, data);
|
|
if (!(res == null ? void 0 : res.bailOptimistic)) {
|
|
appliedPatch = true;
|
|
}
|
|
}
|
|
return appliedPatch;
|
|
}
|
|
function navigateReducer(state, action) {
|
|
const { url, isExternalUrl, navigateType, cache, mutable, forceOptimisticNavigation, shouldScroll } = action;
|
|
const { pathname, hash } = url;
|
|
const href = (0, _createhreffromurl.createHrefFromUrl)(url);
|
|
const pendingPush = navigateType === "push";
|
|
// we want to prune the prefetch cache on every navigation to avoid it growing too large
|
|
(0, _pruneprefetchcache.prunePrefetchCache)(state.prefetchCache);
|
|
const isForCurrentTree = JSON.stringify(mutable.previousTree) === JSON.stringify(state.tree);
|
|
if (isForCurrentTree) {
|
|
return (0, _handlemutable.handleMutable)(state, mutable);
|
|
}
|
|
if (isExternalUrl) {
|
|
return handleExternalUrl(state, mutable, url.toString(), pendingPush);
|
|
}
|
|
let prefetchValues = state.prefetchCache.get((0, _createhreffromurl.createHrefFromUrl)(url, false));
|
|
if (forceOptimisticNavigation && (prefetchValues == null ? void 0 : prefetchValues.kind) !== _routerreducertypes.PrefetchKind.TEMPORARY) {
|
|
const segments = pathname.split("/");
|
|
// TODO-APP: figure out something better for index pages
|
|
segments.push("__PAGE__");
|
|
// Optimistic tree case.
|
|
// If the optimistic tree is deeper than the current state leave that deeper part out of the fetch
|
|
const optimisticTree = (0, _createoptimistictree.createOptimisticTree)(segments, state.tree, false);
|
|
// we need a copy of the cache in case we need to revert to it
|
|
const temporaryCacheNode = {
|
|
...cache
|
|
};
|
|
// Copy subTreeData for the root node of the cache.
|
|
// Note: didn't do it above because typescript doesn't like it.
|
|
temporaryCacheNode.status = _approutercontextsharedruntime.CacheStates.READY;
|
|
temporaryCacheNode.subTreeData = state.cache.subTreeData;
|
|
temporaryCacheNode.parallelRoutes = new Map(state.cache.parallelRoutes);
|
|
let data;
|
|
const fetchResponse = ()=>{
|
|
if (!data) {
|
|
data = (0, _createrecordfromthenable.createRecordFromThenable)((0, _fetchserverresponse.fetchServerResponse)(url, optimisticTree, state.nextUrl, state.buildId));
|
|
}
|
|
return data;
|
|
};
|
|
// TODO-APP: segments.slice(1) strips '', we can get rid of '' altogether.
|
|
// TODO-APP: re-evaluate if we need to strip the last segment
|
|
const optimisticFlightSegmentPath = segments.slice(1).map((segment)=>[
|
|
"children",
|
|
segment
|
|
]).flat();
|
|
// Copy existing cache nodes as far as possible and fill in `data` property with the started data fetch.
|
|
// The `data` property is used to suspend in layout-router during render if it hasn't resolved yet by the time it renders.
|
|
const res = (0, _fillcachewithdataproperty.fillCacheWithDataProperty)(temporaryCacheNode, state.cache, optimisticFlightSegmentPath, fetchResponse, true);
|
|
// If optimistic fetch couldn't happen it falls back to the non-optimistic case.
|
|
if (!(res == null ? void 0 : res.bailOptimistic)) {
|
|
mutable.previousTree = state.tree;
|
|
mutable.patchedTree = optimisticTree;
|
|
mutable.pendingPush = pendingPush;
|
|
mutable.hashFragment = hash;
|
|
mutable.shouldScroll = shouldScroll;
|
|
mutable.scrollableSegments = [];
|
|
mutable.cache = temporaryCacheNode;
|
|
mutable.canonicalUrl = href;
|
|
state.prefetchCache.set((0, _createhreffromurl.createHrefFromUrl)(url, false), {
|
|
data: (0, _createrecordfromthenable.createRecordFromThenable)(Promise.resolve(data)),
|
|
// this will make sure that the entry will be discarded after 30s
|
|
kind: _routerreducertypes.PrefetchKind.TEMPORARY,
|
|
prefetchTime: Date.now(),
|
|
treeAtTimeOfPrefetch: state.tree,
|
|
lastUsedTime: Date.now()
|
|
});
|
|
return (0, _handlemutable.handleMutable)(state, mutable);
|
|
}
|
|
}
|
|
// If we don't have a prefetch value, we need to create one
|
|
if (!prefetchValues) {
|
|
const data = (0, _createrecordfromthenable.createRecordFromThenable)((0, _fetchserverresponse.fetchServerResponse)(url, state.tree, state.nextUrl, state.buildId, // in dev, there's never gonna be a prefetch entry so we want to prefetch here
|
|
// in order to simulate the behavior of the prefetch cache
|
|
process.env.NODE_ENV === "development" ? _routerreducertypes.PrefetchKind.AUTO : undefined));
|
|
const newPrefetchValue = {
|
|
data: (0, _createrecordfromthenable.createRecordFromThenable)(Promise.resolve(data)),
|
|
// this will make sure that the entry will be discarded after 30s
|
|
kind: process.env.NODE_ENV === "development" ? _routerreducertypes.PrefetchKind.AUTO : _routerreducertypes.PrefetchKind.TEMPORARY,
|
|
prefetchTime: Date.now(),
|
|
treeAtTimeOfPrefetch: state.tree,
|
|
lastUsedTime: null
|
|
};
|
|
state.prefetchCache.set((0, _createhreffromurl.createHrefFromUrl)(url, false), newPrefetchValue);
|
|
prefetchValues = newPrefetchValue;
|
|
}
|
|
const prefetchEntryCacheStatus = (0, _getprefetchcacheentrystatus.getPrefetchEntryCacheStatus)(prefetchValues);
|
|
// The one before last item is the router state tree patch
|
|
const { treeAtTimeOfPrefetch, data } = prefetchValues;
|
|
_prefetchreducer.prefetchQueue.bump(data);
|
|
// Unwrap cache data with `use` to suspend here (in the reducer) until the fetch resolves.
|
|
const [flightData, canonicalUrlOverride] = (0, _readrecordvalue.readRecordValue)(data);
|
|
// we only want to mark this once
|
|
if (!prefetchValues.lastUsedTime) {
|
|
// important: we should only mark the cache node as dirty after we unsuspend from the call above
|
|
prefetchValues.lastUsedTime = Date.now();
|
|
}
|
|
// Handle case when navigating to page in `pages` from `app`
|
|
if (typeof flightData === "string") {
|
|
return handleExternalUrl(state, mutable, flightData, pendingPush);
|
|
}
|
|
let currentTree = state.tree;
|
|
let currentCache = state.cache;
|
|
let scrollableSegments = [];
|
|
for (const flightDataPath of flightData){
|
|
const flightSegmentPath = flightDataPath.slice(0, -4);
|
|
// The one before last item is the router state tree patch
|
|
const treePatch = flightDataPath.slice(-3)[0];
|
|
// TODO-APP: remove ''
|
|
const flightSegmentPathWithLeadingEmpty = [
|
|
"",
|
|
...flightSegmentPath
|
|
];
|
|
// Create new tree based on the flightSegmentPath and router state patch
|
|
let newTree = (0, _applyrouterstatepatchtotree.applyRouterStatePatchToTree)(// TODO-APP: remove ''
|
|
flightSegmentPathWithLeadingEmpty, currentTree, treePatch);
|
|
// If the tree patch can't be applied to the current tree then we use the tree at time of prefetch
|
|
// TODO-APP: This should instead fill in the missing pieces in `currentTree` with the data from `treeAtTimeOfPrefetch`, then apply the patch.
|
|
if (newTree === null) {
|
|
newTree = (0, _applyrouterstatepatchtotree.applyRouterStatePatchToTree)(// TODO-APP: remove ''
|
|
flightSegmentPathWithLeadingEmpty, treeAtTimeOfPrefetch, treePatch);
|
|
}
|
|
if (newTree !== null) {
|
|
if ((0, _isnavigatingtonewrootlayout.isNavigatingToNewRootLayout)(currentTree, newTree)) {
|
|
return handleExternalUrl(state, mutable, href, pendingPush);
|
|
}
|
|
let applied = (0, _applyflightdata.applyFlightData)(currentCache, cache, flightDataPath, prefetchValues.kind === "auto" && prefetchEntryCacheStatus === _getprefetchcacheentrystatus.PrefetchCacheEntryStatus.reusable);
|
|
if (!applied && prefetchEntryCacheStatus === _getprefetchcacheentrystatus.PrefetchCacheEntryStatus.stale) {
|
|
applied = addRefetchToLeafSegments(cache, currentCache, flightSegmentPath, treePatch, // eslint-disable-next-line no-loop-func
|
|
()=>(0, _fetchserverresponse.fetchServerResponse)(url, currentTree, state.nextUrl, state.buildId));
|
|
}
|
|
const hardNavigate = (0, _shouldhardnavigate.shouldHardNavigate)(// TODO-APP: remove ''
|
|
flightSegmentPathWithLeadingEmpty, currentTree);
|
|
if (hardNavigate) {
|
|
cache.status = _approutercontextsharedruntime.CacheStates.READY;
|
|
// Copy subTreeData for the root node of the cache.
|
|
cache.subTreeData = currentCache.subTreeData;
|
|
(0, _invalidatecachebelowflightsegmentpath.invalidateCacheBelowFlightSegmentPath)(cache, currentCache, flightSegmentPath);
|
|
// Ensure the existing cache value is used when the cache was not invalidated.
|
|
mutable.cache = cache;
|
|
} else if (applied) {
|
|
mutable.cache = cache;
|
|
}
|
|
currentCache = cache;
|
|
currentTree = newTree;
|
|
for (const subSegment of generateSegmentsFromPatch(treePatch)){
|
|
const scrollableSegmentPath = [
|
|
...flightSegmentPath,
|
|
...subSegment
|
|
];
|
|
// Filter out the __DEFAULT__ paths as they shouldn't be scrolled to in this case.
|
|
if (scrollableSegmentPath[scrollableSegmentPath.length - 1] !== "__DEFAULT__") {
|
|
scrollableSegments.push(scrollableSegmentPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
mutable.previousTree = state.tree;
|
|
mutable.patchedTree = currentTree;
|
|
mutable.canonicalUrl = canonicalUrlOverride ? (0, _createhreffromurl.createHrefFromUrl)(canonicalUrlOverride) : href;
|
|
mutable.pendingPush = pendingPush;
|
|
mutable.scrollableSegments = scrollableSegments;
|
|
mutable.hashFragment = hash;
|
|
mutable.shouldScroll = shouldScroll;
|
|
return (0, _handlemutable.handleMutable)(state, mutable);
|
|
}
|
|
|
|
if ((typeof exports.default === 'function' || (typeof exports.default === 'object' && exports.default !== null)) && typeof exports.default.__esModule === 'undefined') {
|
|
Object.defineProperty(exports.default, '__esModule', { value: true });
|
|
Object.assign(exports.default, exports);
|
|
module.exports = exports.default;
|
|
}
|
|
|
|
//# sourceMappingURL=navigate-reducer.js.map
|