@remix-run/router 1.6.2 → 1.6.3

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/history.ts CHANGED
@@ -634,6 +634,13 @@ function getUrlBasedHistory(
634
634
  try {
635
635
  globalHistory.pushState(historyState, "", url);
636
636
  } catch (error) {
637
+ // If the exception is because `state` can't be serialized, let that throw
638
+ // outwards just like a replace call would so the dev knows the cause
639
+ // https://html.spec.whatwg.org/multipage/nav-history-apis.html#shared-history-push/replace-state-steps
640
+ // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
641
+ if (error instanceof DOMException && error.name === "DataCloneError") {
642
+ throw error;
643
+ }
637
644
  // They are going to lose state here, but there is no real
638
645
  // way to warn them about it since the page will refresh...
639
646
  window.location.assign(url);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remix-run/router",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "description": "Nested/Data-driven/Framework-agnostic Routing",
5
5
  "keywords": [
6
6
  "remix",
package/router.ts CHANGED
@@ -352,6 +352,7 @@ export interface RouterInit {
352
352
  mapRouteProperties?: MapRoutePropertiesFunction;
353
353
  future?: Partial<FutureConfig>;
354
354
  hydrationData?: HydrationState;
355
+ window?: Window;
355
356
  }
356
357
 
357
358
  /**
@@ -661,12 +662,6 @@ export const IDLE_BLOCKER: BlockerUnblocked = {
661
662
 
662
663
  const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
663
664
 
664
- const isBrowser =
665
- typeof window !== "undefined" &&
666
- typeof window.document !== "undefined" &&
667
- typeof window.document.createElement !== "undefined";
668
- const isServer = !isBrowser;
669
-
670
665
  const defaultMapRouteProperties: MapRoutePropertiesFunction = (route) => ({
671
666
  hasErrorBoundary: Boolean(route.hasErrorBoundary),
672
667
  });
@@ -681,6 +676,17 @@ const defaultMapRouteProperties: MapRoutePropertiesFunction = (route) => ({
681
676
  * Create a router and listen to history POP navigations
682
677
  */
683
678
  export function createRouter(init: RouterInit): Router {
679
+ const routerWindow = init.window
680
+ ? init.window
681
+ : typeof window !== "undefined"
682
+ ? window
683
+ : undefined;
684
+ const isBrowser =
685
+ typeof routerWindow !== "undefined" &&
686
+ typeof routerWindow.document !== "undefined" &&
687
+ typeof routerWindow.document.createElement !== "undefined";
688
+ const isServer = !isBrowser;
689
+
684
690
  invariant(
685
691
  init.routes.length > 0,
686
692
  "You must provide a non-empty routes array to createRouter"
@@ -1225,13 +1231,15 @@ export function createRouter(init: RouterInit): Router {
1225
1231
  return;
1226
1232
  }
1227
1233
 
1228
- // Short circuit if it's only a hash change and not a mutation submission.
1234
+ // Short circuit if it's only a hash change and not a revalidation or
1235
+ // mutation submission.
1236
+ //
1229
1237
  // Ignore on initial page loads because since the initial load will always
1230
- // be "same hash".
1231
- // For example, on /page#hash and submit a <Form method="post"> which will
1232
- // default to a navigation to /page
1238
+ // be "same hash". For example, on /page#hash and submit a <Form method="post">
1239
+ // which will default to a navigation to /page
1233
1240
  if (
1234
1241
  state.initialized &&
1242
+ !isRevalidationRequired &&
1235
1243
  isHashChangeOnly(state.location, location) &&
1236
1244
  !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))
1237
1245
  ) {
@@ -1775,7 +1783,6 @@ export function createRouter(init: RouterInit): Router {
1775
1783
  let nextLocation = state.navigation.location || state.location;
1776
1784
  let revalidationRequest = createClientSideRequest(
1777
1785
  init.history,
1778
-
1779
1786
  nextLocation,
1780
1787
  abortController.signal
1781
1788
  );
@@ -1886,16 +1893,20 @@ export function createRouter(init: RouterInit): Router {
1886
1893
  activeDeferreds
1887
1894
  );
1888
1895
 
1889
- let doneFetcher: FetcherStates["Idle"] = {
1890
- state: "idle",
1891
- data: actionResult.data,
1892
- formMethod: undefined,
1893
- formAction: undefined,
1894
- formEncType: undefined,
1895
- formData: undefined,
1896
- " _hasFetcherDoneAnything ": true,
1897
- };
1898
- state.fetchers.set(key, doneFetcher);
1896
+ // Since we let revalidations complete even if the submitting fetcher was
1897
+ // deleted, only put it back to idle if it hasn't been deleted
1898
+ if (state.fetchers.has(key)) {
1899
+ let doneFetcher: FetcherStates["Idle"] = {
1900
+ state: "idle",
1901
+ data: actionResult.data,
1902
+ formMethod: undefined,
1903
+ formAction: undefined,
1904
+ formEncType: undefined,
1905
+ formData: undefined,
1906
+ " _hasFetcherDoneAnything ": true,
1907
+ };
1908
+ state.fetchers.set(key, doneFetcher);
1909
+ }
1899
1910
 
1900
1911
  let didAbortFetchLoads = abortStaleFetchLoads(loadId);
1901
1912
 
@@ -1927,7 +1938,9 @@ export function createRouter(init: RouterInit): Router {
1927
1938
  matches,
1928
1939
  errors
1929
1940
  ),
1930
- ...(didAbortFetchLoads ? { fetchers: new Map(state.fetchers) } : {}),
1941
+ ...(didAbortFetchLoads || revalidatingFetchers.length > 0
1942
+ ? { fetchers: new Map(state.fetchers) }
1943
+ : {}),
1931
1944
  });
1932
1945
  isRevalidationRequired = false;
1933
1946
  }
@@ -2085,19 +2098,15 @@ export function createRouter(init: RouterInit): Router {
2085
2098
  "Expected a location on the redirect navigation"
2086
2099
  );
2087
2100
  // Check if this an absolute external redirect that goes to a new origin
2088
- if (
2089
- ABSOLUTE_URL_REGEX.test(redirect.location) &&
2090
- isBrowser &&
2091
- typeof window?.location !== "undefined"
2092
- ) {
2101
+ if (ABSOLUTE_URL_REGEX.test(redirect.location) && isBrowser) {
2093
2102
  let url = init.history.createURL(redirect.location);
2094
2103
  let isDifferentBasename = stripBasename(url.pathname, basename) == null;
2095
2104
 
2096
- if (window.location.origin !== url.origin || isDifferentBasename) {
2105
+ if (routerWindow.location.origin !== url.origin || isDifferentBasename) {
2097
2106
  if (replace) {
2098
- window.location.replace(redirect.location);
2107
+ routerWindow.location.replace(redirect.location);
2099
2108
  } else {
2100
- window.location.assign(redirect.location);
2109
+ routerWindow.location.assign(redirect.location);
2101
2110
  }
2102
2111
  return;
2103
2112
  }
@@ -2267,7 +2276,16 @@ export function createRouter(init: RouterInit): Router {
2267
2276
  }
2268
2277
 
2269
2278
  function deleteFetcher(key: string): void {
2270
- if (fetchControllers.has(key)) abortFetcher(key);
2279
+ let fetcher = state.fetchers.get(key);
2280
+ // Don't abort the controller if this is a deletion of a fetcher.submit()
2281
+ // in it's loading phase since - we don't want to abort the corresponding
2282
+ // revalidation and want them to complete and land
2283
+ if (
2284
+ fetchControllers.has(key) &&
2285
+ !(fetcher && fetcher.state === "loading" && fetchReloadIds.has(key))
2286
+ ) {
2287
+ abortFetcher(key);
2288
+ }
2271
2289
  fetchLoadMatches.delete(key);
2272
2290
  fetchReloadIds.delete(key);
2273
2291
  fetchRedirectIds.delete(key);