@remix-run/router 1.7.1 → 1.7.2-pre.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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v1.7.1
2
+ * @remix-run/router v1.7.2-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1281,7 +1281,7 @@
1281
1281
 
1282
1282
  // We store a little wrapper promise that will be extended with
1283
1283
  // _data/_error props upon resolve/reject
1284
- let promise = Promise.race([value, this.abortPromise]).then(data => this.onSettle(promise, key, null, data), error => this.onSettle(promise, key, error));
1284
+ let promise = Promise.race([value, this.abortPromise]).then(data => this.onSettle(promise, key, undefined, data), error => this.onSettle(promise, key, error));
1285
1285
 
1286
1286
  // Register rejection listeners to avoid uncaught promise rejections on
1287
1287
  // errors or aborted deferred values
@@ -1304,7 +1304,18 @@
1304
1304
  // Nothing left to abort!
1305
1305
  this.unlistenAbortSignal();
1306
1306
  }
1307
- if (error) {
1307
+
1308
+ // If the promise was resolved/rejected with undefined, we'll throw an error as you
1309
+ // should always resolve with a value or null
1310
+ if (error === undefined && data === undefined) {
1311
+ let undefinedError = new Error("Deferred data for key \"" + key + "\" resolved/rejected with `undefined`, " + "you must resolve/reject with a value or `null`.");
1312
+ Object.defineProperty(promise, "_error", {
1313
+ get: () => undefinedError
1314
+ });
1315
+ this.emit(false, key);
1316
+ return Promise.reject(undefinedError);
1317
+ }
1318
+ if (data === undefined) {
1308
1319
  Object.defineProperty(promise, "_error", {
1309
1320
  get: () => error
1310
1321
  });
@@ -2266,7 +2277,14 @@
2266
2277
  // If any loaders returned a redirect Response, start a new REPLACE navigation
2267
2278
  let redirect = findRedirect(results);
2268
2279
  if (redirect) {
2269
- await startRedirectNavigation(state, redirect, {
2280
+ if (redirect.idx >= matchesToLoad.length) {
2281
+ // If this redirect came from a fetcher make sure we mark it in
2282
+ // fetchRedirectIds so it doesn't get revalidated on the next set of
2283
+ // loader executions
2284
+ let fetcherKey = revalidatingFetchers[redirect.idx - matchesToLoad.length].key;
2285
+ fetchRedirectIds.add(fetcherKey);
2286
+ }
2287
+ await startRedirectNavigation(state, redirect.result, {
2270
2288
  replace
2271
2289
  });
2272
2290
  return {
@@ -2372,6 +2390,7 @@
2372
2390
  let abortController = new AbortController();
2373
2391
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
2374
2392
  fetchControllers.set(key, abortController);
2393
+ let originatingLoadId = incrementingLoadId;
2375
2394
  let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, mapRouteProperties, basename);
2376
2395
  if (fetchRequest.signal.aborted) {
2377
2396
  // We can delete this so long as we weren't aborted by ou our own fetcher
@@ -2383,16 +2402,29 @@
2383
2402
  }
2384
2403
  if (isRedirectResult(actionResult)) {
2385
2404
  fetchControllers.delete(key);
2386
- fetchRedirectIds.add(key);
2387
- let loadingFetcher = getLoadingFetcher(submission);
2388
- state.fetchers.set(key, loadingFetcher);
2389
- updateState({
2390
- fetchers: new Map(state.fetchers)
2391
- });
2392
- return startRedirectNavigation(state, actionResult, {
2393
- submission,
2394
- isFetchActionRedirect: true
2395
- });
2405
+ if (pendingNavigationLoadId > originatingLoadId) {
2406
+ // A new navigation was kicked off after our action started, so that
2407
+ // should take precedence over this redirect navigation. We already
2408
+ // set isRevalidationRequired so all loaders for the new route should
2409
+ // fire unless opted out via shouldRevalidate
2410
+ let doneFetcher = getDoneFetcher(undefined);
2411
+ state.fetchers.set(key, doneFetcher);
2412
+ updateState({
2413
+ fetchers: new Map(state.fetchers)
2414
+ });
2415
+ return;
2416
+ } else {
2417
+ fetchRedirectIds.add(key);
2418
+ let loadingFetcher = getLoadingFetcher(submission);
2419
+ state.fetchers.set(key, loadingFetcher);
2420
+ updateState({
2421
+ fetchers: new Map(state.fetchers)
2422
+ });
2423
+ return startRedirectNavigation(state, actionResult, {
2424
+ submission,
2425
+ isFetchActionRedirect: true
2426
+ });
2427
+ }
2396
2428
  }
2397
2429
 
2398
2430
  // Process any non-redirect errors thrown
@@ -2456,7 +2488,14 @@
2456
2488
  revalidatingFetchers.forEach(r => fetchControllers.delete(r.key));
2457
2489
  let redirect = findRedirect(results);
2458
2490
  if (redirect) {
2459
- return startRedirectNavigation(state, redirect);
2491
+ if (redirect.idx >= matchesToLoad.length) {
2492
+ // If this redirect came from a fetcher make sure we mark it in
2493
+ // fetchRedirectIds so it doesn't get revalidated on the next set of
2494
+ // loader executions
2495
+ let fetcherKey = revalidatingFetchers[redirect.idx - matchesToLoad.length].key;
2496
+ fetchRedirectIds.add(fetcherKey);
2497
+ }
2498
+ return startRedirectNavigation(state, redirect.result);
2460
2499
  }
2461
2500
 
2462
2501
  // Process and commit output from loaders
@@ -2513,6 +2552,7 @@
2513
2552
  let abortController = new AbortController();
2514
2553
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
2515
2554
  fetchControllers.set(key, abortController);
2555
+ let originatingLoadId = incrementingLoadId;
2516
2556
  let result = await callLoaderOrAction("loader", fetchRequest, match, matches, manifest, mapRouteProperties, basename);
2517
2557
 
2518
2558
  // Deferred isn't supported for fetcher loads, await everything and treat it
@@ -2534,9 +2574,20 @@
2534
2574
 
2535
2575
  // If the loader threw a redirect Response, start a new REPLACE navigation
2536
2576
  if (isRedirectResult(result)) {
2537
- fetchRedirectIds.add(key);
2538
- await startRedirectNavigation(state, result);
2539
- return;
2577
+ if (pendingNavigationLoadId > originatingLoadId) {
2578
+ // A new navigation was kicked off after our loader started, so that
2579
+ // should take precedence over this redirect navigation
2580
+ let doneFetcher = getDoneFetcher(undefined);
2581
+ state.fetchers.set(key, doneFetcher);
2582
+ updateState({
2583
+ fetchers: new Map(state.fetchers)
2584
+ });
2585
+ return;
2586
+ } else {
2587
+ fetchRedirectIds.add(key);
2588
+ await startRedirectNavigation(state, result);
2589
+ return;
2590
+ }
2540
2591
  }
2541
2592
 
2542
2593
  // Process any non-redirect errors thrown
@@ -3573,7 +3624,9 @@
3573
3624
  let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
3574
3625
 
3575
3626
  // If the fetcher path no longer matches, push it in with null matches so
3576
- // we can trigger a 404 in callLoadersAndMaybeResolveData
3627
+ // we can trigger a 404 in callLoadersAndMaybeResolveData. Note this is
3628
+ // currently only a use-case for Remix HMR where the route tree can change
3629
+ // at runtime and remove a route previously loaded via a fetcher
3577
3630
  if (!fetcherMatches) {
3578
3631
  revalidatingFetchers.push({
3579
3632
  key,
@@ -3587,30 +3640,35 @@
3587
3640
  }
3588
3641
 
3589
3642
  // Revalidating fetchers are decoupled from the route matches since they
3590
- // load from a static href. They only set `defaultShouldRevalidate` on
3591
- // explicit revalidation due to submission, useRevalidator, or X-Remix-Revalidate
3592
- //
3593
- // They automatically revalidate without even calling shouldRevalidate if:
3594
- // - They were cancelled
3595
- // - They're in the middle of their first load and therefore this is still
3596
- // an initial load and not a revalidation
3597
- //
3598
- // If neither of those is true, then they _always_ check shouldRevalidate
3643
+ // load from a static href. They revalidate based on explicit revalidation
3644
+ // (submission, useRevalidator, or X-Remix-Revalidate)
3599
3645
  let fetcher = state.fetchers.get(key);
3600
- let isPerformingInitialLoad = fetcher && fetcher.state !== "idle" && fetcher.data === undefined &&
3601
- // If a fetcher.load redirected then it'll be "loading" without any data
3602
- // so ensure we're not processing the redirect from this fetcher
3603
- !fetchRedirectIds.has(key);
3604
3646
  let fetcherMatch = getTargetMatch(fetcherMatches, f.path);
3605
- let shouldRevalidate = cancelledFetcherLoads.includes(key) || isPerformingInitialLoad || shouldRevalidateLoader(fetcherMatch, _extends({
3606
- currentUrl,
3607
- currentParams: state.matches[state.matches.length - 1].params,
3608
- nextUrl,
3609
- nextParams: matches[matches.length - 1].params
3610
- }, submission, {
3611
- actionResult,
3612
- defaultShouldRevalidate: isRevalidationRequired
3613
- }));
3647
+ let shouldRevalidate = false;
3648
+ if (fetchRedirectIds.has(key)) {
3649
+ // Never trigger a revalidation of an actively redirecting fetcher
3650
+ shouldRevalidate = false;
3651
+ } else if (cancelledFetcherLoads.includes(key)) {
3652
+ // Always revalidate if the fetcher was cancelled
3653
+ shouldRevalidate = true;
3654
+ } else if (fetcher && fetcher.state !== "idle" && fetcher.data === undefined) {
3655
+ // If the fetcher hasn't ever completed loading yet, then this isn't a
3656
+ // revalidation, it would just be a brand new load if an explicit
3657
+ // revalidation is required
3658
+ shouldRevalidate = isRevalidationRequired;
3659
+ } else {
3660
+ // Otherwise fall back on any user-defined shouldRevalidate, defaulting
3661
+ // to explicit revalidations only
3662
+ shouldRevalidate = shouldRevalidateLoader(fetcherMatch, _extends({
3663
+ currentUrl,
3664
+ currentParams: state.matches[state.matches.length - 1].params,
3665
+ nextUrl,
3666
+ nextParams: matches[matches.length - 1].params
3667
+ }, submission, {
3668
+ actionResult,
3669
+ defaultShouldRevalidate: isRevalidationRequired
3670
+ }));
3671
+ }
3614
3672
  if (shouldRevalidate) {
3615
3673
  revalidatingFetchers.push({
3616
3674
  key,
@@ -4128,7 +4186,10 @@
4128
4186
  for (let i = results.length - 1; i >= 0; i--) {
4129
4187
  let result = results[i];
4130
4188
  if (isRedirectResult(result)) {
4131
- return result;
4189
+ return {
4190
+ result,
4191
+ idx: i
4192
+ };
4132
4193
  }
4133
4194
  }
4134
4195
  }