react-router 6.3.0 → 6.4.0-pre.3
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.
- package/.eslintrc +12 -0
- package/CHANGELOG.md +8 -0
- package/__tests__/.eslintrc +8 -0
- package/__tests__/DataMemoryRouter-test.tsx +1902 -0
- package/__tests__/Route-test.tsx +45 -0
- package/__tests__/Router-basename-test.tsx +110 -0
- package/__tests__/Router-test.tsx +62 -0
- package/__tests__/Routes-location-test.tsx +69 -0
- package/__tests__/Routes-test.tsx +148 -0
- package/__tests__/__snapshots__/route-matching-test.tsx.snap +197 -0
- package/__tests__/absolute-path-matching-test.tsx +61 -0
- package/__tests__/createRoutesFromChildren-test.tsx +189 -0
- package/__tests__/descendant-routes-params-test.tsx +67 -0
- package/__tests__/descendant-routes-splat-matching-test.tsx +241 -0
- package/__tests__/descendant-routes-warning-test.tsx +140 -0
- package/__tests__/generatePath-test.tsx +45 -0
- package/__tests__/gh-issue-8127-test.tsx +32 -0
- package/__tests__/gh-issue-8165-test.tsx +97 -0
- package/__tests__/greedy-matching-test.tsx +89 -0
- package/__tests__/index-routes-test.tsx +24 -0
- package/__tests__/layout-routes-test.tsx +283 -0
- package/__tests__/matchPath-test.tsx +335 -0
- package/__tests__/matchRoutes-test.tsx +144 -0
- package/__tests__/navigate-test.tsx +49 -0
- package/__tests__/params-decode-test.tsx +36 -0
- package/__tests__/path-matching-test.tsx +270 -0
- package/__tests__/resolvePath-test.tsx +50 -0
- package/__tests__/route-depth-order-matching-test.tsx +135 -0
- package/__tests__/route-matching-test.tsx +164 -0
- package/__tests__/same-component-lifecycle-test.tsx +57 -0
- package/__tests__/setup.ts +15 -0
- package/__tests__/useHref-basename-test.tsx +351 -0
- package/__tests__/useHref-test.tsx +287 -0
- package/__tests__/useLocation-test.tsx +29 -0
- package/__tests__/useMatch-test.tsx +137 -0
- package/__tests__/useNavigate-test.tsx +100 -0
- package/__tests__/useOutlet-test.tsx +355 -0
- package/__tests__/useParams-test.tsx +212 -0
- package/__tests__/useResolvedPath-test.tsx +109 -0
- package/__tests__/useRoutes-test.tsx +122 -0
- package/__tests__/utils/renderStrict.tsx +21 -0
- package/__tests__/utils/waitForRedirect.tsx +5 -0
- package/index.ts +187 -0
- package/jest-transformer.js +10 -0
- package/jest.config.js +10 -0
- package/lib/components.tsx +491 -0
- package/lib/context.ts +96 -0
- package/lib/hooks.tsx +689 -0
- package/lib/use-sync-external-store-shim/index.ts +31 -0
- package/lib/use-sync-external-store-shim/useSyncExternalStoreShimClient.ts +153 -0
- package/lib/use-sync-external-store-shim/useSyncExternalStoreShimServer.ts +20 -0
- package/node-main.js +7 -0
- package/package.json +7 -4
- package/tsconfig.json +20 -0
- package/LICENSE.md +0 -22
- package/index.d.ts +0 -14
- package/index.js +0 -941
- package/index.js.map +0 -1
- package/lib/components.d.ts +0 -110
- package/lib/context.d.ts +0 -31
- package/lib/hooks.d.ts +0 -99
- package/lib/router.d.ts +0 -120
- package/main.js +0 -19
- package/react-router.development.js +0 -895
- package/react-router.development.js.map +0 -1
- package/react-router.production.min.js +0 -12
- package/react-router.production.min.js.map +0 -1
- package/umd/react-router.development.js +0 -990
- package/umd/react-router.development.js.map +0 -1
- package/umd/react-router.production.min.js +0 -12
- package/umd/react-router.production.min.js.map +0 -1
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type {
|
|
3
|
+
HydrationState,
|
|
4
|
+
InitialEntry,
|
|
5
|
+
Location,
|
|
6
|
+
MemoryHistory,
|
|
7
|
+
RouteMatch,
|
|
8
|
+
RouteObject,
|
|
9
|
+
Router as DataRouter,
|
|
10
|
+
RouterState,
|
|
11
|
+
To,
|
|
12
|
+
} from "@remix-run/router";
|
|
13
|
+
import {
|
|
14
|
+
Action as NavigationType,
|
|
15
|
+
createMemoryHistory,
|
|
16
|
+
createMemoryRouter,
|
|
17
|
+
invariant,
|
|
18
|
+
normalizePathname,
|
|
19
|
+
parsePath,
|
|
20
|
+
stripBasename,
|
|
21
|
+
warning,
|
|
22
|
+
} from "@remix-run/router";
|
|
23
|
+
import { useSyncExternalStore as useSyncExternalStoreShim } from "./use-sync-external-store-shim";
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
LocationContext,
|
|
27
|
+
NavigationContext,
|
|
28
|
+
Navigator,
|
|
29
|
+
DataRouterContext,
|
|
30
|
+
DataRouterStateContext,
|
|
31
|
+
} from "./context";
|
|
32
|
+
import {
|
|
33
|
+
useInRouterContext,
|
|
34
|
+
useNavigate,
|
|
35
|
+
useOutlet,
|
|
36
|
+
useRoutes,
|
|
37
|
+
_renderMatches,
|
|
38
|
+
} from "./hooks";
|
|
39
|
+
|
|
40
|
+
// Module-scoped singleton to hold the router. Extracted from the React lifecycle
|
|
41
|
+
// to avoid issues w.r.t. dual initialization fetches in concurrent rendering.
|
|
42
|
+
// Data router apps are expected to have a static route tree and are not intended
|
|
43
|
+
// to be unmounted/remounted at runtime.
|
|
44
|
+
let routerSingleton: DataRouter;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Unit-testing-only function to reset the router between tests
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
export function _resetModuleScope() {
|
|
51
|
+
// @ts-expect-error
|
|
52
|
+
routerSingleton = null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @private
|
|
57
|
+
*/
|
|
58
|
+
export function useRenderDataRouter({
|
|
59
|
+
children,
|
|
60
|
+
fallbackElement,
|
|
61
|
+
routes,
|
|
62
|
+
createRouter,
|
|
63
|
+
}: {
|
|
64
|
+
children?: React.ReactNode;
|
|
65
|
+
fallbackElement?: React.ReactNode;
|
|
66
|
+
routes?: RouteObject[];
|
|
67
|
+
createRouter: (routes: RouteObject[]) => DataRouter;
|
|
68
|
+
}): React.ReactElement {
|
|
69
|
+
if (!routerSingleton) {
|
|
70
|
+
routerSingleton = createRouter(
|
|
71
|
+
routes || createRoutesFromChildren(children)
|
|
72
|
+
).initialize();
|
|
73
|
+
}
|
|
74
|
+
let router = routerSingleton;
|
|
75
|
+
|
|
76
|
+
// Sync router state to our component state to force re-renders
|
|
77
|
+
let state: RouterState = useSyncExternalStoreShim(
|
|
78
|
+
router.subscribe,
|
|
79
|
+
() => router.state
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
let navigator = React.useMemo((): Navigator => {
|
|
83
|
+
return {
|
|
84
|
+
createHref: router.createHref,
|
|
85
|
+
go: (n) => router.navigate(n),
|
|
86
|
+
push: (to, state, opts) =>
|
|
87
|
+
router.navigate(to, { state, resetScroll: opts?.resetScroll }),
|
|
88
|
+
replace: (to, state, opts) =>
|
|
89
|
+
router.navigate(to, {
|
|
90
|
+
replace: true,
|
|
91
|
+
state,
|
|
92
|
+
resetScroll: opts?.resetScroll,
|
|
93
|
+
}),
|
|
94
|
+
};
|
|
95
|
+
}, [router]);
|
|
96
|
+
|
|
97
|
+
if (!state.initialized) {
|
|
98
|
+
return <>{fallbackElement}</>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<DataRouterContext.Provider value={router}>
|
|
103
|
+
<DataRouterStateContext.Provider value={state}>
|
|
104
|
+
<Router
|
|
105
|
+
location={state.location}
|
|
106
|
+
navigationType={state.historyAction}
|
|
107
|
+
navigator={navigator}
|
|
108
|
+
>
|
|
109
|
+
<DataRoutes routes={routes} children={children} />
|
|
110
|
+
</Router>
|
|
111
|
+
</DataRouterStateContext.Provider>
|
|
112
|
+
</DataRouterContext.Provider>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface DataMemoryRouterProps {
|
|
117
|
+
children?: React.ReactNode;
|
|
118
|
+
initialEntries?: InitialEntry[];
|
|
119
|
+
initialIndex?: number;
|
|
120
|
+
hydrationData?: HydrationState;
|
|
121
|
+
fallbackElement?: React.ReactNode;
|
|
122
|
+
routes?: RouteObject[];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function DataMemoryRouter({
|
|
126
|
+
children,
|
|
127
|
+
initialEntries,
|
|
128
|
+
initialIndex,
|
|
129
|
+
hydrationData,
|
|
130
|
+
fallbackElement,
|
|
131
|
+
routes,
|
|
132
|
+
}: DataMemoryRouterProps): React.ReactElement {
|
|
133
|
+
return useRenderDataRouter({
|
|
134
|
+
children,
|
|
135
|
+
fallbackElement,
|
|
136
|
+
routes,
|
|
137
|
+
createRouter: (routes) =>
|
|
138
|
+
createMemoryRouter({
|
|
139
|
+
initialEntries,
|
|
140
|
+
initialIndex,
|
|
141
|
+
routes,
|
|
142
|
+
hydrationData,
|
|
143
|
+
}),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface MemoryRouterProps {
|
|
148
|
+
basename?: string;
|
|
149
|
+
children?: React.ReactNode;
|
|
150
|
+
initialEntries?: InitialEntry[];
|
|
151
|
+
initialIndex?: number;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* A <Router> that stores all entries in memory.
|
|
156
|
+
*
|
|
157
|
+
* @see https://reactrouter.com/docs/en/v6/routers/memory-router
|
|
158
|
+
*/
|
|
159
|
+
export function MemoryRouter({
|
|
160
|
+
basename,
|
|
161
|
+
children,
|
|
162
|
+
initialEntries,
|
|
163
|
+
initialIndex,
|
|
164
|
+
}: MemoryRouterProps): React.ReactElement {
|
|
165
|
+
let historyRef = React.useRef<MemoryHistory>();
|
|
166
|
+
if (historyRef.current == null) {
|
|
167
|
+
historyRef.current = createMemoryHistory({
|
|
168
|
+
initialEntries,
|
|
169
|
+
initialIndex,
|
|
170
|
+
v5Compat: true,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let history = historyRef.current;
|
|
175
|
+
let [state, setState] = React.useState({
|
|
176
|
+
action: history.action,
|
|
177
|
+
location: history.location,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
React.useLayoutEffect(() => history.listen(setState), [history]);
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<Router
|
|
184
|
+
basename={basename}
|
|
185
|
+
children={children}
|
|
186
|
+
location={state.location}
|
|
187
|
+
navigationType={state.action}
|
|
188
|
+
navigator={history}
|
|
189
|
+
/>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface NavigateProps {
|
|
194
|
+
to: To;
|
|
195
|
+
replace?: boolean;
|
|
196
|
+
state?: any;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Changes the current location.
|
|
201
|
+
*
|
|
202
|
+
* Note: This API is mostly useful in React.Component subclasses that are not
|
|
203
|
+
* able to use hooks. In functional components, we recommend you use the
|
|
204
|
+
* `useNavigate` hook instead.
|
|
205
|
+
*
|
|
206
|
+
* @see https://reactrouter.com/docs/en/v6/components/navigate
|
|
207
|
+
*/
|
|
208
|
+
export function Navigate({ to, replace, state }: NavigateProps): null {
|
|
209
|
+
invariant(
|
|
210
|
+
useInRouterContext(),
|
|
211
|
+
// TODO: This error is probably because they somehow have 2 versions of
|
|
212
|
+
// the router loaded. We can help them understand how to avoid that.
|
|
213
|
+
`<Navigate> may be used only in the context of a <Router> component.`
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
warning(
|
|
217
|
+
!React.useContext(NavigationContext).static,
|
|
218
|
+
`<Navigate> must not be used on the initial render in a <StaticRouter>. ` +
|
|
219
|
+
`This is a no-op, but you should modify your code so the <Navigate> is ` +
|
|
220
|
+
`only ever rendered in response to some user interaction or state change.`
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
let navigate = useNavigate();
|
|
224
|
+
React.useEffect(() => {
|
|
225
|
+
navigate(to, { replace, state });
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface OutletProps {
|
|
232
|
+
context?: unknown;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Renders the child route's element, if there is one.
|
|
237
|
+
*
|
|
238
|
+
* @see https://reactrouter.com/docs/en/v6/components/outlet
|
|
239
|
+
*/
|
|
240
|
+
export function Outlet(props: OutletProps): React.ReactElement | null {
|
|
241
|
+
return useOutlet(props.context);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
interface DataRouteProps {
|
|
245
|
+
id?: RouteObject["id"];
|
|
246
|
+
loader?: RouteObject["loader"];
|
|
247
|
+
action?: RouteObject["action"];
|
|
248
|
+
errorElement?: RouteObject["errorElement"];
|
|
249
|
+
shouldRevalidate?: RouteObject["shouldRevalidate"];
|
|
250
|
+
handle?: RouteObject["handle"];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface RouteProps extends DataRouteProps {
|
|
254
|
+
caseSensitive?: boolean;
|
|
255
|
+
children?: React.ReactNode;
|
|
256
|
+
element?: React.ReactNode | null;
|
|
257
|
+
index?: boolean;
|
|
258
|
+
path?: string;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export interface PathRouteProps extends DataRouteProps {
|
|
262
|
+
caseSensitive?: boolean;
|
|
263
|
+
children?: React.ReactNode;
|
|
264
|
+
element?: React.ReactNode | null;
|
|
265
|
+
index?: false;
|
|
266
|
+
path: string;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface LayoutRouteProps extends DataRouteProps {
|
|
270
|
+
children?: React.ReactNode;
|
|
271
|
+
element?: React.ReactNode | null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export interface IndexRouteProps extends DataRouteProps {
|
|
275
|
+
element?: React.ReactNode | null;
|
|
276
|
+
index: true;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Declares an element that should be rendered at a certain URL path.
|
|
281
|
+
*
|
|
282
|
+
* @see https://reactrouter.com/docs/en/v6/components/route
|
|
283
|
+
*/
|
|
284
|
+
export function Route(
|
|
285
|
+
_props: PathRouteProps | LayoutRouteProps | IndexRouteProps
|
|
286
|
+
): React.ReactElement | null {
|
|
287
|
+
invariant(
|
|
288
|
+
false,
|
|
289
|
+
`A <Route> is only ever to be used as the child of <Routes> element, ` +
|
|
290
|
+
`never rendered directly. Please wrap your <Route> in a <Routes>.`
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export interface RouterProps {
|
|
295
|
+
basename?: string;
|
|
296
|
+
children?: React.ReactNode;
|
|
297
|
+
location: Partial<Location> | string;
|
|
298
|
+
navigationType?: NavigationType;
|
|
299
|
+
navigator: Navigator;
|
|
300
|
+
static?: boolean;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Provides location context for the rest of the app.
|
|
305
|
+
*
|
|
306
|
+
* Note: You usually won't render a <Router> directly. Instead, you'll render a
|
|
307
|
+
* router that is more specific to your environment such as a <BrowserRouter>
|
|
308
|
+
* in web browsers or a <StaticRouter> for server rendering.
|
|
309
|
+
*
|
|
310
|
+
* @see https://reactrouter.com/docs/en/v6/routers/router
|
|
311
|
+
*/
|
|
312
|
+
export function Router({
|
|
313
|
+
basename: basenameProp = "/",
|
|
314
|
+
children = null,
|
|
315
|
+
location: locationProp,
|
|
316
|
+
navigationType = NavigationType.Pop,
|
|
317
|
+
navigator,
|
|
318
|
+
static: staticProp = false,
|
|
319
|
+
}: RouterProps): React.ReactElement | null {
|
|
320
|
+
invariant(
|
|
321
|
+
!useInRouterContext(),
|
|
322
|
+
`You cannot render a <Router> inside another <Router>.` +
|
|
323
|
+
` You should never have more than one in your app.`
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
let basename = normalizePathname(basenameProp);
|
|
327
|
+
let navigationContext = React.useMemo(
|
|
328
|
+
() => ({ basename, navigator, static: staticProp }),
|
|
329
|
+
[basename, navigator, staticProp]
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
if (typeof locationProp === "string") {
|
|
333
|
+
locationProp = parsePath(locationProp);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
let {
|
|
337
|
+
pathname = "/",
|
|
338
|
+
search = "",
|
|
339
|
+
hash = "",
|
|
340
|
+
state = null,
|
|
341
|
+
key = "default",
|
|
342
|
+
} = locationProp;
|
|
343
|
+
|
|
344
|
+
let location = React.useMemo(() => {
|
|
345
|
+
let trailingPathname = stripBasename(pathname, basename);
|
|
346
|
+
|
|
347
|
+
if (trailingPathname == null) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
pathname: trailingPathname,
|
|
353
|
+
search,
|
|
354
|
+
hash,
|
|
355
|
+
state,
|
|
356
|
+
key,
|
|
357
|
+
};
|
|
358
|
+
}, [basename, pathname, search, hash, state, key]);
|
|
359
|
+
|
|
360
|
+
warning(
|
|
361
|
+
location != null,
|
|
362
|
+
`<Router basename="${basename}"> is not able to match the URL ` +
|
|
363
|
+
`"${pathname}${search}${hash}" because it does not start with the ` +
|
|
364
|
+
`basename, so the <Router> won't render anything.`
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
if (location == null) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return (
|
|
372
|
+
<NavigationContext.Provider value={navigationContext}>
|
|
373
|
+
<LocationContext.Provider
|
|
374
|
+
children={children}
|
|
375
|
+
value={{ location, navigationType }}
|
|
376
|
+
/>
|
|
377
|
+
</NavigationContext.Provider>
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export interface RoutesProps {
|
|
382
|
+
children?: React.ReactNode;
|
|
383
|
+
location?: Partial<Location> | string;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* A container for a nested tree of <Route> elements that renders the branch
|
|
388
|
+
* that best matches the current location.
|
|
389
|
+
*
|
|
390
|
+
* @see https://reactrouter.com/docs/en/v6/components/routes
|
|
391
|
+
*/
|
|
392
|
+
export function Routes({
|
|
393
|
+
children,
|
|
394
|
+
location,
|
|
395
|
+
}: RoutesProps): React.ReactElement | null {
|
|
396
|
+
return useRoutes(createRoutesFromChildren(children), location);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
interface DataRoutesProps extends RoutesProps {
|
|
400
|
+
routes?: RouteObject[];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* @private
|
|
405
|
+
* Used as an extension to <Routes> and accepts a manual `routes` array to be
|
|
406
|
+
* instead of using JSX children. Extracted to it's own component to avoid
|
|
407
|
+
* conditional usage of `useRoutes` if we have to render a `fallbackElement`
|
|
408
|
+
*/
|
|
409
|
+
function DataRoutes({
|
|
410
|
+
children,
|
|
411
|
+
location,
|
|
412
|
+
routes,
|
|
413
|
+
}: DataRoutesProps): React.ReactElement | null {
|
|
414
|
+
return useRoutes(routes || createRoutesFromChildren(children), location);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
418
|
+
// UTILS
|
|
419
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Creates a route config from a React "children" object, which is usually
|
|
423
|
+
* either a `<Route>` element or an array of them. Used internally by
|
|
424
|
+
* `<Routes>` to create a route config from its children.
|
|
425
|
+
*
|
|
426
|
+
* @see https://reactrouter.com/docs/en/v6/utils/create-routes-from-children
|
|
427
|
+
*/
|
|
428
|
+
export function createRoutesFromChildren(
|
|
429
|
+
children: React.ReactNode,
|
|
430
|
+
parentPath: number[] = []
|
|
431
|
+
): RouteObject[] {
|
|
432
|
+
let routes: RouteObject[] = [];
|
|
433
|
+
|
|
434
|
+
React.Children.forEach(children, (element, index) => {
|
|
435
|
+
if (!React.isValidElement(element)) {
|
|
436
|
+
// Ignore non-elements. This allows people to more easily inline
|
|
437
|
+
// conditionals in their route config.
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (element.type === React.Fragment) {
|
|
442
|
+
// Transparently support React.Fragment and its children.
|
|
443
|
+
routes.push.apply(
|
|
444
|
+
routes,
|
|
445
|
+
createRoutesFromChildren(element.props.children, parentPath)
|
|
446
|
+
);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
invariant(
|
|
451
|
+
element.type === Route,
|
|
452
|
+
`[${
|
|
453
|
+
typeof element.type === "string" ? element.type : element.type.name
|
|
454
|
+
}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>`
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
let treePath = [...parentPath, index];
|
|
458
|
+
let route: RouteObject = {
|
|
459
|
+
id: element.props.id || treePath.join("-"),
|
|
460
|
+
caseSensitive: element.props.caseSensitive,
|
|
461
|
+
element: element.props.element,
|
|
462
|
+
index: element.props.index,
|
|
463
|
+
path: element.props.path,
|
|
464
|
+
loader: element.props.loader,
|
|
465
|
+
action: element.props.action,
|
|
466
|
+
errorElement: element.props.errorElement,
|
|
467
|
+
shouldRevalidate: element.props.shouldRevalidate,
|
|
468
|
+
handle: element.props.handle,
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
if (element.props.children) {
|
|
472
|
+
route.children = createRoutesFromChildren(
|
|
473
|
+
element.props.children,
|
|
474
|
+
treePath
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
routes.push(route);
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
return routes;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Renders the result of `matchRoutes()` into a React element.
|
|
486
|
+
*/
|
|
487
|
+
export function renderMatches(
|
|
488
|
+
matches: RouteMatch[] | null
|
|
489
|
+
): React.ReactElement | null {
|
|
490
|
+
return _renderMatches(matches);
|
|
491
|
+
}
|
package/lib/context.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type {
|
|
3
|
+
History,
|
|
4
|
+
Location,
|
|
5
|
+
RouteMatch,
|
|
6
|
+
Router,
|
|
7
|
+
To,
|
|
8
|
+
} from "@remix-run/router";
|
|
9
|
+
import { Action as NavigationType } from "@remix-run/router";
|
|
10
|
+
|
|
11
|
+
// Contexts for data routers
|
|
12
|
+
export const DataRouterContext = React.createContext<Router | null>(null);
|
|
13
|
+
if (__DEV__) {
|
|
14
|
+
DataRouterContext.displayName = "DataRouter";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const DataRouterStateContext = React.createContext<
|
|
18
|
+
Router["state"] | null
|
|
19
|
+
>(null);
|
|
20
|
+
if (__DEV__) {
|
|
21
|
+
DataRouterStateContext.displayName = "DataRouterState";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface NavigateOptions {
|
|
25
|
+
replace?: boolean;
|
|
26
|
+
state?: any;
|
|
27
|
+
resetScroll?: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A Navigator is a "location changer"; it's how you get to different locations.
|
|
32
|
+
*
|
|
33
|
+
* Every history instance conforms to the Navigator interface, but the
|
|
34
|
+
* distinction is useful primarily when it comes to the low-level <Router> API
|
|
35
|
+
* where both the location and a navigator must be provided separately in order
|
|
36
|
+
* to avoid "tearing" that may occur in a suspense-enabled app if the action
|
|
37
|
+
* and/or location were to be read directly from the history instance.
|
|
38
|
+
*/
|
|
39
|
+
export interface Navigator {
|
|
40
|
+
createHref: History["createHref"];
|
|
41
|
+
go: History["go"];
|
|
42
|
+
push(to: To, state?: any, opts?: NavigateOptions): void;
|
|
43
|
+
replace(to: To, state?: any, opts?: NavigateOptions): void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface NavigationContextObject {
|
|
47
|
+
basename: string;
|
|
48
|
+
navigator: Navigator;
|
|
49
|
+
static: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const NavigationContext = React.createContext<NavigationContextObject>(
|
|
53
|
+
null!
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if (__DEV__) {
|
|
57
|
+
NavigationContext.displayName = "Navigation";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface LocationContextObject {
|
|
61
|
+
location: Location;
|
|
62
|
+
navigationType: NavigationType;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const LocationContext = React.createContext<LocationContextObject>(
|
|
66
|
+
null!
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (__DEV__) {
|
|
70
|
+
LocationContext.displayName = "Location";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface RouteContextObject {
|
|
74
|
+
outlet: React.ReactElement | null;
|
|
75
|
+
matches: RouteMatch[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const RouteContext = React.createContext<RouteContextObject>({
|
|
79
|
+
outlet: null,
|
|
80
|
+
matches: [],
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (__DEV__) {
|
|
84
|
+
RouteContext.displayName = "Route";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface RouteContextObject {
|
|
88
|
+
outlet: React.ReactElement | null;
|
|
89
|
+
matches: RouteMatch[];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const RouteErrorContext = React.createContext<any>(null);
|
|
93
|
+
|
|
94
|
+
if (__DEV__) {
|
|
95
|
+
RouteErrorContext.displayName = "RouteError";
|
|
96
|
+
}
|