296 lines
12 KiB
JavaScript
296 lines
12 KiB
JavaScript
"use client";
|
|
import { useLayoutEffect } from "./utils.js";
|
|
import { CatchBoundary, ErrorComponent } from "./CatchBoundary.js";
|
|
import { ClientOnly } from "./ClientOnly.js";
|
|
import { matchContext } from "./matchContext.js";
|
|
import { useRouter } from "./useRouter.js";
|
|
import { CatchNotFound } from "./not-found.js";
|
|
import { SafeFragment } from "./SafeFragment.js";
|
|
import { renderRouteNotFound } from "./renderRouteNotFound.js";
|
|
import { ScrollRestoration } from "./scroll-restoration.js";
|
|
import { createControlledPromise, getLocationChangeInfo, invariant, isNotFound, isRedirect, rootRouteId } from "@tanstack/router-core";
|
|
import * as React$1 from "react";
|
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
import { useStore } from "@tanstack/react-store";
|
|
import { isServer } from "@tanstack/router-core/isServer";
|
|
//#region src/Match.tsx
|
|
var Match = React$1.memo(function MatchImpl({ matchId }) {
|
|
const router = useRouter();
|
|
if (isServer ?? router.isServer) {
|
|
const match = router.stores.matchStores.get(matchId)?.get();
|
|
if (!match) {
|
|
if (process.env.NODE_ENV !== "production") throw new Error(`Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`);
|
|
invariant();
|
|
}
|
|
const routeId = match.routeId;
|
|
const parentRouteId = router.routesById[routeId].parentRoute?.id;
|
|
return /* @__PURE__ */ jsx(MatchView, {
|
|
router,
|
|
matchId,
|
|
resetKey: router.stores.loadedAt.get(),
|
|
matchState: {
|
|
routeId,
|
|
ssr: match.ssr,
|
|
_displayPending: match._displayPending,
|
|
parentRouteId
|
|
}
|
|
});
|
|
}
|
|
const matchStore = router.stores.matchStores.get(matchId);
|
|
if (!matchStore) {
|
|
if (process.env.NODE_ENV !== "production") throw new Error(`Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`);
|
|
invariant();
|
|
}
|
|
const resetKey = useStore(router.stores.loadedAt, (loadedAt) => loadedAt);
|
|
const match = useStore(matchStore, (value) => value);
|
|
return /* @__PURE__ */ jsx(MatchView, {
|
|
router,
|
|
matchId,
|
|
resetKey,
|
|
matchState: React$1.useMemo(() => {
|
|
const routeId = match.routeId;
|
|
const parentRouteId = router.routesById[routeId].parentRoute?.id;
|
|
return {
|
|
routeId,
|
|
ssr: match.ssr,
|
|
_displayPending: match._displayPending,
|
|
parentRouteId
|
|
};
|
|
}, [
|
|
match._displayPending,
|
|
match.routeId,
|
|
match.ssr,
|
|
router.routesById
|
|
])
|
|
});
|
|
});
|
|
function MatchView({ router, matchId, resetKey, matchState }) {
|
|
const route = router.routesById[matchState.routeId];
|
|
const PendingComponent = route.options.pendingComponent ?? router.options.defaultPendingComponent;
|
|
const pendingElement = PendingComponent ? /* @__PURE__ */ jsx(PendingComponent, {}) : null;
|
|
const routeErrorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent;
|
|
const routeOnCatch = route.options.onCatch ?? router.options.defaultOnCatch;
|
|
const routeNotFoundComponent = route.isRoot ? route.options.notFoundComponent ?? router.options.notFoundRoute?.options.component : route.options.notFoundComponent;
|
|
const resolvedNoSsr = matchState.ssr === false || matchState.ssr === "data-only";
|
|
const ResolvedSuspenseBoundary = (!route.isRoot || route.options.wrapInSuspense || resolvedNoSsr) && (route.options.wrapInSuspense ?? PendingComponent ?? (route.options.errorComponent?.preload || resolvedNoSsr)) ? React$1.Suspense : SafeFragment;
|
|
const ResolvedCatchBoundary = routeErrorComponent ? CatchBoundary : SafeFragment;
|
|
const ResolvedNotFoundBoundary = routeNotFoundComponent ? CatchNotFound : SafeFragment;
|
|
return /* @__PURE__ */ jsxs(route.isRoot ? route.options.shellComponent ?? SafeFragment : SafeFragment, { children: [/* @__PURE__ */ jsx(matchContext.Provider, {
|
|
value: matchId,
|
|
children: /* @__PURE__ */ jsx(ResolvedSuspenseBoundary, {
|
|
fallback: pendingElement,
|
|
children: /* @__PURE__ */ jsx(ResolvedCatchBoundary, {
|
|
getResetKey: () => resetKey,
|
|
errorComponent: routeErrorComponent || ErrorComponent,
|
|
onCatch: (error, errorInfo) => {
|
|
if (isNotFound(error)) {
|
|
error.routeId ??= matchState.routeId;
|
|
throw error;
|
|
}
|
|
if (process.env.NODE_ENV !== "production") console.warn(`Warning: Error in route match: ${matchId}`);
|
|
routeOnCatch?.(error, errorInfo);
|
|
},
|
|
children: /* @__PURE__ */ jsx(ResolvedNotFoundBoundary, {
|
|
fallback: (error) => {
|
|
error.routeId ??= matchState.routeId;
|
|
if (!routeNotFoundComponent || error.routeId && error.routeId !== matchState.routeId || !error.routeId && !route.isRoot) throw error;
|
|
return React$1.createElement(routeNotFoundComponent, error);
|
|
},
|
|
children: resolvedNoSsr || matchState._displayPending ? /* @__PURE__ */ jsx(ClientOnly, {
|
|
fallback: pendingElement,
|
|
children: /* @__PURE__ */ jsx(MatchInner, { matchId })
|
|
}) : /* @__PURE__ */ jsx(MatchInner, { matchId })
|
|
})
|
|
})
|
|
})
|
|
}), matchState.parentRouteId === rootRouteId ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(OnRendered, { resetKey }), router.options.scrollRestoration && (isServer ?? router.isServer) ? /* @__PURE__ */ jsx(ScrollRestoration, {}) : null] }) : null] });
|
|
}
|
|
function OnRendered({ resetKey }) {
|
|
const router = useRouter();
|
|
if (isServer ?? router.isServer) return null;
|
|
const prevHrefRef = React$1.useRef(void 0);
|
|
useLayoutEffect(() => {
|
|
const currentHref = router.latestLocation.href;
|
|
if (prevHrefRef.current === void 0 || prevHrefRef.current !== currentHref) {
|
|
router.emit({
|
|
type: "onRendered",
|
|
...getLocationChangeInfo(router.stores.location.get(), router.stores.resolvedLocation.get())
|
|
});
|
|
prevHrefRef.current = currentHref;
|
|
}
|
|
}, [
|
|
router.latestLocation.state.__TSR_key,
|
|
resetKey,
|
|
router
|
|
]);
|
|
return null;
|
|
}
|
|
var MatchInner = React$1.memo(function MatchInnerImpl({ matchId }) {
|
|
const router = useRouter();
|
|
const getMatchPromise = (match, key) => {
|
|
return router.getMatch(match.id)?._nonReactive[key] ?? match._nonReactive[key];
|
|
};
|
|
if (isServer ?? router.isServer) {
|
|
const match = router.stores.matchStores.get(matchId)?.get();
|
|
if (!match) {
|
|
if (process.env.NODE_ENV !== "production") throw new Error(`Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`);
|
|
invariant();
|
|
}
|
|
const routeId = match.routeId;
|
|
const route = router.routesById[routeId];
|
|
const remountDeps = (router.routesById[routeId].options.remountDeps ?? router.options.defaultRemountDeps)?.({
|
|
routeId,
|
|
loaderDeps: match.loaderDeps,
|
|
params: match._strictParams,
|
|
search: match._strictSearch
|
|
});
|
|
const key = remountDeps ? JSON.stringify(remountDeps) : void 0;
|
|
const Comp = route.options.component ?? router.options.defaultComponent;
|
|
const out = Comp ? /* @__PURE__ */ jsx(Comp, {}, key) : /* @__PURE__ */ jsx(Outlet, {});
|
|
if (match._displayPending) throw getMatchPromise(match, "displayPendingPromise");
|
|
if (match._forcePending) throw getMatchPromise(match, "minPendingPromise");
|
|
if (match.status === "pending") throw getMatchPromise(match, "loadPromise");
|
|
if (match.status === "notFound") {
|
|
if (!isNotFound(match.error)) {
|
|
if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: Expected a notFound error");
|
|
invariant();
|
|
}
|
|
return renderRouteNotFound(router, route, match.error);
|
|
}
|
|
if (match.status === "redirected") {
|
|
if (!isRedirect(match.error)) {
|
|
if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: Expected a redirect error");
|
|
invariant();
|
|
}
|
|
throw getMatchPromise(match, "loadPromise");
|
|
}
|
|
if (match.status === "error") return /* @__PURE__ */ jsx((route.options.errorComponent ?? router.options.defaultErrorComponent) || ErrorComponent, {
|
|
error: match.error,
|
|
reset: void 0,
|
|
info: { componentStack: "" }
|
|
});
|
|
return out;
|
|
}
|
|
const matchStore = router.stores.matchStores.get(matchId);
|
|
if (!matchStore) {
|
|
if (process.env.NODE_ENV !== "production") throw new Error(`Invariant failed: Could not find match for matchId "${matchId}". Please file an issue!`);
|
|
invariant();
|
|
}
|
|
const match = useStore(matchStore, (value) => value);
|
|
const routeId = match.routeId;
|
|
const route = router.routesById[routeId];
|
|
const key = React$1.useMemo(() => {
|
|
const remountDeps = (router.routesById[routeId].options.remountDeps ?? router.options.defaultRemountDeps)?.({
|
|
routeId,
|
|
loaderDeps: match.loaderDeps,
|
|
params: match._strictParams,
|
|
search: match._strictSearch
|
|
});
|
|
return remountDeps ? JSON.stringify(remountDeps) : void 0;
|
|
}, [
|
|
routeId,
|
|
match.loaderDeps,
|
|
match._strictParams,
|
|
match._strictSearch,
|
|
router.options.defaultRemountDeps,
|
|
router.routesById
|
|
]);
|
|
const out = React$1.useMemo(() => {
|
|
const Comp = route.options.component ?? router.options.defaultComponent;
|
|
if (Comp) return /* @__PURE__ */ jsx(Comp, {}, key);
|
|
return /* @__PURE__ */ jsx(Outlet, {});
|
|
}, [
|
|
key,
|
|
route.options.component,
|
|
router.options.defaultComponent
|
|
]);
|
|
if (match._displayPending) throw getMatchPromise(match, "displayPendingPromise");
|
|
if (match._forcePending) throw getMatchPromise(match, "minPendingPromise");
|
|
if (match.status === "pending") {
|
|
const pendingMinMs = route.options.pendingMinMs ?? router.options.defaultPendingMinMs;
|
|
if (pendingMinMs) {
|
|
const routerMatch = router.getMatch(match.id);
|
|
if (routerMatch && !routerMatch._nonReactive.minPendingPromise) {
|
|
if (!(isServer ?? router.isServer)) {
|
|
const minPendingPromise = createControlledPromise();
|
|
routerMatch._nonReactive.minPendingPromise = minPendingPromise;
|
|
setTimeout(() => {
|
|
minPendingPromise.resolve();
|
|
routerMatch._nonReactive.minPendingPromise = void 0;
|
|
}, pendingMinMs);
|
|
}
|
|
}
|
|
}
|
|
throw getMatchPromise(match, "loadPromise");
|
|
}
|
|
if (match.status === "notFound") {
|
|
if (!isNotFound(match.error)) {
|
|
if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: Expected a notFound error");
|
|
invariant();
|
|
}
|
|
return renderRouteNotFound(router, route, match.error);
|
|
}
|
|
if (match.status === "redirected") {
|
|
if (!isRedirect(match.error)) {
|
|
if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: Expected a redirect error");
|
|
invariant();
|
|
}
|
|
throw getMatchPromise(match, "loadPromise");
|
|
}
|
|
if (match.status === "error") {
|
|
if (isServer ?? router.isServer) return /* @__PURE__ */ jsx((route.options.errorComponent ?? router.options.defaultErrorComponent) || ErrorComponent, {
|
|
error: match.error,
|
|
reset: void 0,
|
|
info: { componentStack: "" }
|
|
});
|
|
throw match.error;
|
|
}
|
|
return out;
|
|
});
|
|
/**
|
|
* Render the next child match in the route tree. Typically used inside
|
|
* a route component to render nested routes.
|
|
*
|
|
* @link https://tanstack.com/router/latest/docs/framework/react/api/router/outletComponent
|
|
*/
|
|
var Outlet = React$1.memo(function OutletImpl() {
|
|
const router = useRouter();
|
|
const matchId = React$1.useContext(matchContext);
|
|
let routeId;
|
|
let parentGlobalNotFound = false;
|
|
let childMatchId;
|
|
if (isServer ?? router.isServer) {
|
|
const matches = router.stores.matches.get();
|
|
const parentIndex = matchId ? matches.findIndex((match) => match.id === matchId) : -1;
|
|
const parentMatch = parentIndex >= 0 ? matches[parentIndex] : void 0;
|
|
routeId = parentMatch?.routeId;
|
|
parentGlobalNotFound = parentMatch?.globalNotFound ?? false;
|
|
childMatchId = parentIndex >= 0 ? matches[parentIndex + 1]?.id : void 0;
|
|
} else {
|
|
const parentMatchStore = matchId ? router.stores.matchStores.get(matchId) : void 0;
|
|
[routeId, parentGlobalNotFound] = useStore(parentMatchStore, (match) => [match?.routeId, match?.globalNotFound ?? false]);
|
|
childMatchId = useStore(router.stores.matchesId, (ids) => {
|
|
return ids[ids.findIndex((id) => id === matchId) + 1];
|
|
});
|
|
}
|
|
const route = routeId ? router.routesById[routeId] : void 0;
|
|
const pendingElement = router.options.defaultPendingComponent ? /* @__PURE__ */ jsx(router.options.defaultPendingComponent, {}) : null;
|
|
if (parentGlobalNotFound) {
|
|
if (!route) {
|
|
if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: Could not resolve route for Outlet render");
|
|
invariant();
|
|
}
|
|
return renderRouteNotFound(router, route, void 0);
|
|
}
|
|
if (!childMatchId) return null;
|
|
const nextMatch = /* @__PURE__ */ jsx(Match, { matchId: childMatchId });
|
|
if (routeId === rootRouteId) return /* @__PURE__ */ jsx(React$1.Suspense, {
|
|
fallback: pendingElement,
|
|
children: nextMatch
|
|
});
|
|
return nextMatch;
|
|
});
|
|
//#endregion
|
|
export { Match, Outlet };
|
|
|
|
//# sourceMappingURL=Match.js.map
|