UNPKG

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