You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					259 lines
				
				7.7 KiB
			
		
		
			
		
	
	
					259 lines
				
				7.7 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								import * as React from 'react';
							 | 
						||
| 
								 | 
							
								import { Action, invariant, isRouteErrorResponse, UNSAFE_convertRoutesToDataRoutes, IDLE_NAVIGATION, IDLE_FETCHER } from '@remix-run/router';
							 | 
						||
| 
								 | 
							
								import { parsePath, Router, UNSAFE_DataStaticRouterContext, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, Routes, UNSAFE_enhanceManualRouteObjects, createPath } from 'react-router-dom';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * A <Router> that may not navigate to any other location. This is useful
							 | 
						||
| 
								 | 
							
								 * on the server where there is no stateful UI.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function StaticRouter({
							 | 
						||
| 
								 | 
							
								  basename,
							 | 
						||
| 
								 | 
							
								  children,
							 | 
						||
| 
								 | 
							
								  location: locationProp = "/"
							 | 
						||
| 
								 | 
							
								}) {
							 | 
						||
| 
								 | 
							
								  if (typeof locationProp === "string") {
							 | 
						||
| 
								 | 
							
								    locationProp = parsePath(locationProp);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let action = Action.Pop;
							 | 
						||
| 
								 | 
							
								  let location = {
							 | 
						||
| 
								 | 
							
								    pathname: locationProp.pathname || "/",
							 | 
						||
| 
								 | 
							
								    search: locationProp.search || "",
							 | 
						||
| 
								 | 
							
								    hash: locationProp.hash || "",
							 | 
						||
| 
								 | 
							
								    state: locationProp.state || null,
							 | 
						||
| 
								 | 
							
								    key: locationProp.key || "default"
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								  let staticNavigator = getStatelessNavigator();
							 | 
						||
| 
								 | 
							
								  return /*#__PURE__*/React.createElement(Router, {
							 | 
						||
| 
								 | 
							
								    basename: basename,
							 | 
						||
| 
								 | 
							
								    children: children,
							 | 
						||
| 
								 | 
							
								    location: location,
							 | 
						||
| 
								 | 
							
								    navigationType: action,
							 | 
						||
| 
								 | 
							
								    navigator: staticNavigator,
							 | 
						||
| 
								 | 
							
								    static: true
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * A Data Router that may not navigate to any other location. This is useful
							 | 
						||
| 
								 | 
							
								 * on the server where there is no stateful UI.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								function StaticRouterProvider({
							 | 
						||
| 
								 | 
							
								  context,
							 | 
						||
| 
								 | 
							
								  router,
							 | 
						||
| 
								 | 
							
								  hydrate = true,
							 | 
						||
| 
								 | 
							
								  nonce
							 | 
						||
| 
								 | 
							
								}) {
							 | 
						||
| 
								 | 
							
								  !(router && context) ? process.env.NODE_ENV !== "production" ? invariant(false, "You must provide `router` and `context` to <StaticRouterProvider>") : invariant(false) : void 0;
							 | 
						||
| 
								 | 
							
								  let dataRouterContext = {
							 | 
						||
| 
								 | 
							
								    router,
							 | 
						||
| 
								 | 
							
								    navigator: getStatelessNavigator(),
							 | 
						||
| 
								 | 
							
								    static: true,
							 | 
						||
| 
								 | 
							
								    basename: context.basename || "/"
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								  let hydrateScript = "";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (hydrate !== false) {
							 | 
						||
| 
								 | 
							
								    let data = {
							 | 
						||
| 
								 | 
							
								      loaderData: context.loaderData,
							 | 
						||
| 
								 | 
							
								      actionData: context.actionData,
							 | 
						||
| 
								 | 
							
								      errors: serializeErrors(context.errors)
							 | 
						||
| 
								 | 
							
								    }; // Use JSON.parse here instead of embedding a raw JS object here to speed
							 | 
						||
| 
								 | 
							
								    // up parsing on the client.  Dual-stringify is needed to ensure all quotes
							 | 
						||
| 
								 | 
							
								    // are properly escaped in the resulting string.  See:
							 | 
						||
| 
								 | 
							
								    //   https://v8.dev/blog/cost-of-javascript-2019#json
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    let json = JSON.stringify(JSON.stringify(data));
							 | 
						||
| 
								 | 
							
								    hydrateScript = `window.__staticRouterHydrationData = JSON.parse(${json});`;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(UNSAFE_DataStaticRouterContext.Provider, {
							 | 
						||
| 
								 | 
							
								    value: context
							 | 
						||
| 
								 | 
							
								  }, /*#__PURE__*/React.createElement(UNSAFE_DataRouterContext.Provider, {
							 | 
						||
| 
								 | 
							
								    value: dataRouterContext
							 | 
						||
| 
								 | 
							
								  }, /*#__PURE__*/React.createElement(UNSAFE_DataRouterStateContext.Provider, {
							 | 
						||
| 
								 | 
							
								    value: dataRouterContext.router.state
							 | 
						||
| 
								 | 
							
								  }, /*#__PURE__*/React.createElement(Router, {
							 | 
						||
| 
								 | 
							
								    basename: dataRouterContext.basename,
							 | 
						||
| 
								 | 
							
								    location: dataRouterContext.router.state.location,
							 | 
						||
| 
								 | 
							
								    navigationType: dataRouterContext.router.state.historyAction,
							 | 
						||
| 
								 | 
							
								    navigator: dataRouterContext.navigator
							 | 
						||
| 
								 | 
							
								  }, /*#__PURE__*/React.createElement(Routes, null))))), hydrateScript ? /*#__PURE__*/React.createElement("script", {
							 | 
						||
| 
								 | 
							
								    suppressHydrationWarning: true,
							 | 
						||
| 
								 | 
							
								    nonce: nonce,
							 | 
						||
| 
								 | 
							
								    dangerouslySetInnerHTML: {
							 | 
						||
| 
								 | 
							
								      __html: hydrateScript
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }) : null);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function serializeErrors(errors) {
							 | 
						||
| 
								 | 
							
								  if (!errors) return null;
							 | 
						||
| 
								 | 
							
								  let entries = Object.entries(errors);
							 | 
						||
| 
								 | 
							
								  let serialized = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (let [key, val] of entries) {
							 | 
						||
| 
								 | 
							
								    // Hey you!  If you change this, please change the corresponding logic in
							 | 
						||
| 
								 | 
							
								    // deserializeErrors in react-router-dom/index.tsx :)
							 | 
						||
| 
								 | 
							
								    if (isRouteErrorResponse(val)) {
							 | 
						||
| 
								 | 
							
								      serialized[key] = { ...val,
							 | 
						||
| 
								 | 
							
								        __type: "RouteErrorResponse"
							 | 
						||
| 
								 | 
							
								      };
							 | 
						||
| 
								 | 
							
								    } else if (val instanceof Error) {
							 | 
						||
| 
								 | 
							
								      // Do not serialize stack traces from SSR for security reasons
							 | 
						||
| 
								 | 
							
								      serialized[key] = {
							 | 
						||
| 
								 | 
							
								        message: val.message,
							 | 
						||
| 
								 | 
							
								        __type: "Error"
							 | 
						||
| 
								 | 
							
								      };
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      serialized[key] = val;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return serialized;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getStatelessNavigator() {
							 | 
						||
| 
								 | 
							
								  return {
							 | 
						||
| 
								 | 
							
								    createHref,
							 | 
						||
| 
								 | 
							
								    encodeLocation,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    push(to) {
							 | 
						||
| 
								 | 
							
								      throw new Error(`You cannot use navigator.push() on the server because it is a stateless ` + `environment. This error was probably triggered when you did a ` + `\`navigate(${JSON.stringify(to)})\` somewhere in your app.`);
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    replace(to) {
							 | 
						||
| 
								 | 
							
								      throw new Error(`You cannot use navigator.replace() on the server because it is a stateless ` + `environment. This error was probably triggered when you did a ` + `\`navigate(${JSON.stringify(to)}, { replace: true })\` somewhere ` + `in your app.`);
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    go(delta) {
							 | 
						||
| 
								 | 
							
								      throw new Error(`You cannot use navigator.go() on the server because it is a stateless ` + `environment. This error was probably triggered when you did a ` + `\`navigate(${delta})\` somewhere in your app.`);
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    back() {
							 | 
						||
| 
								 | 
							
								      throw new Error(`You cannot use navigator.back() on the server because it is a stateless ` + `environment.`);
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    forward() {
							 | 
						||
| 
								 | 
							
								      throw new Error(`You cannot use navigator.forward() on the server because it is a stateless ` + `environment.`);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								} // Temporary manifest generation - we should optimize this by combining the
							 | 
						||
| 
								 | 
							
								// tree-walks between convertRoutesToDataRoutes, enhanceManualRouteObjects,
							 | 
						||
| 
								 | 
							
								// and generateManifest.
							 | 
						||
| 
								 | 
							
								// Also look into getting rid of `route as AgnosticDataRouteObject` down below?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function generateManifest(routes, manifest = new Map()) {
							 | 
						||
| 
								 | 
							
								  routes.forEach(route => {
							 | 
						||
| 
								 | 
							
								    manifest.set(route.id, route);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (route.children) {
							 | 
						||
| 
								 | 
							
								      generateManifest(route.children, manifest);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								  return manifest;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function createStaticRouter(routes, context) {
							 | 
						||
| 
								 | 
							
								  let dataRoutes = UNSAFE_convertRoutesToDataRoutes(UNSAFE_enhanceManualRouteObjects(routes));
							 | 
						||
| 
								 | 
							
								  let manifest = generateManifest(dataRoutes); // Because our context matches may be from a framework-agnostic set of
							 | 
						||
| 
								 | 
							
								  // routes passed to createStaticHandler(), we update them here with our
							 | 
						||
| 
								 | 
							
								  // newly created/enhanced data routes
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let matches = context.matches.map(match => {
							 | 
						||
| 
								 | 
							
								    let route = manifest.get(match.route.id) || match.route;
							 | 
						||
| 
								 | 
							
								    return { ...match,
							 | 
						||
| 
								 | 
							
								      route: route
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let msg = method => `You cannot use router.${method}() on the server because it is a stateless environment`;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return {
							 | 
						||
| 
								 | 
							
								    get basename() {
							 | 
						||
| 
								 | 
							
								      return context.basename;
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    get state() {
							 | 
						||
| 
								 | 
							
								      return {
							 | 
						||
| 
								 | 
							
								        historyAction: Action.Pop,
							 | 
						||
| 
								 | 
							
								        location: context.location,
							 | 
						||
| 
								 | 
							
								        matches,
							 | 
						||
| 
								 | 
							
								        loaderData: context.loaderData,
							 | 
						||
| 
								 | 
							
								        actionData: context.actionData,
							 | 
						||
| 
								 | 
							
								        errors: context.errors,
							 | 
						||
| 
								 | 
							
								        initialized: true,
							 | 
						||
| 
								 | 
							
								        navigation: IDLE_NAVIGATION,
							 | 
						||
| 
								 | 
							
								        restoreScrollPosition: null,
							 | 
						||
| 
								 | 
							
								        preventScrollReset: false,
							 | 
						||
| 
								 | 
							
								        revalidation: "idle",
							 | 
						||
| 
								 | 
							
								        fetchers: new Map()
							 | 
						||
| 
								 | 
							
								      };
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    get routes() {
							 | 
						||
| 
								 | 
							
								      return dataRoutes;
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    initialize() {
							 | 
						||
| 
								 | 
							
								      throw msg("initialize");
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    subscribe() {
							 | 
						||
| 
								 | 
							
								      throw msg("subscribe");
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    enableScrollRestoration() {
							 | 
						||
| 
								 | 
							
								      throw msg("enableScrollRestoration");
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    navigate() {
							 | 
						||
| 
								 | 
							
								      throw msg("navigate");
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    fetch() {
							 | 
						||
| 
								 | 
							
								      throw msg("fetch");
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    revalidate() {
							 | 
						||
| 
								 | 
							
								      throw msg("revalidate");
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    createHref,
							 | 
						||
| 
								 | 
							
								    encodeLocation,
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    getFetcher() {
							 | 
						||
| 
								 | 
							
								      return IDLE_FETCHER;
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    deleteFetcher() {
							 | 
						||
| 
								 | 
							
								      throw msg("deleteFetcher");
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    dispose() {
							 | 
						||
| 
								 | 
							
								      throw msg("dispose");
							 | 
						||
| 
								 | 
							
								    },
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _internalFetchControllers: new Map(),
							 | 
						||
| 
								 | 
							
								    _internalActiveDeferreds: new Map()
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function createHref(to) {
							 | 
						||
| 
								 | 
							
								  return typeof to === "string" ? to : createPath(to);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function encodeLocation(to) {
							 | 
						||
| 
								 | 
							
								  // Locations should already be encoded on the server, so just return as-is
							 | 
						||
| 
								 | 
							
								  let path = typeof to === "string" ? parsePath(to) : to;
							 | 
						||
| 
								 | 
							
								  return {
							 | 
						||
| 
								 | 
							
								    pathname: path.pathname || "",
							 | 
						||
| 
								 | 
							
								    search: path.search || "",
							 | 
						||
| 
								 | 
							
								    hash: path.hash || ""
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export { StaticRouter, StaticRouterProvider, createStaticRouter };
							 |