/
fast-refresh-reducer.ts
114 lines (96 loc) · 3.33 KB
/
fast-refresh-reducer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import { fetchServerResponse } from '../fetch-server-response'
import { createRecordFromThenable } from '../create-record-from-thenable'
import { readRecordValue } from '../read-record-value'
import { createHrefFromUrl } from '../create-href-from-url'
import { applyRouterStatePatchToTree } from '../apply-router-state-patch-to-tree'
import { isNavigatingToNewRootLayout } from '../is-navigating-to-new-root-layout'
import {
ReadonlyReducerState,
ReducerState,
FastRefreshAction,
} from '../router-reducer-types'
import { handleExternalUrl } from './navigate-reducer'
import { handleMutable } from '../handle-mutable'
import { applyFlightData } from '../apply-flight-data'
// A version of refresh reducer that keeps the cache around instead of wiping all of it.
function fastRefreshReducerImpl(
state: ReadonlyReducerState,
action: FastRefreshAction
): ReducerState {
const { cache, mutable, origin } = action
const href = state.canonicalUrl
const isForCurrentTree =
JSON.stringify(mutable.previousTree) === JSON.stringify(state.tree)
if (isForCurrentTree) {
return handleMutable(state, mutable)
}
if (!cache.data) {
// TODO-APP: verify that `href` is not an external url.
// Fetch data from the root of the tree.
cache.data = createRecordFromThenable(
fetchServerResponse(
new URL(href, origin),
[state.tree[0], state.tree[1], state.tree[2], 'refetch'],
state.nextUrl
)
)
}
const [flightData, canonicalUrlOverride] = readRecordValue(cache.data!)
// Handle case when navigating to page in `pages` from `app`
if (typeof flightData === 'string') {
return handleExternalUrl(
state,
mutable,
flightData,
state.pushRef.pendingPush
)
}
// Remove cache.data as it has been resolved at this point.
cache.data = null
// TODO-APP: Currently the Flight data can only have one item but in the future it can have multiple paths.
const flightDataPath = flightData[0]
// FlightDataPath with more than two items means unexpected Flight data was returned
if (flightDataPath.length !== 3) {
// TODO-APP: handle this case better
console.log('REFRESH FAILED')
return state
}
// Given the path can only have two items the items are only the router state and subTreeData for the root.
const [treePatch] = flightDataPath
const newTree = applyRouterStatePatchToTree(
// TODO-APP: remove ''
[''],
state.tree,
treePatch
)
if (newTree === null) {
throw new Error('SEGMENT MISMATCH')
}
if (isNavigatingToNewRootLayout(state.tree, newTree)) {
return handleExternalUrl(state, mutable, href, state.pushRef.pendingPush)
}
const canonicalUrlOverrideHref = canonicalUrlOverride
? createHrefFromUrl(canonicalUrlOverride)
: undefined
if (canonicalUrlOverride) {
mutable.canonicalUrl = canonicalUrlOverrideHref
}
const applied = applyFlightData(state, cache, flightDataPath)
if (applied) {
mutable.cache = cache
}
mutable.previousTree = state.tree
mutable.patchedTree = newTree
mutable.canonicalUrl = href
return handleMutable(state, mutable)
}
function fastRefreshReducerNoop(
state: ReadonlyReducerState,
_action: FastRefreshAction
): ReducerState {
return state
}
export const fastRefreshReducer =
process.env.NODE_ENV === 'production'
? fastRefreshReducerNoop
: fastRefreshReducerImpl