@remix-run/router 1.1.0 → 1.2.0

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/dist/router.d.ts CHANGED
@@ -338,6 +338,7 @@ declare type FetcherStates<TData = any> = {
338
338
  formEncType: undefined;
339
339
  formData: undefined;
340
340
  data: TData | undefined;
341
+ " _hasFetcherDoneAnything "?: boolean;
341
342
  };
342
343
  Loading: {
343
344
  state: "loading";
@@ -346,6 +347,7 @@ declare type FetcherStates<TData = any> = {
346
347
  formEncType: FormEncType | undefined;
347
348
  formData: FormData | undefined;
348
349
  data: TData | undefined;
350
+ " _hasFetcherDoneAnything "?: boolean;
349
351
  };
350
352
  Submitting: {
351
353
  state: "submitting";
@@ -354,6 +356,7 @@ declare type FetcherStates<TData = any> = {
354
356
  formEncType: FormEncType;
355
357
  formData: FormData;
356
358
  data: TData | undefined;
359
+ " _hasFetcherDoneAnything "?: boolean;
357
360
  };
358
361
  };
359
362
  export declare type Fetcher<TData = any> = FetcherStates<TData>[keyof FetcherStates<TData>];
@@ -363,7 +366,7 @@ export declare const IDLE_FETCHER: FetcherStates["Idle"];
363
366
  * Create a router and listen to history POP navigations
364
367
  */
365
368
  export declare function createRouter(init: RouterInit): Router;
366
- export declare function unstable_createStaticHandler(routes: AgnosticRouteObject[], opts?: {
369
+ export declare function createStaticHandler(routes: AgnosticRouteObject[], opts?: {
367
370
  basename?: string;
368
371
  }): StaticHandler;
369
372
  /**
package/dist/router.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v1.1.0
2
+ * @remix-run/router v1.2.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1367,8 +1367,10 @@ function createRouter(init) {
1367
1367
  // we don't get the saved positions from <ScrollRestoration /> until _after_
1368
1368
  // the initial render, we need to manually trigger a separate updateState to
1369
1369
  // send along the restoreScrollPosition
1370
+ // Set to true if we have `hydrationData` since we assume we were SSR'd and that
1371
+ // SSR did the initial scroll restoration.
1370
1372
 
1371
- let initialScrollRestored = false;
1373
+ let initialScrollRestored = init.hydrationData != null;
1372
1374
  let initialMatches = matchRoutes(dataRoutes, init.history.location, init.basename);
1373
1375
  let initialErrors = null;
1374
1376
 
@@ -1396,7 +1398,8 @@ function createRouter(init) {
1396
1398
  matches: initialMatches,
1397
1399
  initialized,
1398
1400
  navigation: IDLE_NAVIGATION,
1399
- restoreScrollPosition: null,
1401
+ // Don't restore on initial updateState() if we were SSR'd
1402
+ restoreScrollPosition: init.hydrationData != null ? false : null,
1400
1403
  preventScrollReset: false,
1401
1404
  revalidation: "idle",
1402
1405
  loaderData: init.hydrationData && init.hydrationData.loaderData || {},
@@ -1505,14 +1508,29 @@ function createRouter(init) {
1505
1508
  // location, indicating we redirected from the action (avoids false
1506
1509
  // positives for loading/submissionRedirect when actionData returned
1507
1510
  // on a prior submission)
1508
- let isActionReload = state.actionData != null && state.navigation.formMethod != null && state.navigation.state === "loading" && ((_state$navigation$for = state.navigation.formAction) == null ? void 0 : _state$navigation$for.split("?")[0]) === location.pathname; // Always preserve any existing loaderData from re-used routes
1509
-
1510
- let newLoaderData = newState.loaderData ? {
1511
- loaderData: mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [])
1512
- } : {};
1513
- updateState(_extends({}, isActionReload ? {} : {
1514
- actionData: null
1515
- }, newState, newLoaderData, {
1511
+ let isActionReload = state.actionData != null && state.navigation.formMethod != null && state.navigation.state === "loading" && ((_state$navigation$for = state.navigation.formAction) == null ? void 0 : _state$navigation$for.split("?")[0]) === location.pathname;
1512
+ let actionData;
1513
+
1514
+ if (newState.actionData) {
1515
+ if (Object.keys(newState.actionData).length > 0) {
1516
+ actionData = newState.actionData;
1517
+ } else {
1518
+ // Empty actionData -> clear prior actionData due to an action error
1519
+ actionData = null;
1520
+ }
1521
+ } else if (isActionReload) {
1522
+ // Keep the current data if we're wrapping up the action reload
1523
+ actionData = state.actionData;
1524
+ } else {
1525
+ // Clear actionData on any other completed navigations
1526
+ actionData = null;
1527
+ } // Always preserve any existing loaderData from re-used routes
1528
+
1529
+
1530
+ let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData;
1531
+ updateState(_extends({}, newState, {
1532
+ actionData,
1533
+ loaderData,
1516
1534
  historyAction: pendingAction,
1517
1535
  location,
1518
1536
  initialized: true,
@@ -1558,7 +1576,19 @@ function createRouter(init) {
1558
1576
  // without having to touch history
1559
1577
 
1560
1578
  location = _extends({}, location, init.history.encodeLocation(location));
1561
- let historyAction = (opts && opts.replace) === true || submission != null && isMutationMethod(submission.formMethod) ? Action.Replace : Action.Push;
1579
+ let userReplace = opts && opts.replace != null ? opts.replace : undefined;
1580
+ let historyAction = Action.Push;
1581
+
1582
+ if (userReplace === true) {
1583
+ historyAction = Action.Replace;
1584
+ } else if (userReplace === false) ; else if (submission != null && isMutationMethod(submission.formMethod) && submission.formAction === state.location.pathname + state.location.search) {
1585
+ // By default on submissions to the current location we REPLACE so that
1586
+ // users don't have to double-click the back button to get to the prior
1587
+ // location. If the user redirects to a different location from the
1588
+ // action/loader this will be ignored and the redirect will be a PUSH
1589
+ historyAction = Action.Replace;
1590
+ }
1591
+
1562
1592
  let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined;
1563
1593
  return await startNavigation(historyAction, location, {
1564
1594
  submission,
@@ -1702,11 +1732,14 @@ function createRouter(init) {
1702
1732
 
1703
1733
 
1704
1734
  pendingNavigationController = null;
1705
- completeNavigation(location, {
1706
- matches,
1735
+ completeNavigation(location, _extends({
1736
+ matches
1737
+ }, pendingActionData ? {
1738
+ actionData: pendingActionData
1739
+ } : {}, {
1707
1740
  loaderData,
1708
1741
  errors
1709
- });
1742
+ }));
1710
1743
  } // Call the action matched by the leaf route for this navigation and handle
1711
1744
  // redirects/errors
1712
1745
 
@@ -1746,7 +1779,18 @@ function createRouter(init) {
1746
1779
  }
1747
1780
 
1748
1781
  if (isRedirectResult(result)) {
1749
- await startRedirectNavigation(state, result, opts && opts.replace === true);
1782
+ let replace;
1783
+
1784
+ if (opts && opts.replace != null) {
1785
+ replace = opts.replace;
1786
+ } else {
1787
+ // If the user didn't explicity indicate replace behavior, replace if
1788
+ // we redirected to the exact same location we're currently at to avoid
1789
+ // double back-buttons
1790
+ replace = result.location === state.location.pathname + state.location.search;
1791
+ }
1792
+
1793
+ await startRedirectNavigation(state, result, replace);
1750
1794
  return {
1751
1795
  shortCircuited: true
1752
1796
  };
@@ -1765,6 +1809,8 @@ function createRouter(init) {
1765
1809
  }
1766
1810
 
1767
1811
  return {
1812
+ // Send back an empty object we can use to clear out any prior actionData
1813
+ pendingActionData: {},
1768
1814
  pendingActionError: {
1769
1815
  [boundaryMatch.route.id]: result.error
1770
1816
  }
@@ -1808,13 +1854,14 @@ function createRouter(init) {
1808
1854
  cancelActiveDeferreds(routeId => !(matches && matches.some(m => m.route.id === routeId)) || matchesToLoad && matchesToLoad.some(m => m.route.id === routeId)); // Short circuit if we have no loaders to run
1809
1855
 
1810
1856
  if (matchesToLoad.length === 0 && revalidatingFetchers.length === 0) {
1811
- completeNavigation(location, {
1857
+ completeNavigation(location, _extends({
1812
1858
  matches,
1813
- loaderData: mergeLoaderData(state.loaderData, {}, matches),
1859
+ loaderData: {},
1814
1860
  // Commit pending error if we're short circuiting
1815
- errors: pendingError || null,
1816
- actionData: pendingActionData || null
1817
- });
1861
+ errors: pendingError || null
1862
+ }, pendingActionData ? {
1863
+ actionData: pendingActionData
1864
+ } : {}));
1818
1865
  return {
1819
1866
  shortCircuited: true
1820
1867
  };
@@ -1834,14 +1881,19 @@ function createRouter(init) {
1834
1881
  formMethod: undefined,
1835
1882
  formAction: undefined,
1836
1883
  formEncType: undefined,
1837
- formData: undefined
1884
+ formData: undefined,
1885
+ " _hasFetcherDoneAnything ": true
1838
1886
  };
1839
1887
  state.fetchers.set(key, revalidatingFetcher);
1840
1888
  });
1889
+ let actionData = pendingActionData || state.actionData;
1841
1890
  updateState(_extends({
1842
- navigation: loadingNavigation,
1843
- actionData: pendingActionData || state.actionData || null
1844
- }, revalidatingFetchers.length > 0 ? {
1891
+ navigation: loadingNavigation
1892
+ }, actionData ? Object.keys(actionData).length === 0 ? {
1893
+ actionData: null
1894
+ } : {
1895
+ actionData
1896
+ } : {}, revalidatingFetchers.length > 0 ? {
1845
1897
  fetchers: new Map(state.fetchers)
1846
1898
  } : {}));
1847
1899
  }
@@ -1965,7 +2017,8 @@ function createRouter(init) {
1965
2017
  let fetcher = _extends({
1966
2018
  state: "submitting"
1967
2019
  }, submission, {
1968
- data: existingFetcher && existingFetcher.data
2020
+ data: existingFetcher && existingFetcher.data,
2021
+ " _hasFetcherDoneAnything ": true
1969
2022
  });
1970
2023
 
1971
2024
  state.fetchers.set(key, fetcher);
@@ -1995,14 +2048,15 @@ function createRouter(init) {
1995
2048
  let loadingFetcher = _extends({
1996
2049
  state: "loading"
1997
2050
  }, submission, {
1998
- data: undefined
2051
+ data: undefined,
2052
+ " _hasFetcherDoneAnything ": true
1999
2053
  });
2000
2054
 
2001
2055
  state.fetchers.set(key, loadingFetcher);
2002
2056
  updateState({
2003
2057
  fetchers: new Map(state.fetchers)
2004
2058
  });
2005
- return startRedirectNavigation(state, actionResult);
2059
+ return startRedirectNavigation(state, actionResult, false, true);
2006
2060
  } // Process any non-redirect errors thrown
2007
2061
 
2008
2062
 
@@ -2027,7 +2081,9 @@ function createRouter(init) {
2027
2081
  let loadFetcher = _extends({
2028
2082
  state: "loading",
2029
2083
  data: actionResult.data
2030
- }, submission);
2084
+ }, submission, {
2085
+ " _hasFetcherDoneAnything ": true
2086
+ });
2031
2087
 
2032
2088
  state.fetchers.set(key, loadFetcher);
2033
2089
  let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, {
@@ -2049,7 +2105,8 @@ function createRouter(init) {
2049
2105
  formMethod: undefined,
2050
2106
  formAction: undefined,
2051
2107
  formEncType: undefined,
2052
- formData: undefined
2108
+ formData: undefined,
2109
+ " _hasFetcherDoneAnything ": true
2053
2110
  };
2054
2111
  state.fetchers.set(staleKey, revalidatingFetcher);
2055
2112
  fetchControllers.set(staleKey, abortController);
@@ -2090,7 +2147,8 @@ function createRouter(init) {
2090
2147
  formMethod: undefined,
2091
2148
  formAction: undefined,
2092
2149
  formEncType: undefined,
2093
- formData: undefined
2150
+ formData: undefined,
2151
+ " _hasFetcherDoneAnything ": true
2094
2152
  };
2095
2153
  state.fetchers.set(key, doneFetcher);
2096
2154
  let didAbortFetchLoads = abortStaleFetchLoads(loadId); // If we are currently in a navigation loading state and this fetcher is
@@ -2112,7 +2170,7 @@ function createRouter(init) {
2112
2170
  // manually merge here since we aren't going through completeNavigation
2113
2171
  updateState(_extends({
2114
2172
  errors,
2115
- loaderData: mergeLoaderData(state.loaderData, loaderData, matches)
2173
+ loaderData: mergeLoaderData(state.loaderData, loaderData, matches, errors)
2116
2174
  }, didAbortFetchLoads ? {
2117
2175
  fetchers: new Map(state.fetchers)
2118
2176
  } : {}));
@@ -2131,7 +2189,8 @@ function createRouter(init) {
2131
2189
  formEncType: undefined,
2132
2190
  formData: undefined
2133
2191
  }, submission, {
2134
- data: existingFetcher && existingFetcher.data
2192
+ data: existingFetcher && existingFetcher.data,
2193
+ " _hasFetcherDoneAnything ": true
2135
2194
  });
2136
2195
 
2137
2196
  state.fetchers.set(key, loadingFetcher);
@@ -2191,7 +2250,8 @@ function createRouter(init) {
2191
2250
  formMethod: undefined,
2192
2251
  formAction: undefined,
2193
2252
  formEncType: undefined,
2194
- formData: undefined
2253
+ formData: undefined,
2254
+ " _hasFetcherDoneAnything ": true
2195
2255
  };
2196
2256
  state.fetchers.set(key, doneFetcher);
2197
2257
  updateState({
@@ -2219,14 +2279,19 @@ function createRouter(init) {
2219
2279
  */
2220
2280
 
2221
2281
 
2222
- async function startRedirectNavigation(state, redirect, replace) {
2282
+ async function startRedirectNavigation(state, redirect, replace, isFetchActionRedirect) {
2223
2283
  var _window;
2224
2284
 
2225
2285
  if (redirect.revalidate) {
2226
2286
  isRevalidationRequired = true;
2227
2287
  }
2228
2288
 
2229
- let redirectLocation = createLocation(state.location, redirect.location);
2289
+ let redirectLocation = createLocation(state.location, redirect.location, // TODO: This can be removed once we get rid of useTransition in Remix v2
2290
+ _extends({
2291
+ _isRedirect: true
2292
+ }, isFetchActionRedirect ? {
2293
+ _isFetchActionRedirect: true
2294
+ } : {}));
2230
2295
  invariant(redirectLocation, "Expected a location on the redirect navigation"); // Check if this an external redirect that goes to a new origin
2231
2296
 
2232
2297
  if (typeof ((_window = window) == null ? void 0 : _window.location) !== "undefined") {
@@ -2352,7 +2417,8 @@ function createRouter(init) {
2352
2417
  formMethod: undefined,
2353
2418
  formAction: undefined,
2354
2419
  formEncType: undefined,
2355
- formData: undefined
2420
+ formData: undefined,
2421
+ " _hasFetcherDoneAnything ": true
2356
2422
  };
2357
2423
  state.fetchers.set(key, doneFetcher);
2358
2424
  }
@@ -2495,8 +2561,8 @@ function createRouter(init) {
2495
2561
  //#region createStaticHandler
2496
2562
  ////////////////////////////////////////////////////////////////////////////////
2497
2563
 
2498
- function unstable_createStaticHandler(routes, opts) {
2499
- invariant(routes.length > 0, "You must provide a non-empty routes array to unstable_createStaticHandler");
2564
+ function createStaticHandler(routes, opts) {
2565
+ invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
2500
2566
  let dataRoutes = convertRoutesToDataRoutes(routes);
2501
2567
  let basename = (opts ? opts.basename : null) || "/";
2502
2568
  /**
@@ -2818,7 +2884,10 @@ function unstable_createStaticHandler(routes, opts) {
2818
2884
  if (matchesToLoad.length === 0) {
2819
2885
  return {
2820
2886
  matches,
2821
- loaderData: {},
2887
+ // Add a null for all matched routes for proper revalidation on the client
2888
+ loaderData: matches.reduce((acc, m) => Object.assign(acc, {
2889
+ [m.route.id]: null
2890
+ }), {}),
2822
2891
  errors: pendingActionError || null,
2823
2892
  statusCode: 200,
2824
2893
  loaderHeaders: {}
@@ -2830,17 +2899,25 @@ function unstable_createStaticHandler(routes, opts) {
2830
2899
  if (request.signal.aborted) {
2831
2900
  let method = isRouteRequest ? "queryRoute" : "query";
2832
2901
  throw new Error(method + "() call aborted");
2833
- } // Can't do anything with these without the Remix side of things, so just
2834
- // cancel them for now
2902
+ }
2835
2903
 
2904
+ let executedLoaders = new Set();
2905
+ results.forEach((result, i) => {
2906
+ executedLoaders.add(matchesToLoad[i].route.id); // Can't do anything with these without the Remix side of things, so just
2907
+ // cancel them for now
2836
2908
 
2837
- results.forEach(result => {
2838
2909
  if (isDeferredResult(result)) {
2839
2910
  result.deferredData.cancel();
2840
2911
  }
2841
2912
  }); // Process and commit output from loaders
2842
2913
 
2843
- let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError);
2914
+ let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError); // Add a null for any non-loader matches for proper revalidation on the client
2915
+
2916
+ matches.forEach(match => {
2917
+ if (!executedLoaders.has(match.route.id)) {
2918
+ context.loaderData[match.route.id] = null;
2919
+ }
2920
+ });
2844
2921
  return _extends({}, context, {
2845
2922
  matches
2846
2923
  });
@@ -2962,7 +3039,7 @@ function getLoaderMatchesUntilBoundary(matches, boundaryId) {
2962
3039
  }
2963
3040
 
2964
3041
  function getMatchesToLoad(state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
2965
- let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : null; // Pick navigation matches that are net-new or qualify for revalidation
3042
+ let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined; // Pick navigation matches that are net-new or qualify for revalidation
2966
3043
 
2967
3044
  let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
2968
3045
  let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
@@ -3132,9 +3209,10 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
3132
3209
  }
3133
3210
 
3134
3211
  let data;
3135
- let contentType = result.headers.get("Content-Type");
3212
+ let contentType = result.headers.get("Content-Type"); // Check between word boundaries instead of startsWith() due to the last
3213
+ // paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
3136
3214
 
3137
- if (contentType && contentType.startsWith("application/json")) {
3215
+ if (contentType && /\bapplication\/json\b/.test(contentType)) {
3138
3216
  data = await result.json();
3139
3217
  } else {
3140
3218
  data = await result.text();
@@ -3239,9 +3317,11 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
3239
3317
 
3240
3318
  if (errors[boundaryMatch.route.id] == null) {
3241
3319
  errors[boundaryMatch.route.id] = error;
3242
- } // Once we find our first (highest) error, we set the status code and
3243
- // prevent deeper status codes from overriding
3320
+ } // Clear our any prior loaderData for the throwing route
3321
+
3244
3322
 
3323
+ loaderData[id] = undefined; // Once we find our first (highest) error, we set the status code and
3324
+ // prevent deeper status codes from overriding
3245
3325
 
3246
3326
  if (!foundError) {
3247
3327
  foundError = true;
@@ -3267,10 +3347,12 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
3267
3347
  }
3268
3348
  }
3269
3349
  }); // If we didn't consume the pending action error (i.e., all loaders
3270
- // resolved), then consume it here
3350
+ // resolved), then consume it here. Also clear out any loaderData for the
3351
+ // throwing route
3271
3352
 
3272
3353
  if (pendingError) {
3273
3354
  errors = pendingError;
3355
+ loaderData[Object.keys(pendingError)[0]] = undefined;
3274
3356
  }
3275
3357
 
3276
3358
  return {
@@ -3317,7 +3399,8 @@ function processLoaderData(state, matches, matchesToLoad, results, pendingError,
3317
3399
  formMethod: undefined,
3318
3400
  formAction: undefined,
3319
3401
  formEncType: undefined,
3320
- formData: undefined
3402
+ formData: undefined,
3403
+ " _hasFetcherDoneAnything ": true
3321
3404
  };
3322
3405
  state.fetchers.set(key, doneFetcher);
3323
3406
  }
@@ -3329,16 +3412,26 @@ function processLoaderData(state, matches, matchesToLoad, results, pendingError,
3329
3412
  };
3330
3413
  }
3331
3414
 
3332
- function mergeLoaderData(loaderData, newLoaderData, matches) {
3415
+ function mergeLoaderData(loaderData, newLoaderData, matches, errors) {
3333
3416
  let mergedLoaderData = _extends({}, newLoaderData);
3334
3417
 
3335
- matches.forEach(match => {
3418
+ for (let match of matches) {
3336
3419
  let id = match.route.id;
3337
3420
 
3338
- if (newLoaderData[id] === undefined && loaderData[id] !== undefined) {
3421
+ if (newLoaderData.hasOwnProperty(id)) {
3422
+ if (newLoaderData[id] !== undefined) {
3423
+ mergedLoaderData[id] = newLoaderData[id];
3424
+ }
3425
+ } else if (loaderData[id] !== undefined) {
3339
3426
  mergedLoaderData[id] = loaderData[id];
3340
3427
  }
3341
- });
3428
+
3429
+ if (errors && errors.hasOwnProperty(id)) {
3430
+ // Don't keep any loader data below the boundary
3431
+ break;
3432
+ }
3433
+ }
3434
+
3342
3435
  return mergedLoaderData;
3343
3436
  } // Find the nearest error boundary, looking upwards from the leaf route (or the
3344
3437
  // route specified by routeId) for the closest ancestor error boundary,
@@ -3549,5 +3642,5 @@ function getTargetMatch(matches, location) {
3549
3642
  return pathMatches[pathMatches.length - 1];
3550
3643
  } //#endregion
3551
3644
 
3552
- export { AbortedDeferredError, Action, ErrorResponse, IDLE_FETCHER, IDLE_NAVIGATION, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, defer, generatePath, getStaticContextFromError, getToPathname, invariant, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, resolvePath, resolveTo, stripBasename, unstable_createStaticHandler, warning };
3645
+ export { AbortedDeferredError, Action, ErrorResponse, IDLE_FETCHER, IDLE_NAVIGATION, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, invariant, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, resolvePath, resolveTo, stripBasename, warning };
3553
3646
  //# sourceMappingURL=router.js.map