UNPKG

9.81 kBJavaScriptView Raw
1/**
2 * react-router v8.0.0
3 *
4 * Copyright (c) Remix Software Inc.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE.md file in the root directory of this source tree.
8 *
9 * @license MIT
10 */
11import { ABSOLUTE_URL_REGEX } from "../router/url.js";
12import { createPath, invariant, parsePath } from "../router/history.js";
13import { convertRoutesToDataRoutes, isRouteErrorResponse } from "../router/utils.js";
14import { IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION } from "../router/router.js";
15import { DataRouterContext, DataRouterStateContext, FetchersContext, ViewTransitionContext } from "../context.js";
16import { DataRoutes, Router } from "../components.js";
17import { escapeHtml } from "./ssr/markup.js";
18import * as React$1 from "react";
19//#region lib/dom/server.tsx
20/**
21* A {@link Router | `<Router>`} that may not navigate to any other {@link Location}.
22* This is useful on the server where there is no stateful UI.
23*
24* @public
25* @category Declarative Routers
26* @mode declarative
27* @param props Props
28* @param {StaticRouterProps.basename} props.basename n/a
29* @param {StaticRouterProps.children} props.children n/a
30* @param {StaticRouterProps.location} props.location n/a
31* @returns A React element that renders the static {@link Router | `<Router>`}
32*/
33function StaticRouter({ basename, children, location: locationProp = "/" }) {
34 if (typeof locationProp === "string") locationProp = parsePath(locationProp);
35 let action = "POP";
36 let location = {
37 pathname: locationProp.pathname || "/",
38 search: locationProp.search || "",
39 hash: locationProp.hash || "",
40 state: locationProp.state != null ? locationProp.state : null,
41 key: locationProp.key || "default",
42 mask: void 0
43 };
44 let staticNavigator = getStatelessNavigator();
45 return /* @__PURE__ */ React$1.createElement(Router, {
46 basename,
47 children,
48 location,
49 navigationType: action,
50 navigator: staticNavigator,
51 static: true,
52 useTransitions: false
53 });
54}
55/**
56* A {@link DataRouter} that may not navigate to any other {@link Location}.
57* This is useful on the server where there is no stateful UI.
58*
59* @example
60* export async function handleRequest(request: Request) {
61* let { query, dataRoutes } = createStaticHandler(routes);
62* let context = await query(request));
63*
64* if (context instanceof Response) {
65* return context;
66* }
67*
68* let router = createStaticRouter(dataRoutes, context);
69* return new Response(
70* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
71* { headers: { "Content-Type": "text/html" } }
72* );
73* }
74*
75* @public
76* @category Data Routers
77* @mode data
78* @param props Props
79* @param {StaticRouterProviderProps.context} props.context n/a
80* @param {StaticRouterProviderProps.hydrate} props.hydrate n/a
81* @param {StaticRouterProviderProps.nonce} props.nonce n/a
82* @param {StaticRouterProviderProps.router} props.router n/a
83* @returns A React element that renders the static router provider
84*/
85function StaticRouterProvider({ context, router, hydrate = true, nonce }) {
86 invariant(router && context, "You must provide `router` and `context` to <StaticRouterProvider>");
87 let dataRouterContext = {
88 router,
89 navigator: getStatelessNavigator(),
90 static: true,
91 staticContext: context,
92 basename: context.basename || "/"
93 };
94 let fetchersContext = /* @__PURE__ */ new Map();
95 let hydrateScript = "";
96 if (hydrate !== false) {
97 let data = {
98 loaderData: context.loaderData,
99 actionData: context.actionData,
100 errors: serializeErrors(context.errors)
101 };
102 hydrateScript = `window.__staticRouterHydrationData = JSON.parse(${escapeHtml(JSON.stringify(JSON.stringify(data)))});`;
103 }
104 let { state } = dataRouterContext.router;
105 return /* @__PURE__ */ React$1.createElement(React$1.Fragment, null, /* @__PURE__ */ React$1.createElement(DataRouterContext.Provider, { value: dataRouterContext }, /* @__PURE__ */ React$1.createElement(DataRouterStateContext.Provider, { value: state }, /* @__PURE__ */ React$1.createElement(FetchersContext.Provider, { value: fetchersContext }, /* @__PURE__ */ React$1.createElement(ViewTransitionContext.Provider, { value: { isTransitioning: false } }, /* @__PURE__ */ React$1.createElement(Router, {
106 basename: dataRouterContext.basename,
107 location: state.location,
108 navigationType: state.historyAction,
109 navigator: dataRouterContext.navigator,
110 static: dataRouterContext.static,
111 useTransitions: false
112 }, /* @__PURE__ */ React$1.createElement(DataRoutes, {
113 manifest: router.manifest,
114 routes: router.routes,
115 future: router.future,
116 state,
117 isStatic: true
118 })))))), hydrateScript ? /* @__PURE__ */ React$1.createElement("script", {
119 suppressHydrationWarning: true,
120 nonce,
121 dangerouslySetInnerHTML: { __html: hydrateScript }
122 }) : null);
123}
124function serializeErrors(errors) {
125 if (!errors) return null;
126 let entries = Object.entries(errors);
127 let serialized = {};
128 for (let [key, val] of entries) if (isRouteErrorResponse(val)) serialized[key] = {
129 ...val,
130 __type: "RouteErrorResponse"
131 };
132 else if (val instanceof Error) serialized[key] = {
133 message: val.message,
134 __type: "Error",
135 ...val.name !== "Error" ? { __subType: val.name } : {}
136 };
137 else serialized[key] = val;
138 return serialized;
139}
140function getStatelessNavigator() {
141 return {
142 createHref,
143 encodeLocation,
144 push(to) {
145 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.`);
146 },
147 replace(to) {
148 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.`);
149 },
150 go(delta) {
151 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.`);
152 },
153 back() {
154 throw new Error("You cannot use navigator.back() on the server because it is a stateless environment.");
155 },
156 forward() {
157 throw new Error("You cannot use navigator.forward() on the server because it is a stateless environment.");
158 }
159 };
160}
161/**
162* Create a static {@link DataRouter} for server-side rendering
163*
164* @example
165* export async function handleRequest(request: Request) {
166* let { query, dataRoutes } = createStaticHandler(routes);
167* let context = await query(request);
168*
169* if (context instanceof Response) {
170* return context;
171* }
172*
173* let router = createStaticRouter(dataRoutes, context);
174* return new Response(
175* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
176* { headers: { "Content-Type": "text/html" } }
177* );
178* }
179*
180* @public
181* @category Data Routers
182* @mode data
183* @param routes The route objects to create a static {@link DataRouter} for
184* @param context The {@link StaticHandlerContext} returned from {@link StaticHandler}'s
185* `query`
186* @param opts Options
187* @param opts.future Future flags for the static {@link DataRouter}
188* @param opts.branches Optional pre-computed route branches
189* @returns A static {@link DataRouter} that can be used to render the provided routes
190*/
191function createStaticRouter(routes, context, opts = {}) {
192 let manifest = {};
193 let dataRoutes = convertRoutesToDataRoutes(routes, void 0, void 0, manifest);
194 let matches = context.matches.map((match) => {
195 let route = manifest[match.route.id] || match.route;
196 return {
197 ...match,
198 route
199 };
200 });
201 let msg = (method) => `You cannot use router.${method}() on the server because it is a stateless environment`;
202 return {
203 get basename() {
204 return context.basename;
205 },
206 get future() {
207 return { ...opts?.future };
208 },
209 get state() {
210 return {
211 historyAction: "POP",
212 location: context.location,
213 matches,
214 loaderData: context.loaderData,
215 actionData: context.actionData,
216 errors: context.errors,
217 initialized: true,
218 renderFallback: false,
219 navigation: IDLE_NAVIGATION,
220 restoreScrollPosition: null,
221 preventScrollReset: false,
222 revalidation: "idle",
223 fetchers: /* @__PURE__ */ new Map(),
224 blockers: /* @__PURE__ */ new Map()
225 };
226 },
227 get routes() {
228 return dataRoutes;
229 },
230 get branches() {
231 return opts.branches;
232 },
233 get manifest() {
234 return manifest;
235 },
236 get window() {},
237 initialize() {
238 throw msg("initialize");
239 },
240 subscribe() {
241 throw msg("subscribe");
242 },
243 enableScrollRestoration() {
244 throw msg("enableScrollRestoration");
245 },
246 navigate() {
247 throw msg("navigate");
248 },
249 fetch() {
250 throw msg("fetch");
251 },
252 revalidate() {
253 throw msg("revalidate");
254 },
255 createHref,
256 encodeLocation,
257 getFetcher() {
258 return IDLE_FETCHER;
259 },
260 deleteFetcher() {
261 throw msg("deleteFetcher");
262 },
263 resetFetcher() {
264 throw msg("resetFetcher");
265 },
266 dispose() {
267 throw msg("dispose");
268 },
269 getBlocker() {
270 return IDLE_BLOCKER;
271 },
272 deleteBlocker() {
273 throw msg("deleteBlocker");
274 },
275 patchRoutes() {
276 throw msg("patchRoutes");
277 },
278 _internalFetchControllers: /* @__PURE__ */ new Map(),
279 _internalSetRoutes() {
280 throw msg("_internalSetRoutes");
281 },
282 _internalSetStateDoNotUseOrYouWillBreakYourApp() {
283 throw msg("_internalSetStateDoNotUseOrYouWillBreakYourApp");
284 }
285 };
286}
287function createHref(to) {
288 return typeof to === "string" ? to : createPath(to);
289}
290function encodeLocation(to) {
291 let href = typeof to === "string" ? to : createPath(to);
292 href = href.replace(/ $/, "%20");
293 let encoded = ABSOLUTE_URL_REGEX.test(href) ? new URL(href) : new URL(href, "http://localhost");
294 return {
295 pathname: encoded.pathname,
296 search: encoded.search,
297 hash: encoded.hash
298 };
299}
300//#endregion
301export { StaticRouter, StaticRouterProvider, createStaticRouter };