@tanstack/react-router 0.0.1-beta.21 → 0.0.1-beta.211

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/CatchBoundary.js +125 -0
  3. package/build/cjs/CatchBoundary.js.map +1 -0
  4. package/build/cjs/Matches.js +223 -0
  5. package/build/cjs/Matches.js.map +1 -0
  6. package/build/cjs/RouterProvider.js +1020 -0
  7. package/build/cjs/RouterProvider.js.map +1 -0
  8. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +1 -19
  9. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
  10. package/build/cjs/fileRoute.js +29 -0
  11. package/build/cjs/fileRoute.js.map +1 -0
  12. package/build/cjs/index.js +124 -0
  13. package/build/cjs/index.js.map +1 -0
  14. package/build/cjs/lazyRouteComponent.js +57 -0
  15. package/build/cjs/lazyRouteComponent.js.map +1 -0
  16. package/build/cjs/link.js +150 -0
  17. package/build/cjs/link.js.map +1 -0
  18. package/build/cjs/path.js +211 -0
  19. package/build/cjs/path.js.map +1 -0
  20. package/build/cjs/qss.js +65 -0
  21. package/build/cjs/qss.js.map +1 -0
  22. package/build/cjs/redirects.js +27 -0
  23. package/build/cjs/redirects.js.map +1 -0
  24. package/build/cjs/route.js +133 -0
  25. package/build/cjs/route.js.map +1 -0
  26. package/build/cjs/router.js +203 -0
  27. package/build/cjs/router.js.map +1 -0
  28. package/build/cjs/searchParams.js +83 -0
  29. package/build/cjs/searchParams.js.map +1 -0
  30. package/build/cjs/useBlocker.js +64 -0
  31. package/build/cjs/useBlocker.js.map +1 -0
  32. package/build/cjs/useNavigate.js +78 -0
  33. package/build/cjs/useNavigate.js.map +1 -0
  34. package/build/cjs/useParams.js +28 -0
  35. package/build/cjs/useParams.js.map +1 -0
  36. package/build/cjs/useSearch.js +27 -0
  37. package/build/cjs/useSearch.js.map +1 -0
  38. package/build/cjs/utils.js +225 -0
  39. package/build/cjs/utils.js.map +1 -0
  40. package/build/esm/index.js +1894 -2565
  41. package/build/esm/index.js.map +1 -1
  42. package/build/stats-html.html +59 -49
  43. package/build/stats-react.json +815 -43
  44. package/build/types/CatchBoundary.d.ts +33 -0
  45. package/build/types/Matches.d.ts +31 -0
  46. package/build/types/RouterProvider.d.ts +78 -0
  47. package/build/types/awaited.d.ts +0 -0
  48. package/build/types/defer.d.ts +0 -0
  49. package/build/types/fileRoute.d.ts +32 -0
  50. package/build/types/history.d.ts +7 -0
  51. package/build/types/index.d.ts +24 -104
  52. package/build/types/injectHtml.d.ts +0 -0
  53. package/build/types/lazyRouteComponent.d.ts +2 -0
  54. package/build/types/link.d.ts +105 -0
  55. package/build/types/location.d.ts +14 -0
  56. package/build/types/path.d.ts +16 -0
  57. package/build/types/qss.d.ts +2 -0
  58. package/build/types/redirects.d.ts +10 -0
  59. package/build/types/route.d.ts +261 -0
  60. package/build/types/routeInfo.d.ts +22 -0
  61. package/build/types/router.d.ts +117 -0
  62. package/build/types/scroll-restoration.d.ts +0 -0
  63. package/build/types/searchParams.d.ts +7 -0
  64. package/build/types/useBlocker.d.ts +8 -0
  65. package/build/types/useNavigate.d.ts +20 -0
  66. package/build/types/useParams.d.ts +7 -0
  67. package/build/types/useSearch.d.ts +7 -0
  68. package/build/types/utils.d.ts +65 -0
  69. package/build/umd/index.development.js +2131 -2469
  70. package/build/umd/index.development.js.map +1 -1
  71. package/build/umd/index.production.js +4 -4
  72. package/build/umd/index.production.js.map +1 -1
  73. package/package.json +9 -10
  74. package/src/CatchBoundary.tsx +97 -0
  75. package/src/Matches.tsx +315 -0
  76. package/src/RouterProvider.tsx +1457 -0
  77. package/src/awaited.tsx +40 -0
  78. package/src/defer.ts +55 -0
  79. package/src/fileRoute.ts +145 -0
  80. package/src/history.ts +8 -0
  81. package/src/index.tsx +28 -693
  82. package/src/injectHtml.ts +28 -0
  83. package/src/lazyRouteComponent.tsx +33 -0
  84. package/src/link.tsx +507 -0
  85. package/src/location.ts +15 -0
  86. package/src/path.ts +256 -0
  87. package/src/qss.ts +53 -0
  88. package/src/redirects.ts +31 -0
  89. package/src/route.ts +786 -0
  90. package/src/routeInfo.ts +68 -0
  91. package/src/router.ts +374 -0
  92. package/src/scroll-restoration.tsx +205 -0
  93. package/src/searchParams.ts +79 -0
  94. package/src/useBlocker.tsx +34 -0
  95. package/src/useNavigate.tsx +109 -0
  96. package/src/useParams.tsx +25 -0
  97. package/src/useSearch.tsx +25 -0
  98. package/src/utils.ts +337 -0
  99. package/build/cjs/react-router/src/index.js +0 -466
  100. package/build/cjs/react-router/src/index.js.map +0 -1
  101. package/build/cjs/router-core/build/esm/index.js +0 -2523
  102. package/build/cjs/router-core/build/esm/index.js.map +0 -1
@@ -0,0 +1,79 @@
1
+ import { decode, encode } from './qss'
2
+ import { AnySearchSchema } from './route'
3
+
4
+ export const defaultParseSearch = parseSearchWith(JSON.parse)
5
+ export const defaultStringifySearch = stringifySearchWith(
6
+ JSON.stringify,
7
+ JSON.parse,
8
+ )
9
+
10
+ export function parseSearchWith(parser: (str: string) => any) {
11
+ return (searchStr: string): AnySearchSchema => {
12
+ if (searchStr.substring(0, 1) === '?') {
13
+ searchStr = searchStr.substring(1)
14
+ }
15
+
16
+ let query: Record<string, unknown> = decode(searchStr)
17
+
18
+ // Try to parse any query params that might be json
19
+ for (let key in query) {
20
+ const value = query[key]
21
+ if (typeof value === 'string') {
22
+ try {
23
+ query[key] = parser(value)
24
+ } catch (err) {
25
+ //
26
+ }
27
+ }
28
+ }
29
+
30
+ return query
31
+ }
32
+ }
33
+
34
+ export function stringifySearchWith(
35
+ stringify: (search: any) => string,
36
+ parser?: (str: string) => any,
37
+ ) {
38
+ function stringifyValue(val: any) {
39
+ if (typeof val === 'object' && val !== null) {
40
+ try {
41
+ return stringify(val)
42
+ } catch (err) {
43
+ // silent
44
+ }
45
+ } else if (typeof val === 'string' && typeof parser === 'function') {
46
+ try {
47
+ // Check if it's a valid parseable string.
48
+ // If it is, then stringify it again.
49
+ parser(val)
50
+ return stringify(val)
51
+ } catch (err) {
52
+ // silent
53
+ }
54
+ }
55
+ return val
56
+ }
57
+
58
+ return (search: Record<string, any>) => {
59
+ search = { ...search }
60
+
61
+ if (search) {
62
+ Object.keys(search).forEach((key) => {
63
+ const val = search[key]
64
+ if (typeof val === 'undefined' || val === undefined) {
65
+ delete search[key]
66
+ } else {
67
+ search[key] = stringifyValue(val)
68
+ }
69
+ })
70
+ }
71
+
72
+ const searchStr = encode(search as Record<string, string>).toString()
73
+
74
+ return searchStr ? `?${searchStr}` : ''
75
+ }
76
+ }
77
+
78
+ export type SearchSerializer = (searchObj: Record<string, any>) => string
79
+ export type SearchParser = (searchStr: string) => Record<string, any>
@@ -0,0 +1,34 @@
1
+ import * as React from 'react'
2
+ import { ReactNode } from './route'
3
+ import { useRouter } from './RouterProvider'
4
+
5
+ export function useBlocker(
6
+ message: string,
7
+ condition: boolean | any = true,
8
+ ): void {
9
+ const { history } = useRouter()
10
+
11
+ React.useEffect(() => {
12
+ if (!condition) return
13
+
14
+ let unblock = history.block((retry, cancel) => {
15
+ if (window.confirm(message)) {
16
+ unblock()
17
+ retry()
18
+ }
19
+ })
20
+
21
+ return unblock
22
+ })
23
+ }
24
+
25
+ export function Block({ message, condition, children }: PromptProps) {
26
+ useBlocker(message, condition)
27
+ return (children ?? null) as ReactNode
28
+ }
29
+
30
+ export type PromptProps = {
31
+ message: string
32
+ condition?: boolean | any
33
+ children?: ReactNode
34
+ }
@@ -0,0 +1,109 @@
1
+ import * as React from 'react'
2
+ import { useMatch } from './Matches'
3
+ import { useRouter } from './RouterProvider'
4
+ import { LinkOptions, NavigateOptions } from './link'
5
+ import { AnyRoute } from './route'
6
+ import { RoutePaths } from './routeInfo'
7
+ import { RegisteredRouter } from './router'
8
+ import { useLayoutEffect } from './utils'
9
+
10
+ export function useNavigate<
11
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
12
+ TDefaultFrom extends RoutePaths<TRouteTree> = '/',
13
+ >(defaultOpts?: { from?: TDefaultFrom }) {
14
+ const { navigate } = useRouter()
15
+ const match = useMatch({
16
+ strict: false,
17
+ })
18
+ return React.useCallback(
19
+ <
20
+ TFrom extends RoutePaths<TRouteTree> = TDefaultFrom,
21
+ TTo extends string = '',
22
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
23
+ TMaskTo extends string = '',
24
+ >(
25
+ opts?: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
26
+ ) => {
27
+ return navigate({
28
+ from: opts?.to ? match.pathname : undefined,
29
+ ...defaultOpts,
30
+ ...(opts as any),
31
+ })
32
+ },
33
+ [],
34
+ )
35
+ }
36
+
37
+ export function typedNavigate<
38
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
39
+ TDefaultFrom extends RoutePaths<TRouteTree> = '/',
40
+ >(navigate: (opts: NavigateOptions<any>) => Promise<void>) {
41
+ return navigate as <
42
+ TFrom extends RoutePaths<TRouteTree> = TDefaultFrom,
43
+ TTo extends string = '',
44
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
45
+ TMaskTo extends string = '',
46
+ >(
47
+ opts?: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
48
+ ) => Promise<void>
49
+ } //
50
+
51
+ export function Navigate<
52
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
53
+ TFrom extends RoutePaths<TRouteTree> = '/',
54
+ TTo extends string = '',
55
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
56
+ TMaskTo extends string = '',
57
+ >(props: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>): null {
58
+ const { navigate } = useRouter()
59
+ const match = useMatch({ strict: false })
60
+
61
+ useLayoutEffect(() => {
62
+ navigate({
63
+ from: props.to ? match.pathname : undefined,
64
+ ...props,
65
+ } as any)
66
+ }, [])
67
+
68
+ return null
69
+ }
70
+
71
+ export type MakeLinkPropsOptions<
72
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
73
+ TFrom extends RoutePaths<TRouteTree> = '/',
74
+ TTo extends string = '',
75
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
76
+ TMaskTo extends string = '',
77
+ > = LinkPropsOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
78
+ React.AnchorHTMLAttributes<HTMLAnchorElement>
79
+
80
+ export type MakeLinkOptions<
81
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
82
+ TFrom extends RoutePaths<TRouteTree> = '/',
83
+ TTo extends string = '',
84
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
85
+ TMaskTo extends string = '',
86
+ > = LinkPropsOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
87
+ Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
88
+ // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
89
+ children?:
90
+ | React.ReactNode
91
+ | ((state: { isActive: boolean }) => React.ReactNode)
92
+ }
93
+
94
+ export type LinkPropsOptions<
95
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
96
+ TFrom extends RoutePaths<TRouteTree> = '/',
97
+ TTo extends string = '',
98
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
99
+ TMaskTo extends string = '',
100
+ > = LinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
101
+ // A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
102
+ activeProps?:
103
+ | React.AnchorHTMLAttributes<HTMLAnchorElement>
104
+ | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
105
+ // A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
106
+ inactiveProps?:
107
+ | React.AnchorHTMLAttributes<HTMLAnchorElement>
108
+ | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
109
+ }
@@ -0,0 +1,25 @@
1
+ import { AnyRoute } from './route'
2
+ import { RouteIds, RouteById, AllParams } from './routeInfo'
3
+ import { RegisteredRouter } from './router'
4
+ import { last } from './utils'
5
+ import { useRouterState } from './RouterProvider'
6
+ import { StrictOrFrom } from './utils'
7
+
8
+ export function useParams<
9
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
10
+ TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
11
+ TDefaultSelected = AllParams<TRouteTree> &
12
+ RouteById<TRouteTree, TFrom>['types']['allParams'],
13
+ TSelected = TDefaultSelected,
14
+ >(
15
+ opts: StrictOrFrom<TFrom> & {
16
+ select?: (search: TDefaultSelected) => TSelected
17
+ },
18
+ ): TSelected {
19
+ return useRouterState({
20
+ select: (state: any) => {
21
+ const params = (last(state.matches) as any)?.params
22
+ return opts?.select ? opts.select(params) : params
23
+ },
24
+ })
25
+ }
@@ -0,0 +1,25 @@
1
+ import { AnyRoute } from './route'
2
+ import { RouteIds, RouteById } from './routeInfo'
3
+ import { RegisteredRouter } from './router'
4
+ import { RouteMatch } from './RouterProvider'
5
+ import { useMatch } from './Matches'
6
+ import { StrictOrFrom } from './utils'
7
+
8
+ export function useSearch<
9
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
10
+ TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
11
+ TStrict extends boolean = true,
12
+ TSearch = RouteById<TRouteTree, TFrom>['types']['fullSearchSchema'],
13
+ TSelected = TSearch,
14
+ >(
15
+ opts: StrictOrFrom<TFrom> & {
16
+ select?: (search: TSearch) => TSelected
17
+ },
18
+ ): TStrict extends true ? TSelected : TSelected | undefined {
19
+ return useMatch({
20
+ ...(opts as any),
21
+ select: (match: RouteMatch) => {
22
+ return opts?.select ? opts.select(match.search as TSearch) : match.search
23
+ },
24
+ })
25
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,337 @@
1
+ import * as React from 'react'
2
+ import { useMatch } from './Matches'
3
+ import { RouteMatch } from './RouterProvider'
4
+ import { AnyRoute } from './route'
5
+ import { ParseRoute, RouteIds, RoutesById, RouteById } from './routeInfo'
6
+ import { RegisteredRouter } from './router'
7
+
8
+ export type NoInfer<T> = [T][T extends any ? 0 : never]
9
+ export type IsAny<T, Y, N = T> = 1 extends 0 & T ? Y : N
10
+ export type IsAnyBoolean<T> = 1 extends 0 & T ? true : false
11
+ export type IsKnown<T, Y, N> = unknown extends T ? N : Y
12
+ export type PickAsRequired<T, K extends keyof T> = Omit<T, K> &
13
+ Required<Pick<T, K>>
14
+ export type PickAsPartial<T, K extends keyof T> = Omit<T, K> &
15
+ Partial<Pick<T, K>>
16
+ export type PickUnsafe<T, K> = K extends keyof T ? Pick<T, K> : never
17
+ export type PickExtra<T, K> = {
18
+ [TKey in keyof K as string extends TKey
19
+ ? never
20
+ : TKey extends keyof T
21
+ ? never
22
+ : TKey]: K[TKey]
23
+ }
24
+
25
+ export type PickRequired<T> = {
26
+ [K in keyof T as undefined extends T[K] ? never : K]: T[K]
27
+ }
28
+
29
+ // export type Expand<T> = T
30
+ export type Expand<T> = T extends object
31
+ ? T extends infer O
32
+ ? { [K in keyof O]: O[K] }
33
+ : never
34
+ : T
35
+
36
+ export type UnionToIntersection<U> = (
37
+ U extends any ? (k: U) => void : never
38
+ ) extends (k: infer I) => any
39
+ ? I
40
+ : never
41
+
42
+ // type Compute<T> = { [K in keyof T]: T[K] } | never
43
+
44
+ // type AllKeys<T> = T extends any ? keyof T : never
45
+
46
+ // export type MergeUnion<T, Keys extends keyof T = keyof T> = Compute<
47
+ // {
48
+ // [K in Keys]: T[Keys]
49
+ // } & {
50
+ // [K in AllKeys<T>]?: T extends any
51
+ // ? K extends keyof T
52
+ // ? T[K]
53
+ // : never
54
+ // : never
55
+ // }
56
+ // >
57
+
58
+ export type Assign<Left, Right> = Omit<Left, keyof Right> & Right
59
+
60
+ export type AssignAll<T extends any[]> = T extends [infer Left, ...infer Right]
61
+ ? Right extends any[]
62
+ ? Assign<Left, AssignAll<Right>>
63
+ : Left
64
+ : {}
65
+
66
+ // // Sample types to merge
67
+ // type TypeA = {
68
+ // shared: string
69
+ // onlyInA: string
70
+ // nested: {
71
+ // shared: string
72
+ // aProp: string
73
+ // }
74
+ // array: string[]
75
+ // }
76
+
77
+ // type TypeB = {
78
+ // shared: number
79
+ // onlyInB: number
80
+ // nested: {
81
+ // shared: number
82
+ // bProp: number
83
+ // }
84
+ // array: number[]
85
+ // }
86
+
87
+ // type TypeC = {
88
+ // shared: boolean
89
+ // onlyInC: boolean
90
+ // nested: {
91
+ // shared: boolean
92
+ // cProp: boolean
93
+ // }
94
+ // array: boolean[]
95
+ // }
96
+
97
+ // type Test = Expand<Assign<TypeA, TypeB>>
98
+
99
+ // // Using DeepMerge to merge TypeA and TypeB
100
+ // type MergedType = Expand<AssignAll<[TypeA, TypeB, TypeC]>>
101
+
102
+ export type Values<O> = O[ValueKeys<O>]
103
+ export type ValueKeys<O> = Extract<keyof O, PropertyKey>
104
+
105
+ export type DeepAwaited<T> = T extends Promise<infer A>
106
+ ? DeepAwaited<A>
107
+ : T extends Record<infer A, Promise<infer B>>
108
+ ? { [K in A]: DeepAwaited<B> }
109
+ : T
110
+
111
+ export type PathParamMask<TRoutePath extends string> =
112
+ TRoutePath extends `${infer L}/$${infer C}/${infer R}`
113
+ ? PathParamMask<`${L}/${string}/${R}`>
114
+ : TRoutePath extends `${infer L}/$${infer C}`
115
+ ? PathParamMask<`${L}/${string}`>
116
+ : TRoutePath
117
+
118
+ export type Timeout = ReturnType<typeof setTimeout>
119
+
120
+ export type Updater<TPrevious, TResult = TPrevious> =
121
+ | TResult
122
+ | ((prev?: TPrevious) => TResult)
123
+
124
+ export type NonNullableUpdater<TPrevious, TResult = TPrevious> =
125
+ | TResult
126
+ | ((prev: TPrevious) => TResult)
127
+
128
+ export type PickExtract<T, U> = {
129
+ [K in keyof T as T[K] extends U ? K : never]: T[K]
130
+ }
131
+
132
+ export type PickExclude<T, U> = {
133
+ [K in keyof T as T[K] extends U ? never : K]: T[K]
134
+ }
135
+
136
+ //
137
+
138
+ export const isServer = typeof document === 'undefined'
139
+
140
+ export function last<T>(arr: T[]) {
141
+ return arr[arr.length - 1]
142
+ }
143
+
144
+ function isFunction(d: any): d is Function {
145
+ return typeof d === 'function'
146
+ }
147
+
148
+ export function functionalUpdate<TResult>(
149
+ updater: Updater<TResult> | NonNullableUpdater<TResult>,
150
+ previous: TResult,
151
+ ): TResult {
152
+ if (isFunction(updater)) {
153
+ return updater(previous as TResult)
154
+ }
155
+
156
+ return updater
157
+ }
158
+
159
+ export function pick<T, K extends keyof T>(parent: T, keys: K[]): Pick<T, K> {
160
+ return keys.reduce((obj: any, key: K) => {
161
+ obj[key] = parent[key]
162
+ return obj
163
+ }, {} as any)
164
+ }
165
+
166
+ /**
167
+ * This function returns `a` if `b` is deeply equal.
168
+ * If not, it will replace any deeply equal children of `b` with those of `a`.
169
+ * This can be used for structural sharing between immutable JSON values for example.
170
+ * Do not use this with signals
171
+ */
172
+ export function replaceEqualDeep<T>(prev: any, _next: T): T {
173
+ if (prev === _next) {
174
+ return prev
175
+ }
176
+
177
+ const next = _next as any
178
+
179
+ const array = Array.isArray(prev) && Array.isArray(next)
180
+
181
+ if (array || (isPlainObject(prev) && isPlainObject(next))) {
182
+ const prevSize = array ? prev.length : Object.keys(prev).length
183
+ const nextItems = array ? next : Object.keys(next)
184
+ const nextSize = nextItems.length
185
+ const copy: any = array ? [] : {}
186
+
187
+ let equalItems = 0
188
+
189
+ for (let i = 0; i < nextSize; i++) {
190
+ const key = array ? i : nextItems[i]
191
+ copy[key] = replaceEqualDeep(prev[key], next[key])
192
+ if (copy[key] === prev[key]) {
193
+ equalItems++
194
+ }
195
+ }
196
+
197
+ return prevSize === nextSize && equalItems === prevSize ? prev : copy
198
+ }
199
+
200
+ return next
201
+ }
202
+
203
+ // Copied from: https://github.com/jonschlinkert/is-plain-object
204
+ export function isPlainObject(o: any) {
205
+ if (!hasObjectPrototype(o)) {
206
+ return false
207
+ }
208
+
209
+ // If has modified constructor
210
+ const ctor = o.constructor
211
+ if (typeof ctor === 'undefined') {
212
+ return true
213
+ }
214
+
215
+ // If has modified prototype
216
+ const prot = ctor.prototype
217
+ if (!hasObjectPrototype(prot)) {
218
+ return false
219
+ }
220
+
221
+ // If constructor does not have an Object-specific method
222
+ if (!prot.hasOwnProperty('isPrototypeOf')) {
223
+ return false
224
+ }
225
+
226
+ // Most likely a plain Object
227
+ return true
228
+ }
229
+
230
+ function hasObjectPrototype(o: any) {
231
+ return Object.prototype.toString.call(o) === '[object Object]'
232
+ }
233
+
234
+ export function partialDeepEqual(a: any, b: any): boolean {
235
+ if (a === b) {
236
+ return true
237
+ }
238
+
239
+ if (typeof a !== typeof b) {
240
+ return false
241
+ }
242
+
243
+ if (isPlainObject(a) && isPlainObject(b)) {
244
+ return !Object.keys(b).some((key) => !partialDeepEqual(a[key], b[key]))
245
+ }
246
+
247
+ if (Array.isArray(a) && Array.isArray(b)) {
248
+ return !(
249
+ a.length !== b.length ||
250
+ a.some((item, index) => !partialDeepEqual(item, b[index]))
251
+ )
252
+ }
253
+
254
+ return false
255
+ }
256
+
257
+ export function useStableCallback<T extends (...args: any[]) => any>(fn: T): T {
258
+ const fnRef = React.useRef(fn)
259
+ fnRef.current = fn
260
+
261
+ const ref = React.useRef((...args: any[]) => fnRef.current(...args))
262
+ return ref.current as T
263
+ }
264
+
265
+ export function shallow<T>(objA: T, objB: T) {
266
+ if (Object.is(objA, objB)) {
267
+ return true
268
+ }
269
+
270
+ if (
271
+ typeof objA !== 'object' ||
272
+ objA === null ||
273
+ typeof objB !== 'object' ||
274
+ objB === null
275
+ ) {
276
+ return false
277
+ }
278
+
279
+ const keysA = Object.keys(objA)
280
+ if (keysA.length !== Object.keys(objB).length) {
281
+ return false
282
+ }
283
+
284
+ for (let i = 0; i < keysA.length; i++) {
285
+ if (
286
+ !Object.prototype.hasOwnProperty.call(objB, keysA[i] as string) ||
287
+ !Object.is(objA[keysA[i] as keyof T], objB[keysA[i] as keyof T])
288
+ ) {
289
+ return false
290
+ }
291
+ }
292
+ return true
293
+ }
294
+
295
+ export type StrictOrFrom<TFrom> =
296
+ | {
297
+ from: TFrom
298
+ strict?: true
299
+ }
300
+ | {
301
+ from?: never
302
+ strict: false
303
+ }
304
+
305
+ export type RouteFromIdOrRoute<
306
+ T,
307
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
308
+ > = T extends ParseRoute<TRouteTree>
309
+ ? T
310
+ : T extends RouteIds<TRouteTree>
311
+ ? RoutesById<TRouteTree>[T]
312
+ : T extends string
313
+ ? RouteIds<TRouteTree>
314
+ : never
315
+
316
+ export function useRouteContext<
317
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
318
+ TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
319
+ TStrict extends boolean = true,
320
+ TRouteContext = RouteById<TRouteTree, TFrom>['types']['allContext'],
321
+ TSelected = TRouteContext,
322
+ >(
323
+ opts: StrictOrFrom<TFrom> & {
324
+ select?: (search: TRouteContext) => TSelected
325
+ },
326
+ ): TStrict extends true ? TSelected : TSelected | undefined {
327
+ return useMatch({
328
+ ...(opts as any),
329
+ select: (match: RouteMatch) =>
330
+ opts?.select
331
+ ? opts.select(match.context as TRouteContext)
332
+ : match.context,
333
+ })
334
+ }
335
+
336
+ export const useLayoutEffect =
337
+ typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect