|
1 | | -import { |
2 | | - fetchServerResponse, |
3 | | - type FetchServerResponseResult, |
4 | | -} from '../fetch-server-response' |
5 | | -import { createHrefFromUrl } from '../create-href-from-url' |
6 | | -import { applyRouterStatePatchToTree } from '../apply-router-state-patch-to-tree' |
7 | | -import { isNavigatingToNewRootLayout } from '../is-navigating-to-new-root-layout' |
8 | 1 | import type { |
| 2 | + Mutable, |
9 | 3 | ReadonlyReducerState, |
10 | 4 | ReducerState, |
11 | 5 | HmrRefreshAction, |
12 | | - Mutable, |
13 | 6 | } from '../router-reducer-types' |
14 | | -import { handleExternalUrl } from './navigate-reducer' |
15 | | -import { handleMutable } from '../handle-mutable' |
16 | | -import { applyFlightData } from '../apply-flight-data' |
17 | | -import type { CacheNode } from '../../../../shared/lib/app-router-types' |
18 | | -import { createEmptyCacheNode } from '../../app-router' |
19 | | -import { handleSegmentMismatch } from '../handle-segment-mismatch' |
20 | | -import { hasInterceptionRouteInCurrentTree } from './has-interception-route-in-current-tree' |
| 7 | +import { handleNavigationResult } from './navigate-reducer' |
| 8 | +import { refresh as refreshUsingSegmentCache } from '../../segment-cache/navigation' |
| 9 | +import { revalidateEntireCache } from '../../segment-cache/cache' |
21 | 10 |
|
22 | | -// A version of refresh reducer that keeps the cache around instead of wiping all of it. |
23 | | -function hmrRefreshReducerImpl( |
| 11 | +export function hmrRefreshReducerImpl( |
24 | 12 | state: ReadonlyReducerState, |
25 | 13 | action: HmrRefreshAction |
26 | 14 | ): ReducerState { |
27 | | - const { origin } = action |
28 | | - const mutable: Mutable = {} |
29 | | - const href = state.canonicalUrl |
| 15 | + // There was a code change. Purge the entire prefetch cache. This is the main |
| 16 | + // difference between an HMR refresh and a normal refresh. |
| 17 | + revalidateEntireCache(state.nextUrl, state.tree) |
| 18 | + |
| 19 | + const currentUrl = new URL(state.canonicalUrl, action.origin) |
| 20 | + const result = refreshUsingSegmentCache( |
| 21 | + currentUrl, |
| 22 | + state.tree, |
| 23 | + state.nextUrl, |
| 24 | + state.renderedSearch, |
| 25 | + state.canonicalUrl |
| 26 | + ) |
30 | 27 |
|
| 28 | + const mutable: Mutable = {} |
31 | 29 | mutable.preserveCustomHistoryState = false |
32 | 30 |
|
33 | | - const cache: CacheNode = createEmptyCacheNode() |
34 | | - // If the current tree was intercepted, the nextUrl should be included in the request. |
35 | | - // This is to ensure that the refresh request doesn't get intercepted, accidentally triggering the interception route. |
36 | | - const includeNextUrl = hasInterceptionRouteInCurrentTree(state.tree) |
37 | | - |
38 | | - // TODO-APP: verify that `href` is not an external url. |
39 | | - // Fetch data from the root of the tree. |
40 | | - const navigatedAt = Date.now() |
41 | | - cache.lazyData = fetchServerResponse(new URL(href, origin), { |
42 | | - flightRouterState: [state.tree[0], state.tree[1], state.tree[2], 'refetch'], |
43 | | - nextUrl: includeNextUrl ? state.nextUrl : null, |
44 | | - isHmrRefresh: true, |
45 | | - }) |
46 | | - |
47 | | - return cache.lazyData.then( |
48 | | - (result: FetchServerResponseResult) => { |
49 | | - // Handle case when navigating to page in `pages` from `app` |
50 | | - if (typeof result === 'string') { |
51 | | - return handleExternalUrl( |
52 | | - state, |
53 | | - mutable, |
54 | | - result, |
55 | | - state.pushRef.pendingPush |
56 | | - ) |
57 | | - } |
58 | | - |
59 | | - const { flightData, canonicalUrl, renderedSearch } = result |
60 | | - |
61 | | - // Remove cache.lazyData as it has been resolved at this point. |
62 | | - cache.lazyData = null |
63 | | - |
64 | | - let currentTree = state.tree |
65 | | - let currentCache = state.cache |
66 | | - |
67 | | - for (const normalizedFlightData of flightData) { |
68 | | - const { tree: treePatch, isRootRender } = normalizedFlightData |
69 | | - if (!isRootRender) { |
70 | | - // TODO-APP: handle this case better |
71 | | - console.log('REFRESH FAILED') |
72 | | - return state |
73 | | - } |
74 | | - |
75 | | - const newTree = applyRouterStatePatchToTree( |
76 | | - // TODO-APP: remove '' |
77 | | - [''], |
78 | | - currentTree, |
79 | | - treePatch, |
80 | | - state.canonicalUrl |
81 | | - ) |
82 | | - |
83 | | - if (newTree === null) { |
84 | | - return handleSegmentMismatch(state, action, treePatch) |
85 | | - } |
86 | | - |
87 | | - if (isNavigatingToNewRootLayout(currentTree, newTree)) { |
88 | | - return handleExternalUrl( |
89 | | - state, |
90 | | - mutable, |
91 | | - href, |
92 | | - state.pushRef.pendingPush |
93 | | - ) |
94 | | - } |
95 | | - |
96 | | - const applied = applyFlightData( |
97 | | - navigatedAt, |
98 | | - currentCache, |
99 | | - cache, |
100 | | - normalizedFlightData |
101 | | - ) |
102 | | - |
103 | | - if (applied) { |
104 | | - mutable.cache = cache |
105 | | - currentCache = cache |
106 | | - } |
107 | | - |
108 | | - mutable.patchedTree = newTree |
109 | | - mutable.renderedSearch = renderedSearch |
110 | | - mutable.canonicalUrl = createHrefFromUrl(canonicalUrl) |
111 | | - |
112 | | - currentTree = newTree |
113 | | - } |
114 | | - return handleMutable(state, mutable) |
115 | | - }, |
116 | | - () => state |
117 | | - ) |
| 31 | + return handleNavigationResult(currentUrl, state, mutable, false, result) |
118 | 32 | } |
119 | 33 |
|
120 | 34 | function hmrRefreshReducerNoop( |
|
0 commit comments