@tanstack/solid-router
Version:
Modern and scalable routing for Solid applications
343 lines (342 loc) • 13.1 kB
JavaScript
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