| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 |
|
| 11 | import { ABSOLUTE_URL_REGEX } from "../router/url.js";
|
| 12 | import { createPath, invariant, parsePath } from "../router/history.js";
|
| 13 | import { convertRoutesToDataRoutes, isRouteErrorResponse } from "../router/utils.js";
|
| 14 | import { IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION } from "../router/router.js";
|
| 15 | import { DataRouterContext, DataRouterStateContext, FetchersContext, ViewTransitionContext } from "../context.js";
|
| 16 | import { DataRoutes, Router } from "../components.js";
|
| 17 | import { escapeHtml } from "./ssr/markup.js";
|
| 18 | import * as React$1 from "react";
|
| 19 |
|
| 20 | |
| 21 | |
| 22 | |
| 23 | |
| 24 | |
| 25 | |
| 26 | |
| 27 | |
| 28 | |
| 29 | |
| 30 | |
| 31 | |
| 32 |
|
| 33 | function 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 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 | |
| 57 | |
| 58 | |
| 59 | |
| 60 | |
| 61 | |
| 62 | |
| 63 | |
| 64 | |
| 65 | |
| 66 | |
| 67 | |
| 68 | |
| 69 | |
| 70 | |
| 71 | |
| 72 | |
| 73 | |
| 74 | |
| 75 | |
| 76 | |
| 77 | |
| 78 | |
| 79 | |
| 80 | |
| 81 | |
| 82 | |
| 83 | |
| 84 |
|
| 85 | function 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 = 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 React$1.createElement(React$1.Fragment, null, React$1.createElement(DataRouterContext.Provider, { value: dataRouterContext }, React$1.createElement(DataRouterStateContext.Provider, { value: state }, React$1.createElement(FetchersContext.Provider, { value: fetchersContext }, React$1.createElement(ViewTransitionContext.Provider, { value: { isTransitioning: false } }, 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 | }, React$1.createElement(DataRoutes, {
|
| 113 | manifest: router.manifest,
|
| 114 | routes: router.routes,
|
| 115 | future: router.future,
|
| 116 | state,
|
| 117 | isStatic: true
|
| 118 | })))))), hydrateScript ? React$1.createElement("script", {
|
| 119 | suppressHydrationWarning: true,
|
| 120 | nonce,
|
| 121 | dangerouslySetInnerHTML: { __html: hydrateScript }
|
| 122 | }) : null);
|
| 123 | }
|
| 124 | function 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 | }
|
| 140 | function 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 | |
| 163 | |
| 164 | |
| 165 | |
| 166 | |
| 167 | |
| 168 | |
| 169 | |
| 170 | |
| 171 | |
| 172 | |
| 173 | |
| 174 | |
| 175 | |
| 176 | |
| 177 | |
| 178 | |
| 179 | |
| 180 | |
| 181 | |
| 182 | |
| 183 | |
| 184 | |
| 185 | |
| 186 | |
| 187 | |
| 188 | |
| 189 | |
| 190 |
|
| 191 | function 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: new Map(),
|
| 224 | blockers: 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: new Map(),
|
| 279 | _internalSetRoutes() {
|
| 280 | throw msg("_internalSetRoutes");
|
| 281 | },
|
| 282 | _internalSetStateDoNotUseOrYouWillBreakYourApp() {
|
| 283 | throw msg("_internalSetStateDoNotUseOrYouWillBreakYourApp");
|
| 284 | }
|
| 285 | };
|
| 286 | }
|
| 287 | function createHref(to) {
|
| 288 | return typeof to === "string" ? to : createPath(to);
|
| 289 | }
|
| 290 | function 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 |
|
| 301 | export { StaticRouter, StaticRouterProvider, createStaticRouter };
|