1 | import * as React from 'react';
|
2 | import { Action, UNSAFE_invariant, isRouteErrorResponse, createStaticHandler as createStaticHandler$1, UNSAFE_convertRoutesToDataRoutes, IDLE_NAVIGATION, IDLE_FETCHER, IDLE_BLOCKER } from '@remix-run/router';
|
3 | import { UNSAFE_useRoutesImpl, UNSAFE_mapRouteProperties } from 'react-router';
|
4 | import { parsePath, Router, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, UNSAFE_FetchersContext, UNSAFE_ViewTransitionContext, createPath } from 'react-router-dom';
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | function StaticRouter({
|
11 | basename,
|
12 | children,
|
13 | location: locationProp = "/",
|
14 | future
|
15 | }) {
|
16 | if (typeof locationProp === "string") {
|
17 | locationProp = parsePath(locationProp);
|
18 | }
|
19 | let action = Action.Pop;
|
20 | let location = {
|
21 | pathname: locationProp.pathname || "/",
|
22 | search: locationProp.search || "",
|
23 | hash: locationProp.hash || "",
|
24 | state: locationProp.state || null,
|
25 | key: locationProp.key || "default"
|
26 | };
|
27 | let staticNavigator = getStatelessNavigator();
|
28 | return React.createElement(Router, {
|
29 | basename: basename,
|
30 | children: children,
|
31 | location: location,
|
32 | navigationType: action,
|
33 | navigator: staticNavigator,
|
34 | future: future,
|
35 | static: true
|
36 | });
|
37 | }
|
38 |
|
39 |
|
40 |
|
41 |
|
42 | function StaticRouterProvider({
|
43 | context,
|
44 | router,
|
45 | hydrate = true,
|
46 | nonce
|
47 | }) {
|
48 | !(router && context) ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "You must provide `router` and `context` to <StaticRouterProvider>") : UNSAFE_invariant(false) : void 0;
|
49 | let dataRouterContext = {
|
50 | router,
|
51 | navigator: getStatelessNavigator(),
|
52 | static: true,
|
53 | staticContext: context,
|
54 | basename: context.basename || "/"
|
55 | };
|
56 | let fetchersContext = new Map();
|
57 | let hydrateScript = "";
|
58 | if (hydrate !== false) {
|
59 | let data = {
|
60 | loaderData: context.loaderData,
|
61 | actionData: context.actionData,
|
62 | errors: serializeErrors(context.errors)
|
63 | };
|
64 |
|
65 |
|
66 |
|
67 |
|
68 | let json = htmlEscape(JSON.stringify(JSON.stringify(data)));
|
69 | hydrateScript = `window.__staticRouterHydrationData = JSON.parse(${json});`;
|
70 | }
|
71 | let {
|
72 | state
|
73 | } = dataRouterContext.router;
|
74 | return React.createElement(React.Fragment, null, React.createElement(UNSAFE_DataRouterContext.Provider, {
|
75 | value: dataRouterContext
|
76 | }, React.createElement(UNSAFE_DataRouterStateContext.Provider, {
|
77 | value: state
|
78 | }, React.createElement(UNSAFE_FetchersContext.Provider, {
|
79 | value: fetchersContext
|
80 | }, React.createElement(UNSAFE_ViewTransitionContext.Provider, {
|
81 | value: {
|
82 | isTransitioning: false
|
83 | }
|
84 | }, React.createElement(Router, {
|
85 | basename: dataRouterContext.basename,
|
86 | location: state.location,
|
87 | navigationType: state.historyAction,
|
88 | navigator: dataRouterContext.navigator,
|
89 | static: dataRouterContext.static,
|
90 | future: {
|
91 | v7_relativeSplatPath: router.future.v7_relativeSplatPath
|
92 | }
|
93 | }, React.createElement(DataRoutes, {
|
94 | routes: router.routes,
|
95 | future: router.future,
|
96 | state: state
|
97 | })))))), hydrateScript ? React.createElement("script", {
|
98 | suppressHydrationWarning: true,
|
99 | nonce: nonce,
|
100 | dangerouslySetInnerHTML: {
|
101 | __html: hydrateScript
|
102 | }
|
103 | }) : null);
|
104 | }
|
105 | function DataRoutes({
|
106 | routes,
|
107 | future,
|
108 | state
|
109 | }) {
|
110 | return UNSAFE_useRoutesImpl(routes, undefined, state, future);
|
111 | }
|
112 | function serializeErrors(errors) {
|
113 | if (!errors) return null;
|
114 | let entries = Object.entries(errors);
|
115 | let serialized = {};
|
116 | for (let [key, val] of entries) {
|
117 |
|
118 |
|
119 | if (isRouteErrorResponse(val)) {
|
120 | serialized[key] = {
|
121 | ...val,
|
122 | __type: "RouteErrorResponse"
|
123 | };
|
124 | } else if (val instanceof Error) {
|
125 |
|
126 | serialized[key] = {
|
127 | message: val.message,
|
128 | __type: "Error",
|
129 |
|
130 |
|
131 | ...(val.name !== "Error" ? {
|
132 | __subType: val.name
|
133 | } : {})
|
134 | };
|
135 | } else {
|
136 | serialized[key] = val;
|
137 | }
|
138 | }
|
139 | return serialized;
|
140 | }
|
141 | function getStatelessNavigator() {
|
142 | return {
|
143 | createHref,
|
144 | encodeLocation,
|
145 | push(to) {
|
146 | 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.`);
|
147 | },
|
148 | replace(to) {
|
149 | 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.`);
|
150 | },
|
151 | go(delta) {
|
152 | 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.`);
|
153 | },
|
154 | back() {
|
155 | throw new Error(`You cannot use navigator.back() on the server because it is a stateless ` + `environment.`);
|
156 | },
|
157 | forward() {
|
158 | throw new Error(`You cannot use navigator.forward() on the server because it is a stateless ` + `environment.`);
|
159 | }
|
160 | };
|
161 | }
|
162 | function createStaticHandler(routes, opts) {
|
163 | return createStaticHandler$1(routes, {
|
164 | ...opts,
|
165 | mapRouteProperties: UNSAFE_mapRouteProperties
|
166 | });
|
167 | }
|
168 | function createStaticRouter(routes, context, opts = {}) {
|
169 | let manifest = {};
|
170 | let dataRoutes = UNSAFE_convertRoutesToDataRoutes(routes, UNSAFE_mapRouteProperties, undefined, manifest);
|
171 |
|
172 |
|
173 |
|
174 |
|
175 | let matches = context.matches.map(match => {
|
176 | let route = manifest[match.route.id] || match.route;
|
177 | return {
|
178 | ...match,
|
179 | route
|
180 | };
|
181 | });
|
182 | let msg = method => `You cannot use router.${method}() on the server because it is a stateless environment`;
|
183 | return {
|
184 | get basename() {
|
185 | return context.basename;
|
186 | },
|
187 | get future() {
|
188 | return {
|
189 | v7_fetcherPersist: false,
|
190 | v7_normalizeFormMethod: false,
|
191 | v7_partialHydration: opts.future?.v7_partialHydration === true,
|
192 | v7_prependBasename: false,
|
193 | v7_relativeSplatPath: opts.future?.v7_relativeSplatPath === true
|
194 | };
|
195 | },
|
196 | get state() {
|
197 | return {
|
198 | historyAction: Action.Pop,
|
199 | location: context.location,
|
200 | matches,
|
201 | loaderData: context.loaderData,
|
202 | actionData: context.actionData,
|
203 | errors: context.errors,
|
204 | initialized: true,
|
205 | navigation: IDLE_NAVIGATION,
|
206 | restoreScrollPosition: null,
|
207 | preventScrollReset: false,
|
208 | revalidation: "idle",
|
209 | fetchers: new Map(),
|
210 | blockers: new Map()
|
211 | };
|
212 | },
|
213 | get routes() {
|
214 | return dataRoutes;
|
215 | },
|
216 | get window() {
|
217 | return undefined;
|
218 | },
|
219 | initialize() {
|
220 | throw msg("initialize");
|
221 | },
|
222 | subscribe() {
|
223 | throw msg("subscribe");
|
224 | },
|
225 | enableScrollRestoration() {
|
226 | throw msg("enableScrollRestoration");
|
227 | },
|
228 | navigate() {
|
229 | throw msg("navigate");
|
230 | },
|
231 | fetch() {
|
232 | throw msg("fetch");
|
233 | },
|
234 | revalidate() {
|
235 | throw msg("revalidate");
|
236 | },
|
237 | createHref,
|
238 | encodeLocation,
|
239 | getFetcher() {
|
240 | return IDLE_FETCHER;
|
241 | },
|
242 | deleteFetcher() {
|
243 | throw msg("deleteFetcher");
|
244 | },
|
245 | dispose() {
|
246 | throw msg("dispose");
|
247 | },
|
248 | getBlocker() {
|
249 | return IDLE_BLOCKER;
|
250 | },
|
251 | deleteBlocker() {
|
252 | throw msg("deleteBlocker");
|
253 | },
|
254 | _internalFetchControllers: new Map(),
|
255 | _internalActiveDeferreds: new Map(),
|
256 | _internalSetRoutes() {
|
257 | throw msg("_internalSetRoutes");
|
258 | }
|
259 | };
|
260 | }
|
261 | function createHref(to) {
|
262 | return typeof to === "string" ? to : createPath(to);
|
263 | }
|
264 | function encodeLocation(to) {
|
265 | let href = typeof to === "string" ? to : createPath(to);
|
266 |
|
267 |
|
268 |
|
269 | href = href.replace(/ $/, "%20");
|
270 | let encoded = ABSOLUTE_URL_REGEX.test(href) ? new URL(href) : new URL(href, "http://localhost");
|
271 | return {
|
272 | pathname: encoded.pathname,
|
273 | search: encoded.search,
|
274 | hash: encoded.hash
|
275 | };
|
276 | }
|
277 | const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
|
278 |
|
279 |
|
280 |
|
281 | const ESCAPE_LOOKUP = {
|
282 | "&": "\\u0026",
|
283 | ">": "\\u003e",
|
284 | "<": "\\u003c",
|
285 | "\u2028": "\\u2028",
|
286 | "\u2029": "\\u2029"
|
287 | };
|
288 | const ESCAPE_REGEX = /[&><\u2028\u2029]/g;
|
289 | function htmlEscape(str) {
|
290 | return str.replace(ESCAPE_REGEX, match => ESCAPE_LOOKUP[match]);
|
291 | }
|
292 |
|
293 | export { StaticRouter, StaticRouterProvider, createStaticHandler, createStaticRouter };
|