react-router 7.0.0-pre.0 → 7.0.0-pre.1

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
- * React Router v7.0.0-pre.0
2
+ * React Router v7.0.0-pre.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1237,6 +1237,7 @@ const normalizeSearch = search => !search || search === "?" ? "" : search.starts
1237
1237
  * @private
1238
1238
  */
1239
1239
  const normalizeHash = hash => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
1240
+
1240
1241
  /**
1241
1242
  * This is a shortcut for creating `application/json` responses. Converts `data`
1242
1243
  * to JSON and sets the `Content-Type` header.
@@ -1596,25 +1597,12 @@ function createRouter(init) {
1596
1597
  // were marked for explicit hydration
1597
1598
  let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
1598
1599
  let errors = init.hydrationData ? init.hydrationData.errors : null;
1599
- let isRouteInitialized = m => {
1600
- // No loader, nothing to initialize
1601
- if (!m.route.loader) {
1602
- return true;
1603
- }
1604
- // Explicitly opting-in to running on hydration
1605
- if (typeof m.route.loader === "function" && m.route.loader.hydrate === true) {
1606
- return false;
1607
- }
1608
- // Otherwise, initialized if hydrated with data or an error
1609
- return loaderData && loaderData[m.route.id] !== undefined || errors && errors[m.route.id] !== undefined;
1610
- };
1611
-
1612
1600
  // If errors exist, don't consider routes below the boundary
1613
1601
  if (errors) {
1614
1602
  let idx = initialMatches.findIndex(m => errors[m.route.id] !== undefined);
1615
- initialized = initialMatches.slice(0, idx + 1).every(isRouteInitialized);
1603
+ initialized = initialMatches.slice(0, idx + 1).every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
1616
1604
  } else {
1617
- initialized = initialMatches.every(isRouteInitialized);
1605
+ initialized = initialMatches.every(m => !shouldLoadRouteOnHydration(m.route, loaderData, errors));
1618
1606
  }
1619
1607
  }
1620
1608
  let router;
@@ -1700,10 +1688,6 @@ function createRouter(init) {
1700
1688
  // we don't need to update UI state if they change
1701
1689
  let blockerFunctions = new Map();
1702
1690
 
1703
- // Map of pending patchRoutesOnNavigation() promises (keyed by path/matches) so
1704
- // that we only kick them off once for a given combo
1705
- let pendingPatchRoutes = new Map();
1706
-
1707
1691
  // Flag to ignore the next history update, so we can revert the URL change on
1708
1692
  // a POP navigation that was blocked by the user without touching router state
1709
1693
  let unblockBlockerHistoryUpdate = undefined;
@@ -2145,7 +2129,7 @@ function createRouter(init) {
2145
2129
  // Short circuit if it's only a hash change and not a revalidation or
2146
2130
  // mutation submission.
2147
2131
  //
2148
- // Ignore on initial page loads because since the initial load will always
2132
+ // Ignore on initial page loads because since the initial hydration will always
2149
2133
  // be "same hash". For example, on /page#hash and submit a <Form method="post">
2150
2134
  // which will default to a navigation to /page
2151
2135
  if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
@@ -2249,15 +2233,12 @@ function createRouter(init) {
2249
2233
  shortCircuited: true
2250
2234
  };
2251
2235
  } else if (discoverResult.type === "error") {
2252
- let {
2253
- boundaryId,
2254
- error
2255
- } = handleDiscoverRouteError(location.pathname, discoverResult);
2236
+ let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
2256
2237
  return {
2257
2238
  matches: discoverResult.partialMatches,
2258
2239
  pendingActionResult: [boundaryId, {
2259
2240
  type: ResultType.error,
2260
- error
2241
+ error: discoverResult.error
2261
2242
  }]
2262
2243
  };
2263
2244
  } else if (!discoverResult.matches) {
@@ -2383,15 +2364,12 @@ function createRouter(init) {
2383
2364
  shortCircuited: true
2384
2365
  };
2385
2366
  } else if (discoverResult.type === "error") {
2386
- let {
2387
- boundaryId,
2388
- error
2389
- } = handleDiscoverRouteError(location.pathname, discoverResult);
2367
+ let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
2390
2368
  return {
2391
2369
  matches: discoverResult.partialMatches,
2392
2370
  loaderData: {},
2393
2371
  errors: {
2394
- [boundaryId]: error
2372
+ [boundaryId]: discoverResult.error
2395
2373
  }
2396
2374
  };
2397
2375
  } else if (!discoverResult.matches) {
@@ -2516,13 +2494,12 @@ function createRouter(init) {
2516
2494
  errors
2517
2495
  } = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults);
2518
2496
 
2519
- // With "partial hydration", preserve SSR errors for routes that don't re-run
2497
+ // Preserve SSR errors during partial hydration
2520
2498
  if (initialHydration && state.errors) {
2521
- Object.entries(state.errors).filter(([id]) => !matchesToLoad.some(m => m.route.id === id)).forEach(([routeId, error]) => {
2522
- errors = Object.assign(errors || {}, {
2523
- [routeId]: error
2524
- });
2525
- });
2499
+ errors = {
2500
+ ...state.errors,
2501
+ ...errors
2502
+ };
2526
2503
  }
2527
2504
  let updatedFetchers = markFetchRedirectsDone();
2528
2505
  let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId);
@@ -2642,10 +2619,7 @@ function createRouter(init) {
2642
2619
  if (discoverResult.type === "aborted") {
2643
2620
  return;
2644
2621
  } else if (discoverResult.type === "error") {
2645
- let {
2646
- error
2647
- } = handleDiscoverRouteError(path, discoverResult);
2648
- setFetcherError(key, routeId, error, {
2622
+ setFetcherError(key, routeId, discoverResult.error, {
2649
2623
  flushSync
2650
2624
  });
2651
2625
  return;
@@ -2825,10 +2799,7 @@ function createRouter(init) {
2825
2799
  if (discoverResult.type === "aborted") {
2826
2800
  return;
2827
2801
  } else if (discoverResult.type === "error") {
2828
- let {
2829
- error
2830
- } = handleDiscoverRouteError(path, discoverResult);
2831
- setFetcherError(key, routeId, error, {
2802
+ setFetcherError(key, routeId, discoverResult.error, {
2832
2803
  flushSync
2833
2804
  });
2834
2805
  return;
@@ -3243,16 +3214,6 @@ function createRouter(init) {
3243
3214
  error
3244
3215
  };
3245
3216
  }
3246
- function handleDiscoverRouteError(pathname, discoverResult) {
3247
- return {
3248
- boundaryId: findNearestBoundary(discoverResult.partialMatches).route.id,
3249
- error: getInternalRouterError(400, {
3250
- type: "route-discovery",
3251
- pathname,
3252
- message: discoverResult.error != null && "message" in discoverResult.error ? discoverResult.error : String(discoverResult.error)
3253
- })
3254
- };
3255
- }
3256
3217
 
3257
3218
  // Opt in to capturing and reporting scroll positions during navigations,
3258
3219
  // used by the <ScrollRestoration> component
@@ -3329,12 +3290,26 @@ function createRouter(init) {
3329
3290
  };
3330
3291
  }
3331
3292
  async function discoverRoutes(matches, pathname, signal) {
3293
+ if (!patchRoutesOnNavigationImpl) {
3294
+ return {
3295
+ type: "success",
3296
+ matches
3297
+ };
3298
+ }
3332
3299
  let partialMatches = matches;
3333
3300
  while (true) {
3334
3301
  let isNonHMR = inFlightDataRoutes == null;
3335
3302
  let routesToUse = inFlightDataRoutes || dataRoutes;
3303
+ let localManifest = manifest;
3336
3304
  try {
3337
- await loadLazyRouteChildren(patchRoutesOnNavigationImpl, pathname, partialMatches, routesToUse, manifest, mapRouteProperties, pendingPatchRoutes, signal);
3305
+ await patchRoutesOnNavigationImpl({
3306
+ path: pathname,
3307
+ matches: partialMatches,
3308
+ patch: (routeId, children) => {
3309
+ if (signal.aborted) return;
3310
+ patchRoutesImpl(routeId, children, routesToUse, localManifest, mapRouteProperties);
3311
+ }
3312
+ });
3338
3313
  } catch (e) {
3339
3314
  return {
3340
3315
  type: "error",
@@ -3348,7 +3323,7 @@ function createRouter(init) {
3348
3323
  // trigger a re-run of memoized `router.routes` dependencies.
3349
3324
  // HMR will already update the identity and reflow when it lands
3350
3325
  // `inFlightDataRoutes` in `completeNavigation`
3351
- if (isNonHMR) {
3326
+ if (isNonHMR && !signal.aborted) {
3352
3327
  dataRoutes = [...dataRoutes];
3353
3328
  }
3354
3329
  }
@@ -4041,26 +4016,34 @@ function normalizeNavigateOptions(isFetcher, path, opts) {
4041
4016
  };
4042
4017
  }
4043
4018
 
4044
- // Filter out all routes below any caught error as they aren't going to
4019
+ // Filter out all routes at/below any caught error as they aren't going to
4045
4020
  // render so we don't need to load them
4046
- function getLoaderMatchesUntilBoundary(matches, boundaryId) {
4047
- let boundaryMatches = matches;
4048
- if (boundaryId) {
4049
- let index = matches.findIndex(m => m.route.id === boundaryId);
4050
- if (index >= 0) {
4051
- boundaryMatches = matches.slice(0, index);
4052
- }
4021
+ function getLoaderMatchesUntilBoundary(matches, boundaryId, includeBoundary = false) {
4022
+ let index = matches.findIndex(m => m.route.id === boundaryId);
4023
+ if (index >= 0) {
4024
+ return matches.slice(0, includeBoundary ? index + 1 : index);
4053
4025
  }
4054
- return boundaryMatches;
4026
+ return matches;
4055
4027
  }
4056
- function getMatchesToLoad(history, state, matches, submission, location, isInitialLoad, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
4028
+ function getMatchesToLoad(history, state, matches, submission, location, initialHydration, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
4057
4029
  let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : undefined;
4058
4030
  let currentUrl = history.createURL(state.location);
4059
4031
  let nextUrl = history.createURL(location);
4060
4032
 
4061
4033
  // Pick navigation matches that are net-new or qualify for revalidation
4062
- let boundaryId = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[0] : undefined;
4063
- let boundaryMatches = boundaryId ? getLoaderMatchesUntilBoundary(matches, boundaryId) : matches;
4034
+ let boundaryMatches = matches;
4035
+ if (initialHydration && state.errors) {
4036
+ // On initial hydration, only consider matches up to _and including_ the boundary.
4037
+ // This is inclusive to handle cases where a server loader ran successfully,
4038
+ // a child server loader bubbled up to this route, but this route has
4039
+ // `clientLoader.hydrate` so we want to still run the `clientLoader` so that
4040
+ // we have a complete version of `loaderData`
4041
+ boundaryMatches = getLoaderMatchesUntilBoundary(matches, Object.keys(state.errors)[0], true);
4042
+ } else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
4043
+ // If an action threw an error, we call loaders up to, but not including the
4044
+ // boundary
4045
+ boundaryMatches = getLoaderMatchesUntilBoundary(matches, pendingActionResult[0]);
4046
+ }
4064
4047
 
4065
4048
  // Don't revalidate loaders by default after action 4xx/5xx responses
4066
4049
  // when the flag is enabled. They can still opt-into revalidation via
@@ -4078,13 +4061,8 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
4078
4061
  if (route.loader == null) {
4079
4062
  return false;
4080
4063
  }
4081
- if (isInitialLoad) {
4082
- if (typeof route.loader !== "function" || route.loader.hydrate) {
4083
- return true;
4084
- }
4085
- return !state.loaderData.hasOwnProperty(route.id) && (
4086
- // Don't re-run if the loader ran and threw an error
4087
- !state.errors || state.errors[route.id] === undefined);
4064
+ if (initialHydration) {
4065
+ return shouldLoadRouteOnHydration(route, state.loaderData, state.errors);
4088
4066
  }
4089
4067
 
4090
4068
  // Always call the loader on new route instances
@@ -4118,10 +4096,11 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
4118
4096
  let revalidatingFetchers = [];
4119
4097
  fetchLoadMatches.forEach((f, key) => {
4120
4098
  // Don't revalidate:
4121
- // - on initial load (shouldn't be any fetchers then anyway)
4122
- // - if fetcher no longer matches the URL
4123
- // - if fetcher was unmounted
4124
- if (isInitialLoad || !matches.some(m => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) {
4099
+ // - on initial hydration (shouldn't be any fetchers then anyway)
4100
+ // - if fetcher won't be present in the subsequent render
4101
+ // - no longer matches the URL (v7_fetcherPersist=false)
4102
+ // - was unmounted but persisted due to v7_fetcherPersist=true
4103
+ if (initialHydration || !matches.some(m => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) {
4125
4104
  return;
4126
4105
  }
4127
4106
  let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
@@ -4187,6 +4166,32 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
4187
4166
  });
4188
4167
  return [navigationMatches, revalidatingFetchers];
4189
4168
  }
4169
+ function shouldLoadRouteOnHydration(route, loaderData, errors) {
4170
+ // We dunno if we have a loader - gotta find out!
4171
+ if (route.lazy) {
4172
+ return true;
4173
+ }
4174
+
4175
+ // No loader, nothing to initialize
4176
+ if (!route.loader) {
4177
+ return false;
4178
+ }
4179
+ let hasData = loaderData != null && loaderData[route.id] !== undefined;
4180
+ let hasError = errors != null && errors[route.id] !== undefined;
4181
+
4182
+ // Don't run if we error'd during SSR
4183
+ if (!hasData && hasError) {
4184
+ return false;
4185
+ }
4186
+
4187
+ // Explicitly opting-in to running on hydration
4188
+ if (typeof route.loader === "function" && route.loader.hydrate === true) {
4189
+ return true;
4190
+ }
4191
+
4192
+ // Otherwise, run if we're not yet initialized with anything
4193
+ return !hasData && !hasError;
4194
+ }
4190
4195
  function isNewLoader(currentLoaderData, currentMatch, match) {
4191
4196
  let isNew =
4192
4197
  // [a] -> [a, b]
@@ -4220,34 +4225,6 @@ function shouldRevalidateLoader(loaderMatch, arg) {
4220
4225
  }
4221
4226
  return arg.defaultShouldRevalidate;
4222
4227
  }
4223
-
4224
- /**
4225
- * Idempotent utility to execute patchRoutesOnNavigation() to lazily load route
4226
- * definitions and update the routes/routeManifest
4227
- */
4228
- async function loadLazyRouteChildren(patchRoutesOnNavigationImpl, path, matches, routes, manifest, mapRouteProperties, pendingRouteChildren, signal) {
4229
- let key = [path, ...matches.map(m => m.route.id)].join("-");
4230
- try {
4231
- let pending = pendingRouteChildren.get(key);
4232
- if (!pending) {
4233
- pending = patchRoutesOnNavigationImpl({
4234
- path,
4235
- matches,
4236
- patch: (routeId, children) => {
4237
- if (!signal.aborted) {
4238
- patchRoutesImpl(routeId, children, routes, manifest, mapRouteProperties);
4239
- }
4240
- }
4241
- });
4242
- pendingRouteChildren.set(key, pending);
4243
- }
4244
- if (pending && isPromise(pending)) {
4245
- await pending;
4246
- }
4247
- } finally {
4248
- pendingRouteChildren.delete(key);
4249
- }
4250
- }
4251
4228
  function patchRoutesImpl(routeId, children, routesToUse, manifest, mapRouteProperties) {
4252
4229
  let childrenToPatch;
4253
4230
  if (routeId) {
@@ -4852,9 +4829,7 @@ function getInternalRouterError(status, {
4852
4829
  let errorMessage = "Unknown @remix-run/router error";
4853
4830
  if (status === 400) {
4854
4831
  statusText = "Bad Request";
4855
- if (type === "route-discovery") {
4856
- errorMessage = `Unable to match URL "${pathname}" - the \`patchRoutesOnNavigation()\` ` + `function threw the following error:\n${message}`;
4857
- } else if (method && pathname && routeId) {
4832
+ if (method && pathname && routeId) {
4858
4833
  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.`;
4859
4834
  } else if (type === "invalid-body") {
4860
4835
  errorMessage = "Unable to encode submission body";
@@ -4915,9 +4890,6 @@ function isHashChangeOnly(a, b) {
4915
4890
  // /page#hash -> /page
4916
4891
  return false;
4917
4892
  }
4918
- function isPromise(val) {
4919
- return typeof val === "object" && val != null && "then" in val;
4920
- }
4921
4893
  function isDataStrategyResult(result) {
4922
4894
  return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
4923
4895
  }
@@ -5884,7 +5856,7 @@ var DataRouterStateHook$1 = /*#__PURE__*/function (DataRouterStateHook) {
5884
5856
  return DataRouterStateHook;
5885
5857
  }(DataRouterStateHook$1 || {});
5886
5858
  function getDataRouterConsoleError$1(hookName) {
5887
- return `${hookName} must be used within a data router. See https://reactrouter.com/routers/picking-a-router.`;
5859
+ return `${hookName} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`;
5888
5860
  }
5889
5861
  function useDataRouterContext$2(hookName) {
5890
5862
  let ctx = React.useContext(DataRouterContext);
@@ -8334,8 +8306,10 @@ function createClientRoutes(manifest, routeModulesCache, initialState, isSpaMode
8334
8306
  handle: routeModule.handle,
8335
8307
  shouldRevalidate: needsRevalidation ? wrapShouldRevalidateForHdr(route.id, routeModule.shouldRevalidate, needsRevalidation) : routeModule.shouldRevalidate
8336
8308
  });
8337
- let initialData = initialState?.loaderData?.[route.id];
8338
- let initialError = initialState?.errors?.[route.id];
8309
+ let hasInitialData = initialState && initialState.loaderData && route.id in initialState.loaderData;
8310
+ let initialData = hasInitialData ? initialState?.loaderData?.[route.id] : undefined;
8311
+ let hasInitialError = initialState && initialState.errors && route.id in initialState.errors;
8312
+ let initialError = hasInitialError ? initialState?.errors?.[route.id] : undefined;
8339
8313
  let isHydrationRequest = needsRevalidation == null && (routeModule.clientLoader?.hydrate === true || !route.hasLoader);
8340
8314
  dataRoute.loader = async ({
8341
8315
  request,
@@ -8357,10 +8331,12 @@ function createClientRoutes(manifest, routeModulesCache, initialState, isSpaMode
8357
8331
 
8358
8332
  // On the first call, resolve with the server result
8359
8333
  if (isHydrationRequest) {
8360
- if (initialError !== undefined) {
8334
+ if (hasInitialData) {
8335
+ return initialData;
8336
+ }
8337
+ if (hasInitialError) {
8361
8338
  throw initialError;
8362
8339
  }
8363
- return initialData;
8364
8340
  }
8365
8341
 
8366
8342
  // Call the server loader for client-side navigations
@@ -9950,7 +9926,7 @@ var DataRouterStateHook = /*#__PURE__*/function (DataRouterStateHook) {
9950
9926
  return DataRouterStateHook;
9951
9927
  }(DataRouterStateHook || {}); // Internal hooks
9952
9928
  function getDataRouterConsoleError(hookName) {
9953
- return `${hookName} must be used within a data router. See https://reactrouter.com/routers/picking-a-router.`;
9929
+ return `${hookName} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`;
9954
9930
  }
9955
9931
  function useDataRouterContext(hookName) {
9956
9932
  let ctx = React.useContext(DataRouterContext);
@@ -11485,6 +11461,13 @@ function stripRoutesParam(request) {
11485
11461
  return new Request(url.href, init);
11486
11462
  }
11487
11463
 
11464
+ function invariant(value, message) {
11465
+ if (value === false || value === null || typeof value === "undefined") {
11466
+ console.error("The following error is a bug in Remix; please open an issue! https://github.com/remix-run/remix/issues/new");
11467
+ throw new Error(message);
11468
+ }
11469
+ }
11470
+
11488
11471
  // NOTE: make sure to change the Route in remix-react/react-router-dev if you change this
11489
11472
 
11490
11473
  // NOTE: make sure to change the EntryRoute in react-router/react-router-dev if you change this
@@ -11521,7 +11504,29 @@ function createStaticHandlerDataRoutes(manifest, future, parentId = "", routesBy
11521
11504
  path: route.path,
11522
11505
  // Need to use RR's version in the param typed here to permit the optional
11523
11506
  // context even though we know it'll always be provided in remix
11524
- loader: route.module.loader ? args => callRouteHandler(route.module.loader, args) : undefined,
11507
+ loader: route.module.loader ? async args => {
11508
+ // If we're prerendering, use the data passed in from prerendering
11509
+ // the .data route so we dom't call loaders twice
11510
+ if (args.request.headers.has("X-React-Router-Prerender-Data")) {
11511
+ let encoded = args.request.headers.get("X-React-Router-Prerender-Data");
11512
+ !encoded ? invariant(false, "Missing prerendered data for route") : void 0;
11513
+ let uint8array = new TextEncoder().encode(encoded);
11514
+ let stream = new ReadableStream({
11515
+ start(controller) {
11516
+ controller.enqueue(uint8array);
11517
+ controller.close();
11518
+ }
11519
+ });
11520
+ let decoded = await decodeViaTurboStream(stream, global);
11521
+ let data = decoded.value;
11522
+ !(data && route.id in data) ? invariant(false, "Unable to decode prerendered data") : void 0;
11523
+ let result = data[route.id];
11524
+ !("data" in result) ? invariant(false, "Unable to process prerendered data") : void 0;
11525
+ return result.data;
11526
+ }
11527
+ let val = await callRouteHandler(route.module.loader, args);
11528
+ return val;
11529
+ } : undefined,
11525
11530
  action: route.module.action ? args => callRouteHandler(route.module.action, args) : undefined,
11526
11531
  handle: route.module.handle
11527
11532
  };
@@ -11901,13 +11906,6 @@ function encodeViaTurboStream(data, requestSignal, streamTimeout, serverMode) {
11901
11906
  });
11902
11907
  }
11903
11908
 
11904
- function invariant(value, message) {
11905
- if (value === false || value === null || typeof value === "undefined") {
11906
- console.error("The following error is a bug in Remix; please open an issue! https://github.com/remix-run/remix/issues/new");
11907
- throw new Error(message);
11908
- }
11909
- }
11910
-
11911
11909
  function derive(build, mode) {
11912
11910
  let routes = createRoutes(build.routes);
11913
11911
  let dataRoutes = createStaticHandlerDataRoutes(build.routes, build.future);