@remix-run/router 1.8.0 → 1.9.0-pre.1
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/CHANGELOG.md +24 -0
- package/dist/history.d.ts +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/router.cjs.js +67 -69
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +2 -12
- package/dist/router.js +56 -54
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +67 -69
- package/dist/router.umd.js.map +1 -1
- package/dist/router.umd.min.js +2 -2
- package/dist/router.umd.min.js.map +1 -1
- package/dist/utils.d.ts +38 -25
- package/history.ts +5 -2
- package/index.ts +8 -4
- package/package.json +1 -1
- package/router.ts +61 -82
- package/utils.ts +61 -26
package/dist/utils.d.ts
CHANGED
|
@@ -111,20 +111,20 @@ export type Submission = {
|
|
|
111
111
|
* Arguments passed to route loader/action functions. Same for now but we keep
|
|
112
112
|
* this as a private implementation detail in case they diverge in the future.
|
|
113
113
|
*/
|
|
114
|
-
interface DataFunctionArgs {
|
|
114
|
+
interface DataFunctionArgs<Context> {
|
|
115
115
|
request: Request;
|
|
116
116
|
params: Params;
|
|
117
|
-
context?:
|
|
117
|
+
context?: Context;
|
|
118
118
|
}
|
|
119
119
|
/**
|
|
120
120
|
* Arguments passed to loader functions
|
|
121
121
|
*/
|
|
122
|
-
export interface LoaderFunctionArgs extends DataFunctionArgs {
|
|
122
|
+
export interface LoaderFunctionArgs<C = any> extends DataFunctionArgs<C> {
|
|
123
123
|
}
|
|
124
124
|
/**
|
|
125
125
|
* Arguments passed to action functions
|
|
126
126
|
*/
|
|
127
|
-
export interface ActionFunctionArgs extends DataFunctionArgs {
|
|
127
|
+
export interface ActionFunctionArgs<C = any> extends DataFunctionArgs<C> {
|
|
128
128
|
}
|
|
129
129
|
/**
|
|
130
130
|
* Loaders and actions can return anything except `undefined` (`null` is a
|
|
@@ -135,14 +135,31 @@ type DataFunctionValue = Response | NonNullable<unknown> | null;
|
|
|
135
135
|
/**
|
|
136
136
|
* Route loader function signature
|
|
137
137
|
*/
|
|
138
|
-
export interface LoaderFunction {
|
|
139
|
-
(args: LoaderFunctionArgs): Promise<DataFunctionValue> | DataFunctionValue;
|
|
138
|
+
export interface LoaderFunction<C = any> {
|
|
139
|
+
(args: LoaderFunctionArgs<C>): Promise<DataFunctionValue> | DataFunctionValue;
|
|
140
140
|
}
|
|
141
141
|
/**
|
|
142
142
|
* Route action function signature
|
|
143
143
|
*/
|
|
144
|
-
export interface ActionFunction {
|
|
145
|
-
(args: ActionFunctionArgs): Promise<DataFunctionValue> | DataFunctionValue;
|
|
144
|
+
export interface ActionFunction<C = any> {
|
|
145
|
+
(args: ActionFunctionArgs<C>): Promise<DataFunctionValue> | DataFunctionValue;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Arguments passed to shouldRevalidate function
|
|
149
|
+
*/
|
|
150
|
+
export interface ShouldRevalidateFunctionArgs {
|
|
151
|
+
currentUrl: URL;
|
|
152
|
+
currentParams: AgnosticDataRouteMatch["params"];
|
|
153
|
+
nextUrl: URL;
|
|
154
|
+
nextParams: AgnosticDataRouteMatch["params"];
|
|
155
|
+
formMethod?: Submission["formMethod"];
|
|
156
|
+
formAction?: Submission["formAction"];
|
|
157
|
+
formEncType?: Submission["formEncType"];
|
|
158
|
+
text?: Submission["text"];
|
|
159
|
+
formData?: Submission["formData"];
|
|
160
|
+
json?: Submission["json"];
|
|
161
|
+
actionResult?: any;
|
|
162
|
+
defaultShouldRevalidate: boolean;
|
|
146
163
|
}
|
|
147
164
|
/**
|
|
148
165
|
* Route shouldRevalidate function signature. This runs after any submission
|
|
@@ -152,20 +169,7 @@ export interface ActionFunction {
|
|
|
152
169
|
* have to re-run based on the data models that were potentially mutated.
|
|
153
170
|
*/
|
|
154
171
|
export interface ShouldRevalidateFunction {
|
|
155
|
-
(args:
|
|
156
|
-
currentUrl: URL;
|
|
157
|
-
currentParams: AgnosticDataRouteMatch["params"];
|
|
158
|
-
nextUrl: URL;
|
|
159
|
-
nextParams: AgnosticDataRouteMatch["params"];
|
|
160
|
-
formMethod?: Submission["formMethod"];
|
|
161
|
-
formAction?: Submission["formAction"];
|
|
162
|
-
formEncType?: Submission["formEncType"];
|
|
163
|
-
text?: Submission["text"];
|
|
164
|
-
formData?: Submission["formData"];
|
|
165
|
-
json?: Submission["json"];
|
|
166
|
-
actionResult?: DataResult;
|
|
167
|
-
defaultShouldRevalidate: boolean;
|
|
168
|
-
}): boolean;
|
|
172
|
+
(args: ShouldRevalidateFunctionArgs): boolean;
|
|
169
173
|
}
|
|
170
174
|
/**
|
|
171
175
|
* Function provided by the framework-aware layers to set `hasErrorBoundary`
|
|
@@ -297,6 +301,14 @@ export declare function convertRoutesToDataRoutes(routes: AgnosticRouteObject[],
|
|
|
297
301
|
* @see https://reactrouter.com/utils/match-routes
|
|
298
302
|
*/
|
|
299
303
|
export declare function matchRoutes<RouteObjectType extends AgnosticRouteObject = AgnosticRouteObject>(routes: RouteObjectType[], locationArg: Partial<Location> | string, basename?: string): AgnosticRouteMatch<string, RouteObjectType>[] | null;
|
|
304
|
+
export interface UIMatch<D = unknown, H = unknown> {
|
|
305
|
+
id: string;
|
|
306
|
+
pathname: string;
|
|
307
|
+
params: AgnosticRouteMatch["params"];
|
|
308
|
+
data: D;
|
|
309
|
+
handle: H;
|
|
310
|
+
}
|
|
311
|
+
export declare function convertRouteMatchToUiMatch(match: AgnosticDataRouteMatch, loaderData: RouteData): UIMatch;
|
|
300
312
|
/**
|
|
301
313
|
* Returns a path with params interpolated.
|
|
302
314
|
*
|
|
@@ -462,14 +474,15 @@ export declare const redirectDocument: RedirectFunction;
|
|
|
462
474
|
* @private
|
|
463
475
|
* Utility class we use to hold auto-unwrapped 4xx/5xx Response bodies
|
|
464
476
|
*/
|
|
465
|
-
export declare class
|
|
477
|
+
export declare class ErrorResponseImpl {
|
|
466
478
|
status: number;
|
|
467
479
|
statusText: string;
|
|
468
480
|
data: any;
|
|
469
|
-
error
|
|
470
|
-
internal
|
|
481
|
+
private error?;
|
|
482
|
+
private internal;
|
|
471
483
|
constructor(status: number, statusText: string | undefined, data: any, internal?: boolean);
|
|
472
484
|
}
|
|
485
|
+
export type ErrorResponse = InstanceType<typeof ErrorResponseImpl>;
|
|
473
486
|
/**
|
|
474
487
|
* Check if the given error is an ErrorResponse generated from a 4xx/5xx
|
|
475
488
|
* Response thrown from an action/loader
|
package/history.ts
CHANGED
|
@@ -49,15 +49,18 @@ export interface Path {
|
|
|
49
49
|
hash: string;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// TODO: (v7) Change the Location generic default from `any` to `unknown` and
|
|
53
|
+
// remove Remix `useLocation` wrapper.
|
|
54
|
+
|
|
52
55
|
/**
|
|
53
56
|
* An entry in a history stack. A location contains information about the
|
|
54
57
|
* URL path, as well as possibly some arbitrary state and a key.
|
|
55
58
|
*/
|
|
56
|
-
export interface Location extends Path {
|
|
59
|
+
export interface Location<S = any> extends Path {
|
|
57
60
|
/**
|
|
58
61
|
* A value of arbitrary data associated with this location.
|
|
59
62
|
*/
|
|
60
|
-
state:
|
|
63
|
+
state: S;
|
|
61
64
|
|
|
62
65
|
/**
|
|
63
66
|
* A unique string associated with this location. May be used to safely store
|
package/index.ts
CHANGED
|
@@ -9,12 +9,12 @@ export type {
|
|
|
9
9
|
AgnosticNonIndexRouteObject,
|
|
10
10
|
AgnosticRouteMatch,
|
|
11
11
|
AgnosticRouteObject,
|
|
12
|
-
|
|
13
|
-
TrackedPromise,
|
|
12
|
+
ErrorResponse,
|
|
14
13
|
FormEncType,
|
|
15
14
|
FormMethod,
|
|
16
15
|
HTMLFormMethod,
|
|
17
16
|
JsonFunction,
|
|
17
|
+
LazyRouteFunction,
|
|
18
18
|
LoaderFunction,
|
|
19
19
|
LoaderFunctionArgs,
|
|
20
20
|
ParamParseKey,
|
|
@@ -23,12 +23,14 @@ export type {
|
|
|
23
23
|
PathPattern,
|
|
24
24
|
RedirectFunction,
|
|
25
25
|
ShouldRevalidateFunction,
|
|
26
|
+
ShouldRevalidateFunctionArgs,
|
|
27
|
+
TrackedPromise,
|
|
28
|
+
UIMatch,
|
|
26
29
|
V7_FormMethod,
|
|
27
30
|
} from "./utils";
|
|
28
31
|
|
|
29
32
|
export {
|
|
30
33
|
AbortedDeferredError,
|
|
31
|
-
ErrorResponse,
|
|
32
34
|
defer,
|
|
33
35
|
generatePath,
|
|
34
36
|
getToPathname,
|
|
@@ -62,9 +64,9 @@ export type {
|
|
|
62
64
|
export {
|
|
63
65
|
Action,
|
|
64
66
|
createBrowserHistory,
|
|
65
|
-
createPath,
|
|
66
67
|
createHashHistory,
|
|
67
68
|
createMemoryHistory,
|
|
69
|
+
createPath,
|
|
68
70
|
parsePath,
|
|
69
71
|
} from "./history";
|
|
70
72
|
|
|
@@ -81,7 +83,9 @@ export * from "./router";
|
|
|
81
83
|
export type { RouteManifest as UNSAFE_RouteManifest } from "./utils";
|
|
82
84
|
export {
|
|
83
85
|
DeferredData as UNSAFE_DeferredData,
|
|
86
|
+
ErrorResponseImpl as UNSAFE_ErrorResponseImpl,
|
|
84
87
|
convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes,
|
|
88
|
+
convertRouteMatchToUiMatch as UNSAFE_convertRouteMatchToUiMatch,
|
|
85
89
|
getPathContributingMatches as UNSAFE_getPathContributingMatches,
|
|
86
90
|
} from "./utils";
|
|
87
91
|
|
package/package.json
CHANGED
package/router.ts
CHANGED
|
@@ -8,35 +8,36 @@ import {
|
|
|
8
8
|
warning,
|
|
9
9
|
} from "./history";
|
|
10
10
|
import type {
|
|
11
|
-
|
|
12
|
-
DeferredData,
|
|
11
|
+
ActionFunction,
|
|
13
12
|
AgnosticDataRouteMatch,
|
|
14
13
|
AgnosticDataRouteObject,
|
|
14
|
+
AgnosticRouteObject,
|
|
15
|
+
DataResult,
|
|
16
|
+
DeferredData,
|
|
15
17
|
DeferredResult,
|
|
18
|
+
DetectErrorBoundaryFunction,
|
|
16
19
|
ErrorResult,
|
|
17
20
|
FormEncType,
|
|
18
21
|
FormMethod,
|
|
19
|
-
|
|
22
|
+
HTMLFormMethod,
|
|
23
|
+
ImmutableRouteKey,
|
|
24
|
+
LoaderFunction,
|
|
25
|
+
MapRoutePropertiesFunction,
|
|
26
|
+
MutationFormMethod,
|
|
20
27
|
RedirectResult,
|
|
21
28
|
RouteData,
|
|
22
|
-
|
|
29
|
+
RouteManifest,
|
|
30
|
+
ShouldRevalidateFunctionArgs,
|
|
23
31
|
Submission,
|
|
24
32
|
SuccessResult,
|
|
25
|
-
|
|
26
|
-
ShouldRevalidateFunction,
|
|
27
|
-
RouteManifest,
|
|
28
|
-
ImmutableRouteKey,
|
|
29
|
-
ActionFunction,
|
|
30
|
-
LoaderFunction,
|
|
31
|
-
V7_MutationFormMethod,
|
|
33
|
+
UIMatch,
|
|
32
34
|
V7_FormMethod,
|
|
33
|
-
|
|
34
|
-
MutationFormMethod,
|
|
35
|
-
MapRoutePropertiesFunction,
|
|
35
|
+
V7_MutationFormMethod,
|
|
36
36
|
} from "./utils";
|
|
37
37
|
import {
|
|
38
|
-
|
|
38
|
+
ErrorResponseImpl,
|
|
39
39
|
ResultType,
|
|
40
|
+
convertRouteMatchToUiMatch,
|
|
40
41
|
convertRoutesToDataRoutes,
|
|
41
42
|
getPathContributingMatches,
|
|
42
43
|
immutableRouteKeys,
|
|
@@ -394,20 +395,12 @@ export interface RouterSubscriber {
|
|
|
394
395
|
(state: RouterState): void;
|
|
395
396
|
}
|
|
396
397
|
|
|
397
|
-
interface UseMatchesMatch {
|
|
398
|
-
id: string;
|
|
399
|
-
pathname: string;
|
|
400
|
-
params: AgnosticRouteMatch["params"];
|
|
401
|
-
data: unknown;
|
|
402
|
-
handle: unknown;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
398
|
/**
|
|
406
399
|
* Function signature for determining the key to be used in scroll restoration
|
|
407
400
|
* for a given location
|
|
408
401
|
*/
|
|
409
402
|
export interface GetScrollRestorationKeyFunction {
|
|
410
|
-
(location: Location, matches:
|
|
403
|
+
(location: Location, matches: UIMatch[]): string | null;
|
|
411
404
|
}
|
|
412
405
|
|
|
413
406
|
/**
|
|
@@ -526,7 +519,6 @@ type FetcherStates<TData = any> = {
|
|
|
526
519
|
formData: undefined;
|
|
527
520
|
json: undefined;
|
|
528
521
|
data: TData | undefined;
|
|
529
|
-
" _hasFetcherDoneAnything "?: boolean;
|
|
530
522
|
};
|
|
531
523
|
Loading: {
|
|
532
524
|
state: "loading";
|
|
@@ -537,7 +529,6 @@ type FetcherStates<TData = any> = {
|
|
|
537
529
|
formData: Submission["formData"] | undefined;
|
|
538
530
|
json: Submission["json"] | undefined;
|
|
539
531
|
data: TData | undefined;
|
|
540
|
-
" _hasFetcherDoneAnything "?: boolean;
|
|
541
532
|
};
|
|
542
533
|
Submitting: {
|
|
543
534
|
state: "submitting";
|
|
@@ -548,7 +539,6 @@ type FetcherStates<TData = any> = {
|
|
|
548
539
|
formData: Submission["formData"];
|
|
549
540
|
json: Submission["json"];
|
|
550
541
|
data: TData | undefined;
|
|
551
|
-
" _hasFetcherDoneAnything "?: boolean;
|
|
552
542
|
};
|
|
553
543
|
};
|
|
554
544
|
|
|
@@ -1229,7 +1219,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1229
1219
|
submission?: Submission;
|
|
1230
1220
|
fetcherSubmission?: Submission;
|
|
1231
1221
|
overrideNavigation?: Navigation;
|
|
1232
|
-
pendingError?:
|
|
1222
|
+
pendingError?: ErrorResponseImpl;
|
|
1233
1223
|
startUninterruptedRevalidation?: boolean;
|
|
1234
1224
|
preventScrollReset?: boolean;
|
|
1235
1225
|
replace?: boolean;
|
|
@@ -1786,8 +1776,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1786
1776
|
updateState({ fetchers: new Map(state.fetchers) });
|
|
1787
1777
|
|
|
1788
1778
|
return startRedirectNavigation(state, actionResult, {
|
|
1789
|
-
submission,
|
|
1790
|
-
isFetchActionRedirect: true,
|
|
1779
|
+
fetcherSubmission: submission,
|
|
1791
1780
|
});
|
|
1792
1781
|
}
|
|
1793
1782
|
}
|
|
@@ -2086,27 +2075,21 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2086
2075
|
redirect: RedirectResult,
|
|
2087
2076
|
{
|
|
2088
2077
|
submission,
|
|
2078
|
+
fetcherSubmission,
|
|
2089
2079
|
replace,
|
|
2090
|
-
isFetchActionRedirect,
|
|
2091
2080
|
}: {
|
|
2092
2081
|
submission?: Submission;
|
|
2082
|
+
fetcherSubmission?: Submission;
|
|
2093
2083
|
replace?: boolean;
|
|
2094
|
-
isFetchActionRedirect?: boolean;
|
|
2095
2084
|
} = {}
|
|
2096
2085
|
) {
|
|
2097
2086
|
if (redirect.revalidate) {
|
|
2098
2087
|
isRevalidationRequired = true;
|
|
2099
2088
|
}
|
|
2100
2089
|
|
|
2101
|
-
let redirectLocation = createLocation(
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
// TODO: This can be removed once we get rid of useTransition in Remix v2
|
|
2105
|
-
{
|
|
2106
|
-
_isRedirect: true,
|
|
2107
|
-
...(isFetchActionRedirect ? { _isFetchActionRedirect: true } : {}),
|
|
2108
|
-
}
|
|
2109
|
-
);
|
|
2090
|
+
let redirectLocation = createLocation(state.location, redirect.location, {
|
|
2091
|
+
_isRedirect: true,
|
|
2092
|
+
});
|
|
2110
2093
|
invariant(
|
|
2111
2094
|
redirectLocation,
|
|
2112
2095
|
"Expected a location on the redirect navigation"
|
|
@@ -2146,12 +2129,21 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2146
2129
|
|
|
2147
2130
|
// Use the incoming submission if provided, fallback on the active one in
|
|
2148
2131
|
// state.navigation
|
|
2149
|
-
let
|
|
2150
|
-
|
|
2132
|
+
let { formMethod, formAction, formEncType } = state.navigation;
|
|
2133
|
+
if (
|
|
2134
|
+
!submission &&
|
|
2135
|
+
!fetcherSubmission &&
|
|
2136
|
+
formMethod &&
|
|
2137
|
+
formAction &&
|
|
2138
|
+
formEncType
|
|
2139
|
+
) {
|
|
2140
|
+
submission = getSubmissionFromNavigation(state.navigation);
|
|
2141
|
+
}
|
|
2151
2142
|
|
|
2152
2143
|
// If this was a 307/308 submission we want to preserve the HTTP method and
|
|
2153
2144
|
// re-submit the GET/POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2154
2145
|
// redirected location
|
|
2146
|
+
let activeSubmission = submission || fetcherSubmission;
|
|
2155
2147
|
if (
|
|
2156
2148
|
redirectPreserveMethodStatusCodes.has(redirect.status) &&
|
|
2157
2149
|
activeSubmission &&
|
|
@@ -2165,23 +2157,17 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2165
2157
|
// Preserve this flag across redirects
|
|
2166
2158
|
preventScrollReset: pendingPreventScrollReset,
|
|
2167
2159
|
});
|
|
2168
|
-
} else if (isFetchActionRedirect) {
|
|
2169
|
-
// For a fetch action redirect, we kick off a new loading navigation
|
|
2170
|
-
// without the fetcher submission, but we send it along for shouldRevalidate
|
|
2171
|
-
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2172
|
-
overrideNavigation: getLoadingNavigation(redirectLocation),
|
|
2173
|
-
fetcherSubmission: activeSubmission,
|
|
2174
|
-
// Preserve this flag across redirects
|
|
2175
|
-
preventScrollReset: pendingPreventScrollReset,
|
|
2176
|
-
});
|
|
2177
2160
|
} else {
|
|
2178
|
-
// If we have a submission, we will preserve it through the
|
|
2161
|
+
// If we have a navigation submission, we will preserve it through the
|
|
2162
|
+
// redirect navigation
|
|
2179
2163
|
let overrideNavigation = getLoadingNavigation(
|
|
2180
2164
|
redirectLocation,
|
|
2181
|
-
|
|
2165
|
+
submission
|
|
2182
2166
|
);
|
|
2183
2167
|
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2184
2168
|
overrideNavigation,
|
|
2169
|
+
// Send fetcher submissions through for shouldRevalidate
|
|
2170
|
+
fetcherSubmission,
|
|
2185
2171
|
// Preserve this flag across redirects
|
|
2186
2172
|
preventScrollReset: pendingPreventScrollReset,
|
|
2187
2173
|
});
|
|
@@ -2468,7 +2454,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2468
2454
|
if (getScrollRestorationKey) {
|
|
2469
2455
|
let key = getScrollRestorationKey(
|
|
2470
2456
|
location,
|
|
2471
|
-
matches.map((m) =>
|
|
2457
|
+
matches.map((m) => convertRouteMatchToUiMatch(m, state.loaderData))
|
|
2472
2458
|
);
|
|
2473
2459
|
return key || location.key;
|
|
2474
2460
|
}
|
|
@@ -2850,7 +2836,9 @@ export function createStaticHandler(
|
|
|
2850
2836
|
|
|
2851
2837
|
if (request.signal.aborted) {
|
|
2852
2838
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
2853
|
-
throw new Error(
|
|
2839
|
+
throw new Error(
|
|
2840
|
+
`${method}() call aborted: ${request.method} ${request.url}`
|
|
2841
|
+
);
|
|
2854
2842
|
}
|
|
2855
2843
|
}
|
|
2856
2844
|
|
|
@@ -3018,7 +3006,9 @@ export function createStaticHandler(
|
|
|
3018
3006
|
|
|
3019
3007
|
if (request.signal.aborted) {
|
|
3020
3008
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
3021
|
-
throw new Error(
|
|
3009
|
+
throw new Error(
|
|
3010
|
+
`${method}() call aborted: ${request.method} ${request.url}`
|
|
3011
|
+
);
|
|
3022
3012
|
}
|
|
3023
3013
|
|
|
3024
3014
|
// Process and commit output from loaders
|
|
@@ -3172,7 +3162,7 @@ function normalizeNavigateOptions(
|
|
|
3172
3162
|
): {
|
|
3173
3163
|
path: string;
|
|
3174
3164
|
submission?: Submission;
|
|
3175
|
-
error?:
|
|
3165
|
+
error?: ErrorResponseImpl;
|
|
3176
3166
|
} {
|
|
3177
3167
|
// Return location verbatim on non-submission navigations
|
|
3178
3168
|
if (!opts || !isSubmissionNavigation(opts)) {
|
|
@@ -3513,7 +3503,7 @@ function isNewRouteInstance(
|
|
|
3513
3503
|
|
|
3514
3504
|
function shouldRevalidateLoader(
|
|
3515
3505
|
loaderMatch: AgnosticDataRouteMatch,
|
|
3516
|
-
arg:
|
|
3506
|
+
arg: ShouldRevalidateFunctionArgs
|
|
3517
3507
|
) {
|
|
3518
3508
|
if (loaderMatch.route.shouldRevalidate) {
|
|
3519
3509
|
let routeChoice = loaderMatch.route.shouldRevalidate(arg);
|
|
@@ -3643,10 +3633,19 @@ async function callLoaderOrAction(
|
|
|
3643
3633
|
if (match.route.lazy) {
|
|
3644
3634
|
if (handler) {
|
|
3645
3635
|
// Run statically defined handler in parallel with lazy()
|
|
3636
|
+
let handlerError;
|
|
3646
3637
|
let values = await Promise.all([
|
|
3647
|
-
|
|
3638
|
+
// If the handler throws, don't let it immediately bubble out,
|
|
3639
|
+
// since we need to let the lazy() execution finish so we know if this
|
|
3640
|
+
// route has a boundary that can handle the error
|
|
3641
|
+
runHandler(handler).catch((e) => {
|
|
3642
|
+
handlerError = e;
|
|
3643
|
+
}),
|
|
3648
3644
|
loadLazyRouteModule(match.route, mapRouteProperties, manifest),
|
|
3649
3645
|
]);
|
|
3646
|
+
if (handlerError) {
|
|
3647
|
+
throw handlerError;
|
|
3648
|
+
}
|
|
3650
3649
|
result = values[0];
|
|
3651
3650
|
} else {
|
|
3652
3651
|
// Load lazy route module, then run any returned handler
|
|
@@ -3774,7 +3773,7 @@ async function callLoaderOrAction(
|
|
|
3774
3773
|
if (resultType === ResultType.error) {
|
|
3775
3774
|
return {
|
|
3776
3775
|
type: resultType,
|
|
3777
|
-
error: new
|
|
3776
|
+
error: new ErrorResponseImpl(status, result.statusText, data),
|
|
3778
3777
|
headers: result.headers,
|
|
3779
3778
|
};
|
|
3780
3779
|
}
|
|
@@ -4139,7 +4138,7 @@ function getInternalRouterError(
|
|
|
4139
4138
|
}
|
|
4140
4139
|
}
|
|
4141
4140
|
|
|
4142
|
-
return new
|
|
4141
|
+
return new ErrorResponseImpl(
|
|
4143
4142
|
status || 500,
|
|
4144
4143
|
statusText,
|
|
4145
4144
|
new Error(errorMessage),
|
|
@@ -4326,22 +4325,6 @@ function hasNakedIndexQuery(search: string): boolean {
|
|
|
4326
4325
|
return new URLSearchParams(search).getAll("index").some((v) => v === "");
|
|
4327
4326
|
}
|
|
4328
4327
|
|
|
4329
|
-
// Note: This should match the format exported by useMatches, so if you change
|
|
4330
|
-
// this please also change that :) Eventually we'll DRY this up
|
|
4331
|
-
function createUseMatchesMatch(
|
|
4332
|
-
match: AgnosticDataRouteMatch,
|
|
4333
|
-
loaderData: RouteData
|
|
4334
|
-
): UseMatchesMatch {
|
|
4335
|
-
let { route, pathname, params } = match;
|
|
4336
|
-
return {
|
|
4337
|
-
id: route.id,
|
|
4338
|
-
pathname,
|
|
4339
|
-
params,
|
|
4340
|
-
data: loaderData[route.id] as unknown,
|
|
4341
|
-
handle: route.handle as unknown,
|
|
4342
|
-
};
|
|
4343
|
-
}
|
|
4344
|
-
|
|
4345
4328
|
function getTargetMatch(
|
|
4346
4329
|
matches: AgnosticDataRouteMatch[],
|
|
4347
4330
|
location: Location | string
|
|
@@ -4462,7 +4445,6 @@ function getLoadingFetcher(
|
|
|
4462
4445
|
json: submission.json,
|
|
4463
4446
|
text: submission.text,
|
|
4464
4447
|
data,
|
|
4465
|
-
" _hasFetcherDoneAnything ": true,
|
|
4466
4448
|
};
|
|
4467
4449
|
return fetcher;
|
|
4468
4450
|
} else {
|
|
@@ -4475,7 +4457,6 @@ function getLoadingFetcher(
|
|
|
4475
4457
|
json: undefined,
|
|
4476
4458
|
text: undefined,
|
|
4477
4459
|
data,
|
|
4478
|
-
" _hasFetcherDoneAnything ": true,
|
|
4479
4460
|
};
|
|
4480
4461
|
return fetcher;
|
|
4481
4462
|
}
|
|
@@ -4494,7 +4475,6 @@ function getSubmittingFetcher(
|
|
|
4494
4475
|
json: submission.json,
|
|
4495
4476
|
text: submission.text,
|
|
4496
4477
|
data: existingFetcher ? existingFetcher.data : undefined,
|
|
4497
|
-
" _hasFetcherDoneAnything ": true,
|
|
4498
4478
|
};
|
|
4499
4479
|
return fetcher;
|
|
4500
4480
|
}
|
|
@@ -4509,7 +4489,6 @@ function getDoneFetcher(data: Fetcher["data"]): FetcherStates["Idle"] {
|
|
|
4509
4489
|
json: undefined,
|
|
4510
4490
|
text: undefined,
|
|
4511
4491
|
data,
|
|
4512
|
-
" _hasFetcherDoneAnything ": true,
|
|
4513
4492
|
};
|
|
4514
4493
|
return fetcher;
|
|
4515
4494
|
}
|
package/utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Location, Path, To } from "./history";
|
|
2
|
-
import {
|
|
2
|
+
import { invariant, parsePath, warning } from "./history";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Map of routeId -> data returned from a loader/action/error
|
|
@@ -137,21 +137,24 @@ export type Submission =
|
|
|
137
137
|
* Arguments passed to route loader/action functions. Same for now but we keep
|
|
138
138
|
* this as a private implementation detail in case they diverge in the future.
|
|
139
139
|
*/
|
|
140
|
-
interface DataFunctionArgs {
|
|
140
|
+
interface DataFunctionArgs<Context> {
|
|
141
141
|
request: Request;
|
|
142
142
|
params: Params;
|
|
143
|
-
context?:
|
|
143
|
+
context?: Context;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
// TODO: (v7) Change the defaults from any to unknown in and remove Remix wrappers:
|
|
147
|
+
// ActionFunction, ActionFunctionArgs, LoaderFunction, LoaderFunctionArgs
|
|
148
|
+
|
|
146
149
|
/**
|
|
147
150
|
* Arguments passed to loader functions
|
|
148
151
|
*/
|
|
149
|
-
export interface LoaderFunctionArgs extends DataFunctionArgs {}
|
|
152
|
+
export interface LoaderFunctionArgs<C = any> extends DataFunctionArgs<C> {}
|
|
150
153
|
|
|
151
154
|
/**
|
|
152
155
|
* Arguments passed to action functions
|
|
153
156
|
*/
|
|
154
|
-
export interface ActionFunctionArgs extends DataFunctionArgs {}
|
|
157
|
+
export interface ActionFunctionArgs<C = any> extends DataFunctionArgs<C> {}
|
|
155
158
|
|
|
156
159
|
/**
|
|
157
160
|
* Loaders and actions can return anything except `undefined` (`null` is a
|
|
@@ -163,15 +166,33 @@ type DataFunctionValue = Response | NonNullable<unknown> | null;
|
|
|
163
166
|
/**
|
|
164
167
|
* Route loader function signature
|
|
165
168
|
*/
|
|
166
|
-
export interface LoaderFunction {
|
|
167
|
-
(args: LoaderFunctionArgs): Promise<DataFunctionValue> | DataFunctionValue;
|
|
169
|
+
export interface LoaderFunction<C = any> {
|
|
170
|
+
(args: LoaderFunctionArgs<C>): Promise<DataFunctionValue> | DataFunctionValue;
|
|
168
171
|
}
|
|
169
172
|
|
|
170
173
|
/**
|
|
171
174
|
* Route action function signature
|
|
172
175
|
*/
|
|
173
|
-
export interface ActionFunction {
|
|
174
|
-
(args: ActionFunctionArgs): Promise<DataFunctionValue> | DataFunctionValue;
|
|
176
|
+
export interface ActionFunction<C = any> {
|
|
177
|
+
(args: ActionFunctionArgs<C>): Promise<DataFunctionValue> | DataFunctionValue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Arguments passed to shouldRevalidate function
|
|
182
|
+
*/
|
|
183
|
+
export interface ShouldRevalidateFunctionArgs {
|
|
184
|
+
currentUrl: URL;
|
|
185
|
+
currentParams: AgnosticDataRouteMatch["params"];
|
|
186
|
+
nextUrl: URL;
|
|
187
|
+
nextParams: AgnosticDataRouteMatch["params"];
|
|
188
|
+
formMethod?: Submission["formMethod"];
|
|
189
|
+
formAction?: Submission["formAction"];
|
|
190
|
+
formEncType?: Submission["formEncType"];
|
|
191
|
+
text?: Submission["text"];
|
|
192
|
+
formData?: Submission["formData"];
|
|
193
|
+
json?: Submission["json"];
|
|
194
|
+
actionResult?: any;
|
|
195
|
+
defaultShouldRevalidate: boolean;
|
|
175
196
|
}
|
|
176
197
|
|
|
177
198
|
/**
|
|
@@ -182,20 +203,7 @@ export interface ActionFunction {
|
|
|
182
203
|
* have to re-run based on the data models that were potentially mutated.
|
|
183
204
|
*/
|
|
184
205
|
export interface ShouldRevalidateFunction {
|
|
185
|
-
(args:
|
|
186
|
-
currentUrl: URL;
|
|
187
|
-
currentParams: AgnosticDataRouteMatch["params"];
|
|
188
|
-
nextUrl: URL;
|
|
189
|
-
nextParams: AgnosticDataRouteMatch["params"];
|
|
190
|
-
formMethod?: Submission["formMethod"];
|
|
191
|
-
formAction?: Submission["formAction"];
|
|
192
|
-
formEncType?: Submission["formEncType"];
|
|
193
|
-
text?: Submission["text"];
|
|
194
|
-
formData?: Submission["formData"];
|
|
195
|
-
json?: Submission["json"];
|
|
196
|
-
actionResult?: DataResult;
|
|
197
|
-
defaultShouldRevalidate: boolean;
|
|
198
|
-
}): boolean;
|
|
206
|
+
(args: ShouldRevalidateFunctionArgs): boolean;
|
|
199
207
|
}
|
|
200
208
|
|
|
201
209
|
/**
|
|
@@ -485,6 +493,28 @@ export function matchRoutes<
|
|
|
485
493
|
return matches;
|
|
486
494
|
}
|
|
487
495
|
|
|
496
|
+
export interface UIMatch<D = unknown, H = unknown> {
|
|
497
|
+
id: string;
|
|
498
|
+
pathname: string;
|
|
499
|
+
params: AgnosticRouteMatch["params"];
|
|
500
|
+
data: D;
|
|
501
|
+
handle: H;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export function convertRouteMatchToUiMatch(
|
|
505
|
+
match: AgnosticDataRouteMatch,
|
|
506
|
+
loaderData: RouteData
|
|
507
|
+
): UIMatch {
|
|
508
|
+
let { route, pathname, params } = match;
|
|
509
|
+
return {
|
|
510
|
+
id: route.id,
|
|
511
|
+
pathname,
|
|
512
|
+
params,
|
|
513
|
+
data: loaderData[route.id],
|
|
514
|
+
handle: route.handle,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
488
518
|
interface RouteMeta<
|
|
489
519
|
RouteObjectType extends AgnosticRouteObject = AgnosticRouteObject
|
|
490
520
|
> {
|
|
@@ -1500,12 +1530,12 @@ export const redirectDocument: RedirectFunction = (url, init) => {
|
|
|
1500
1530
|
* @private
|
|
1501
1531
|
* Utility class we use to hold auto-unwrapped 4xx/5xx Response bodies
|
|
1502
1532
|
*/
|
|
1503
|
-
export class
|
|
1533
|
+
export class ErrorResponseImpl {
|
|
1504
1534
|
status: number;
|
|
1505
1535
|
statusText: string;
|
|
1506
1536
|
data: any;
|
|
1507
|
-
error?: Error;
|
|
1508
|
-
internal: boolean;
|
|
1537
|
+
private error?: Error;
|
|
1538
|
+
private internal: boolean;
|
|
1509
1539
|
|
|
1510
1540
|
constructor(
|
|
1511
1541
|
status: number,
|
|
@@ -1525,6 +1555,11 @@ export class ErrorResponse {
|
|
|
1525
1555
|
}
|
|
1526
1556
|
}
|
|
1527
1557
|
|
|
1558
|
+
// We don't want the class exported since usage of it at runtime is an
|
|
1559
|
+
// implementation detail, but we do want to export the shape so folks can
|
|
1560
|
+
// build their own abstractions around instances via isRouteErrorResponse()
|
|
1561
|
+
export type ErrorResponse = InstanceType<typeof ErrorResponseImpl>;
|
|
1562
|
+
|
|
1528
1563
|
/**
|
|
1529
1564
|
* Check if the given error is an ErrorResponse generated from a 4xx/5xx
|
|
1530
1565
|
* Response thrown from an action/loader
|