UNPKG

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