@remix-run/router 0.0.0-experimental-e192105b → 0.0.0-experimental-178bc9ee

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
@@ -12,6 +12,13 @@ export interface Router {
12
12
  * Return the basename for the router
13
13
  */
14
14
  get basename(): RouterInit["basename"];
15
+ /**
16
+ * @internal
17
+ * PRIVATE - DO NOT USE
18
+ *
19
+ * Return the future config for the router
20
+ */
21
+ get future(): FutureConfig;
15
22
  /**
16
23
  * @internal
17
24
  * PRIVATE - DO NOT USE
@@ -248,6 +255,7 @@ export type HydrationState = Partial<Pick<RouterState, "loaderData" | "actionDat
248
255
  export interface FutureConfig {
249
256
  v7_fetcherPersist: boolean;
250
257
  v7_normalizeFormMethod: boolean;
258
+ v7_partialHydration: boolean;
251
259
  v7_prependBasename: boolean;
252
260
  }
253
261
  /**
@@ -306,7 +314,7 @@ export interface RouterSubscriber {
306
314
  (state: RouterState, opts: {
307
315
  deletedFetchers: string[];
308
316
  unstable_viewTransitionOpts?: ViewTransitionOpts;
309
- unstable_flushSync?: boolean;
317
+ unstable_flushSync: boolean;
310
318
  }): void;
311
319
  }
312
320
  /**
package/dist/router.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v0.0.0-experimental-e192105b
2
+ * @remix-run/router v0.0.0-experimental-178bc9ee
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -938,6 +938,12 @@ function getInvalidPathError(char, field, dest, path) {
938
938
  function getPathContributingMatches(matches) {
939
939
  return matches.filter((match, index) => index === 0 || match.route.path && match.route.path.length > 0);
940
940
  }
941
+ // Return the array of pathnames for the current route matches - used to
942
+ // generate the routePathnames input for resolveTo()
943
+ function getResolveToMatches(matches) {
944
+ // Use the full pathname for the leaf match so we include splat values for "." links
945
+ return getPathContributingMatches(matches).map((match, idx) => idx === matches.length - 1 ? match.pathname : match.pathnameBase);
946
+ }
941
947
  /**
942
948
  * @private
943
949
  */
@@ -966,15 +972,28 @@ function resolveTo(toArg, routePathnames, locationPathname, isPathRelative) {
966
972
  // `to` values that do not provide a pathname. `to` can simply be a search or
967
973
  // hash string, in which case we should assume that the navigation is relative
968
974
  // to the current location's pathname and *not* the route pathname.
969
- if (isPathRelative || toPathname == null) {
975
+ if (toPathname == null) {
970
976
  from = locationPathname;
977
+ } else if (isPathRelative) {
978
+ let fromSegments = routePathnames[routePathnames.length - 1].replace(/^\//, "").split("/");
979
+ if (toPathname.startsWith("..")) {
980
+ let toSegments = toPathname.split("/");
981
+ // With relative="path", each leading .. segment means "go up one URL segment"
982
+ while (toSegments[0] === "..") {
983
+ toSegments.shift();
984
+ fromSegments.pop();
985
+ }
986
+ to.pathname = toSegments.join("/");
987
+ }
988
+ from = "/" + fromSegments.join("/");
971
989
  } else {
972
990
  let routePathnameIndex = routePathnames.length - 1;
973
991
  if (toPathname.startsWith("..")) {
974
992
  let toSegments = toPathname.split("/");
975
- // Each leading .. segment means "go up one route" instead of "go up one
976
- // URL segment". This is a key difference from how <a href> works and a
977
- // major reason we call this a "to" value instead of a "href".
993
+ // With relative="route" (the default), each leading .. segment means
994
+ // "go up one route" instead of "go up one URL segment". This is a key
995
+ // difference from how <a href> works and a major reason we call this a
996
+ // "to" value instead of a "href".
978
997
  while (toSegments[0] === "..") {
979
998
  toSegments.shift();
980
999
  routePathnameIndex -= 1;
@@ -1317,6 +1336,7 @@ function createRouter(init) {
1317
1336
  let future = _extends({
1318
1337
  v7_fetcherPersist: false,
1319
1338
  v7_normalizeFormMethod: false,
1339
+ v7_partialHydration: false,
1320
1340
  v7_prependBasename: false
1321
1341
  }, init.future);
1322
1342
  // Cleanup function for history
@@ -1353,7 +1373,12 @@ function createRouter(init) {
1353
1373
  [route.id]: error
1354
1374
  };
1355
1375
  }
1356
- let initialized =
1376
+ // "Initialized" here really means "Can `RouterProvider` render my route tree?"
1377
+ // Prior to `route.Fallback`, we only had a root `fallbackElement` so we used
1378
+ // `state.initialized` to render that instead of `<DataRoutes>`. Now that we
1379
+ // support route level fallbacks we can always render and we'll just render
1380
+ // as deep as we have data for and detect the nearest ancestor Fallback
1381
+ let initialized = future.v7_partialHydration ||
1357
1382
  // All initialMatches need to be loaded before we're ready. If we have lazy
1358
1383
  // functions around still then we'll need to run them in initialize()
1359
1384
  !initialMatches.some(m => m.route.lazy) && (
@@ -1386,8 +1411,6 @@ function createRouter(init) {
1386
1411
  let pendingNavigationController;
1387
1412
  // Should the current navigation enable document.startViewTransition?
1388
1413
  let pendingViewTransitionEnabled = false;
1389
- // Should the current navigation use flushSync for state updates?
1390
- let pendingFlushSync = false;
1391
1414
  // Store applied view transitions so we can apply them on POP
1392
1415
  let appliedViewTransitions = new Map();
1393
1416
  // Cleanup function for persisting applied transitions to sessionStorage
@@ -1483,8 +1506,6 @@ function createRouter(init) {
1483
1506
  blockers.set(blockerKey, IDLE_BLOCKER);
1484
1507
  updateState({
1485
1508
  blockers
1486
- }, {
1487
- flushSync: false
1488
1509
  });
1489
1510
  }
1490
1511
  });
@@ -1505,8 +1526,10 @@ function createRouter(init) {
1505
1526
  // in the normal navigation flow. For SSR it's expected that lazy modules are
1506
1527
  // resolved prior to router creation since we can't go into a fallbackElement
1507
1528
  // UI for SSR'd apps
1508
- if (!state.initialized) {
1509
- startNavigation(Action.Pop, state.location);
1529
+ if (!state.initialized || future.v7_partialHydration && state.matches.some(m => m.route.loader && state.loaderData[m.route.id] === undefined)) {
1530
+ startNavigation(Action.Pop, state.location, {
1531
+ initialHydration: true
1532
+ });
1510
1533
  }
1511
1534
  return router;
1512
1535
  }
@@ -1530,6 +1553,9 @@ function createRouter(init) {
1530
1553
  }
1531
1554
  // Update our state and notify the calling context of the change
1532
1555
  function updateState(newState, opts) {
1556
+ if (opts === void 0) {
1557
+ opts = {};
1558
+ }
1533
1559
  state = _extends({}, state, newState);
1534
1560
  // Prep fetcher cleanup so we can tell the UI which fetcher data entries
1535
1561
  // can be removed
@@ -1555,7 +1581,7 @@ function createRouter(init) {
1555
1581
  [...subscribers].forEach(subscriber => subscriber(state, {
1556
1582
  deletedFetchers: deletedFetchersKeys,
1557
1583
  unstable_viewTransitionOpts: opts.viewTransitionOpts,
1558
- unstable_flushSync: opts.flushSync
1584
+ unstable_flushSync: opts.flushSync === true
1559
1585
  }));
1560
1586
  // Remove idle fetchers from state since we only care about in-flight fetchers.
1561
1587
  if (future.v7_fetcherPersist) {
@@ -1568,8 +1594,11 @@ function createRouter(init) {
1568
1594
  // - Location is a required param
1569
1595
  // - Navigation will always be set to IDLE_NAVIGATION
1570
1596
  // - Can pass any other state in newState
1571
- function completeNavigation(location, newState) {
1597
+ function completeNavigation(location, newState, _temp) {
1572
1598
  var _location$state, _location$state2;
1599
+ let {
1600
+ flushSync
1601
+ } = _temp === void 0 ? {} : _temp;
1573
1602
  // Deduce if we're in a loading/actionReload state:
1574
1603
  // - We have committed actionData in the store
1575
1604
  // - The current navigation was a mutation submission
@@ -1657,13 +1686,12 @@ function createRouter(init) {
1657
1686
  blockers
1658
1687
  }), {
1659
1688
  viewTransitionOpts,
1660
- flushSync: pendingFlushSync
1689
+ flushSync: flushSync === true
1661
1690
  });
1662
1691
  // Reset stateful navigation vars
1663
1692
  pendingAction = Action.Pop;
1664
1693
  pendingPreventScrollReset = false;
1665
1694
  pendingViewTransitionEnabled = false;
1666
- pendingFlushSync = false;
1667
1695
  isUninterruptedRevalidation = false;
1668
1696
  isRevalidationRequired = false;
1669
1697
  cancelledDeferredRoutes = [];
@@ -1728,8 +1756,6 @@ function createRouter(init) {
1728
1756
  blockers.set(blockerKey, IDLE_BLOCKER);
1729
1757
  updateState({
1730
1758
  blockers
1731
- }, {
1732
- flushSync
1733
1759
  });
1734
1760
  }
1735
1761
  });
@@ -1753,8 +1779,6 @@ function createRouter(init) {
1753
1779
  interruptActiveLoads();
1754
1780
  updateState({
1755
1781
  revalidation: "loading"
1756
- }, {
1757
- flushSync: false
1758
1782
  });
1759
1783
  // If we're currently submitting an action, we don't need to start a new
1760
1784
  // navigation, we'll just let the follow up loader execution call all loaders
@@ -1793,10 +1817,10 @@ function createRouter(init) {
1793
1817
  saveScrollPosition(state.location, state.matches);
1794
1818
  pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
1795
1819
  pendingViewTransitionEnabled = (opts && opts.enableViewTransition) === true;
1796
- pendingFlushSync = (opts && opts.flushSync) === true;
1797
1820
  let routesToUse = inFlightDataRoutes || dataRoutes;
1798
1821
  let loadingNavigation = opts && opts.overrideNavigation;
1799
1822
  let matches = matchRoutes(routesToUse, location, basename);
1823
+ let flushSync = (opts && opts.flushSync) === true;
1800
1824
  // Short circuit with a 404 on the root error boundary if we match nothing
1801
1825
  if (!matches) {
1802
1826
  let error = getInternalRouterError(404, {
@@ -1814,6 +1838,8 @@ function createRouter(init) {
1814
1838
  errors: {
1815
1839
  [route.id]: error
1816
1840
  }
1841
+ }, {
1842
+ flushSync
1817
1843
  });
1818
1844
  return;
1819
1845
  }
@@ -1826,12 +1852,14 @@ function createRouter(init) {
1826
1852
  if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
1827
1853
  completeNavigation(location, {
1828
1854
  matches
1855
+ }, {
1856
+ flushSync
1829
1857
  });
1830
1858
  return;
1831
1859
  }
1832
1860
  // Create a controller/Request for this navigation
1833
1861
  pendingNavigationController = new AbortController();
1834
- let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);
1862
+ let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.initialHydration === true, opts && opts.submission);
1835
1863
  let pendingActionData;
1836
1864
  let pendingError;
1837
1865
  if (opts && opts.pendingError) {
@@ -1845,7 +1873,8 @@ function createRouter(init) {
1845
1873
  } else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {
1846
1874
  // Call action if we received an action submission
1847
1875
  let actionOutput = await handleAction(request, location, opts.submission, matches, {
1848
- replace: opts.replace
1876
+ replace: opts.replace,
1877
+ flushSync
1849
1878
  });
1850
1879
  if (actionOutput.shortCircuited) {
1851
1880
  return;
@@ -1853,6 +1882,7 @@ function createRouter(init) {
1853
1882
  pendingActionData = actionOutput.pendingActionData;
1854
1883
  pendingError = actionOutput.pendingActionError;
1855
1884
  loadingNavigation = getLoadingNavigation(location, opts.submission);
1885
+ flushSync = false;
1856
1886
  // Create a GET request for the loaders
1857
1887
  request = new Request(request.url, {
1858
1888
  signal: request.signal
@@ -1863,7 +1893,7 @@ function createRouter(init) {
1863
1893
  shortCircuited,
1864
1894
  loaderData,
1865
1895
  errors
1866
- } = await handleLoaders(request, location, matches, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, pendingActionData, pendingError);
1896
+ } = await handleLoaders(request, location, matches, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, opts && opts.initialHydration === true, flushSync, pendingActionData, pendingError);
1867
1897
  if (shortCircuited) {
1868
1898
  return;
1869
1899
  }
@@ -1892,7 +1922,7 @@ function createRouter(init) {
1892
1922
  updateState({
1893
1923
  navigation
1894
1924
  }, {
1895
- flushSync: pendingFlushSync
1925
+ flushSync: opts.flushSync === true
1896
1926
  });
1897
1927
  // Call our action and get the result
1898
1928
  let result;
@@ -1964,14 +1994,14 @@ function createRouter(init) {
1964
1994
  }
1965
1995
  // Call all applicable loaders for the given matches, handling redirects,
1966
1996
  // errors, etc.
1967
- async function handleLoaders(request, location, matches, overrideNavigation, submission, fetcherSubmission, replace, pendingActionData, pendingError) {
1997
+ async function handleLoaders(request, location, matches, overrideNavigation, submission, fetcherSubmission, replace, initialHydration, flushSync, pendingActionData, pendingError) {
1968
1998
  // Figure out the right navigation we want to use for data loading
1969
1999
  let loadingNavigation = overrideNavigation || getLoadingNavigation(location, submission);
1970
2000
  // If this was a redirect from an action we don't have a "submission" but
1971
2001
  // we have it on the loading navigation so use that if available
1972
2002
  let activeSubmission = submission || fetcherSubmission || getSubmissionFromNavigation(loadingNavigation);
1973
2003
  let routesToUse = inFlightDataRoutes || dataRoutes;
1974
- let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError);
2004
+ let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, future.v7_partialHydration && initialHydration === true, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError);
1975
2005
  // Cancel pending deferreds for no-longer-matched routes or routes we're
1976
2006
  // about to reload. Note that if this is an action reload we would have
1977
2007
  // already cancelled all pending deferreds so this would be a no-op
@@ -1989,7 +2019,9 @@ function createRouter(init) {
1989
2019
  actionData: pendingActionData
1990
2020
  } : {}, updatedFetchers ? {
1991
2021
  fetchers: new Map(state.fetchers)
1992
- } : {}));
2022
+ } : {}), {
2023
+ flushSync
2024
+ });
1993
2025
  return {
1994
2026
  shortCircuited: true
1995
2027
  };
@@ -2014,7 +2046,7 @@ function createRouter(init) {
2014
2046
  } : {}, revalidatingFetchers.length > 0 ? {
2015
2047
  fetchers: new Map(state.fetchers)
2016
2048
  } : {}), {
2017
- flushSync: pendingFlushSync
2049
+ flushSync
2018
2050
  });
2019
2051
  }
2020
2052
  revalidatingFetchers.forEach(rf => {
@@ -2159,7 +2191,7 @@ function createRouter(init) {
2159
2191
  });
2160
2192
  // Call the action for the fetcher
2161
2193
  let abortController = new AbortController();
2162
- let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
2194
+ let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, false, submission);
2163
2195
  fetchControllers.set(key, abortController);
2164
2196
  let originatingLoadId = incrementingLoadId;
2165
2197
  let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, mapRouteProperties, basename);
@@ -2172,9 +2204,7 @@ function createRouter(init) {
2172
2204
  return;
2173
2205
  }
2174
2206
  if (deletedFetchers.has(key)) {
2175
- updateFetcherState(key, getDoneFetcher(undefined), {
2176
- flushSync
2177
- });
2207
+ updateFetcherState(key, getDoneFetcher(undefined));
2178
2208
  return;
2179
2209
  }
2180
2210
  if (isRedirectResult(actionResult)) {
@@ -2184,15 +2214,11 @@ function createRouter(init) {
2184
2214
  // should take precedence over this redirect navigation. We already
2185
2215
  // set isRevalidationRequired so all loaders for the new route should
2186
2216
  // fire unless opted out via shouldRevalidate
2187
- updateFetcherState(key, getDoneFetcher(undefined), {
2188
- flushSync
2189
- });
2217
+ updateFetcherState(key, getDoneFetcher(undefined));
2190
2218
  return;
2191
2219
  } else {
2192
2220
  fetchRedirectIds.add(key);
2193
- updateFetcherState(key, getLoadingFetcher(submission), {
2194
- flushSync
2195
- });
2221
+ updateFetcherState(key, getLoadingFetcher(submission));
2196
2222
  return startRedirectNavigation(state, actionResult, {
2197
2223
  fetcherSubmission: submission
2198
2224
  });
@@ -2200,9 +2226,7 @@ function createRouter(init) {
2200
2226
  }
2201
2227
  // Process any non-redirect errors thrown
2202
2228
  if (isErrorResult(actionResult)) {
2203
- setFetcherError(key, routeId, actionResult.error, {
2204
- flushSync
2205
- });
2229
+ setFetcherError(key, routeId, actionResult.error);
2206
2230
  return;
2207
2231
  }
2208
2232
  if (isDeferredResult(actionResult)) {
@@ -2213,7 +2237,7 @@ function createRouter(init) {
2213
2237
  // Start the data load for current matches, or the next location if we're
2214
2238
  // in the middle of a navigation
2215
2239
  let nextLocation = state.navigation.location || state.location;
2216
- let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);
2240
+ let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal, false);
2217
2241
  let routesToUse = inFlightDataRoutes || dataRoutes;
2218
2242
  let matches = state.navigation.state !== "idle" ? matchRoutes(routesToUse, state.navigation.location, basename) : state.matches;
2219
2243
  invariant(matches, "Didn't find any matches after fetcher action");
@@ -2221,7 +2245,7 @@ function createRouter(init) {
2221
2245
  fetchReloadIds.set(key, loadId);
2222
2246
  let loadFetcher = getLoadingFetcher(submission, actionResult.data);
2223
2247
  state.fetchers.set(key, loadFetcher);
2224
- let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, {
2248
+ let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, false, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, {
2225
2249
  [match.route.id]: actionResult.data
2226
2250
  }, undefined // No need to send through errors since we short circuit above
2227
2251
  );
@@ -2242,8 +2266,6 @@ function createRouter(init) {
2242
2266
  });
2243
2267
  updateState({
2244
2268
  fetchers: new Map(state.fetchers)
2245
- }, {
2246
- flushSync
2247
2269
  });
2248
2270
  let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach(rf => abortFetcher(rf.key));
2249
2271
  abortController.signal.addEventListener("abort", abortPendingFetchRevalidations);
@@ -2302,8 +2324,6 @@ function createRouter(init) {
2302
2324
  errors,
2303
2325
  loaderData: mergeLoaderData(state.loaderData, loaderData, matches, errors),
2304
2326
  fetchers: new Map(state.fetchers)
2305
- }, {
2306
- flushSync
2307
2327
  });
2308
2328
  isRevalidationRequired = false;
2309
2329
  }
@@ -2316,7 +2336,7 @@ function createRouter(init) {
2316
2336
  });
2317
2337
  // Call the loader for this fetcher route match
2318
2338
  let abortController = new AbortController();
2319
- let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
2339
+ let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, false);
2320
2340
  fetchControllers.set(key, abortController);
2321
2341
  let originatingLoadId = incrementingLoadId;
2322
2342
  let result = await callLoaderOrAction("loader", fetchRequest, match, matches, manifest, mapRouteProperties, basename);
@@ -2336,9 +2356,7 @@ function createRouter(init) {
2336
2356
  return;
2337
2357
  }
2338
2358
  if (deletedFetchers.has(key)) {
2339
- updateFetcherState(key, getDoneFetcher(undefined), {
2340
- flushSync
2341
- });
2359
+ updateFetcherState(key, getDoneFetcher(undefined));
2342
2360
  return;
2343
2361
  }
2344
2362
  // If the loader threw a redirect Response, start a new REPLACE navigation
@@ -2346,9 +2364,7 @@ function createRouter(init) {
2346
2364
  if (pendingNavigationLoadId > originatingLoadId) {
2347
2365
  // A new navigation was kicked off after our loader started, so that
2348
2366
  // should take precedence over this redirect navigation
2349
- updateFetcherState(key, getDoneFetcher(undefined), {
2350
- flushSync
2351
- });
2367
+ updateFetcherState(key, getDoneFetcher(undefined));
2352
2368
  return;
2353
2369
  } else {
2354
2370
  fetchRedirectIds.add(key);
@@ -2358,16 +2374,12 @@ function createRouter(init) {
2358
2374
  }
2359
2375
  // Process any non-redirect errors thrown
2360
2376
  if (isErrorResult(result)) {
2361
- setFetcherError(key, routeId, result.error, {
2362
- flushSync
2363
- });
2377
+ setFetcherError(key, routeId, result.error);
2364
2378
  return;
2365
2379
  }
2366
2380
  invariant(!isDeferredResult(result), "Unhandled fetcher deferred data");
2367
2381
  // Put the fetcher back into an idle state
2368
- updateFetcherState(key, getDoneFetcher(result.data), {
2369
- flushSync
2370
- });
2382
+ updateFetcherState(key, getDoneFetcher(result.data));
2371
2383
  }
2372
2384
  /**
2373
2385
  * Utility function to handle redirects returned from an action or loader.
@@ -2388,12 +2400,12 @@ function createRouter(init) {
2388
2400
  * actually touch history until we've processed redirects, so we just use
2389
2401
  * the history action from the original navigation (PUSH or REPLACE).
2390
2402
  */
2391
- async function startRedirectNavigation(state, redirect, _temp) {
2403
+ async function startRedirectNavigation(state, redirect, _temp2) {
2392
2404
  let {
2393
2405
  submission,
2394
2406
  fetcherSubmission,
2395
2407
  replace
2396
- } = _temp === void 0 ? {} : _temp;
2408
+ } = _temp2 === void 0 ? {} : _temp2;
2397
2409
  if (redirect.revalidate) {
2398
2410
  isRevalidationRequired = true;
2399
2411
  }
@@ -2468,7 +2480,7 @@ function createRouter(init) {
2468
2480
  // accordingly
2469
2481
  let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, mapRouteProperties, basename)), ...fetchersToLoad.map(f => {
2470
2482
  if (f.matches && f.match && f.controller) {
2471
- return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, f.controller.signal), f.match, f.matches, manifest, mapRouteProperties, basename);
2483
+ return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, f.controller.signal, false), f.match, f.matches, manifest, mapRouteProperties, basename);
2472
2484
  } else {
2473
2485
  let error = {
2474
2486
  type: ResultType.error,
@@ -2503,14 +2515,20 @@ function createRouter(init) {
2503
2515
  });
2504
2516
  }
2505
2517
  function updateFetcherState(key, fetcher, opts) {
2518
+ if (opts === void 0) {
2519
+ opts = {};
2520
+ }
2506
2521
  state.fetchers.set(key, fetcher);
2507
2522
  updateState({
2508
2523
  fetchers: new Map(state.fetchers)
2509
2524
  }, {
2510
- flushSync: opts.flushSync
2525
+ flushSync: (opts && opts.flushSync) === true
2511
2526
  });
2512
2527
  }
2513
2528
  function setFetcherError(key, routeId, error, opts) {
2529
+ if (opts === void 0) {
2530
+ opts = {};
2531
+ }
2514
2532
  let boundaryMatch = findNearestBoundary(state.matches, routeId);
2515
2533
  deleteFetcher(key);
2516
2534
  updateState({
@@ -2519,7 +2537,7 @@ function createRouter(init) {
2519
2537
  },
2520
2538
  fetchers: new Map(state.fetchers)
2521
2539
  }, {
2522
- flushSync: opts.flushSync
2540
+ flushSync: (opts && opts.flushSync) === true
2523
2541
  });
2524
2542
  }
2525
2543
  function getFetcher(key) {
@@ -2561,8 +2579,6 @@ function createRouter(init) {
2561
2579
  }
2562
2580
  updateState({
2563
2581
  fetchers: new Map(state.fetchers)
2564
- }, {
2565
- flushSync: false
2566
2582
  });
2567
2583
  }
2568
2584
  function abortFetcher(key) {
@@ -2630,8 +2646,6 @@ function createRouter(init) {
2630
2646
  blockers.set(key, newBlocker);
2631
2647
  updateState({
2632
2648
  blockers
2633
- }, {
2634
- flushSync: false
2635
2649
  });
2636
2650
  }
2637
2651
  function shouldBlockNavigation(_ref2) {
@@ -2695,8 +2709,6 @@ function createRouter(init) {
2695
2709
  if (y != null) {
2696
2710
  updateState({
2697
2711
  restoreScrollPosition: y
2698
- }, {
2699
- flushSync: false
2700
2712
  });
2701
2713
  }
2702
2714
  }
@@ -2737,6 +2749,9 @@ function createRouter(init) {
2737
2749
  get basename() {
2738
2750
  return basename;
2739
2751
  },
2752
+ get future() {
2753
+ return future;
2754
+ },
2740
2755
  get state() {
2741
2756
  return state;
2742
2757
  },
@@ -2810,10 +2825,10 @@ function createStaticHandler(routes, opts) {
2810
2825
  * propagate that out and return the raw Response so the HTTP server can
2811
2826
  * return it directly.
2812
2827
  */
2813
- async function query(request, _temp2) {
2828
+ async function query(request, _temp3) {
2814
2829
  let {
2815
2830
  requestContext
2816
- } = _temp2 === void 0 ? {} : _temp2;
2831
+ } = _temp3 === void 0 ? {} : _temp3;
2817
2832
  let url = new URL(request.url);
2818
2833
  let method = request.method;
2819
2834
  let location = createLocation("", createPath(url), null, "default");
@@ -2896,11 +2911,11 @@ function createStaticHandler(routes, opts) {
2896
2911
  * code. Examples here are 404 and 405 errors that occur prior to reaching
2897
2912
  * any user-defined loaders.
2898
2913
  */
2899
- async function queryRoute(request, _temp3) {
2914
+ async function queryRoute(request, _temp4) {
2900
2915
  let {
2901
2916
  routeId,
2902
2917
  requestContext
2903
- } = _temp3 === void 0 ? {} : _temp3;
2918
+ } = _temp4 === void 0 ? {} : _temp4;
2904
2919
  let url = new URL(request.url);
2905
2920
  let method = request.method;
2906
2921
  let location = createLocation("", createPath(url), null, "default");
@@ -3167,11 +3182,9 @@ function isSubmissionNavigation(opts) {
3167
3182
  function normalizeTo(location, matches, basename, prependBasename, to, fromRouteId, relative) {
3168
3183
  let contextualMatches;
3169
3184
  let activeRouteMatch;
3170
- if (fromRouteId != null && relative !== "path") {
3185
+ if (fromRouteId) {
3171
3186
  // Grab matches up to the calling route so our route-relative logic is
3172
- // relative to the correct source route. When using relative:path,
3173
- // fromRouteId is ignored since that is always relative to the current
3174
- // location path
3187
+ // relative to the correct source route
3175
3188
  contextualMatches = [];
3176
3189
  for (let match of matches) {
3177
3190
  contextualMatches.push(match);
@@ -3185,7 +3198,7 @@ function normalizeTo(location, matches, basename, prependBasename, to, fromRoute
3185
3198
  activeRouteMatch = matches[matches.length - 1];
3186
3199
  }
3187
3200
  // Resolve the relative path
3188
- let path = resolveTo(to ? to : ".", getPathContributingMatches(contextualMatches).map(m => m.pathnameBase), stripBasename(location.pathname, basename) || location.pathname, relative === "path");
3201
+ let path = resolveTo(to ? to : ".", getResolveToMatches(contextualMatches), stripBasename(location.pathname, basename) || location.pathname, relative === "path");
3189
3202
  // When `to` is not specified we inherit search/hash from the current
3190
3203
  // location, unlike when to="." and we just inherit the path.
3191
3204
  // See https://github.com/remix-run/remix/issues/927
@@ -3342,7 +3355,7 @@ function getLoaderMatchesUntilBoundary(matches, boundaryId) {
3342
3355
  }
3343
3356
  return boundaryMatches;
3344
3357
  }
3345
- function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError) {
3358
+ function getMatchesToLoad(history, state, matches, submission, location, isInitialLoad, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError) {
3346
3359
  let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined;
3347
3360
  let currentUrl = history.createURL(state.location);
3348
3361
  let nextUrl = history.createURL(location);
@@ -3350,6 +3363,11 @@ function getMatchesToLoad(history, state, matches, submission, location, isReval
3350
3363
  let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
3351
3364
  let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
3352
3365
  let navigationMatches = boundaryMatches.filter((match, index) => {
3366
+ if (isInitialLoad) {
3367
+ // On initial hydration we don't do any shouldRevalidate stuff - we just
3368
+ // call the loaders that don't have hydration data
3369
+ return match.route.loader && state.loaderData[match.route.id] === undefined;
3370
+ }
3353
3371
  if (match.route.lazy) {
3354
3372
  // We haven't loaded this route yet so we don't know if it's got a loader!
3355
3373
  return true;
@@ -3386,8 +3404,12 @@ function getMatchesToLoad(history, state, matches, submission, location, isReval
3386
3404
  // Pick fetcher.loads that need to be revalidated
3387
3405
  let revalidatingFetchers = [];
3388
3406
  fetchLoadMatches.forEach((f, key) => {
3389
- // Don't revalidate if fetcher won't be present in the subsequent render
3390
- if (!matches.some(m => m.route.id === f.routeId)) {
3407
+ // Don't revalidate:
3408
+ // - on initial load (shouldn't be any fetchers then anyway)
3409
+ // - if fetcher won't be present in the subsequent render
3410
+ // - no longer matches the URL (v7_fetcherPersist=false)
3411
+ // - was unmounted but persisted due to v7_fetcherPersist=true
3412
+ if (isInitialLoad || !matches.some(m => m.route.id === f.routeId) || deletedFetchers.has(key)) {
3391
3413
  return;
3392
3414
  }
3393
3415
  let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
@@ -3700,11 +3722,16 @@ async function callLoaderOrAction(type, request, match, matches, manifest, mapRo
3700
3722
  // Utility method for creating the Request instances for loaders/actions during
3701
3723
  // client-side navigations and fetches. During SSR we will always have a
3702
3724
  // Request instance from the static handler (query/queryRoute)
3703
- function createClientSideRequest(history, location, signal, submission) {
3725
+ function createClientSideRequest(history, location, signal, initial, submission) {
3704
3726
  let url = history.createURL(stripHashFromPath(location)).toString();
3705
3727
  let init = {
3706
3728
  signal
3707
3729
  };
3730
+ if (initial) {
3731
+ init.headers = {
3732
+ "X-Remix-Initial-Load": "yes"
3733
+ };
3734
+ }
3708
3735
  if (submission && isMutationMethod(submission.formMethod)) {
3709
3736
  let {
3710
3737
  formMethod,
@@ -3903,13 +3930,13 @@ function getShortCircuitMatches(routes) {
3903
3930
  route
3904
3931
  };
3905
3932
  }
3906
- function getInternalRouterError(status, _temp4) {
3933
+ function getInternalRouterError(status, _temp5) {
3907
3934
  let {
3908
3935
  pathname,
3909
3936
  routeId,
3910
3937
  method,
3911
3938
  type
3912
- } = _temp4 === void 0 ? {} : _temp4;
3939
+ } = _temp5 === void 0 ? {} : _temp5;
3913
3940
  let statusText = "Unknown Server Error";
3914
3941
  let errorMessage = "Unknown @remix-run/router error";
3915
3942
  if (status === 400) {
@@ -4237,5 +4264,5 @@ function persistAppliedTransitions(_window, transitions) {
4237
4264
  }
4238
4265
  //#endregion
4239
4266
 
4240
- export { AbortedDeferredError, Action, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, UNSAFE_DEFERRED_SYMBOL, DeferredData as UNSAFE_DeferredData, ErrorResponseImpl as UNSAFE_ErrorResponseImpl, convertRouteMatchToUiMatch as UNSAFE_convertRouteMatchToUiMatch, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, invariant as UNSAFE_invariant, warning as UNSAFE_warning, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, isDeferredData, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, redirectDocument, resolvePath, resolveTo, stripBasename };
4267
+ export { AbortedDeferredError, Action, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, UNSAFE_DEFERRED_SYMBOL, DeferredData as UNSAFE_DeferredData, ErrorResponseImpl as UNSAFE_ErrorResponseImpl, convertRouteMatchToUiMatch as UNSAFE_convertRouteMatchToUiMatch, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getResolveToMatches as UNSAFE_getResolveToMatches, invariant as UNSAFE_invariant, warning as UNSAFE_warning, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, isDeferredData, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, redirectDocument, resolvePath, resolveTo, stripBasename };
4241
4268
  //# sourceMappingURL=router.js.map