@remix-run/router 1.3.0-pre.3 → 1.3.1-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.3.0-pre.3
2
+ * @remix-run/router v1.3.1-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -451,10 +451,7 @@
451
451
  });
452
452
  }
453
453
  } else {
454
- warning$1(false, // TODO: Write up a doc that explains our blocking strategy in detail
455
- // and link to it here so people can understand better what is going on
456
- // and how to avoid it.
457
- "You are trying to block a POP navigation to a location that was not " + "created by @remix-run/router. The block will fail silently in " + "production, but in general you should do all navigation with the " + "router (instead of using window.history.pushState directly) " + "to avoid this situation.");
454
+ warning$1(false, "You are trying to perform a POP navigation to a location that was not " + "created by @remix-run/router. This will fail silently in production. " + "You should navigate via the router to avoid this situation (instead of " + "using window.history.pushState/window.location.hash).");
458
455
  }
459
456
  }
460
457
 
@@ -1252,6 +1249,12 @@
1252
1249
  [key]: this.trackPromise(key, value)
1253
1250
  });
1254
1251
  }, {});
1252
+
1253
+ if (this.done) {
1254
+ // All incoming values were resolved
1255
+ this.unlistenAbortSignal();
1256
+ }
1257
+
1255
1258
  this.init = responseInit;
1256
1259
  }
1257
1260
 
@@ -1439,11 +1442,11 @@
1439
1442
  }
1440
1443
  /**
1441
1444
  * Check if the given error is an ErrorResponse generated from a 4xx/5xx
1442
- * Response throw from an action/loader
1445
+ * Response thrown from an action/loader
1443
1446
  */
1444
1447
 
1445
- function isRouteErrorResponse(e) {
1446
- return e instanceof ErrorResponse;
1448
+ function isRouteErrorResponse(error) {
1449
+ return error != null && typeof error.status === "number" && typeof error.statusText === "string" && typeof error.internal === "boolean" && "data" in error;
1447
1450
  }
1448
1451
 
1449
1452
  //#region Types and Constants
@@ -1907,10 +1910,12 @@
1907
1910
  }
1908
1911
  });
1909
1912
  return;
1910
- } // Short circuit if it's only a hash change
1913
+ } // Short circuit if it's only a hash change and not a mutation submission
1914
+ // For example, on /page#hash and submit a <Form method="post"> which will
1915
+ // default to a navigation to /page
1911
1916
 
1912
1917
 
1913
- if (isHashChangeOnly(state.location, location)) {
1918
+ if (isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
1914
1919
  completeNavigation(location, {
1915
1920
  matches
1916
1921
  });
@@ -2124,9 +2129,8 @@
2124
2129
 
2125
2130
 
2126
2131
  if (!isUninterruptedRevalidation) {
2127
- revalidatingFetchers.forEach(_ref2 => {
2128
- let [key] = _ref2;
2129
- let fetcher = state.fetchers.get(key);
2132
+ revalidatingFetchers.forEach(rf => {
2133
+ let fetcher = state.fetchers.get(rf.key);
2130
2134
  let revalidatingFetcher = {
2131
2135
  state: "loading",
2132
2136
  data: fetcher && fetcher.data,
@@ -2136,7 +2140,7 @@
2136
2140
  formData: undefined,
2137
2141
  " _hasFetcherDoneAnything ": true
2138
2142
  };
2139
- state.fetchers.set(key, revalidatingFetcher);
2143
+ state.fetchers.set(rf.key, revalidatingFetcher);
2140
2144
  });
2141
2145
  let actionData = pendingActionData || state.actionData;
2142
2146
  updateState(_extends({
@@ -2151,10 +2155,7 @@
2151
2155
  }
2152
2156
 
2153
2157
  pendingNavigationLoadId = ++incrementingLoadId;
2154
- revalidatingFetchers.forEach(_ref3 => {
2155
- let [key] = _ref3;
2156
- return fetchControllers.set(key, pendingNavigationController);
2157
- });
2158
+ revalidatingFetchers.forEach(rf => fetchControllers.set(rf.key, pendingNavigationController));
2158
2159
  let {
2159
2160
  results,
2160
2161
  loaderResults,
@@ -2170,10 +2171,7 @@
2170
2171
  // reassigned to new controllers for the next navigation
2171
2172
 
2172
2173
 
2173
- revalidatingFetchers.forEach(_ref4 => {
2174
- let [key] = _ref4;
2175
- return fetchControllers.delete(key);
2176
- }); // If any loaders returned a redirect Response, start a new REPLACE navigation
2174
+ revalidatingFetchers.forEach(rf => fetchControllers.delete(rf.key)); // If any loaders returned a redirect Response, start a new REPLACE navigation
2177
2175
 
2178
2176
  let redirect = findRedirect(results);
2179
2177
 
@@ -2237,6 +2235,7 @@
2237
2235
  submission
2238
2236
  } = normalizeNavigateOptions(href, opts, true);
2239
2237
  let match = getTargetMatch(matches, path);
2238
+ pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
2240
2239
 
2241
2240
  if (submission && isMutationMethod(submission.formMethod)) {
2242
2241
  handleFetcherAction(key, routeId, path, match, matches, submission);
@@ -2245,7 +2244,12 @@
2245
2244
  // revalidations
2246
2245
 
2247
2246
 
2248
- fetchLoadMatches.set(key, [path, match, matches]);
2247
+ fetchLoadMatches.set(key, {
2248
+ routeId,
2249
+ path,
2250
+ match,
2251
+ matches
2252
+ });
2249
2253
  handleFetcherLoader(key, routeId, path, match, matches, submission);
2250
2254
  } // Call the action for the matched fetcher.submit(), and then handle redirects,
2251
2255
  // errors, and revalidation
@@ -2351,11 +2355,8 @@
2351
2355
  // current fetcher which we want to keep in it's current loading state which
2352
2356
  // contains it's action submission info + action data
2353
2357
 
2354
- revalidatingFetchers.filter(_ref5 => {
2355
- let [staleKey] = _ref5;
2356
- return staleKey !== key;
2357
- }).forEach(_ref6 => {
2358
- let [staleKey] = _ref6;
2358
+ revalidatingFetchers.filter(rf => rf.key !== key).forEach(rf => {
2359
+ let staleKey = rf.key;
2359
2360
  let existingFetcher = state.fetchers.get(staleKey);
2360
2361
  let revalidatingFetcher = {
2361
2362
  state: "loading",
@@ -2384,10 +2385,7 @@
2384
2385
 
2385
2386
  fetchReloadIds.delete(key);
2386
2387
  fetchControllers.delete(key);
2387
- revalidatingFetchers.forEach(_ref7 => {
2388
- let [staleKey] = _ref7;
2389
- return fetchControllers.delete(staleKey);
2390
- });
2388
+ revalidatingFetchers.forEach(r => fetchControllers.delete(r.key));
2391
2389
  let redirect = findRedirect(results);
2392
2390
 
2393
2391
  if (redirect) {
@@ -2627,16 +2625,10 @@
2627
2625
  // Call all navigation loaders and revalidating fetcher loaders in parallel,
2628
2626
  // then slice off the results into separate arrays so we can handle them
2629
2627
  // accordingly
2630
- let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(_ref8 => {
2631
- let [, href, match, fetchMatches] = _ref8;
2632
- return callLoaderOrAction("loader", createClientSideRequest(init.history, href, request.signal), match, fetchMatches, router.basename);
2633
- })]);
2628
+ let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(f => callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, router.basename))]);
2634
2629
  let loaderResults = results.slice(0, matchesToLoad.length);
2635
2630
  let fetcherResults = results.slice(matchesToLoad.length);
2636
- await Promise.all([resolveDeferredResults(currentMatches, matchesToLoad, loaderResults, request.signal, false, state.loaderData), resolveDeferredResults(currentMatches, fetchersToLoad.map(_ref9 => {
2637
- let [,, match] = _ref9;
2638
- return match;
2639
- }), fetcherResults, request.signal, true)]);
2631
+ await Promise.all([resolveDeferredResults(currentMatches, matchesToLoad, loaderResults, request.signal, false, state.loaderData), resolveDeferredResults(currentMatches, fetchersToLoad.map(f => f.match), fetcherResults, request.signal, true)]);
2640
2632
  return {
2641
2633
  results,
2642
2634
  loaderResults,
@@ -2775,12 +2767,12 @@
2775
2767
  });
2776
2768
  }
2777
2769
 
2778
- function shouldBlockNavigation(_ref10) {
2770
+ function shouldBlockNavigation(_ref2) {
2779
2771
  let {
2780
2772
  currentLocation,
2781
2773
  nextLocation,
2782
2774
  historyAction
2783
- } = _ref10;
2775
+ } = _ref2;
2784
2776
 
2785
2777
  if (activeBlocker == null) {
2786
2778
  return;
@@ -3374,24 +3366,15 @@
3374
3366
 
3375
3367
 
3376
3368
  let parsedPath = parsePath(path);
3369
+ let searchParams = convertFormDataToSearchParams(opts.formData); // Since fetcher GET submissions only run a single loader (as opposed to
3370
+ // navigation GET submissions which run all loaders), we need to preserve
3371
+ // any incoming ?index params
3377
3372
 
3378
- try {
3379
- let searchParams = convertFormDataToSearchParams(opts.formData); // Since fetcher GET submissions only run a single loader (as opposed to
3380
- // navigation GET submissions which run all loaders), we need to preserve
3381
- // any incoming ?index params
3382
-
3383
- if (isFetcher && parsedPath.search && hasNakedIndexQuery(parsedPath.search)) {
3384
- searchParams.append("index", "");
3385
- }
3386
-
3387
- parsedPath.search = "?" + searchParams;
3388
- } catch (e) {
3389
- return {
3390
- path,
3391
- error: getInternalRouterError(400)
3392
- };
3373
+ if (isFetcher && parsedPath.search && hasNakedIndexQuery(parsedPath.search)) {
3374
+ searchParams.append("index", "");
3393
3375
  }
3394
3376
 
3377
+ parsedPath.search = "?" + searchParams;
3395
3378
  return {
3396
3379
  path: createPath(parsedPath),
3397
3380
  submission
@@ -3415,25 +3398,73 @@
3415
3398
  }
3416
3399
 
3417
3400
  function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
3418
- let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined; // Pick navigation matches that are net-new or qualify for revalidation
3401
+ let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined;
3402
+ let currentUrl = history.createURL(state.location);
3403
+ let nextUrl = history.createURL(location);
3404
+ let defaultShouldRevalidate = // Forced revalidation due to submission, useRevalidate, or X-Remix-Revalidate
3405
+ isRevalidationRequired || // Clicked the same link, resubmitted a GET form
3406
+ currentUrl.toString() === nextUrl.toString() || // Search params affect all loaders
3407
+ currentUrl.search !== nextUrl.search; // Pick navigation matches that are net-new or qualify for revalidation
3419
3408
 
3420
3409
  let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
3421
3410
  let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
3422
- let navigationMatches = boundaryMatches.filter((match, index) => match.route.loader != null && (isNewLoader(state.loaderData, state.matches[index], match) || // If this route had a pending deferred cancelled it must be revalidated
3423
- cancelledDeferredRoutes.some(id => id === match.route.id) || shouldRevalidateLoader(history, state.location, state.matches[index], submission, location, match, isRevalidationRequired, actionResult))); // Pick fetcher.loads that need to be revalidated
3411
+ let navigationMatches = boundaryMatches.filter((match, index) => {
3412
+ if (match.route.loader == null) {
3413
+ return false;
3414
+ } // Always call the loader on new route instances and pending defer cancellations
3424
3415
 
3425
- let revalidatingFetchers = [];
3426
- fetchLoadMatches && fetchLoadMatches.forEach((_ref11, key) => {
3427
- let [href, match, fetchMatches] = _ref11;
3428
3416
 
3429
- // This fetcher was cancelled from a prior action submission - force reload
3430
- if (cancelledFetcherLoads.includes(key)) {
3431
- revalidatingFetchers.push([key, href, match, fetchMatches]);
3432
- } else if (isRevalidationRequired) {
3433
- let shouldRevalidate = shouldRevalidateLoader(history, href, match, submission, href, match, isRevalidationRequired, actionResult);
3417
+ if (isNewLoader(state.loaderData, state.matches[index], match) || cancelledDeferredRoutes.some(id => id === match.route.id)) {
3418
+ return true;
3419
+ } // This is the default implementation for when we revalidate. If the route
3420
+ // provides it's own implementation, then we give them full control but
3421
+ // provide this value so they can leverage it if needed after they check
3422
+ // their own specific use cases
3423
+
3424
+
3425
+ let currentRouteMatch = state.matches[index];
3426
+ let nextRouteMatch = match;
3427
+ return shouldRevalidateLoader(match, _extends({
3428
+ currentUrl,
3429
+ currentParams: currentRouteMatch.params,
3430
+ nextUrl,
3431
+ nextParams: nextRouteMatch.params
3432
+ }, submission, {
3433
+ actionResult,
3434
+ defaultShouldRevalidate: defaultShouldRevalidate || isNewRouteInstance(currentRouteMatch, nextRouteMatch)
3435
+ }));
3436
+ }); // Pick fetcher.loads that need to be revalidated
3437
+
3438
+ let revalidatingFetchers = [];
3439
+ fetchLoadMatches && fetchLoadMatches.forEach((f, key) => {
3440
+ if (!matches.some(m => m.route.id === f.routeId)) {
3441
+ // This fetcher is not going to be present in the subsequent render so
3442
+ // there's no need to revalidate it
3443
+ return;
3444
+ } else if (cancelledFetcherLoads.includes(key)) {
3445
+ // This fetcher was cancelled from a prior action submission - force reload
3446
+ revalidatingFetchers.push(_extends({
3447
+ key
3448
+ }, f));
3449
+ } else {
3450
+ // Revalidating fetchers are decoupled from the route matches since they
3451
+ // hit a static href, so they _always_ check shouldRevalidate and the
3452
+ // default is strictly if a revalidation is explicitly required (action
3453
+ // submissions, useRevalidator, X-Remix-Revalidate).
3454
+ let shouldRevalidate = shouldRevalidateLoader(f.match, _extends({
3455
+ currentUrl,
3456
+ currentParams: state.matches[state.matches.length - 1].params,
3457
+ nextUrl,
3458
+ nextParams: matches[matches.length - 1].params
3459
+ }, submission, {
3460
+ actionResult,
3461
+ defaultShouldRevalidate
3462
+ }));
3434
3463
 
3435
3464
  if (shouldRevalidate) {
3436
- revalidatingFetchers.push([key, href, match, fetchMatches]);
3465
+ revalidatingFetchers.push(_extends({
3466
+ key
3467
+ }, f));
3437
3468
  }
3438
3469
  }
3439
3470
  });
@@ -3456,43 +3487,20 @@
3456
3487
  return (// param change for this match, /users/123 -> /users/456
3457
3488
  currentMatch.pathname !== match.pathname || // splat param changed, which is not present in match.path
3458
3489
  // e.g. /files/images/avatar.jpg -> files/finances.xls
3459
- currentPath && currentPath.endsWith("*") && currentMatch.params["*"] !== match.params["*"]
3490
+ currentPath != null && currentPath.endsWith("*") && currentMatch.params["*"] !== match.params["*"]
3460
3491
  );
3461
3492
  }
3462
3493
 
3463
- function shouldRevalidateLoader(history, currentLocation, currentMatch, submission, location, match, isRevalidationRequired, actionResult) {
3464
- let currentUrl = history.createURL(currentLocation);
3465
- let currentParams = currentMatch.params;
3466
- let nextUrl = history.createURL(location);
3467
- let nextParams = match.params; // This is the default implementation as to when we revalidate. If the route
3468
- // provides it's own implementation, then we give them full control but
3469
- // provide this value so they can leverage it if needed after they check
3470
- // their own specific use cases
3471
- // Note that fetchers always provide the same current/next locations so the
3472
- // URL-based checks here don't apply to fetcher shouldRevalidate calls
3473
-
3474
- let defaultShouldRevalidate = isNewRouteInstance(currentMatch, match) || // Clicked the same link, resubmitted a GET form
3475
- currentUrl.toString() === nextUrl.toString() || // Search params affect all loaders
3476
- currentUrl.search !== nextUrl.search || // Forced revalidation due to submission, useRevalidate, or X-Remix-Revalidate
3477
- isRevalidationRequired;
3478
-
3479
- if (match.route.shouldRevalidate) {
3480
- let routeChoice = match.route.shouldRevalidate(_extends({
3481
- currentUrl,
3482
- currentParams,
3483
- nextUrl,
3484
- nextParams
3485
- }, submission, {
3486
- actionResult,
3487
- defaultShouldRevalidate
3488
- }));
3494
+ function shouldRevalidateLoader(loaderMatch, arg) {
3495
+ if (loaderMatch.route.shouldRevalidate) {
3496
+ let routeChoice = loaderMatch.route.shouldRevalidate(arg);
3489
3497
 
3490
3498
  if (typeof routeChoice === "boolean") {
3491
3499
  return routeChoice;
3492
3500
  }
3493
3501
  }
3494
3502
 
3495
- return defaultShouldRevalidate;
3503
+ return arg.defaultShouldRevalidate;
3496
3504
  }
3497
3505
 
3498
3506
  async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest, requestContext) {
@@ -3667,8 +3675,8 @@
3667
3675
  let searchParams = new URLSearchParams();
3668
3676
 
3669
3677
  for (let [key, value] of formData.entries()) {
3670
- invariant(typeof value === "string", 'File inputs are not supported with encType "application/x-www-form-urlencoded", ' + 'please use "multipart/form-data" instead.');
3671
- searchParams.append(key, value);
3678
+ // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#converting-an-entry-list-to-a-list-of-name-value-pairs
3679
+ searchParams.append(key, value instanceof File ? value.name : value);
3672
3680
  }
3673
3681
 
3674
3682
  return searchParams;
@@ -3759,7 +3767,10 @@
3759
3767
  } = processRouteLoaderData(matches, matchesToLoad, results, pendingError, activeDeferreds); // Process results from our revalidating fetchers
3760
3768
 
3761
3769
  for (let index = 0; index < revalidatingFetchers.length; index++) {
3762
- let [key,, match] = revalidatingFetchers[index];
3770
+ let {
3771
+ key,
3772
+ match
3773
+ } = revalidatingFetchers[index];
3763
3774
  invariant(fetcherResults !== undefined && fetcherResults[index] !== undefined, "Did not find corresponding fetcher result");
3764
3775
  let result = fetcherResults[index]; // Process fetcher non-redirect errors
3765
3776
 
@@ -3865,8 +3876,6 @@
3865
3876
  errorMessage = "You made a " + method + " request to \"" + pathname + "\" but " + ("did not provide a `loader` for route \"" + routeId + "\", ") + "so there is no way to handle the request.";
3866
3877
  } else if (type === "defer-action") {
3867
3878
  errorMessage = "defer() is not supported in actions";
3868
- } else {
3869
- errorMessage = "Cannot submit binary form data using GET";
3870
3879
  }
3871
3880
  } else if (status === 403) {
3872
3881
  statusText = "Forbidden";