UNPKG

@tanstack/solid-router

Version:

Modern and scalable routing for Solid applications

343 lines (342 loc) 13.1 kB
import { nonRouteComponentContext, renderInNonRouteComponentContext } from "./nonRouteComponentContext.js"; import { CatchBoundary, ErrorComponent } from "./CatchBoundary.js"; import { ClientOnly } from "./ClientOnly.js"; import { useRouter } from "./useRouter.js"; import { nearestMatchContext } from "./matchContext.js"; import { SafeFragment } from "./SafeFragment.js"; import { CatchNotFound, getNotFound } from "./not-found.js"; import { renderRouteNotFound } from "./renderRouteNotFound.js"; import { ScrollRestoration } from "./scroll-restoration.js"; import { rootRouteId } from "@tanstack/router-core"; import { Dynamic, createComponent, memo, mergeProps } from "@solidjs/web"; import * as Solid from "solid-js"; import { isServer } from "@tanstack/router-core/isServer"; //#region src/Match.tsx var NearestMatchContext = nearestMatchContext; var renderScrollRestoration = isServer === false ? void 0 : (router, route) => (isServer ?? router.isServer) && route.parentRoute?.id === rootRouteId && router.options.scrollRestoration ? createComponent(ScrollRestoration, {}) : null; var Match = (props) => { const router = useRouter(); const currentMatch = Solid.createMemo(() => router.stores.getMatchStore(props.routeId).get()); const matchState = Solid.createMemo(() => { const match = currentMatch(); if (!match) return null; return { routeId: match.routeId, ssr: match.ssr, status: match.status }; }); const nearestMatch = [() => props.routeId, currentMatch]; return createComponent(Solid.Show, { get when() { return matchState(); }, children: (currentMatchState) => { const route = router.routesById[props.routeId]; const routeOptions = () => { currentMatchState(); return route.options; }; const resolvePendingComponent = Solid.createMemo(() => routeOptions().pendingComponent ?? router.options.defaultPendingComponent); const routeErrorComponent = Solid.createMemo(() => routeOptions().errorComponent ?? router.options.defaultErrorComponent); const routeNotFoundComponent = Solid.createMemo(() => route.isRoot ? routeOptions().notFoundComponent ?? router.options.notFoundRoute?.options.component ?? router.options.defaultNotFoundComponent : routeOptions().notFoundComponent ?? router.options.defaultNotFoundComponent); const resolvedNoSsr = Solid.createMemo(() => currentMatchState().ssr === false || currentMatchState().ssr === "data-only"); const ResolvedLoadingBoundary = Solid.createMemo(() => resolvedNoSsr() ? SafeFragment : Solid.Loading); const shouldSkipLoadingFallback = Solid.createMemo(() => isServer ?? router.isServer ? resolvedNoSsr() : currentMatchState().ssr === "data-only"); const ResolvedNotFoundBoundary = Solid.createMemo(() => routeNotFoundComponent() ? CatchNotFound : SafeFragment); const ShellComponent = Solid.createMemo(() => route.isRoot ? route.options.shellComponent ?? SafeFragment : SafeFragment); const MatchContent = () => createComponent(Solid.Show, { get when() { return currentMatchState().status !== "pending"; }, get fallback() { if (process.env.NODE_ENV !== "production") return renderInNonRouteComponentContext(() => createComponent(Dynamic, { get component() { return resolvePendingComponent(); } }), "pendingComponent"); return createComponent(Dynamic, { get component() { return resolvePendingComponent(); } }); }, get children() { return createComponent(MatchInner, {}); } }); const RouteContent = () => createComponent(Dynamic, { get component() { return ResolvedNotFoundBoundary(); }, fallback: (error) => { const notFoundError = getNotFound(error) ?? error; notFoundError.routeId ??= currentMatchState().routeId; if (notFoundError.routeId !== currentMatchState().routeId) throw notFoundError; return process.env.NODE_ENV !== "production" ? renderInNonRouteComponentContext(() => createComponent(Dynamic, mergeProps({ get component() { return routeNotFoundComponent(); } }, notFoundError)), "notFoundComponent") : createComponent(Dynamic, mergeProps({ get component() { return routeNotFoundComponent(); } }, notFoundError)); }, get children() { return createComponent(Solid.Switch, { get children() { return [createComponent(Solid.Match, { get when() { return resolvedNoSsr(); }, get children() { return createComponent(ClientOnly, { get fallback() { if (process.env.NODE_ENV !== "production") return renderInNonRouteComponentContext(() => createComponent(Dynamic, { get component() { return resolvePendingComponent(); } }), "pendingComponent"); return createComponent(Dynamic, { get component() { return resolvePendingComponent(); } }); }, get children() { return createComponent(MatchContent, {}); } }); } }), createComponent(Solid.Match, { get when() { return !resolvedNoSsr(); }, get children() { return createComponent(MatchContent, {}); } })]; } }); } }); return createComponent(Dynamic, { get component() { return ShellComponent(); }, get children() { return [createComponent(NearestMatchContext, { value: nearestMatch, get children() { return createComponent(Dynamic, { get component() { return ResolvedLoadingBoundary(); }, get fallback() { if (shouldSkipLoadingFallback()) return; if (process.env.NODE_ENV !== "production") return renderInNonRouteComponentContext(() => createComponent(Dynamic, { get component() { return resolvePendingComponent(); } }), "pendingComponent"); return createComponent(Dynamic, { get component() { return resolvePendingComponent(); } }); }, get children() { return createComponent(Solid.Show, { get when() { return routeErrorComponent(); }, get fallback() { return createComponent(RouteContent, {}); }, children: (errorComponent) => createComponent(CatchBoundary, { getResetKey: () => { const matches = router.stores.matches.get(); const index = matches.findIndex((match) => match.routeId === props.routeId); if (index === -1) return ""; let key = ""; for (let i = index; i < matches.length; i++) { const match = matches[i]; key += `${match.status}|${match.updatedAt},`; } return key; }, get errorComponent() { return errorComponent(); }, onCatch: (error) => { const notFoundError = getNotFound(error); if (notFoundError) { notFoundError.routeId ??= currentMatchState().routeId; throw notFoundError; } if (process.env.NODE_ENV !== "production") console.warn(`Warning: Error in route match: ${currentMatchState().routeId}`); (routeOptions().onCatch ?? router.options.defaultOnCatch)?.(error); }, render: RouteContent }) }); } }); } }), memo(() => { return renderScrollRestoration?.(router, route); })]; } }); } }); }; var MatchInner = () => { const router = useRouter(); const nearestMatch = Solid.useContext(nearestMatchContext); const match = nearestMatch[1]; const routeId = nearestMatch[0]; const matchState = Solid.createMemo(() => { const currentMatch = match(); const currentRouteId = routeId(); if (!currentMatch || !currentRouteId) return null; const route = router.routesById[currentRouteId]; const remount = route.options.remountDeps ?? router.options.defaultRemountDeps; let componentKey; if (!remount) componentKey = currentRouteId; else { const deps = remount({ routeId: currentRouteId, loaderDeps: currentMatch.loaderDeps, params: currentMatch._strictParams, search: currentMatch._strictSearch }); componentKey = JSON.stringify(deps) ?? currentRouteId; } return { route, routeId: currentRouteId, match: { id: currentMatch.id, status: currentMatch.status, error: currentMatch.error }, componentKey }; }); return createComponent(Solid.Show, { get when() { return matchState(); }, children: (currentMatchState) => { const route = Solid.createMemo(() => currentMatchState().route); const currentMatch = Solid.createMemo(() => currentMatchState().match); const componentKey = Solid.createMemo(() => currentMatchState().componentKey); const OutComponent = Solid.createMemo(() => route().options.component ?? router.options.defaultComponent ?? Outlet); const keyedOut = () => createComponent(Solid.Show, { get when() { return componentKey(); }, keyed: true, children: (_key) => createComponent(Dynamic, { get component() { return OutComponent(); } }) }); return createComponent(Solid.Switch, { get children() { return [ createComponent(Solid.Match, { get when() { return currentMatch().status === "notFound"; }, children: (_) => Solid.untrack(() => renderRouteNotFound(router, route(), currentMatch().error)) }), createComponent(Solid.Match, { get when() { return currentMatch().status === "error"; }, children: (_) => { const matchError = Solid.untrack(() => currentMatch().error); if (isServer ?? router.isServer) { const RouteErrorComponent = (route().options.errorComponent ?? router.options.defaultErrorComponent) || ErrorComponent; return process.env.NODE_ENV !== "production" ? renderInNonRouteComponentContext(() => createComponent(RouteErrorComponent, { error: matchError, reset: void 0, info: { componentStack: "" } }), "errorComponent") : createComponent(RouteErrorComponent, { error: matchError, reset: void 0, info: { componentStack: "" } }); } throw matchError; } }), createComponent(Solid.Match, { get when() { return currentMatch().status === "success"; }, get children() { return keyedOut(); } }) ]; } }); } }); }; var Outlet = () => { if (process.env.NODE_ENV !== "production") { const nonRouteComponent = Solid.useContext(nonRouteComponentContext); if (nonRouteComponent) console.warn(`Warning: An <Outlet /> was rendered inside a ${nonRouteComponent}. <Outlet /> should only be rendered inside a route component.`); } const router = useRouter(); const nearestParentMatch = Solid.useContext(nearestMatchContext); const parentMatch = nearestParentMatch[1]; const routeId = nearestParentMatch[0]; const route = Solid.createMemo(() => { const currentRouteId = routeId(); return currentRouteId ? router.routesById[currentRouteId] : void 0; }); const parentNotFound = Solid.createMemo(() => parentMatch()?._notFound); const parentNotFoundError = Solid.createMemo(() => parentMatch()?.error); const childRouteId = Solid.createMemo(() => { if (parentNotFound()) return; const currentRouteId = routeId(); if (!currentRouteId) return; const ids = router.stores.ids.get(); return ids[ids.indexOf(currentRouteId) + 1]; }); const childPendingComponent = Solid.createMemo(() => { const childId = childRouteId(); return childId ? router.routesById[childId].options.pendingComponent ?? router.options.defaultPendingComponent : router.options.defaultPendingComponent; }); return createComponent(Solid.Show, { get when() { return childRouteId(); }, keyed: true, get fallback() { return createComponent(Solid.Show, { get when() { return memo(() => { return !!parentNotFound(); })() ? route() : parentNotFound(); }, children: (resolvedRoute) => Solid.untrack(() => renderRouteNotFound(router, resolvedRoute(), parentNotFoundError())) }); }, children: (currentChildRouteId) => { const nextMatch = () => createComponent(Match, { routeId: currentChildRouteId }); return createComponent(Solid.Show, { get when() { return routeId() === rootRouteId; }, get fallback() { return nextMatch(); }, get children() { return createComponent(Solid.Loading, { get fallback() { if (!childPendingComponent()) return null; if (process.env.NODE_ENV !== "production") return renderInNonRouteComponentContext(() => createComponent(Dynamic, { get component() { return childPendingComponent(); } }), "pendingComponent"); return createComponent(Dynamic, { get component() { return childPendingComponent(); } }); }, get children() { return nextMatch(); } }); } }); } }); }; //#endregion export { Match, Outlet }; //# sourceMappingURL=Match.js.map