| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 |
|
| 11 | import { ErrorResponseImpl, compilePath } from "../../router/utils.js";
|
| 12 | import { useRouteError } from "../../hooks.js";
|
| 13 | import invariant from "./invariant.js";
|
| 14 | import { loadRouteModule } from "./routeModules.js";
|
| 15 | import { prefetchRouteCss, prefetchStyleLinks } from "./links.js";
|
| 16 | import { RemixRootDefaultErrorBoundary } from "./errorBoundaries.js";
|
| 17 | import { RemixRootDefaultHydrateFallback } from "./fallback.js";
|
| 18 | import * as React$1 from "react";
|
| 19 |
|
| 20 | function groupRoutesByParentId(manifest) {
|
| 21 | let routes = {};
|
| 22 | Object.values(manifest).forEach((route) => {
|
| 23 | if (route) {
|
| 24 | let parentId = route.parentId || "";
|
| 25 | if (!routes[parentId]) routes[parentId] = [];
|
| 26 | routes[parentId].push(route);
|
| 27 | }
|
| 28 | });
|
| 29 | return routes;
|
| 30 | }
|
| 31 | function getRouteComponents(route, routeModule, isSpaMode) {
|
| 32 | let Component = getRouteModuleComponent(routeModule);
|
| 33 | let HydrateFallback = routeModule.HydrateFallback && (!isSpaMode || route.id === "root") ? routeModule.HydrateFallback : route.id === "root" ? RemixRootDefaultHydrateFallback : void 0;
|
| 34 | let ErrorBoundary = routeModule.ErrorBoundary ? routeModule.ErrorBoundary : route.id === "root" ? () => React$1.createElement(RemixRootDefaultErrorBoundary, { error: useRouteError() }) : void 0;
|
| 35 | if (route.id === "root" && routeModule.Layout) return {
|
| 36 | ...Component ? { element: React$1.createElement(routeModule.Layout, null, React$1.createElement(Component, null)) } : { Component },
|
| 37 | ...ErrorBoundary ? { errorElement: React$1.createElement(routeModule.Layout, null, React$1.createElement(ErrorBoundary, null)) } : { ErrorBoundary },
|
| 38 | ...HydrateFallback ? { hydrateFallbackElement: React$1.createElement(routeModule.Layout, null, React$1.createElement(HydrateFallback, null)) } : { HydrateFallback }
|
| 39 | };
|
| 40 | return {
|
| 41 | Component,
|
| 42 | ErrorBoundary,
|
| 43 | HydrateFallback
|
| 44 | };
|
| 45 | }
|
| 46 | function createServerRoutes(manifest, routeModules, future, isSpaMode, parentId = "", routesByParentId = groupRoutesByParentId(manifest), spaModeLazyPromise = Promise.resolve({ Component: () => null })) {
|
| 47 | return (routesByParentId[parentId] || []).map((route) => {
|
| 48 | let routeModule = routeModules[route.id];
|
| 49 | invariant(routeModule, "No `routeModule` available to create server routes");
|
| 50 | let dataRoute = {
|
| 51 | ...getRouteComponents(route, routeModule, isSpaMode),
|
| 52 | caseSensitive: route.caseSensitive,
|
| 53 | id: route.id,
|
| 54 | index: route.index,
|
| 55 | path: route.path,
|
| 56 | handle: routeModule.handle,
|
| 57 | lazy: isSpaMode ? () => spaModeLazyPromise : void 0,
|
| 58 | loader: route.hasLoader || route.hasClientLoader ? () => null : void 0
|
| 59 | };
|
| 60 | let children = createServerRoutes(manifest, routeModules, future, isSpaMode, route.id, routesByParentId, spaModeLazyPromise);
|
| 61 | if (children.length > 0) dataRoute.children = children;
|
| 62 | return dataRoute;
|
| 63 | });
|
| 64 | }
|
| 65 | function createClientRoutesWithHMRRevalidationOptOut(needsRevalidation, manifest, routeModulesCache, initialState, ssr, isSpaMode) {
|
| 66 | return createClientRoutes(manifest, routeModulesCache, initialState, ssr, isSpaMode, "", groupRoutesByParentId(manifest), needsRevalidation);
|
| 67 | }
|
| 68 | function preventInvalidServerHandlerCall(type, route) {
|
| 69 | if (type === "loader" && !route.hasLoader || type === "action" && !route.hasAction) {
|
| 70 | let msg = `You are trying to call ${type === "action" ? "serverAction()" : "serverLoader()"} on a route that does not have a server ${type} (routeId: "${route.id}")`;
|
| 71 | console.error(msg);
|
| 72 | throw new ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
|
| 73 | }
|
| 74 | }
|
| 75 | function noActionDefinedError(type, routeId) {
|
| 76 | let article = type === "clientAction" ? "a" : "an";
|
| 77 | let msg = `Route "${routeId}" does not have ${article} ${type}, but you are trying to submit to it. To fix this, please add ${article} \`${type}\` function to the route`;
|
| 78 | console.error(msg);
|
| 79 | throw new ErrorResponseImpl(405, "Method Not Allowed", new Error(msg), true);
|
| 80 | }
|
| 81 | function createClientRoutes(manifest, routeModulesCache, initialState, ssr, isSpaMode, parentId = "", routesByParentId = groupRoutesByParentId(manifest), needsRevalidation) {
|
| 82 | return (routesByParentId[parentId] || []).map((route) => {
|
| 83 | let routeModule = routeModulesCache[route.id];
|
| 84 | function fetchServerHandler(singleFetch) {
|
| 85 | invariant(typeof singleFetch === "function", "No single fetch function available for route handler");
|
| 86 | return singleFetch();
|
| 87 | }
|
| 88 | function fetchServerLoader(singleFetch) {
|
| 89 | if (!route.hasLoader) return Promise.resolve(null);
|
| 90 | return fetchServerHandler(singleFetch);
|
| 91 | }
|
| 92 | function fetchServerAction(singleFetch) {
|
| 93 | if (!route.hasAction) throw noActionDefinedError("action", route.id);
|
| 94 | return fetchServerHandler(singleFetch);
|
| 95 | }
|
| 96 | function prefetchModule(modulePath) {
|
| 97 | import(
|
| 98 |
|
| 99 |
|
| 100 | modulePath
|
| 101 | );
|
| 102 | }
|
| 103 | function prefetchRouteModuleChunks(route) {
|
| 104 | if (route.clientActionModule) prefetchModule(route.clientActionModule);
|
| 105 | if (route.clientLoaderModule) prefetchModule(route.clientLoaderModule);
|
| 106 | }
|
| 107 | async function prefetchStylesAndCallHandler(handler) {
|
| 108 | let cachedModule = routeModulesCache[route.id];
|
| 109 | let linkPrefetchPromise = cachedModule ? prefetchStyleLinks(route, cachedModule) : Promise.resolve();
|
| 110 | try {
|
| 111 | return handler();
|
| 112 | } finally {
|
| 113 | await linkPrefetchPromise;
|
| 114 | }
|
| 115 | }
|
| 116 | let dataRoute = {
|
| 117 | id: route.id,
|
| 118 | index: route.index,
|
| 119 | path: route.path
|
| 120 | };
|
| 121 | if (routeModule) {
|
| 122 | Object.assign(dataRoute, {
|
| 123 | ...dataRoute,
|
| 124 | ...getRouteComponents(route, routeModule, isSpaMode),
|
| 125 | middleware: routeModule.clientMiddleware,
|
| 126 | handle: routeModule.handle,
|
| 127 | shouldRevalidate: getShouldRevalidateFunction(dataRoute.path, routeModule, route, ssr, needsRevalidation)
|
| 128 | });
|
| 129 | let hasInitialData = initialState && initialState.loaderData && route.id in initialState.loaderData;
|
| 130 | let initialData = hasInitialData ? initialState?.loaderData?.[route.id] : void 0;
|
| 131 | let hasInitialError = initialState && initialState.errors && route.id in initialState.errors;
|
| 132 | let initialError = hasInitialError ? initialState?.errors?.[route.id] : void 0;
|
| 133 | let isHydrationRequest = needsRevalidation == null && (routeModule.clientLoader?.hydrate === true || !route.hasLoader);
|
| 134 | dataRoute.loader = async ({ request, params, context, pattern, url }, singleFetch) => {
|
| 135 | let _isHydrationRequest = isHydrationRequest;
|
| 136 | isHydrationRequest = false;
|
| 137 | return await prefetchStylesAndCallHandler(async () => {
|
| 138 | invariant(routeModule, "No `routeModule` available for critical-route loader");
|
| 139 | if (!routeModule.clientLoader) return fetchServerLoader(singleFetch);
|
| 140 | return routeModule.clientLoader({
|
| 141 | request,
|
| 142 | params,
|
| 143 | context,
|
| 144 | pattern,
|
| 145 | url,
|
| 146 | async serverLoader() {
|
| 147 | preventInvalidServerHandlerCall("loader", route);
|
| 148 | if (_isHydrationRequest) {
|
| 149 | if (hasInitialData) return initialData;
|
| 150 | if (hasInitialError) throw initialError;
|
| 151 | }
|
| 152 | return fetchServerLoader(singleFetch);
|
| 153 | }
|
| 154 | });
|
| 155 | });
|
| 156 | };
|
| 157 | dataRoute.loader.hydrate = shouldHydrateRouteLoader(route.id, routeModule.clientLoader, route.hasLoader, isSpaMode);
|
| 158 | dataRoute.action = ({ request, params, context, pattern, url }, singleFetch) => {
|
| 159 | return prefetchStylesAndCallHandler(async () => {
|
| 160 | invariant(routeModule, "No `routeModule` available for critical-route action");
|
| 161 | if (!routeModule.clientAction) {
|
| 162 | if (isSpaMode) throw noActionDefinedError("clientAction", route.id);
|
| 163 | return fetchServerAction(singleFetch);
|
| 164 | }
|
| 165 | return routeModule.clientAction({
|
| 166 | request,
|
| 167 | params,
|
| 168 | context,
|
| 169 | pattern,
|
| 170 | url,
|
| 171 | async serverAction() {
|
| 172 | preventInvalidServerHandlerCall("action", route);
|
| 173 | return fetchServerAction(singleFetch);
|
| 174 | }
|
| 175 | });
|
| 176 | });
|
| 177 | };
|
| 178 | } else {
|
| 179 | if (!route.hasClientLoader) dataRoute.loader = (_, singleFetch) => prefetchStylesAndCallHandler(() => {
|
| 180 | return fetchServerLoader(singleFetch);
|
| 181 | });
|
| 182 | if (!route.hasClientAction) dataRoute.action = (_, singleFetch) => prefetchStylesAndCallHandler(() => {
|
| 183 | if (isSpaMode) throw noActionDefinedError("clientAction", route.id);
|
| 184 | return fetchServerAction(singleFetch);
|
| 185 | });
|
| 186 | let lazyRoutePromise;
|
| 187 | async function getLazyRoute() {
|
| 188 | if (lazyRoutePromise) return await lazyRoutePromise;
|
| 189 | lazyRoutePromise = (async () => {
|
| 190 | if (route.clientLoaderModule || route.clientActionModule) await new Promise((resolve) => setTimeout(resolve, 0));
|
| 191 | let routeModulePromise = loadRouteModuleWithBlockingLinks(route, routeModulesCache);
|
| 192 | prefetchRouteModuleChunks(route);
|
| 193 | return await routeModulePromise;
|
| 194 | })();
|
| 195 | return await lazyRoutePromise;
|
| 196 | }
|
| 197 | dataRoute.lazy = {
|
| 198 | loader: route.hasClientLoader ? async () => {
|
| 199 | let { clientLoader } = route.clientLoaderModule ? await import(
|
| 200 |
|
| 201 |
|
| 202 | route.clientLoaderModule
|
| 203 | ) : await getLazyRoute();
|
| 204 | invariant(clientLoader, "No `clientLoader` export found");
|
| 205 | return (args, singleFetch) => clientLoader({
|
| 206 | ...args,
|
| 207 | async serverLoader() {
|
| 208 | preventInvalidServerHandlerCall("loader", route);
|
| 209 | return fetchServerLoader(singleFetch);
|
| 210 | }
|
| 211 | });
|
| 212 | } : void 0,
|
| 213 | action: route.hasClientAction ? async () => {
|
| 214 | let clientActionPromise = route.clientActionModule ? import(
|
| 215 |
|
| 216 |
|
| 217 | route.clientActionModule
|
| 218 | ) : getLazyRoute();
|
| 219 | prefetchRouteModuleChunks(route);
|
| 220 | let { clientAction } = await clientActionPromise;
|
| 221 | invariant(clientAction, "No `clientAction` export found");
|
| 222 | return (args, singleFetch) => clientAction({
|
| 223 | ...args,
|
| 224 | async serverAction() {
|
| 225 | preventInvalidServerHandlerCall("action", route);
|
| 226 | return fetchServerAction(singleFetch);
|
| 227 | }
|
| 228 | });
|
| 229 | } : void 0,
|
| 230 | middleware: route.hasClientMiddleware ? async () => {
|
| 231 | let { clientMiddleware } = route.clientMiddlewareModule ? await import(
|
| 232 |
|
| 233 |
|
| 234 | route.clientMiddlewareModule
|
| 235 | ) : await getLazyRoute();
|
| 236 | invariant(clientMiddleware, "No `clientMiddleware` export found");
|
| 237 | return clientMiddleware;
|
| 238 | } : void 0,
|
| 239 | shouldRevalidate: async () => {
|
| 240 | let lazyRoute = await getLazyRoute();
|
| 241 | return getShouldRevalidateFunction(dataRoute.path, lazyRoute, route, ssr, needsRevalidation);
|
| 242 | },
|
| 243 | handle: async () => (await getLazyRoute()).handle,
|
| 244 | Component: async () => (await getLazyRoute()).Component,
|
| 245 | ErrorBoundary: route.hasErrorBoundary ? async () => (await getLazyRoute()).ErrorBoundary : void 0
|
| 246 | };
|
| 247 | }
|
| 248 | let children = createClientRoutes(manifest, routeModulesCache, initialState, ssr, isSpaMode, route.id, routesByParentId, needsRevalidation);
|
| 249 | if (children.length > 0) dataRoute.children = children;
|
| 250 | return dataRoute;
|
| 251 | });
|
| 252 | }
|
| 253 | function getShouldRevalidateFunction(path, route, manifestRoute, ssr, needsRevalidation) {
|
| 254 | if (needsRevalidation) return wrapShouldRevalidateForHdr(manifestRoute.id, route.shouldRevalidate, needsRevalidation);
|
| 255 | if (!ssr && manifestRoute.hasLoader && !manifestRoute.hasClientLoader) {
|
| 256 | let myParams = path ? compilePath(path)[1].map((p) => p.paramName) : [];
|
| 257 | const didParamsChange = (opts) => myParams.some((p) => opts.currentParams[p] !== opts.nextParams[p]);
|
| 258 | if (route.shouldRevalidate) {
|
| 259 | let fn = route.shouldRevalidate;
|
| 260 | return (opts) => fn({
|
| 261 | ...opts,
|
| 262 | defaultShouldRevalidate: didParamsChange(opts)
|
| 263 | });
|
| 264 | } else return (opts) => didParamsChange(opts);
|
| 265 | }
|
| 266 | return route.shouldRevalidate;
|
| 267 | }
|
| 268 | function wrapShouldRevalidateForHdr(routeId, routeShouldRevalidate, needsRevalidation) {
|
| 269 | let handledRevalidation = false;
|
| 270 | return (arg) => {
|
| 271 | if (!handledRevalidation) {
|
| 272 | handledRevalidation = true;
|
| 273 | return needsRevalidation.has(routeId);
|
| 274 | }
|
| 275 | return routeShouldRevalidate ? routeShouldRevalidate(arg) : arg.defaultShouldRevalidate;
|
| 276 | };
|
| 277 | }
|
| 278 | async function loadRouteModuleWithBlockingLinks(route, routeModules) {
|
| 279 | let routeModulePromise = loadRouteModule(route, routeModules);
|
| 280 | let prefetchRouteCssPromise = prefetchRouteCss(route);
|
| 281 | let routeModule = await routeModulePromise;
|
| 282 | await Promise.all([prefetchRouteCssPromise, prefetchStyleLinks(route, routeModule)]);
|
| 283 | return {
|
| 284 | Component: getRouteModuleComponent(routeModule),
|
| 285 | ErrorBoundary: routeModule.ErrorBoundary,
|
| 286 | clientMiddleware: routeModule.clientMiddleware,
|
| 287 | clientAction: routeModule.clientAction,
|
| 288 | clientLoader: routeModule.clientLoader,
|
| 289 | handle: routeModule.handle,
|
| 290 | links: routeModule.links,
|
| 291 | meta: routeModule.meta,
|
| 292 | shouldRevalidate: routeModule.shouldRevalidate
|
| 293 | };
|
| 294 | }
|
| 295 | function getRouteModuleComponent(routeModule) {
|
| 296 | if (routeModule.default == null) return void 0;
|
| 297 | if (!(typeof routeModule.default === "object" && Object.keys(routeModule.default).length === 0)) return routeModule.default;
|
| 298 | }
|
| 299 | function shouldHydrateRouteLoader(routeId, clientLoader, hasLoader, isSpaMode) {
|
| 300 | return isSpaMode && routeId !== "root" || clientLoader != null && (clientLoader.hydrate === true || hasLoader !== true);
|
| 301 | }
|
| 302 |
|
| 303 | export { createClientRoutes, createClientRoutesWithHMRRevalidationOptOut, createServerRoutes, noActionDefinedError, shouldHydrateRouteLoader };
|