UNPKG

20.2 kBTypeScriptView Raw
1
2import { Action, History, Location, Path, To } from "./history.js";
3import { DataRouteMatch, DataRouteObject, DataStrategyFunction, FormEncType, HTMLFormMethod, MapRoutePropertiesFunction, MaybePromise, PatchRoutesOnNavigationFunction, RouteBranch, RouteData, RouteManifest, RouteObject, RouterContextProvider, Submission, UIMatch } from "./utils.js";
4import { ClientInstrumentation, ServerInstrumentation } from "./instrumentation.js";
5
6//#region lib/router/router.d.ts
7/**
8 * A Router instance manages all navigation and data loading/mutations
9 */
10interface Router {
11 /**
12 * @private
13 * PRIVATE - DO NOT USE
14 *
15 * Return the basename for the router
16 */
17 get basename(): RouterInit["basename"];
18 /**
19 * @private
20 * PRIVATE - DO NOT USE
21 *
22 * Return the future config for the router
23 */
24 get future(): FutureConfig;
25 /**
26 * @private
27 * PRIVATE - DO NOT USE
28 *
29 * Return the current state of the router
30 */
31 get state(): RouterState;
32 /**
33 * @private
34 * PRIVATE - DO NOT USE
35 *
36 * Return the routes for this router instance
37 */
38 get routes(): DataRouteObject[];
39 /**
40 * @private
41 * PRIVATE - DO NOT USE
42 *
43 * Return the route branches for this router instance
44 */
45 get branches(): RouteBranch<DataRouteObject>[] | undefined;
46 /**
47 * @private
48 * PRIVATE - DO NOT USE
49 *
50 * Return the manifest for this router instance
51 */
52 get manifest(): RouteManifest;
53 /**
54 * @private
55 * PRIVATE - DO NOT USE
56 *
57 * Return the window associated with the router
58 */
59 get window(): RouterInit["window"];
60 /**
61 * @private
62 * PRIVATE - DO NOT USE
63 *
64 * Initialize the router, including adding history listeners and kicking off
65 * initial data fetches. Returns a function to cleanup listeners and abort
66 * any in-progress loads
67 */
68 initialize(): Router;
69 /**
70 * @private
71 * PRIVATE - DO NOT USE
72 *
73 * Subscribe to router.state updates
74 *
75 * @param fn function to call with the new state
76 */
77 subscribe(fn: RouterSubscriber): () => void;
78 /**
79 * @private
80 * PRIVATE - DO NOT USE
81 *
82 * Enable scroll restoration behavior in the router
83 *
84 * @param savedScrollPositions Object that will manage positions, in case
85 * it's being restored from sessionStorage
86 * @param getScrollPosition Function to get the active Y scroll position
87 * @param getKey Function to get the key to use for restoration
88 */
89 enableScrollRestoration(savedScrollPositions: Record<string, number>, getScrollPosition: GetScrollPositionFunction, getKey?: GetScrollRestorationKeyFunction): () => void;
90 /**
91 * @private
92 * PRIVATE - DO NOT USE
93 *
94 * Navigate forward/backward in the history stack
95 * @param to Delta to move in the history stack
96 */
97 navigate(to: number): Promise<void>;
98 /**
99 * Navigate to the given path
100 * @param to Path to navigate to
101 * @param opts Navigation options (method, submission, etc.)
102 */
103 navigate(to: To | null, opts?: RouterNavigateOptions): Promise<void>;
104 /**
105 * @private
106 * PRIVATE - DO NOT USE
107 *
108 * Trigger a fetcher load/submission
109 *
110 * @param key Fetcher key
111 * @param routeId Route that owns the fetcher
112 * @param href href to fetch
113 * @param opts Fetcher options, (method, submission, etc.)
114 */
115 fetch(key: string, routeId: string, href: string | null, opts?: RouterFetchOptions): Promise<void>;
116 /**
117 * @private
118 * PRIVATE - DO NOT USE
119 *
120 * Trigger a revalidation of all current route loaders and fetcher loads
121 */
122 revalidate(): Promise<void>;
123 /**
124 * @private
125 * PRIVATE - DO NOT USE
126 *
127 * Utility function to create an href for the given location
128 * @param location
129 */
130 createHref(location: Location | URL): string;
131 /**
132 * @private
133 * PRIVATE - DO NOT USE
134 *
135 * Utility function to URL encode a destination path according to the internal
136 * history implementation
137 * @param to
138 */
139 encodeLocation(to: To): Path;
140 /**
141 * @private
142 * PRIVATE - DO NOT USE
143 *
144 * Get/create a fetcher for the given key
145 * @param key
146 */
147 getFetcher<TData = any>(key: string): Fetcher<TData>;
148 /**
149 * @internal
150 * PRIVATE - DO NOT USE
151 *
152 * Reset the fetcher for a given key
153 * @param key
154 */
155 resetFetcher(key: string, opts?: {
156 reason?: unknown;
157 }): void;
158 /**
159 * @private
160 * PRIVATE - DO NOT USE
161 *
162 * Delete the fetcher for a given key
163 * @param key
164 */
165 deleteFetcher(key: string): void;
166 /**
167 * @private
168 * PRIVATE - DO NOT USE
169 *
170 * Cleanup listeners and abort any in-progress loads
171 */
172 dispose(): void;
173 /**
174 * @private
175 * PRIVATE - DO NOT USE
176 *
177 * Get a navigation blocker
178 * @param key The identifier for the blocker
179 * @param fn The blocker function implementation
180 */
181 getBlocker(key: string, fn: BlockerFunction): Blocker;
182 /**
183 * @private
184 * PRIVATE - DO NOT USE
185 *
186 * Delete a navigation blocker
187 * @param key The identifier for the blocker
188 */
189 deleteBlocker(key: string): void;
190 /**
191 * @private
192 * PRIVATE DO NOT USE
193 *
194 * Patch additional children routes into an existing parent route
195 * @param routeId The parent route id or a callback function accepting `patch`
196 * to perform batch patching
197 * @param children The additional children routes
198 * @param unstable_allowElementMutations Allow mutation or route elements on
199 * existing routes. Intended for RSC-usage
200 * only.
201 */
202 patchRoutes(routeId: string | null, children: RouteObject[], unstable_allowElementMutations?: boolean): void;
203 /**
204 * @private
205 * PRIVATE - DO NOT USE
206 *
207 * HMR needs to pass in-flight route updates to React Router
208 * TODO: Replace this with granular route update APIs (addRoute, updateRoute, deleteRoute)
209 */
210 _internalSetRoutes(routes: RouteObject[]): void;
211 /**
212 * @private
213 * PRIVATE - DO NOT USE
214 *
215 * Cause subscribers to re-render. This is used to force a re-render.
216 */
217 _internalSetStateDoNotUseOrYouWillBreakYourApp(state: Partial<RouterState>): void;
218 /**
219 * @private
220 * PRIVATE - DO NOT USE
221 *
222 * Internal fetch AbortControllers accessed by unit tests
223 */
224 _internalFetchControllers: Map<string, AbortController>;
225}
226/**
227 * State maintained internally by the router. During a navigation, all states
228 * reflect the "old" location unless otherwise noted.
229 */
230interface RouterState {
231 /**
232 * The action of the most recent navigation
233 */
234 historyAction: Action;
235 /**
236 * The current location reflected by the router
237 */
238 location: Location;
239 /**
240 * The current set of route matches
241 */
242 matches: DataRouteMatch[];
243 /**
244 * Tracks whether we've completed our initial data load
245 */
246 initialized: boolean;
247 /**
248 * Tracks whether we should be rendering a HydrateFallback during hydration
249 */
250 renderFallback: boolean;
251 /**
252 * Current scroll position we should start at for a new view
253 * - number -> scroll position to restore to
254 * - false -> do not restore scroll at all (used during submissions/revalidations)
255 * - null -> don't have a saved position, scroll to hash or top of page
256 */
257 restoreScrollPosition: number | false | null;
258 /**
259 * Indicate whether this navigation should skip resetting the scroll position
260 * if we are unable to restore the scroll position
261 */
262 preventScrollReset: boolean;
263 /**
264 * Tracks the state of the current navigation
265 */
266 navigation: Navigation;
267 /**
268 * Tracks any in-progress revalidations
269 */
270 revalidation: RevalidationState;
271 /**
272 * Data from the loaders for the current matches
273 */
274 loaderData: RouteData;
275 /**
276 * Data from the action for the current matches
277 */
278 actionData: RouteData | null;
279 /**
280 * Errors caught from loaders for the current matches
281 */
282 errors: RouteData | null;
283 /**
284 * Map of current fetchers
285 */
286 fetchers: Map<string, Fetcher>;
287 /**
288 * Map of current blockers
289 */
290 blockers: Map<string, Blocker>;
291}
292/**
293 * Data that can be passed into hydrate a Router from SSR
294 */
295type HydrationState = Partial<Pick<RouterState, "loaderData" | "actionData" | "errors">>;
296/**
297 * Future flags to toggle new feature behavior
298 */
299interface FutureConfig {}
300/**
301 * Initialization options for createRouter
302 */
303interface RouterInit {
304 routes: RouteObject[];
305 history: History;
306 basename?: string;
307 getContext?: () => MaybePromise<RouterContextProvider>;
308 instrumentations?: ClientInstrumentation[];
309 mapRouteProperties?: MapRoutePropertiesFunction;
310 future?: Partial<FutureConfig>;
311 hydrationRouteProperties?: string[];
312 hydrationData?: HydrationState;
313 window?: Window;
314 dataStrategy?: DataStrategyFunction;
315 patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
316}
317/**
318 * State returned from a server-side query() call
319 */
320interface StaticHandlerContext {
321 basename: Router["basename"];
322 location: RouterState["location"];
323 matches: RouterState["matches"];
324 loaderData: RouterState["loaderData"];
325 actionData: RouterState["actionData"];
326 errors: RouterState["errors"];
327 statusCode: number;
328 loaderHeaders: Record<string, Headers>;
329 actionHeaders: Record<string, Headers>;
330 _deepestRenderedBoundaryId?: string | null;
331}
332/**
333 * A StaticHandler instance manages a singular SSR navigation/fetch event
334 */
335interface StaticHandler {
336 /**
337 * The set of data routes managed by this handler
338 */
339 dataRoutes: DataRouteObject[];
340 /**
341 * @private
342 * PRIVATE - DO NOT USE
343 *
344 * The route branches derived from the data routes, used for internal route
345 * matching in Framework Mode
346 */
347 _internalRouteBranches: RouteBranch<DataRouteObject>[];
348 /**
349 * Perform a query for a given request - executing all matched route
350 * loaders/actions. Used for document requests.
351 *
352 * @param request The request to query
353 * @param opts Optional query options
354 * @param opts.dataStrategy Alternate dataStrategy implementation
355 * @param opts.filterMatchesToLoad Predicate function to filter which matches should be loaded
356 * @param opts.generateMiddlewareResponse To enable middleware, provide a function
357 * to generate a response to bubble back up the middleware chain
358 * @param opts.requestContext Context object to pass to loaders/actions
359 * @param opts.skipLoaderErrorBubbling Skip loader error bubbling
360 * @param opts.skipRevalidation Skip revalidation after action submission
361 * @param opts.normalizePath Normalize the request path
362 */
363 query(request: Request, opts?: {
364 requestContext?: unknown;
365 filterMatchesToLoad?: (match: DataRouteMatch) => boolean;
366 skipLoaderErrorBubbling?: boolean;
367 skipRevalidation?: boolean;
368 dataStrategy?: DataStrategyFunction<unknown>;
369 generateMiddlewareResponse?: (query: (r: Request, args?: {
370 filterMatchesToLoad?: (match: DataRouteMatch) => boolean;
371 }) => Promise<StaticHandlerContext | Response>) => MaybePromise<Response>;
372 normalizePath?: (request: Request) => Path;
373 }): Promise<StaticHandlerContext | Response>;
374 /**
375 * Perform a query for a specific route. Used for resource requests.
376 *
377 * @param request The request to query
378 * @param opts Optional queryRoute options
379 * @param opts.dataStrategy Alternate dataStrategy implementation
380 * @param opts.generateMiddlewareResponse To enable middleware, provide a function
381 * to generate a response to bubble back up the middleware chain
382 * @param opts.requestContext Context object to pass to loaders/actions
383 * @param opts.routeId The ID of the route to query
384 * @param opts.normalizePath Normalize the request path
385 */
386 queryRoute(request: Request, opts?: {
387 routeId?: string;
388 requestContext?: unknown;
389 dataStrategy?: DataStrategyFunction<unknown>;
390 generateMiddlewareResponse?: (queryRoute: (r: Request) => Promise<Response>) => MaybePromise<Response>;
391 normalizePath?: (request: Request) => Path;
392 }): Promise<any>;
393}
394type ViewTransitionOpts = {
395 currentLocation: Location;
396 nextLocation: Location;
397};
398/**
399 * Subscriber function signature for changes to router state
400 */
401interface RouterSubscriber {
402 (state: RouterState, opts: {
403 deletedFetchers: string[];
404 newErrors: RouteData | null;
405 viewTransitionOpts?: ViewTransitionOpts;
406 flushSync: boolean;
407 }): void;
408}
409/**
410 * Function signature for determining the key to be used in scroll restoration
411 * for a given location
412 */
413interface GetScrollRestorationKeyFunction {
414 (location: Location, matches: UIMatch[]): string | null;
415}
416/**
417 * Function signature for determining the current scroll position
418 */
419interface GetScrollPositionFunction {
420 (): number;
421}
422/**
423 * - "route": relative to the route hierarchy so `..` means remove all segments
424 * of the current route even if it has many. For example, a `route("posts/:id")`
425 * would have both `:id` and `posts` removed from the url.
426 * - "path": relative to the pathname so `..` means remove one segment of the
427 * pathname. For example, a `route("posts/:id")` would have only `:id` removed
428 * from the url.
429 */
430type RelativeRoutingType = "route" | "path";
431type BaseNavigateOrFetchOptions = {
432 preventScrollReset?: boolean;
433 relative?: RelativeRoutingType;
434 flushSync?: boolean;
435 defaultShouldRevalidate?: boolean;
436};
437type BaseNavigateOptions = BaseNavigateOrFetchOptions & {
438 replace?: boolean;
439 state?: any;
440 fromRouteId?: string;
441 viewTransition?: boolean;
442 mask?: To;
443};
444type BaseSubmissionOptions = {
445 formMethod?: HTMLFormMethod;
446 formEncType?: FormEncType;
447} & ({
448 formData: FormData;
449 body?: undefined;
450} | {
451 formData?: undefined;
452 body: any;
453});
454/**
455 * Options for a navigate() call for a normal (non-submission) navigation
456 */
457type LinkNavigateOptions = BaseNavigateOptions;
458/**
459 * Options for a navigate() call for a submission navigation
460 */
461type SubmissionNavigateOptions = BaseNavigateOptions & BaseSubmissionOptions;
462/**
463 * Options to pass to navigate() for a navigation
464 */
465type RouterNavigateOptions = LinkNavigateOptions | SubmissionNavigateOptions;
466/**
467 * Options for a fetch() load
468 */
469type LoadFetchOptions = BaseNavigateOrFetchOptions;
470/**
471 * Options for a fetch() submission
472 */
473type SubmitFetchOptions = BaseNavigateOrFetchOptions & BaseSubmissionOptions;
474/**
475 * Options to pass to fetch()
476 */
477type RouterFetchOptions = LoadFetchOptions | SubmitFetchOptions;
478/**
479 * Potential states for state.navigation
480 */
481type NavigationStates = {
482 Idle: {
483 state: "idle";
484 location: undefined;
485 matches: undefined;
486 historyAction: undefined;
487 formMethod: undefined;
488 formAction: undefined;
489 formEncType: undefined;
490 formData: undefined;
491 json: undefined;
492 text: undefined;
493 };
494 Loading: {
495 state: "loading";
496 location: Location;
497 matches: DataRouteMatch[];
498 historyAction: Action;
499 formMethod: Submission["formMethod"] | undefined;
500 formAction: Submission["formAction"] | undefined;
501 formEncType: Submission["formEncType"] | undefined;
502 formData: Submission["formData"] | undefined;
503 json: Submission["json"] | undefined;
504 text: Submission["text"] | undefined;
505 };
506 Submitting: {
507 state: "submitting";
508 location: Location;
509 matches: DataRouteMatch[];
510 historyAction: Action;
511 formMethod: Submission["formMethod"];
512 formAction: Submission["formAction"];
513 formEncType: Submission["formEncType"];
514 formData: Submission["formData"];
515 json: Submission["json"];
516 text: Submission["text"];
517 };
518};
519type Navigation = NavigationStates[keyof NavigationStates];
520type RevalidationState = "idle" | "loading";
521/**
522 * Potential states for fetchers
523 */
524type FetcherStates<TData = any> = {
525 /**
526 * The fetcher is not calling a loader or action
527 *
528 * ```tsx
529 * fetcher.state === "idle"
530 * ```
531 */
532 Idle: {
533 state: "idle";
534 formMethod: undefined;
535 formAction: undefined;
536 formEncType: undefined;
537 text: undefined;
538 formData: undefined;
539 json: undefined;
540 /**
541 * If the fetcher has never been called, this will be undefined.
542 */
543 data: TData | undefined;
544 };
545 /**
546 * The fetcher is loading data from a {@link LoaderFunction | loader} from a
547 * call to {@link FetcherWithComponents.load | `fetcher.load`}.
548 *
549 * ```tsx
550 * // somewhere
551 * <button onClick={() => fetcher.load("/some/route") }>Load</button>
552 *
553 * // the state will update
554 * fetcher.state === "loading"
555 * ```
556 */
557 Loading: {
558 state: "loading";
559 formMethod: Submission["formMethod"] | undefined;
560 formAction: Submission["formAction"] | undefined;
561 formEncType: Submission["formEncType"] | undefined;
562 text: Submission["text"] | undefined;
563 formData: Submission["formData"] | undefined;
564 json: Submission["json"] | undefined;
565 data: TData | undefined;
566 };
567 /**
568 The fetcher is submitting to a {@link LoaderFunction} (GET) or {@link ActionFunction} (POST) from a {@link FetcherWithComponents.Form | `fetcher.Form`} or {@link FetcherWithComponents.submit | `fetcher.submit`}.
569 ```tsx
570 // somewhere
571 <input
572 onChange={e => {
573 fetcher.submit(event.currentTarget.form, { method: "post" });
574 }}
575 />
576 // the state will update
577 fetcher.state === "submitting"
578 // and formData will be available
579 fetcher.formData
580 ```
581 */
582 Submitting: {
583 state: "submitting";
584 formMethod: Submission["formMethod"];
585 formAction: Submission["formAction"];
586 formEncType: Submission["formEncType"];
587 text: Submission["text"];
588 formData: Submission["formData"];
589 json: Submission["json"];
590 data: TData | undefined;
591 };
592};
593type Fetcher<TData = any> = FetcherStates<TData>[keyof FetcherStates<TData>];
594interface BlockerBlocked {
595 state: "blocked";
596 reset: () => void;
597 proceed: () => void;
598 location: Location;
599}
600interface BlockerUnblocked {
601 state: "unblocked";
602 reset: undefined;
603 proceed: undefined;
604 location: undefined;
605}
606interface BlockerProceeding {
607 state: "proceeding";
608 reset: undefined;
609 proceed: undefined;
610 location: Location;
611}
612type Blocker = BlockerUnblocked | BlockerBlocked | BlockerProceeding;
613type BlockerFunction = (args: {
614 currentLocation: Location;
615 nextLocation: Location;
616 historyAction: Action;
617}) => boolean;
618declare const IDLE_NAVIGATION: NavigationStates["Idle"];
619declare const IDLE_FETCHER: FetcherStates["Idle"];
620declare const IDLE_BLOCKER: BlockerUnblocked;
621/**
622 * Create a router and listen to history POP navigations
623 */
624declare function createRouter(init: RouterInit): Router;
625interface CreateStaticHandlerOptions {
626 basename?: string;
627 mapRouteProperties?: MapRoutePropertiesFunction;
628 instrumentations?: Pick<ServerInstrumentation, "route">[];
629 future?: Partial<FutureConfig>;
630}
631/**
632 * Create a static handler to perform server-side data loading
633 *
634 * @example
635 * export async function handleRequest(request: Request) {
636 * let { query, dataRoutes } = createStaticHandler(routes);
637 * let context = await query(request);
638 *
639 * if (context instanceof Response) {
640 * return context;
641 * }
642 *
643 * let router = createStaticRouter(dataRoutes, context);
644 * return new Response(
645 * ReactDOMServer.renderToString(<StaticRouterProvider ... />),
646 * { headers: { "Content-Type": "text/html" } }
647 * );
648 * }
649 *
650 * @public
651 * @category Data Routers
652 * @mode data
653 * @param routes The {@link RouteObject | route objects} to create a static
654 * handler for
655 * @param opts Options
656 * @param opts.basename The base URL for the static handler (default: `/`)
657 * @param opts.future Future flags for the static handler
658 * @returns A static handler that can be used to query data for the provided
659 * routes
660 */
661declare function createStaticHandler(routes: RouteObject[], opts?: CreateStaticHandlerOptions): StaticHandler;
662//#endregion
663export { Blocker, BlockerFunction, Fetcher, FutureConfig, GetScrollPositionFunction, GetScrollRestorationKeyFunction, HydrationState, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, Navigation, NavigationStates, RelativeRoutingType, RevalidationState, Router, RouterFetchOptions, RouterInit, RouterNavigateOptions, RouterState, RouterSubscriber, StaticHandler, StaticHandlerContext, createRouter, createStaticHandler };
\No newline at end of file