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
						
					
					
				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 };
 |