UNPKG

@tanstack/solid-router

Version:

Modern and scalable routing for Solid applications

173 lines (172 loc) 7.81 kB
const require_runtime = require("./_virtual/_rolldown/runtime.cjs"); const require_nonRouteComponentContext = require("./nonRouteComponentContext.cjs"); const require_CatchBoundary = require("./CatchBoundary.cjs"); const require_useRouter = require("./useRouter.cjs"); const require_matchContext = require("./matchContext.cjs"); const require_Transitioner = require("./Transitioner.cjs"); const require_SafeFragment = require("./SafeFragment.cjs"); const require_Match = require("./Match.cjs"); let _tanstack_router_core = require("@tanstack/router-core"); let _solidjs_web = require("@solidjs/web"); let solid_js = require("solid-js"); solid_js = require_runtime.__toESM(solid_js, 1); //#region src/Matches.tsx var NearestMatchContext = require_matchContext.nearestMatchContext; /** * Select the global loading boundary for `Matches`. * * Loading UI belongs to the application, not the router. Solid 2's async * model never injects fallbacks the app didn't write: pending state * propagates through the graph to whatever boundary the app placed (or is * held at the root when there is none), and an unresolved `lazy()` chunk is * just a pending async value. The wrapper here is therefore strictly opt-in: * it renders only when the app configured root pending UI * (`pendingComponent` on the root route or `defaultPendingComponent`). * Nothing configured means no wrapper — pending match/chunk states propagate * as ordinary Solid async. * * The decision is deliberately SYMMETRIC between server and client, and * consults no hydration or environment state. Solid's hydration claims * server nodes positionally through the boundary structure, so the client * must render a boundary exactly where the server rendered one — a * client-only wrapper (even a settled one that renders its children * directly) desyncs node claiming and detaches the app from the server DOM. * The inputs below (`pendingComponent` configuration, * `disableGlobalCatchBoundary`, `router.ssr`) all resolve identically on the * server and on the hydrating client, so the trees always agree. This also * keeps the decision stable across the app's lifetime: it is made once per * `Matches` instance, so gating it on transient state (like "currently * hydrating") would permanently freeze the configured pending UI off for * every post-hydration navigation of a hydrated app. * * `router.ssr` (TanStack's `$_TSR` SSR utilities) is symmetric too: * `attachRouterServerSsrUtils` sets it on the server before rendering, and * router-core `hydrate()` (invoked by `RouterClient`, which TanStack Start * builds on) sets it on the client before rendering. It short-circuits the * wrapper exactly as before: that protocol legitimately hydrates matches in * pending states (`ssr: 'data-only'`), where a settled boundary is not * guaranteed. * * When disableGlobalCatchBoundary is true, we must NOT wrap with * Solid.Loading because Solid.Loading transforms STATUS_ERROR into * STATUS_PENDING, which prevents errors from propagating to an external * Errored boundary. */ function _resolveMatchesLoadingBoundary(router) { return !(router.routesById[_tanstack_router_core.rootRouteId]?.options.pendingComponent ?? router.options.defaultPendingComponent) || router.options.disableGlobalCatchBoundary || router.ssr ? require_SafeFragment.SafeFragment : solid_js.Loading; } function Matches() { const router = require_useRouter.useRouter(); const ResolvedSuspense = _resolveMatchesLoadingBoundary(router); const rootRoute = () => router.routesById[_tanstack_router_core.rootRouteId]; const PendingComponent = rootRoute().options.pendingComponent ?? router.options.defaultPendingComponent; return (0, _solidjs_web.createComponent)(router.options.InnerWrap || require_SafeFragment.SafeFragment, { get children() { return (0, _solidjs_web.createComponent)(ResolvedSuspense, { get fallback() { return PendingComponent ? (() => { if (process.env.NODE_ENV !== "production") return require_nonRouteComponentContext.renderInNonRouteComponentContext(() => (0, _solidjs_web.createComponent)(PendingComponent, {}), "pendingComponent"); return (0, _solidjs_web.createComponent)(PendingComponent, {}); })() : null; }, get children() { return [ (0, _solidjs_web.createComponent)(require_Transitioner.Transitioner, {}), (0, _solidjs_web.createComponent)(MatchesInner, {}), (0, _solidjs_web.createComponent)(require_Transitioner.Rendered, {}) ]; } }); } }); } function MatchesInner() { const router = require_useRouter.useRouter(); const routeId = () => router.stores.ids.get()[0]; const match = () => routeId() ? router.stores.byRoute.get(routeId())?.get() : void 0; const nearestMatch = [routeId, match]; const matchContent = () => (0, _solidjs_web.createComponent)(solid_js.Show, { get when() { return routeId(); }, keyed: true, children: (currentRouteId) => (0, _solidjs_web.createComponent)(require_Match.Match, { routeId: currentRouteId }) }); if (router.options.disableGlobalCatchBoundary) return (0, _solidjs_web.createComponent)(NearestMatchContext, { value: nearestMatch, get children() { return matchContent(); } }); return (0, _solidjs_web.createComponent)(NearestMatchContext, { value: nearestMatch, get children() { return (0, _solidjs_web.createComponent)(require_CatchBoundary.CatchBoundary, { getResetKey: () => router.stores.matches.get(), render: matchContent, errorComponent: require_CatchBoundary.ErrorComponent, get onCatch() { return process.env.NODE_ENV !== "production" ? (error) => { console.warn(`Warning: The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`); console.warn(`Warning: ${error.message || error.toString()}`); } : void 0; } }); } }); } function useMatchRoute() { const router = require_useRouter.useRouter(); return (opts) => { return solid_js.createMemo(() => { const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts; router.stores.location.get(); router.stores.resolvedLocation.get(); router.stores.status.get(); return router.matchRoute(rest, { pending, caseSensitive, fuzzy, includeSearch }); }); }; } function MatchRoute(props) { const params = useMatchRoute()(props); return (0, _solidjs_web.memo)(solid_js.createMemo(() => { const matchedParams = params(); const child = props.children; if (typeof child === "function") return child(matchedParams); return matchedParams ? child : null; })); } function useMatches(opts) { const router = require_useRouter.useRouter(); return solid_js.createMemo((prev) => { const matches = router.stores.matches.get(); const res = opts?.select ? opts.select(matches) : matches; if (prev === void 0) return res; return (0, _tanstack_router_core.replaceEqualDeep)(prev, res); }); } function useParentMatches(opts) { const contextRouteId = solid_js.useContext(require_matchContext.nearestMatchContext)[0]; return useMatches({ select: (matches) => { matches = matches.slice(0, matches.findIndex((d) => d.routeId === contextRouteId())); return opts?.select ? opts.select(matches) : matches; } }); } function useChildMatches(opts) { const contextRouteId = solid_js.useContext(require_matchContext.nearestMatchContext)[0]; return useMatches({ select: (matches) => { matches = matches.slice(matches.findIndex((d) => d.routeId === contextRouteId()) + 1); return opts?.select ? opts.select(matches) : matches; } }); } //#endregion exports.MatchRoute = MatchRoute; exports.Matches = Matches; exports.useChildMatches = useChildMatches; exports.useMatchRoute = useMatchRoute; exports.useMatches = useMatches; exports.useParentMatches = useParentMatches; //# sourceMappingURL=Matches.cjs.map