| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 |
|
| 11 | import { PROTOCOL_RELATIVE_URL_REGEX } from "../router/url.js";
|
| 12 | import { createBrowserHistory, createPath, invariant } from "../router/history.js";
|
| 13 | import { ErrorResponseImpl, createContext, resolvePath } from "../router/utils.js";
|
| 14 | import { validateNavigationTarget } from "../router/navigation.js";
|
| 15 | import { createRouter, hasInvalidProtocol, isMutationMethod } from "../router/router.js";
|
| 16 | import { RSCRouterContext } from "../context.js";
|
| 17 | import { RouterProvider } from "../components.js";
|
| 18 | import { createRequestInit } from "../dom/ssr/data.js";
|
| 19 | import { getSingleFetchDataStrategyImpl, singleFetchUrl, stripIndexParam } from "../dom/ssr/single-fetch.js";
|
| 20 | import { noActionDefinedError, shouldHydrateRouteLoader } from "../dom/ssr/routes.js";
|
| 21 | import { getPathsWithAncestors, handleClientVersionMismatch } from "../dom/ssr/fog-of-war.js";
|
| 22 | import { FrameworkContext, setIsHydrated } from "../dom/ssr/components.js";
|
| 23 | import { RSCRouterGlobalErrorBoundary } from "./errorBoundaries.js";
|
| 24 | import { populateRSCRouteModules } from "./route-modules.js";
|
| 25 | import { getHydrationData } from "../dom/ssr/hydration.js";
|
| 26 | import * as React$1 from "react";
|
| 27 | import * as ReactDOM from "react-dom";
|
| 28 |
|
| 29 | const defaultManifestPath = "/__manifest";
|
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | |
| 39 | |
| 40 | |
| 41 | |
| 42 | |
| 43 | |
| 44 | |
| 45 | |
| 46 | |
| 47 | |
| 48 | |
| 49 | |
| 50 | |
| 51 | |
| 52 | |
| 53 | |
| 54 | |
| 55 | |
| 56 | |
| 57 | |
| 58 | |
| 59 | |
| 60 | |
| 61 | |
| 62 | |
| 63 | |
| 64 | |
| 65 |
|
| 66 | function createCallServer({ createFromReadableStream, createTemporaryReferenceSet, encodeReply, fetch: fetchImplementation = fetch }) {
|
| 67 | const globalVar = window;
|
| 68 | let landedActionId = 0;
|
| 69 | return async (id, args) => {
|
| 70 | let actionId = globalVar.__routerActionID = (globalVar.__routerActionID ??= 0) + 1;
|
| 71 | const temporaryReferences = createTemporaryReferenceSet();
|
| 72 | const payloadPromise = fetchImplementation(new Request(location.href, {
|
| 73 | body: await encodeReply(args, { temporaryReferences }),
|
| 74 | method: "POST",
|
| 75 | headers: {
|
| 76 | Accept: "text/x-component",
|
| 77 | "rsc-action-id": id
|
| 78 | }
|
| 79 | })).then((response) => {
|
| 80 | if (!response.body) throw new Error("No response body");
|
| 81 | return createFromReadableStream(response.body, { temporaryReferences });
|
| 82 | });
|
| 83 | React$1.startTransition(() => Promise.resolve(payloadPromise).then(async (payload) => {
|
| 84 | if (payload.type === "redirect") {
|
| 85 | let location = normalizeRedirectLocation(payload.location);
|
| 86 | validateNavigationTarget(payload.location, location, new URL(window.location.href), "allow-explicit");
|
| 87 | if (payload.reload || isExternalLocation(location)) {
|
| 88 | if (hasInvalidProtocol(location)) throw new Error("Invalid redirect location");
|
| 89 | window.location.href = location;
|
| 90 | return;
|
| 91 | }
|
| 92 | React$1.startTransition(() => {
|
| 93 | globalVar.__reactRouterDataRouter.navigate(location, { replace: payload.replace });
|
| 94 | });
|
| 95 | return;
|
| 96 | }
|
| 97 | if (payload.type !== "action") throw new Error("Unexpected payload type");
|
| 98 | const rerender = await payload.rerender;
|
| 99 | if (rerender && landedActionId < actionId && globalVar.__routerActionID <= actionId) {
|
| 100 | if (rerender.type === "redirect") {
|
| 101 | let location = normalizeRedirectLocation(rerender.location);
|
| 102 | validateNavigationTarget(rerender.location, location, new URL(window.location.href), "allow-explicit");
|
| 103 | if (rerender.reload || isExternalLocation(location)) {
|
| 104 | if (hasInvalidProtocol(location)) throw new Error("Invalid redirect location");
|
| 105 | window.location.href = location;
|
| 106 | return;
|
| 107 | }
|
| 108 | React$1.startTransition(() => {
|
| 109 | globalVar.__reactRouterDataRouter.navigate(location, { replace: rerender.replace });
|
| 110 | });
|
| 111 | return;
|
| 112 | }
|
| 113 | React$1.startTransition(() => {
|
| 114 | let lastMatch;
|
| 115 | for (const match of rerender.matches) {
|
| 116 | globalVar.__reactRouterDataRouter.patchRoutes(lastMatch?.id ?? null, [createRouteFromServerManifest(match)], true);
|
| 117 | lastMatch = match;
|
| 118 | }
|
| 119 | window.__reactRouterDataRouter._internalSetStateDoNotUseOrYouWillBreakYourApp({
|
| 120 | loaderData: Object.assign({}, globalVar.__reactRouterDataRouter.state.loaderData, rerender.loaderData),
|
| 121 | errors: rerender.errors ? Object.assign({}, globalVar.__reactRouterDataRouter.state.errors, rerender.errors) : null
|
| 122 | });
|
| 123 | });
|
| 124 | }
|
| 125 | }).catch(() => {}));
|
| 126 | return payloadPromise.then((payload) => {
|
| 127 | if (payload.type !== "action" && payload.type !== "redirect") throw new Error("Unexpected payload type");
|
| 128 | return payload.actionResult;
|
| 129 | });
|
| 130 | };
|
| 131 | }
|
| 132 | function createRouterFromPayload({ fetchImplementation, createFromReadableStream, getContext, payload }) {
|
| 133 | const globalVar = window;
|
| 134 | if (globalVar.__reactRouterDataRouter && globalVar.__reactRouterRouteModules) return {
|
| 135 | router: globalVar.__reactRouterDataRouter,
|
| 136 | routeModules: globalVar.__reactRouterRouteModules
|
| 137 | };
|
| 138 | if (payload.type !== "render") throw new Error("Invalid payload type");
|
| 139 | let { clientVersion } = payload;
|
| 140 | globalVar.__reactRouterRouteModules = globalVar.__reactRouterRouteModules ?? {};
|
| 141 | populateRSCRouteModules(globalVar.__reactRouterRouteModules, payload.matches);
|
| 142 | let routes = payload.matches.reduceRight((previous, match) => {
|
| 143 | const route = createRouteFromServerManifest(match, payload);
|
| 144 | if (previous.length > 0) route.children = previous;
|
| 145 | else if (!route.index) route.children = [];
|
| 146 | return [route];
|
| 147 | }, []);
|
| 148 | let applyPatchesPromise;
|
| 149 | globalVar.__reactRouterDataRouter = createRouter({
|
| 150 | routes,
|
| 151 | getContext,
|
| 152 | basename: payload.basename,
|
| 153 | history: createBrowserHistory(),
|
| 154 | hydrationData: getHydrationData({
|
| 155 | state: {
|
| 156 | loaderData: payload.loaderData,
|
| 157 | actionData: payload.actionData,
|
| 158 | errors: payload.errors
|
| 159 | },
|
| 160 | routes,
|
| 161 | getRouteInfo: (routeId) => {
|
| 162 | let match = payload.matches.find((m) => m.id === routeId);
|
| 163 | invariant(match, "Route not found in payload");
|
| 164 | return {
|
| 165 | clientLoader: match.clientLoader,
|
| 166 | hasLoader: match.hasLoader,
|
| 167 | hasHydrateFallback: match.hydrateFallbackElement != null
|
| 168 | };
|
| 169 | },
|
| 170 | location: payload.location,
|
| 171 | basename: payload.basename,
|
| 172 | future: {},
|
| 173 | isSpaMode: false
|
| 174 | }),
|
| 175 | async patchRoutesOnNavigation({ path, signal, fetcherKey }) {
|
| 176 | if (payload.routeDiscovery.mode === "initial") {
|
| 177 | if (!applyPatchesPromise) applyPatchesPromise = (async () => {
|
| 178 | if (!payload.patches) return;
|
| 179 | let patches = await payload.patches;
|
| 180 | React$1.startTransition(() => {
|
| 181 | patches.forEach((p) => {
|
| 182 | window.__reactRouterDataRouter.patchRoutes(p.parentId ?? null, [createRouteFromServerManifest(p)]);
|
| 183 | });
|
| 184 | });
|
| 185 | })();
|
| 186 | await applyPatchesPromise;
|
| 187 | return;
|
| 188 | }
|
| 189 | if (discoveredPaths.has(path)) return;
|
| 190 | let { state } = globalVar.__reactRouterDataRouter;
|
| 191 | await fetchAndApplyManifestPatches([path], createFromReadableStream, fetchImplementation, clientVersion, fetcherKey ? window.location.href : createPath(state.navigation.location || state.location), signal);
|
| 192 | },
|
| 193 | dataStrategy: getRSCSingleFetchDataStrategy(() => globalVar.__reactRouterDataRouter, true, createFromReadableStream, fetchImplementation, clientVersion)
|
| 194 | });
|
| 195 | if (globalVar.__reactRouterDataRouter.state.initialized) {
|
| 196 | globalVar.__routerInitialized = true;
|
| 197 | globalVar.__reactRouterDataRouter.initialize();
|
| 198 | } else globalVar.__routerInitialized = false;
|
| 199 | let lastLoaderData = void 0;
|
| 200 | globalVar.__reactRouterDataRouter.subscribe(({ loaderData, actionData }) => {
|
| 201 | if (lastLoaderData !== loaderData) globalVar.__routerActionID = (globalVar.__routerActionID ??= 0) + 1;
|
| 202 | });
|
| 203 | globalVar.__reactRouterDataRouter._updateRoutesForHMR = (routeUpdateByRouteId) => {
|
| 204 | const oldRoutes = window.__reactRouterDataRouter.routes;
|
| 205 | const newRoutes = [];
|
| 206 | function walkRoutes(routes, parentId) {
|
| 207 | return routes.map((route) => {
|
| 208 | const routeUpdate = routeUpdateByRouteId.get(route.id);
|
| 209 | if (routeUpdate) {
|
| 210 | const { routeModule, hasAction, hasComponent, hasLoader } = routeUpdate;
|
| 211 | const newRoute = createRouteFromServerManifest({
|
| 212 | clientAction: routeModule.clientAction,
|
| 213 | clientLoader: routeModule.clientLoader,
|
| 214 | element: route.element,
|
| 215 | errorElement: route.errorElement,
|
| 216 | handle: route.handle,
|
| 217 | hasAction,
|
| 218 | hasComponent,
|
| 219 | hasLoader,
|
| 220 | hydrateFallbackElement: route.hydrateFallbackElement,
|
| 221 | id: route.id,
|
| 222 | index: route.index,
|
| 223 | links: routeModule.links,
|
| 224 | meta: routeModule.meta,
|
| 225 | parentId,
|
| 226 | path: route.path,
|
| 227 | shouldRevalidate: routeModule.shouldRevalidate
|
| 228 | });
|
| 229 | if (route.children) newRoute.children = walkRoutes(route.children, route.id);
|
| 230 | return newRoute;
|
| 231 | }
|
| 232 | const updatedRoute = { ...route };
|
| 233 | if (route.children) updatedRoute.children = walkRoutes(route.children, route.id);
|
| 234 | return updatedRoute;
|
| 235 | });
|
| 236 | }
|
| 237 | newRoutes.push(...walkRoutes(oldRoutes, void 0));
|
| 238 | window.__reactRouterDataRouter._internalSetRoutes(newRoutes);
|
| 239 | };
|
| 240 | return {
|
| 241 | router: globalVar.__reactRouterDataRouter,
|
| 242 | routeModules: globalVar.__reactRouterRouteModules
|
| 243 | };
|
| 244 | }
|
| 245 | const renderedRoutesContext = createContext();
|
| 246 | function getRSCSingleFetchDataStrategy(getRouter, ssr, createFromReadableStream, fetchImplementation, clientVersion) {
|
| 247 | let dataStrategy = getSingleFetchDataStrategyImpl(getRouter, (match) => {
|
| 248 | let M = match;
|
| 249 | return {
|
| 250 | hasLoader: M.route.hasLoader,
|
| 251 | hasClientLoader: M.route.hasClientLoader
|
| 252 | };
|
| 253 | }, getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion), ssr, (match) => {
|
| 254 | let M = match;
|
| 255 | return !M.route.hasComponent || M.route.element != null;
|
| 256 | });
|
| 257 | return async (args) => args.runClientMiddleware(async () => {
|
| 258 | args.context.set(renderedRoutesContext, []);
|
| 259 | let results = await dataStrategy(args);
|
| 260 | const renderedRoutesById = new Map();
|
| 261 | for (const route of args.context.get(renderedRoutesContext)) {
|
| 262 | if (!renderedRoutesById.has(route.id)) renderedRoutesById.set(route.id, []);
|
| 263 | renderedRoutesById.get(route.id).push(route);
|
| 264 | }
|
| 265 | React$1.startTransition(() => {
|
| 266 | for (const match of args.matches) {
|
| 267 | const renderedRoutes = renderedRoutesById.get(match.route.id);
|
| 268 | if (renderedRoutes) for (const rendered of renderedRoutes) window.__reactRouterDataRouter.patchRoutes(rendered.parentId ?? null, [createRouteFromServerManifest(rendered)], true);
|
| 269 | }
|
| 270 | });
|
| 271 | return results;
|
| 272 | });
|
| 273 | }
|
| 274 | function getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion) {
|
| 275 | return async (args, targetRoutes) => {
|
| 276 | let { request, context } = args;
|
| 277 | let url = singleFetchUrl(request.url, "rsc");
|
| 278 | if (request.method === "GET") {
|
| 279 | url = stripIndexParam(url);
|
| 280 | if (targetRoutes) url.searchParams.set("_routes", targetRoutes.join(","));
|
| 281 | }
|
| 282 | let res = await fetchImplementation(new Request(url, await createRequestInit(request)));
|
| 283 | if (res.status >= 400 && !res.headers.has("X-Remix-Response")) throw new ErrorResponseImpl(res.status, res.statusText, await res.text());
|
| 284 | invariant(res.body, "No response body to decode");
|
| 285 | try {
|
| 286 | const payload = await createFromReadableStream(res.body, { temporaryReferences: void 0 });
|
| 287 | if (payload.type === "redirect") return {
|
| 288 | status: res.status,
|
| 289 | data: { redirect: {
|
| 290 | redirect: payload.location,
|
| 291 | reload: payload.reload,
|
| 292 | replace: payload.replace,
|
| 293 | revalidate: false,
|
| 294 | status: payload.status
|
| 295 | } }
|
| 296 | };
|
| 297 | if (payload.type !== "render") throw new Error("Unexpected payload type");
|
| 298 | if (clientVersion !== void 0 && await handleClientVersionMismatch(payload.clientVersion !== clientVersion, clientVersion, createPath(getRouter().state.navigation.location || getRouter().state.location))) return new Promise(() => {});
|
| 299 | context.get(renderedRoutesContext).push(...payload.matches);
|
| 300 | let results = { routes: {} };
|
| 301 | const dataKey = isMutationMethod(request.method) ? "actionData" : "loaderData";
|
| 302 | for (let [routeId, data] of Object.entries(payload[dataKey] || {})) results.routes[routeId] = { data };
|
| 303 | if (payload.errors) for (let [routeId, error] of Object.entries(payload.errors)) results.routes[routeId] = { error };
|
| 304 | return {
|
| 305 | status: res.status,
|
| 306 | data: results
|
| 307 | };
|
| 308 | } catch (cause) {
|
| 309 | throw new Error("Unable to decode RSC response", { cause });
|
| 310 | }
|
| 311 | };
|
| 312 | }
|
| 313 | |
| 314 | |
| 315 | |
| 316 | |
| 317 | |
| 318 | |
| 319 | |
| 320 | |
| 321 | |
| 322 | |
| 323 | |
| 324 | |
| 325 | |
| 326 | |
| 327 | |
| 328 | |
| 329 | |
| 330 | |
| 331 | |
| 332 | |
| 333 | |
| 334 | |
| 335 | |
| 336 | |
| 337 | |
| 338 | |
| 339 | |
| 340 | |
| 341 | |
| 342 | |
| 343 | |
| 344 | |
| 345 | |
| 346 | |
| 347 | |
| 348 | |
| 349 | |
| 350 | |
| 351 |
|
| 352 | function RSCHydratedRouter({ createFromReadableStream, fetch: fetchImplementation = fetch, payload, getContext }) {
|
| 353 | if (payload.type !== "render") throw new Error("Invalid payload type");
|
| 354 | let { routeDiscovery, clientVersion } = payload;
|
| 355 | let { router, routeModules } = React$1.useMemo(() => createRouterFromPayload({
|
| 356 | payload,
|
| 357 | fetchImplementation,
|
| 358 | getContext,
|
| 359 | createFromReadableStream
|
| 360 | }), [
|
| 361 | createFromReadableStream,
|
| 362 | payload,
|
| 363 | fetchImplementation,
|
| 364 | getContext
|
| 365 | ]);
|
| 366 | React$1.useEffect(() => {
|
| 367 | setIsHydrated();
|
| 368 | }, []);
|
| 369 | React$1.useLayoutEffect(() => {
|
| 370 | const globalVar = window;
|
| 371 | if (!globalVar.__routerInitialized) {
|
| 372 | globalVar.__routerInitialized = true;
|
| 373 | globalVar.__reactRouterDataRouter.initialize();
|
| 374 | }
|
| 375 | }, []);
|
| 376 | let [{ routes, state }, setState] = React$1.useState(() => ({
|
| 377 | routes: cloneRoutes(router.routes),
|
| 378 | state: router.state
|
| 379 | }));
|
| 380 | React$1.useLayoutEffect(() => router.subscribe((newState) => {
|
| 381 | if (diffRoutes(router.routes, routes)) React$1.startTransition(() => {
|
| 382 | setState({
|
| 383 | routes: cloneRoutes(router.routes),
|
| 384 | state: newState
|
| 385 | });
|
| 386 | });
|
| 387 | }), [
|
| 388 | router.subscribe,
|
| 389 | routes,
|
| 390 | router
|
| 391 | ]);
|
| 392 | const transitionEnabledRouter = React$1.useMemo(() => ({
|
| 393 | ...router,
|
| 394 | state,
|
| 395 | routes
|
| 396 | }), [
|
| 397 | router,
|
| 398 | routes,
|
| 399 | state
|
| 400 | ]);
|
| 401 | React$1.useEffect(() => {
|
| 402 | if (routeDiscovery.mode === "initial" || window.navigator?.connection?.saveData === true) return;
|
| 403 | function registerElement(el) {
|
| 404 | let path = el.tagName === "FORM" ? el.getAttribute("action") : el.getAttribute("href");
|
| 405 | if (!path) return;
|
| 406 | let pathname = el.tagName === "A" ? el.pathname : new URL(path, window.location.origin).pathname;
|
| 407 | if (!discoveredPaths.has(pathname)) nextPaths.add(pathname);
|
| 408 | }
|
| 409 | async function fetchPatches() {
|
| 410 | document.querySelectorAll("a[data-discover], form[data-discover]").forEach(registerElement);
|
| 411 | let paths = Array.from(nextPaths.keys()).filter((path) => {
|
| 412 | if (discoveredPaths.has(path)) {
|
| 413 | nextPaths.delete(path);
|
| 414 | return false;
|
| 415 | }
|
| 416 | return true;
|
| 417 | });
|
| 418 | if (paths.length === 0) return;
|
| 419 | try {
|
| 420 | await fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, null);
|
| 421 | } catch (e) {
|
| 422 | console.error("Failed to fetch manifest patches", e);
|
| 423 | }
|
| 424 | }
|
| 425 | let debouncedFetchPatches = debounce(fetchPatches, 100);
|
| 426 | fetchPatches();
|
| 427 | new MutationObserver(() => debouncedFetchPatches()).observe(document.documentElement, {
|
| 428 | subtree: true,
|
| 429 | childList: true,
|
| 430 | attributes: true,
|
| 431 | attributeFilter: [
|
| 432 | "data-discover",
|
| 433 | "href",
|
| 434 | "action"
|
| 435 | ]
|
| 436 | });
|
| 437 | }, [
|
| 438 | routeDiscovery,
|
| 439 | createFromReadableStream,
|
| 440 | fetchImplementation,
|
| 441 | clientVersion
|
| 442 | ]);
|
| 443 | const frameworkContext = {
|
| 444 | future: {},
|
| 445 | isSpaMode: false,
|
| 446 | ssr: true,
|
| 447 | criticalCss: "",
|
| 448 | manifest: {
|
| 449 | routes: {},
|
| 450 | version: "1",
|
| 451 | url: "",
|
| 452 | entry: {
|
| 453 | module: "",
|
| 454 | imports: []
|
| 455 | }
|
| 456 | },
|
| 457 | routeDiscovery: payload.routeDiscovery.mode === "initial" ? {
|
| 458 | mode: "initial",
|
| 459 | manifestPath: defaultManifestPath
|
| 460 | } : {
|
| 461 | mode: "lazy",
|
| 462 | manifestPath: payload.routeDiscovery.manifestPath || defaultManifestPath
|
| 463 | },
|
| 464 | routeModules
|
| 465 | };
|
| 466 | return React$1.createElement(RSCRouterContext.Provider, { value: true }, React$1.createElement(RSCRouterGlobalErrorBoundary, { location: state.location }, React$1.createElement(FrameworkContext.Provider, { value: frameworkContext }, React$1.createElement(RouterProvider, {
|
| 467 | router: transitionEnabledRouter,
|
| 468 | flushSync: ReactDOM.flushSync
|
| 469 | }))));
|
| 470 | }
|
| 471 | function createRouteFromServerManifest(match, payload) {
|
| 472 | let hasInitialData = payload && match.id in payload.loaderData;
|
| 473 | let initialData = payload?.loaderData[match.id];
|
| 474 | let hasInitialError = payload?.errors && match.id in payload.errors;
|
| 475 | let initialError = payload?.errors?.[match.id];
|
| 476 | let isHydrationRequest = match.clientLoader?.hydrate === true || !match.hasLoader || match.hasComponent && !match.element;
|
| 477 | invariant(window.__reactRouterRouteModules);
|
| 478 | populateRSCRouteModules(window.__reactRouterRouteModules, match);
|
| 479 | let dataRoute = {
|
| 480 | id: match.id,
|
| 481 | element: match.element,
|
| 482 | errorElement: match.errorElement,
|
| 483 | handle: match.handle,
|
| 484 | hydrateFallbackElement: match.hydrateFallbackElement,
|
| 485 | index: match.index,
|
| 486 | loader: match.clientLoader ? async (args, singleFetch) => {
|
| 487 | let _isHydrationRequest = isHydrationRequest;
|
| 488 | isHydrationRequest = false;
|
| 489 | return await match.clientLoader({
|
| 490 | ...args,
|
| 491 | serverLoader: () => {
|
| 492 | preventInvalidServerHandlerCall("loader", match.id, match.hasLoader);
|
| 493 | if (_isHydrationRequest) {
|
| 494 | if (hasInitialData) return initialData;
|
| 495 | if (hasInitialError) throw initialError;
|
| 496 | }
|
| 497 | return callSingleFetch(singleFetch);
|
| 498 | }
|
| 499 | });
|
| 500 | } : (_, singleFetch) => callSingleFetch(singleFetch),
|
| 501 | action: match.clientAction ? (args, singleFetch) => match.clientAction({
|
| 502 | ...args,
|
| 503 | serverAction: async () => {
|
| 504 | preventInvalidServerHandlerCall("action", match.id, match.hasLoader);
|
| 505 | return await callSingleFetch(singleFetch);
|
| 506 | }
|
| 507 | }) : match.hasAction ? (_, singleFetch) => callSingleFetch(singleFetch) : () => {
|
| 508 | throw noActionDefinedError("action", match.id);
|
| 509 | },
|
| 510 | path: match.path,
|
| 511 | shouldRevalidate: match.shouldRevalidate,
|
| 512 | hasLoader: true,
|
| 513 | hasClientLoader: match.clientLoader != null,
|
| 514 | hasComponent: match.hasComponent,
|
| 515 | hasAction: match.hasAction,
|
| 516 | hasClientAction: match.clientAction != null
|
| 517 | };
|
| 518 | if (typeof dataRoute.loader === "function") dataRoute.loader.hydrate = shouldHydrateRouteLoader(match.id, match.clientLoader, match.hasLoader, false);
|
| 519 | return dataRoute;
|
| 520 | }
|
| 521 | function callSingleFetch(singleFetch) {
|
| 522 | invariant(typeof singleFetch === "function", "Invalid singleFetch parameter");
|
| 523 | return singleFetch();
|
| 524 | }
|
| 525 | function preventInvalidServerHandlerCall(type, routeId, hasHandler) {
|
| 526 | if (!hasHandler) {
|
| 527 | let msg = `You are trying to call ${type === "action" ? "serverAction()" : "serverLoader()"} on a route that does not have a server ${type} (routeId: "${routeId}")`;
|
| 528 | console.error(msg);
|
| 529 | throw new ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
|
| 530 | }
|
| 531 | }
|
| 532 | const nextPaths = new Set();
|
| 533 | const discoveredPathsMaxSize = 1e3;
|
| 534 | const discoveredPaths = new Set();
|
| 535 | function getManifestUrl(paths, clientVersion) {
|
| 536 | if (paths.length === 0) return null;
|
| 537 | let url;
|
| 538 | if (paths.length === 1) url = new URL(`${paths[0]}.manifest`, window.location.origin);
|
| 539 | else {
|
| 540 | let basename = (window.__reactRouterDataRouter.basename ?? "").replace(/^\/|\/$/g, "");
|
| 541 | url = new URL(`${basename}/.manifest`, window.location.origin);
|
| 542 | url.searchParams.set("paths", paths.sort().join(","));
|
| 543 | }
|
| 544 | if (clientVersion !== void 0) url.searchParams.set("version", clientVersion);
|
| 545 | return url;
|
| 546 | }
|
| 547 | async function fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, errorReloadPath, signal) {
|
| 548 | paths = getPathsWithAncestors(paths);
|
| 549 | let url = getManifestUrl(paths, clientVersion);
|
| 550 | if (url == null) return;
|
| 551 | if (url.toString().length > 7680) {
|
| 552 | nextPaths.clear();
|
| 553 | return;
|
| 554 | }
|
| 555 | let response = await fetchImplementation(new Request(url, { signal }));
|
| 556 | if (clientVersion !== void 0 && response.status === 204 && response.headers.has("X-Remix-Reload-Document")) {
|
| 557 | await handleClientVersionMismatch(true, clientVersion, errorReloadPath);
|
| 558 | return;
|
| 559 | }
|
| 560 | if (!response.body || response.status < 200 || response.status >= 300) throw new Error("Unable to fetch new route matches from the server");
|
| 561 | let payload = await createFromReadableStream(response.body, { temporaryReferences: void 0 });
|
| 562 | if (payload.type !== "manifest") throw new Error("Failed to patch routes");
|
| 563 | paths.forEach((p) => addToFifoQueue(p, discoveredPaths));
|
| 564 | let patches = await payload.patches;
|
| 565 | React$1.startTransition(() => {
|
| 566 | patches.forEach((p) => {
|
| 567 | window.__reactRouterDataRouter.patchRoutes(p.parentId ?? null, [createRouteFromServerManifest(p)]);
|
| 568 | });
|
| 569 | });
|
| 570 | }
|
| 571 | function addToFifoQueue(path, queue) {
|
| 572 | if (queue.size >= discoveredPathsMaxSize) {
|
| 573 | let first = queue.values().next().value;
|
| 574 | if (typeof first === "string") queue.delete(first);
|
| 575 | }
|
| 576 | queue.add(path);
|
| 577 | }
|
| 578 | function debounce(callback, wait) {
|
| 579 | let timeoutId;
|
| 580 | return (...args) => {
|
| 581 | window.clearTimeout(timeoutId);
|
| 582 | timeoutId = window.setTimeout(() => callback(...args), wait);
|
| 583 | };
|
| 584 | }
|
| 585 | function isExternalLocation(location) {
|
| 586 | return new URL(location, window.location.href).origin !== window.location.origin;
|
| 587 | }
|
| 588 | function normalizeRedirectLocation(location) {
|
| 589 | if (PROTOCOL_RELATIVE_URL_REGEX.test(location)) {
|
| 590 | let path = resolvePath(location);
|
| 591 | return path.pathname + path.search + path.hash;
|
| 592 | }
|
| 593 | return location;
|
| 594 | }
|
| 595 | function cloneRoutes(routes) {
|
| 596 | if (!routes) return void 0;
|
| 597 | return routes.map((route) => ({
|
| 598 | ...route,
|
| 599 | children: cloneRoutes(route.children)
|
| 600 | }));
|
| 601 | }
|
| 602 | function diffRoutes(a, b) {
|
| 603 | if (a.length !== b.length) return true;
|
| 604 | return a.some((route, index) => {
|
| 605 | if (route.element !== b[index].element) return true;
|
| 606 | if (route.errorElement !== b[index].errorElement) return true;
|
| 607 | if (route.hydrateFallbackElement !== b[index].hydrateFallbackElement) return true;
|
| 608 | if (route.hasLoader !== b[index].hasLoader) return true;
|
| 609 | if (route.hasClientLoader !== b[index].hasClientLoader) return true;
|
| 610 | if (route.hasAction !== b[index].hasAction) return true;
|
| 611 | if (route.hasClientAction !== b[index].hasClientAction) return true;
|
| 612 | return diffRoutes(route.children || [], b[index].children || []);
|
| 613 | });
|
| 614 | }
|
| 615 |
|
| 616 | export { RSCHydratedRouter, createCallServer };
|