@tanstack/react-router 0.0.1-beta.22 → 0.0.1-beta.220

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 (108) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/CatchBoundary.js +126 -0
  3. package/build/cjs/CatchBoundary.js.map +1 -0
  4. package/build/cjs/Matches.js +235 -0
  5. package/build/cjs/Matches.js.map +1 -0
  6. package/build/cjs/RouterProvider.js +1085 -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/awaited.js +45 -0
  11. package/build/cjs/awaited.js.map +1 -0
  12. package/build/cjs/defer.js +39 -0
  13. package/build/cjs/defer.js.map +1 -0
  14. package/build/cjs/fileRoute.js +29 -0
  15. package/build/cjs/fileRoute.js.map +1 -0
  16. package/build/cjs/index.js +135 -0
  17. package/build/cjs/index.js.map +1 -0
  18. package/build/cjs/lazyRouteComponent.js +57 -0
  19. package/build/cjs/lazyRouteComponent.js.map +1 -0
  20. package/build/cjs/link.js +151 -0
  21. package/build/cjs/link.js.map +1 -0
  22. package/build/cjs/path.js +211 -0
  23. package/build/cjs/path.js.map +1 -0
  24. package/build/cjs/qss.js +65 -0
  25. package/build/cjs/qss.js.map +1 -0
  26. package/build/cjs/redirects.js +27 -0
  27. package/build/cjs/redirects.js.map +1 -0
  28. package/build/cjs/route.js +139 -0
  29. package/build/cjs/route.js.map +1 -0
  30. package/build/cjs/router.js +160 -0
  31. package/build/cjs/router.js.map +1 -0
  32. package/build/cjs/scroll-restoration.js +186 -0
  33. package/build/cjs/scroll-restoration.js.map +1 -0
  34. package/build/cjs/searchParams.js +83 -0
  35. package/build/cjs/searchParams.js.map +1 -0
  36. package/build/cjs/useBlocker.js +64 -0
  37. package/build/cjs/useBlocker.js.map +1 -0
  38. package/build/cjs/useNavigate.js +78 -0
  39. package/build/cjs/useNavigate.js.map +1 -0
  40. package/build/cjs/useParams.js +28 -0
  41. package/build/cjs/useParams.js.map +1 -0
  42. package/build/cjs/useSearch.js +27 -0
  43. package/build/cjs/useSearch.js.map +1 -0
  44. package/build/cjs/utils.js +236 -0
  45. package/build/cjs/utils.js.map +1 -0
  46. package/build/esm/index.js +2136 -2564
  47. package/build/esm/index.js.map +1 -1
  48. package/build/stats-html.html +59 -49
  49. package/build/stats-react.json +922 -43
  50. package/build/types/CatchBoundary.d.ts +33 -0
  51. package/build/types/Matches.d.ts +34 -0
  52. package/build/types/RouterProvider.d.ts +87 -0
  53. package/build/types/awaited.d.ts +8 -0
  54. package/build/types/defer.d.ts +19 -0
  55. package/build/types/fileRoute.d.ts +32 -0
  56. package/build/types/history.d.ts +7 -0
  57. package/build/types/index.d.ts +27 -104
  58. package/build/types/injectHtml.d.ts +0 -0
  59. package/build/types/lazyRouteComponent.d.ts +2 -0
  60. package/build/types/link.d.ts +105 -0
  61. package/build/types/location.d.ts +14 -0
  62. package/build/types/path.d.ts +16 -0
  63. package/build/types/qss.d.ts +2 -0
  64. package/build/types/redirects.d.ts +10 -0
  65. package/build/types/route.d.ts +270 -0
  66. package/build/types/routeInfo.d.ts +22 -0
  67. package/build/types/router.d.ts +123 -0
  68. package/build/types/scroll-restoration.d.ts +6 -0
  69. package/build/types/searchParams.d.ts +7 -0
  70. package/build/types/useBlocker.d.ts +8 -0
  71. package/build/types/useNavigate.d.ts +20 -0
  72. package/build/types/useParams.d.ts +7 -0
  73. package/build/types/useSearch.d.ts +7 -0
  74. package/build/types/utils.d.ts +66 -0
  75. package/build/umd/index.development.js +2399 -2484
  76. package/build/umd/index.development.js.map +1 -1
  77. package/build/umd/index.production.js +4 -4
  78. package/build/umd/index.production.js.map +1 -1
  79. package/package.json +9 -10
  80. package/src/CatchBoundary.tsx +98 -0
  81. package/src/Matches.tsx +345 -0
  82. package/src/RouterProvider.tsx +1575 -0
  83. package/src/awaited.tsx +40 -0
  84. package/src/defer.ts +55 -0
  85. package/src/fileRoute.ts +153 -0
  86. package/src/history.ts +8 -0
  87. package/src/index.tsx +28 -693
  88. package/src/injectHtml.ts +28 -0
  89. package/src/lazyRouteComponent.tsx +33 -0
  90. package/src/link.tsx +508 -0
  91. package/src/location.ts +15 -0
  92. package/src/path.ts +256 -0
  93. package/src/qss.ts +53 -0
  94. package/src/redirects.ts +31 -0
  95. package/src/route.ts +837 -0
  96. package/src/routeInfo.ts +68 -0
  97. package/src/router.ts +330 -0
  98. package/src/scroll-restoration.tsx +192 -0
  99. package/src/searchParams.ts +79 -0
  100. package/src/useBlocker.tsx +34 -0
  101. package/src/useNavigate.tsx +109 -0
  102. package/src/useParams.tsx +25 -0
  103. package/src/useSearch.tsx +25 -0
  104. package/src/utils.ts +350 -0
  105. package/build/cjs/react-router/src/index.js +0 -466
  106. package/build/cjs/react-router/src/index.js.map +0 -1
  107. package/build/cjs/router-core/build/esm/index.js +0 -2523
  108. package/build/cjs/router-core/build/esm/index.js.map +0 -1
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
3
  "author": "Tanner Linsley",
4
- "version": "0.0.1-beta.22",
4
+ "version": "0.0.1-beta.220",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
- "homepage": "https://tanstack.com/router/",
7
+ "homepage": "https://tanstack.com/router",
8
8
  "description": "",
9
9
  "publishConfig": {
10
10
  "registry": "https://registry.npmjs.org/"
@@ -12,7 +12,6 @@
12
12
  "keywords": [
13
13
  "react",
14
14
  "location",
15
- "@tanstack/react-router",
16
15
  "router",
17
16
  "routing",
18
17
  "async",
@@ -24,7 +23,7 @@
24
23
  "url": "https://github.com/sponsors/tannerlinsley"
25
24
  },
26
25
  "module": "build/esm/index.js",
27
- "main": "build/cjs/react-router/src/index.js",
26
+ "main": "build/cjs/index.js",
28
27
  "browser": "build/umd/index.production.js",
29
28
  "types": "build/types/index.d.ts",
30
29
  "engines": {
@@ -41,11 +40,11 @@
41
40
  },
42
41
  "dependencies": {
43
42
  "@babel/runtime": "^7.16.7",
44
- "@tanstack/router-core": "0.0.1-beta.21",
45
- "use-sync-external-store": "^1.2.0"
43
+ "tiny-invariant": "^1.3.1",
44
+ "tiny-warning": "^1.0.3",
45
+ "@tanstack/history": "0.0.1-beta.220"
46
46
  },
47
- "devDependencies": {
48
- "@types/use-sync-external-store": "^0.0.3",
49
- "babel-plugin-transform-async-to-promises": "^0.8.18"
47
+ "scripts": {
48
+ "build": "rollup --config rollup.config.js"
50
49
  }
51
- }
50
+ }
@@ -0,0 +1,98 @@
1
+ import * as React from 'react'
2
+
3
+ export function CatchBoundary(props: {
4
+ resetKey: string
5
+ children: any
6
+ errorComponent?: any
7
+ onCatch: (error: any) => void
8
+ }) {
9
+ const errorComponent = props.errorComponent ?? ErrorComponent
10
+
11
+ return (
12
+ <CatchBoundaryImpl
13
+ resetKey={props.resetKey}
14
+ onCatch={props.onCatch}
15
+ children={({ error }) => {
16
+ if (error) {
17
+ return React.createElement(errorComponent, {
18
+ error,
19
+ })
20
+ }
21
+
22
+ return props.children
23
+ }}
24
+ />
25
+ )
26
+ }
27
+
28
+ export class CatchBoundaryImpl extends React.Component<{
29
+ resetKey: string
30
+ children: (props: { error: any; reset: () => void }) => any
31
+ onCatch?: (error: any) => void
32
+ }> {
33
+ state = { error: null } as any
34
+ static getDerivedStateFromError(error: any) {
35
+ return { error }
36
+ }
37
+ componentDidUpdate(
38
+ prevProps: Readonly<{
39
+ resetKey: string
40
+ children: (props: { error: any; reset: () => void }) => any
41
+ onCatch?: ((error: any, info: any) => void) | undefined
42
+ }>,
43
+ prevState: any,
44
+ ): void {
45
+ if (prevState.error && prevProps.resetKey !== this.props.resetKey) {
46
+ this.setState({ error: null })
47
+ }
48
+ }
49
+ componentDidCatch(error: any) {
50
+ console.error(error)
51
+ this.props.onCatch?.(error)
52
+ }
53
+ render() {
54
+ return this.props.children(this.state)
55
+ }
56
+ }
57
+
58
+ export function ErrorComponent({ error }: { error: any }) {
59
+ const [show, setShow] = React.useState(process.env.NODE_ENV !== 'production')
60
+
61
+ return (
62
+ <div style={{ padding: '.5rem', maxWidth: '100%' }}>
63
+ <div style={{ display: 'flex', alignItems: 'center', gap: '.5rem' }}>
64
+ <strong style={{ fontSize: '1rem' }}>Something went wrong!</strong>
65
+ <button
66
+ style={{
67
+ appearance: 'none',
68
+ fontSize: '.6em',
69
+ border: '1px solid currentColor',
70
+ padding: '.1rem .2rem',
71
+ fontWeight: 'bold',
72
+ borderRadius: '.25rem',
73
+ }}
74
+ onClick={() => setShow((d) => !d)}
75
+ >
76
+ {show ? 'Hide Error' : 'Show Error'}
77
+ </button>
78
+ </div>
79
+ <div style={{ height: '.25rem' }} />
80
+ {show ? (
81
+ <div>
82
+ <pre
83
+ style={{
84
+ fontSize: '.7em',
85
+ border: '1px solid red',
86
+ borderRadius: '.25rem',
87
+ padding: '.3rem',
88
+ color: 'red',
89
+ overflow: 'auto',
90
+ }}
91
+ >
92
+ {error.message ? <code>{error.message}</code> : null}
93
+ </pre>
94
+ </div>
95
+ ) : null}
96
+ </div>
97
+ )
98
+ }
@@ -0,0 +1,345 @@
1
+ import * as React from 'react'
2
+ import invariant from 'tiny-invariant'
3
+ import warning from 'tiny-warning'
4
+ import { CatchBoundary, ErrorComponent } from './CatchBoundary'
5
+ import { RouteMatch } from './RouterProvider'
6
+ import { useRouter, useRouterState } from './RouterProvider'
7
+ import { ResolveRelativePath, ToOptions } from './link'
8
+ import { AnyRoute, ReactNode, rootRouteId } from './route'
9
+ import { RouteById, RouteByPath, RouteIds, RoutePaths } from './routeInfo'
10
+ import { RegisteredRouter } from './router'
11
+ import { NoInfer, StrictOrFrom } from './utils'
12
+
13
+ export function Matches() {
14
+ const { routesById, state } = useRouter()
15
+ const { matches } = state
16
+
17
+ const locationKey = useRouterState().location.state.key
18
+
19
+ const route = routesById[rootRouteId]!
20
+
21
+ const errorComponent = React.useCallback(
22
+ (props: any) => {
23
+ return React.createElement(ErrorComponent, {
24
+ ...props,
25
+ useMatch: route.useMatch,
26
+ useRouteContext: route.useRouteContext,
27
+ useSearch: route.useSearch,
28
+ useParams: route.useParams,
29
+ })
30
+ },
31
+ [route],
32
+ )
33
+
34
+ return (
35
+ <matchesContext.Provider value={matches}>
36
+ <CatchBoundary
37
+ resetKey={locationKey}
38
+ errorComponent={errorComponent}
39
+ onCatch={() => {
40
+ warning(
41
+ false,
42
+ `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`,
43
+ )
44
+ }}
45
+ >
46
+ {matches.length ? <Match matches={matches} /> : null}
47
+ </CatchBoundary>
48
+ </matchesContext.Provider>
49
+ )
50
+ }
51
+
52
+ const defaultPending = () => null
53
+ function SafeFragment(props: any) {
54
+ return <>{props.children}</>
55
+ }
56
+
57
+ export function Match({ matches }: { matches: RouteMatch[] }) {
58
+ const { options, routesById } = useRouter()
59
+ const match = matches[0]!
60
+ const routeId = match?.routeId
61
+ const route = routesById[routeId]!
62
+ const locationKey = useRouterState().location.state?.key
63
+
64
+ const PendingComponent = (route.options.pendingComponent ??
65
+ options.defaultPendingComponent ??
66
+ defaultPending) as any
67
+
68
+ const routeErrorComponent =
69
+ route.options.errorComponent ??
70
+ options.defaultErrorComponent ??
71
+ ErrorComponent
72
+
73
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense
74
+ ? React.Suspense
75
+ : SafeFragment
76
+
77
+ const errorComponent = routeErrorComponent
78
+ ? React.useCallback(
79
+ (props: any) => {
80
+ return React.createElement(routeErrorComponent, {
81
+ ...props,
82
+ useMatch: route.useMatch,
83
+ useRouteContext: route.useRouteContext,
84
+ useSearch: route.useSearch,
85
+ useParams: route.useParams,
86
+ })
87
+ },
88
+ [route],
89
+ )
90
+ : undefined
91
+
92
+ return (
93
+ <matchesContext.Provider value={matches}>
94
+ <ResolvedSuspenseBoundary
95
+ fallback={React.createElement(PendingComponent, {
96
+ useMatch: route.useMatch,
97
+ useRouteContext: route.useRouteContext,
98
+ useSearch: route.useSearch,
99
+ useParams: route.useParams,
100
+ })}
101
+ >
102
+ {errorComponent ? (
103
+ <CatchBoundary
104
+ resetKey={locationKey}
105
+ errorComponent={errorComponent}
106
+ onCatch={() => {
107
+ warning(false, `Error in route match: ${match.id}`)
108
+ }}
109
+ >
110
+ <MatchInner match={match} />
111
+ </CatchBoundary>
112
+ ) : (
113
+ <SafeFragment>
114
+ <MatchInner match={match} />
115
+ </SafeFragment>
116
+ )}
117
+ </ResolvedSuspenseBoundary>
118
+ </matchesContext.Provider>
119
+ )
120
+ }
121
+ function MatchInner({ match }: { match: RouteMatch }): any {
122
+ const { options, routesById } = useRouter()
123
+ const route = routesById[match.routeId]!
124
+
125
+ if (match.status === 'error') {
126
+ throw match.error
127
+ }
128
+
129
+ if (match.status === 'pending') {
130
+ throw match.loadPromise
131
+ }
132
+
133
+ if (match.status === 'success') {
134
+ let comp = route.options.component ?? options.defaultComponent
135
+
136
+ if (comp) {
137
+ return React.createElement(comp, {
138
+ useMatch: route.useMatch,
139
+ useRouteContext: route.useRouteContext as any,
140
+ useSearch: route.useSearch,
141
+ useParams: route.useParams as any,
142
+ useLoaderData: route.useLoaderData,
143
+ })
144
+ }
145
+
146
+ return <Outlet />
147
+ }
148
+
149
+ invariant(
150
+ false,
151
+ 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!',
152
+ )
153
+ }
154
+
155
+ export function Outlet() {
156
+ const matches = React.useContext(matchesContext).slice(1)
157
+
158
+ if (!matches[0]) {
159
+ return null
160
+ }
161
+
162
+ return <Match matches={matches} />
163
+ }
164
+
165
+ export interface MatchRouteOptions {
166
+ pending?: boolean
167
+ caseSensitive?: boolean
168
+ includeSearch?: boolean
169
+ fuzzy?: boolean
170
+ }
171
+
172
+ export type MakeUseMatchRouteOptions<
173
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
174
+ TFrom extends RoutePaths<TRouteTree> = '/',
175
+ TTo extends string = '',
176
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
177
+ TMaskTo extends string = '',
178
+ > = ToOptions<AnyRoute, TFrom, TTo, TMaskFrom, TMaskTo> & MatchRouteOptions
179
+
180
+ export function useMatchRoute<
181
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
182
+ >() {
183
+ const { matchRoute } = useRouter()
184
+
185
+ return React.useCallback(
186
+ <
187
+ TFrom extends RoutePaths<TRouteTree> = '/',
188
+ TTo extends string = '',
189
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
190
+ TMaskTo extends string = '',
191
+ TResolved extends string = ResolveRelativePath<TFrom, NoInfer<TTo>>,
192
+ >(
193
+ opts: MakeUseMatchRouteOptions<
194
+ TRouteTree,
195
+ TFrom,
196
+ TTo,
197
+ TMaskFrom,
198
+ TMaskTo
199
+ >,
200
+ ): false | RouteById<TRouteTree, TResolved>['types']['allParams'] => {
201
+ const { pending, caseSensitive, ...rest } = opts
202
+
203
+ return matchRoute(rest as any, {
204
+ pending,
205
+ caseSensitive,
206
+ })
207
+ },
208
+ [],
209
+ )
210
+ }
211
+
212
+ export type MakeMatchRouteOptions<
213
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
214
+ TFrom extends RoutePaths<TRouteTree> = '/',
215
+ TTo extends string = '',
216
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
217
+ TMaskTo extends string = '',
218
+ > = ToOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
219
+ MatchRouteOptions & {
220
+ // 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
221
+ children?:
222
+ | ((
223
+ params?: RouteByPath<
224
+ TRouteTree,
225
+ ResolveRelativePath<TFrom, NoInfer<TTo>>
226
+ >['types']['allParams'],
227
+ ) => ReactNode)
228
+ | React.ReactNode
229
+ }
230
+
231
+ export function MatchRoute<
232
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
233
+ TFrom extends RoutePaths<TRouteTree> = '/',
234
+ TTo extends string = '',
235
+ TMaskFrom extends RoutePaths<TRouteTree> = '/',
236
+ TMaskTo extends string = '',
237
+ >(
238
+ props: MakeMatchRouteOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
239
+ ): any {
240
+ const matchRoute = useMatchRoute()
241
+ const params = matchRoute(props as any)
242
+
243
+ if (typeof props.children === 'function') {
244
+ return (props.children as any)(params)
245
+ }
246
+
247
+ return !!params ? props.children : null
248
+ }
249
+
250
+ export function useMatch<
251
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
252
+ TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
253
+ TStrict extends boolean = true,
254
+ TRouteMatchState = RouteMatch<TRouteTree, TFrom>,
255
+ TSelected = TRouteMatchState,
256
+ >(
257
+ opts: StrictOrFrom<TFrom> & {
258
+ select?: (match: TRouteMatchState) => TSelected
259
+ },
260
+ ): TStrict extends true ? TSelected : TSelected | undefined {
261
+ const nearestMatch = React.useContext(matchesContext)[0]!
262
+ const nearestMatchRouteId = nearestMatch?.routeId
263
+
264
+ const matchRouteId = useRouterState({
265
+ select: (state) => {
266
+ const match = opts?.from
267
+ ? state.matches.find((d) => d.routeId === opts?.from)
268
+ : state.matches.find((d) => d.id === nearestMatch.id)
269
+
270
+ return match!.routeId
271
+ },
272
+ })
273
+
274
+ if (opts?.strict ?? true) {
275
+ invariant(
276
+ nearestMatchRouteId == matchRouteId,
277
+ `useMatch("${
278
+ matchRouteId as string
279
+ }") is being called in a component that is meant to render the '${nearestMatchRouteId}' route. Did you mean to 'useMatch("${
280
+ matchRouteId as string
281
+ }", { strict: false })' or 'useRoute("${
282
+ matchRouteId as string
283
+ }")' instead?`,
284
+ )
285
+ }
286
+
287
+ const matchSelection = useRouterState({
288
+ select: (state) => {
289
+ const match = opts?.from
290
+ ? state.matches.find((d) => d.routeId === opts?.from)
291
+ : state.matches.find((d) => d.id === nearestMatch.id)
292
+
293
+ invariant(
294
+ match,
295
+ `Could not find ${
296
+ opts?.from
297
+ ? `an active match from "${opts.from}"`
298
+ : 'a nearest match!'
299
+ }`,
300
+ )
301
+
302
+ return opts?.select ? opts.select(match as any) : match
303
+ },
304
+ })
305
+
306
+ return matchSelection as any
307
+ }
308
+
309
+ export const matchesContext = React.createContext<RouteMatch[]>(null!)
310
+
311
+ export function useMatches<T = RouteMatch[]>(opts?: {
312
+ select?: (matches: RouteMatch[]) => T
313
+ }): T {
314
+ const contextMatches = React.useContext(matchesContext)
315
+
316
+ return useRouterState({
317
+ select: (state) => {
318
+ const matches = state.matches.slice(
319
+ state.matches.findIndex((d) => d.id === contextMatches[0]?.id),
320
+ )
321
+ return opts?.select ? opts.select(matches) : (matches as T)
322
+ },
323
+ })
324
+ }
325
+
326
+ export function useLoaderData<
327
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
328
+ TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
329
+ TStrict extends boolean = true,
330
+ TRouteMatch extends RouteMatch<TRouteTree, TFrom> = RouteMatch<
331
+ TRouteTree,
332
+ TFrom
333
+ >,
334
+ TSelected = TRouteMatch['loaderData'],
335
+ >(
336
+ opts: StrictOrFrom<TFrom> & {
337
+ select?: (match: TRouteMatch) => TSelected
338
+ },
339
+ ): TStrict extends true ? TSelected : TSelected | undefined {
340
+ const match = useMatch({ ...opts, select: undefined })!
341
+
342
+ return typeof opts.select === 'function'
343
+ ? opts.select(match?.loaderData)
344
+ : match?.loaderData
345
+ }