UNPKG

11.1 kBJavaScriptView Raw
1/**
2 * react-router v8.4.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, warning } from "../router/history.js";
13import { convertRoutesToDataRoutes, isRouteErrorResponse } from "../router/utils.js";
14import { IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION } from "../router/router.js";
15import { DataRouterContext, DataRouterDataContext, DataRouterNavigationContext, 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 hydrateScript = "";
95 if (hydrate !== false) {
96 let data = {
97 loaderData: context.loaderData,
98 actionData: context.actionData,
99 errors: serializeErrors(context.errors)
100 };
101 hydrateScript = `window.__staticRouterHydrationData = JSON.parse(${escapeHtml(JSON.stringify(JSON.stringify(data)))});`;
102 }
103 let { state } = dataRouterContext.router;
104 let dataRouterState = {
105 historyAction: state.historyAction,
106 location: state.location,
107 matches: state.matches,
108 initialized: state.initialized,
109 renderFallback: state.renderFallback,
110 restoreScrollPosition: state.restoreScrollPosition,
111 preventScrollReset: state.preventScrollReset,
112 blockers: state.blockers
113 };
114 let dataRouterNavigation = {
115 navigation: state.navigation,
116 revalidation: state.revalidation
117 };
118 let dataRouterData = {
119 loaderData: state.loaderData,
120 actionData: state.actionData,
121 errors: state.errors
122 };
123 let fetchersContext = {
124 fetchers: state.fetchers,
125 fetcherData: /* @__PURE__ */ new Map()
126 };
127 return /* @__PURE__ */ React$1.createElement(React$1.Fragment, null, /* @__PURE__ */ React$1.createElement(DataRouterContext.Provider, { value: dataRouterContext }, /* @__PURE__ */ React$1.createElement(DataRouterStateContext.Provider, { value: dataRouterState }, /* @__PURE__ */ React$1.createElement(DataRouterNavigationContext.Provider, { value: dataRouterNavigation }, /* @__PURE__ */ React$1.createElement(DataRouterDataContext.Provider, { value: dataRouterData }, /* @__PURE__ */ React$1.createElement(FetchersContext.Provider, { value: fetchersContext }, /* @__PURE__ */ React$1.createElement(ViewTransitionContext.Provider, { value: { isTransitioning: false } }, /* @__PURE__ */ React$1.createElement(Router, {
128 basename: dataRouterContext.basename,
129 location: state.location,
130 navigationType: state.historyAction,
131 navigator: dataRouterContext.navigator,
132 static: dataRouterContext.static,
133 useTransitions: false
134 }, /* @__PURE__ */ React$1.createElement(DataRoutes, {
135 manifest: router.manifest,
136 routes: router.routes,
137 state,
138 isStatic: true
139 })))))))), hydrateScript ? /* @__PURE__ */ React$1.createElement("script", {
140 suppressHydrationWarning: true,
141 nonce,
142 dangerouslySetInnerHTML: { __html: hydrateScript }
143 }) : null);
144}
145function serializeErrors(errors) {
146 if (!errors) return null;
147 let entries = Object.entries(errors);
148 let serialized = {};
149 for (let [key, val] of entries) if (isRouteErrorResponse(val)) serialized[key] = {
150 ...val,
151 __type: "RouteErrorResponse"
152 };
153 else if (val instanceof Error) serialized[key] = {
154 message: val.message,
155 __type: "Error",
156 ...val.name !== "Error" ? { __subType: val.name } : {}
157 };
158 else serialized[key] = val;
159 return serialized;
160}
161function getStatelessNavigator() {
162 return {
163 createHref,
164 encodeLocation,
165 push(to) {
166 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.`);
167 },
168 replace(to) {
169 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.`);
170 },
171 go(delta) {
172 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.`);
173 },
174 back() {
175 throw new Error("You cannot use navigator.back() on the server because it is a stateless environment.");
176 },
177 forward() {
178 throw new Error("You cannot use navigator.forward() on the server because it is a stateless environment.");
179 }
180 };
181}
182/**
183* Create a static {@link DataRouter} for server-side rendering
184*
185* @example
186* export async function handleRequest(request: Request) {
187* let { query, dataRoutes } = createStaticHandler(routes);
188* let context = await query(request);
189*
190* if (context instanceof Response) {
191* return context;
192* }
193*
194* let router = createStaticRouter(dataRoutes, context);
195* return new Response(
196* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
197* { headers: { "Content-Type": "text/html" } }
198* );
199* }
200*
201* @public
202* @category Data Routers
203* @mode data
204* @param routes The route objects to create a static {@link DataRouter} for
205* @param context The {@link StaticHandlerContext} returned from {@link StaticHandler}'s
206* `query`
207* @param opts Options
208* @param opts.future Future flags for the static {@link DataRouter}
209* @param opts.branches Deprecated optional pre-computed route branches. This option
210* is no longer used because branch caching is done automatically inside the static router.
211* @returns A static {@link DataRouter} that can be used to render the provided routes
212*/
213function createStaticRouter(routes, context, opts = {}) {
214 warning(opts.branches == null, "`createStaticRouter({ branches })` is deprecated and no longer used. Branch caching is done automatically inside the static router.");
215 let manifest = {};
216 let dataRoutes = convertRoutesToDataRoutes(routes, void 0, void 0, manifest);
217 let future = { ...opts?.future };
218 let matchRoutes = context._match;
219 let mapRouteMatch = (match) => {
220 let route = manifest[match.route.id] || match.route;
221 return {
222 ...match,
223 route
224 };
225 };
226 let matches = context.matches.map(mapRouteMatch);
227 let msg = (method) => `You cannot use router.${method}() on the server because it is a stateless environment`;
228 return {
229 get basename() {
230 return context.basename;
231 },
232 get future() {
233 return future;
234 },
235 get state() {
236 return {
237 historyAction: "POP",
238 location: context.location,
239 matches,
240 loaderData: context.loaderData,
241 actionData: context.actionData,
242 errors: context.errors,
243 initialized: true,
244 renderFallback: false,
245 navigation: IDLE_NAVIGATION,
246 restoreScrollPosition: null,
247 preventScrollReset: false,
248 revalidation: "idle",
249 fetchers: /* @__PURE__ */ new Map(),
250 blockers: /* @__PURE__ */ new Map()
251 };
252 },
253 get routes() {
254 return dataRoutes;
255 },
256 get manifest() {
257 return manifest;
258 },
259 get window() {},
260 match(locationArg) {
261 return matchRoutes(locationArg)?.map(mapRouteMatch) ?? null;
262 },
263 initialize() {
264 throw msg("initialize");
265 },
266 subscribe() {
267 throw msg("subscribe");
268 },
269 enableScrollRestoration() {
270 throw msg("enableScrollRestoration");
271 },
272 navigate() {
273 throw msg("navigate");
274 },
275 fetch() {
276 throw msg("fetch");
277 },
278 revalidate() {
279 throw msg("revalidate");
280 },
281 createHref,
282 encodeLocation,
283 getFetcher() {
284 return IDLE_FETCHER;
285 },
286 deleteFetcher() {
287 throw msg("deleteFetcher");
288 },
289 resetFetcher() {
290 throw msg("resetFetcher");
291 },
292 dispose() {
293 throw msg("dispose");
294 },
295 getBlocker() {
296 return IDLE_BLOCKER;
297 },
298 deleteBlocker() {
299 throw msg("deleteBlocker");
300 },
301 patchRoutes() {
302 throw msg("patchRoutes");
303 },
304 _internalFetchControllers: /* @__PURE__ */ new Map(),
305 _internalSetRoutes() {
306 throw msg("_internalSetRoutes");
307 },
308 _internalSetStateDoNotUseOrYouWillBreakYourApp() {
309 throw msg("_internalSetStateDoNotUseOrYouWillBreakYourApp");
310 }
311 };
312}
313function createHref(to) {
314 return typeof to === "string" ? to : createPath(to);
315}
316function encodeLocation(to) {
317 let href = typeof to === "string" ? to : createPath(to);
318 href = href.replace(/ $/, "%20");
319 let encoded = ABSOLUTE_URL_REGEX.test(href) ? new URL(href) : new URL(href, "http://localhost");
320 return {
321 pathname: encoded.pathname,
322 search: encoded.search,
323 hash: encoded.hash
324 };
325}
326//#endregion
327export { StaticRouter, StaticRouterProvider, createStaticRouter };