@remix-run/router 0.0.0-experimental-bc2c864b → 0.0.0-experimental-acfea932

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # `@remix-run/router`
2
2
 
3
+ ## 1.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add a `createStaticHandler` `future.v7_throwAbortReason` flag to throw `request.signal.reason` (defaults to a `DOMException`) when a request is aborted instead of an `Error` such as `new Error("query() call aborted: GET /path")` ([#11104](https://github.com/remix-run/react-router/pull/11104))
8
+
9
+ - Please note that `DOMException` was added in Node v17 so you will not get a `DOMException` on Node 16 and below.
10
+
11
+ ### Patch Changes
12
+
13
+ - Respect the `ErrorResponse` status code if passed to `getStaticContextFormError` ([#11213](https://github.com/remix-run/react-router/pull/11213))
14
+
3
15
  ## 1.14.2
4
16
 
5
17
  ### Patch Changes
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v0.0.0-experimental-bc2c864b
2
+ * @remix-run/router v0.0.0-experimental-acfea932
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -496,6 +496,10 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
496
496
  // See https://bugzilla.mozilla.org/show_bug.cgi?id=878297
497
497
  let base = window.location.origin !== "null" ? window.location.origin : window.location.href;
498
498
  let href = typeof to === "string" ? to : createPath(to);
499
+ // Treating this as a full URL will strip any trailing spaces so we need to
500
+ // pre-encode them since they might be part of a matching splat param from
501
+ // an ancestor route
502
+ href = href.replace(/ $/, "%20");
499
503
  invariant(base, "No window.location.(origin|href) available to create URL for href: " + href);
500
504
  return new URL(href, base);
501
505
  }
@@ -756,14 +760,14 @@ function matchRoutes(routes, locationArg, basename) {
756
760
  rankRouteBranches(branches);
757
761
  let matches = null;
758
762
  for (let i = 0; matches == null && i < branches.length; ++i) {
759
- matches = matchRouteBranch(branches[i],
760
763
  // Incoming pathnames are generally encoded from either window.location
761
764
  // or from router.navigate, but we want to match against the unencoded
762
765
  // paths in the route definitions. Memory router locations won't be
763
766
  // encoded here but there also shouldn't be anything to decode so this
764
767
  // should be a safe operation. This avoids needing matchRoutes to be
765
768
  // history-aware.
766
- safelyDecodeURI(pathname));
769
+ let decoded = decodePath(pathname);
770
+ matches = matchRouteBranch(branches[i], decoded);
767
771
  }
768
772
  return matches;
769
773
  }
@@ -1042,7 +1046,7 @@ function matchPath(pattern, pathname) {
1042
1046
  if (isOptional && !value) {
1043
1047
  memo[paramName] = undefined;
1044
1048
  } else {
1045
- memo[paramName] = safelyDecodeURIComponent(value || "", paramName);
1049
+ memo[paramName] = (value || "").replace(/%2F/g, "/");
1046
1050
  }
1047
1051
  return memo;
1048
1052
  }, {});
@@ -1094,22 +1098,14 @@ function compilePath(path, caseSensitive, end) {
1094
1098
  let matcher = new RegExp(regexpSource, caseSensitive ? undefined : "i");
1095
1099
  return [matcher, params];
1096
1100
  }
1097
- function safelyDecodeURI(value) {
1101
+ function decodePath(value) {
1098
1102
  try {
1099
- return decodeURI(value);
1103
+ return value.split("/").map(v => decodeURIComponent(v).replace(/\//g, "%2F")).join("/");
1100
1104
  } catch (error) {
1101
1105
  warning(false, "The URL path \"" + value + "\" could not be decoded because it is is a " + "malformed URL segment. This is probably due to a bad percent " + ("encoding (" + error + ")."));
1102
1106
  return value;
1103
1107
  }
1104
1108
  }
1105
- function safelyDecodeURIComponent(value, paramName) {
1106
- try {
1107
- return decodeURIComponent(value);
1108
- } catch (error) {
1109
- warning(false, "The value for the URL param \"" + paramName + "\" will not be decoded because" + (" the string \"" + value + "\" is a malformed URL segment. This is probably") + (" due to a bad percent encoding (" + error + ")."));
1110
- return value;
1111
- }
1112
- }
1113
1109
 
1114
1110
  /**
1115
1111
  * @private
@@ -1731,7 +1727,7 @@ function createRouter(init) {
1731
1727
  // were marked for explicit hydration
1732
1728
  let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
1733
1729
  let errors = init.hydrationData ? init.hydrationData.errors : null;
1734
- initialized = initialMatches.every(m => m.route.loader && m.route.loader.hydrate !== true && (loaderData && loaderData[m.route.id] !== undefined || errors && errors[m.route.id] !== undefined));
1730
+ initialized = initialMatches.every(m => m.route.loader && (typeof m.route.loader !== "function" || m.route.loader.hydrate !== true) && (loaderData && loaderData[m.route.id] !== undefined || errors && errors[m.route.id] !== undefined));
1735
1731
  } else {
1736
1732
  // Without partial hydration - we're initialized if we were provided any
1737
1733
  // hydrationData - which is expected to be complete
@@ -3332,9 +3328,15 @@ function createStaticHandler(routes, opts) {
3332
3328
  * redirect response is returned or thrown from any action/loader. We
3333
3329
  * propagate that out and return the raw Response so the HTTP server can
3334
3330
  * return it directly.
3331
+ *
3332
+ * - `opts.loadRouteIds` is an optional array of routeIds if you wish to only
3333
+ * run a subset of route loaders on a GET request
3334
+ * - `opts.requestContext` is an optional server context that will be passed
3335
+ * to actions/loaders in the `context` parameter
3335
3336
  */
3336
3337
  async function query(request, _temp3) {
3337
3338
  let {
3339
+ loadRouteIds,
3338
3340
  requestContext
3339
3341
  } = _temp3 === void 0 ? {} : _temp3;
3340
3342
  let url = new URL(request.url);
@@ -3388,7 +3390,7 @@ function createStaticHandler(routes, opts) {
3388
3390
  activeDeferreds: null
3389
3391
  };
3390
3392
  }
3391
- let result = await queryImpl(request, location, matches, requestContext);
3393
+ let result = await queryImpl(request, location, matches, requestContext, loadRouteIds || null, null);
3392
3394
  if (isResponse(result)) {
3393
3395
  return result;
3394
3396
  }
@@ -3421,6 +3423,12 @@ function createStaticHandler(routes, opts) {
3421
3423
  * serialize the error as they see fit while including the proper response
3422
3424
  * code. Examples here are 404 and 405 errors that occur prior to reaching
3423
3425
  * any user-defined loaders.
3426
+ *
3427
+ * - `opts.routeId` allows you to specify the specific route handler to call.
3428
+ * If not provided the handler will determine the proper route by matching
3429
+ * against `request.url`
3430
+ * - `opts.requestContext` is an optional server context that will be passed
3431
+ * to actions/loaders in the `context` parameter
3424
3432
  */
3425
3433
  async function queryRoute(request, _temp4) {
3426
3434
  let {
@@ -3454,7 +3462,7 @@ function createStaticHandler(routes, opts) {
3454
3462
  pathname: location.pathname
3455
3463
  });
3456
3464
  }
3457
- let result = await queryImpl(request, location, matches, requestContext, match);
3465
+ let result = await queryImpl(request, location, matches, requestContext, null, match);
3458
3466
  if (isResponse(result)) {
3459
3467
  return result;
3460
3468
  }
@@ -3481,14 +3489,14 @@ function createStaticHandler(routes, opts) {
3481
3489
  }
3482
3490
  return undefined;
3483
3491
  }
3484
- async function queryImpl(request, location, matches, requestContext, routeMatch) {
3492
+ async function queryImpl(request, location, matches, requestContext, loadRouteIds, routeMatch) {
3485
3493
  invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
3486
3494
  try {
3487
3495
  if (isMutationMethod(request.method.toLowerCase())) {
3488
- let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), requestContext, routeMatch != null);
3496
+ let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), requestContext, loadRouteIds, routeMatch != null);
3489
3497
  return result;
3490
3498
  }
3491
- let result = await loadRouteData(request, matches, requestContext, routeMatch);
3499
+ let result = await loadRouteData(request, matches, requestContext, loadRouteIds, routeMatch);
3492
3500
  return isResponse(result) ? result : _extends({}, result, {
3493
3501
  actionData: null,
3494
3502
  actionHeaders: {}
@@ -3511,7 +3519,7 @@ function createStaticHandler(routes, opts) {
3511
3519
  throw e;
3512
3520
  }
3513
3521
  }
3514
- async function submit(request, matches, actionMatch, requestContext, isRouteRequest) {
3522
+ async function submit(request, matches, actionMatch, requestContext, loadRouteIds, isRouteRequest) {
3515
3523
  let result;
3516
3524
  if (!actionMatch.route.action && !actionMatch.route.lazy) {
3517
3525
  let error = getInternalRouterError(405, {
@@ -3578,11 +3586,18 @@ function createStaticHandler(routes, opts) {
3578
3586
  activeDeferreds: null
3579
3587
  };
3580
3588
  }
3589
+
3590
+ // Create a GET request for the loaders
3591
+ let loaderRequest = new Request(request.url, {
3592
+ headers: request.headers,
3593
+ redirect: request.redirect,
3594
+ signal: request.signal
3595
+ });
3581
3596
  if (isErrorResult(result)) {
3582
3597
  // Store off the pending error - we use it to determine which loaders
3583
3598
  // to call and will commit it when we complete the navigation
3584
3599
  let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);
3585
- let context = await loadRouteData(request, matches, requestContext, undefined, {
3600
+ let context = await loadRouteData(loaderRequest, matches, requestContext, loadRouteIds, null, {
3586
3601
  [boundaryMatch.route.id]: result.error
3587
3602
  });
3588
3603
 
@@ -3595,14 +3610,7 @@ function createStaticHandler(routes, opts) {
3595
3610
  } : {})
3596
3611
  });
3597
3612
  }
3598
-
3599
- // Create a GET request for the loaders
3600
- let loaderRequest = new Request(request.url, {
3601
- headers: request.headers,
3602
- redirect: request.redirect,
3603
- signal: request.signal
3604
- });
3605
- let context = await loadRouteData(loaderRequest, matches, requestContext);
3613
+ let context = await loadRouteData(loaderRequest, matches, requestContext, loadRouteIds, null);
3606
3614
  return _extends({}, context, {
3607
3615
  actionData: {
3608
3616
  [actionMatch.route.id]: result.data
@@ -3616,7 +3624,7 @@ function createStaticHandler(routes, opts) {
3616
3624
  actionHeaders: {}
3617
3625
  });
3618
3626
  }
3619
- async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) {
3627
+ async function loadRouteData(request, matches, requestContext, loadRouteIds, routeMatch, pendingActionError) {
3620
3628
  let isRouteRequest = routeMatch != null;
3621
3629
 
3622
3630
  // Short circuit if we have no loaders to run (queryRoute())
@@ -3629,6 +3637,9 @@ function createStaticHandler(routes, opts) {
3629
3637
  }
3630
3638
  let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]);
3631
3639
  let matchesToLoad = requestMatches.filter(m => m.route.loader || m.route.lazy);
3640
+ if (loadRouteIds) {
3641
+ matchesToLoad = matchesToLoad.filter(m => loadRouteIds.includes(m.route.id));
3642
+ }
3632
3643
 
3633
3644
  // Short circuit if we have no loaders to run (query())
3634
3645
  if (matchesToLoad.length === 0) {
@@ -3924,7 +3935,7 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
3924
3935
  return false;
3925
3936
  }
3926
3937
  if (isInitialLoad) {
3927
- if (route.loader.hydrate) {
3938
+ if (typeof route.loader !== "function" || route.loader.hydrate) {
3928
3939
  return true;
3929
3940
  }
3930
3941
  return state.loaderData[route.id] === undefined && (
@@ -4133,13 +4144,14 @@ async function callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLo
4133
4144
  // back out below.
4134
4145
  let results = await dataStrategyImpl({
4135
4146
  matches: matches.map(match => {
4147
+ let bikeshed_load = routeIdsToLoad.has(match.route.id);
4136
4148
  // `bikeshed_loadRoute` encapsulates the route.lazy, executing the
4137
4149
  // loader/action, and mapping return values/thrown errors to a
4138
4150
  // HandlerResult. Users can pass a callback to take fine-grained control
4139
4151
  // over the execution of the loader/action
4140
- let bikeshed_loadRoute = bikeshed_handlerOverride => {
4152
+ let bikeshed_loadRoute = handlerOverride => {
4141
4153
  loadedMatches.add(match.route.id);
4142
- return routeIdsToLoad.has(match.route.id) ? callLoaderOrAction(type, request, match, manifest, mapRouteProperties, bikeshed_handlerOverride, requestContext) :
4154
+ return bikeshed_load ? callLoaderOrAction(type, request, match, manifest, mapRouteProperties, handlerOverride, requestContext) :
4143
4155
  // TODO: What's the best thing to do here - return an empty "success" result?
4144
4156
  // Or return a success result with the current route loader/action data?
4145
4157
  // We strip these results out if the route didn't need to be revalidated in
@@ -4151,6 +4163,7 @@ async function callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLo
4151
4163
  });
4152
4164
  };
4153
4165
  return _extends({}, match, {
4166
+ bikeshed_load,
4154
4167
  bikeshed_loadRoute
4155
4168
  });
4156
4169
  }),
@@ -4169,7 +4182,7 @@ async function callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLo
4169
4182
  }
4170
4183
 
4171
4184
  // Default logic for calling a loader/action is the user has no specified a dataStrategy
4172
- async function callLoaderOrAction(type, request, match, manifest, mapRouteProperties, bikeshed_handlerOverride, staticContext) {
4185
+ async function callLoaderOrAction(type, request, match, manifest, mapRouteProperties, handlerOverride, staticContext) {
4173
4186
  let result;
4174
4187
  let onReject;
4175
4188
  let runHandler = handler => {
@@ -4178,12 +4191,17 @@ async function callLoaderOrAction(type, request, match, manifest, mapRouteProper
4178
4191
  let abortPromise = new Promise((_, r) => reject = r);
4179
4192
  onReject = () => reject();
4180
4193
  request.signal.addEventListener("abort", onReject);
4181
- let handlerArg = {
4182
- request,
4183
- params: match.params,
4184
- context: staticContext
4194
+ let runHandlerForReal = ctx => {
4195
+ if (typeof handler !== "function") {
4196
+ return Promise.reject(new Error("You cannot call the handler for a route which defines a boolean " + ("\"" + type + "\" [routeId: " + match.route.id + "]")));
4197
+ }
4198
+ return handler({
4199
+ request,
4200
+ params: match.params,
4201
+ context: staticContext
4202
+ }, ...(ctx !== undefined ? [ctx] : []));
4185
4203
  };
4186
- let handlerPromise = bikeshed_handlerOverride ? bikeshed_handlerOverride(async ctx => ctx !== undefined ? handler(handlerArg, ctx) : handler(handlerArg)) : handler(handlerArg);
4204
+ let handlerPromise = handlerOverride ? handlerOverride(async ctx => runHandlerForReal(ctx)) : runHandlerForReal();
4187
4205
  return Promise.race([handlerPromise, abortPromise]);
4188
4206
  };
4189
4207
  try {