-
Notifications
You must be signed in to change notification settings - Fork 26k
/
compute-changed-path.ts
120 lines (97 loc) · 3.06 KB
/
compute-changed-path.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
115
116
117
118
119
120
import { FlightRouterState, Segment } from '../../../server/app-render/types'
import { INTERCEPTION_ROUTE_MARKERS } from '../../../server/future/helpers/interception-routes'
import { matchSegment } from '../match-segments'
const segmentToPathname = (segment: Segment): string => {
if (typeof segment === 'string') {
return segment
}
return segment[1]
}
function normalizePathname(pathname: string): string {
return (
pathname.split('/').reduce((acc, segment) => {
if (
segment === '' ||
(segment.startsWith('(') && segment.endsWith(')'))
) {
return acc
}
return `${acc}/${segment}`
}, '') || '/'
)
}
export function extractPathFromFlightRouterState(
flightRouterState: FlightRouterState
): string | undefined {
const segment = Array.isArray(flightRouterState[0])
? flightRouterState[0][1]
: flightRouterState[0]
if (
segment === '__DEFAULT__' ||
INTERCEPTION_ROUTE_MARKERS.some((m) => segment.startsWith(m))
)
return undefined
if (segment.startsWith('__PAGE__')) return ''
const path = [segment]
const parallelRoutes = flightRouterState[1] ?? {}
const childrenPath = parallelRoutes.children
? extractPathFromFlightRouterState(parallelRoutes.children)
: undefined
if (childrenPath !== undefined) {
path.push(childrenPath)
} else {
for (const [key, value] of Object.entries(parallelRoutes)) {
if (key === 'children') continue
const childPath = extractPathFromFlightRouterState(value)
if (childPath !== undefined) {
path.push(childPath)
}
}
}
// TODO-APP: optimise this, it's not ideal to join and split
return normalizePathname(path.join('/'))
}
function computeChangedPathImpl(
treeA: FlightRouterState,
treeB: FlightRouterState
): string | null {
const [segmentA, parallelRoutesA] = treeA
const [segmentB, parallelRoutesB] = treeB
const normalizedSegmentA = segmentToPathname(segmentA)
const normalizedSegmentB = segmentToPathname(segmentB)
if (
INTERCEPTION_ROUTE_MARKERS.some(
(m) =>
normalizedSegmentA.startsWith(m) || normalizedSegmentB.startsWith(m)
)
) {
return ''
}
if (!matchSegment(segmentA, segmentB)) {
// once we find where the tree changed, we compute the rest of the path by traversing the tree
return extractPathFromFlightRouterState(treeB) ?? ''
}
for (const parallelRouterKey in parallelRoutesA) {
if (parallelRoutesB[parallelRouterKey]) {
const changedPath = computeChangedPathImpl(
parallelRoutesA[parallelRouterKey],
parallelRoutesB[parallelRouterKey]
)
if (changedPath !== null) {
return `${segmentToPathname(segmentB)}/${changedPath}`
}
}
}
return null
}
export function computeChangedPath(
treeA: FlightRouterState,
treeB: FlightRouterState
): string | null {
const changedPath = computeChangedPathImpl(treeA, treeB)
if (changedPath == null || changedPath === '/') {
return changedPath
}
// lightweight normalization to remove route groups
return normalizePathname(changedPath)
}