@tanstack/router-core
Version:
Modern and scalable routing for React applications
183 lines (182 loc) • 6.75 kB
JavaScript
const require_utils = require("../utils.cjs");
const require_invariant = require("../invariant.cjs");
const require_not_found = require("../not-found.cjs");
const require_ssr_match_id = require("./ssr-match-id.cjs");
//#region src/ssr/ssr-client.ts
function hydrateMatch(match, deyhydratedMatch) {
match.id = deyhydratedMatch.i;
match.__beforeLoadContext = deyhydratedMatch.b;
match.loaderData = deyhydratedMatch.l;
match.status = deyhydratedMatch.s;
match.ssr = deyhydratedMatch.ssr;
match.updatedAt = deyhydratedMatch.u;
match.error = deyhydratedMatch.e;
if (deyhydratedMatch.g !== void 0) match.globalNotFound = deyhydratedMatch.g;
}
async function hydrate(router) {
if (!window.$_TSR) {
if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: Expected to find bootstrap data on window.$_TSR, but we did not. Please file an issue!");
require_invariant.invariant();
}
const serializationAdapters = router.options.serializationAdapters;
if (serializationAdapters?.length) {
const fromSerializableMap = /* @__PURE__ */ new Map();
serializationAdapters.forEach((adapter) => {
fromSerializableMap.set(adapter.key, adapter.fromSerializable);
});
window.$_TSR.t = fromSerializableMap;
window.$_TSR.buffer.forEach((script) => script());
}
window.$_TSR.initialized = true;
if (!window.$_TSR.router) {
if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!");
require_invariant.invariant();
}
const dehydratedRouter = window.$_TSR.router;
dehydratedRouter.matches.forEach((dehydratedMatch) => {
dehydratedMatch.i = require_ssr_match_id.hydrateSsrMatchId(dehydratedMatch.i);
});
if (dehydratedRouter.lastMatchId) dehydratedRouter.lastMatchId = require_ssr_match_id.hydrateSsrMatchId(dehydratedRouter.lastMatchId);
const { manifest, dehydratedData, lastMatchId } = dehydratedRouter;
router.ssr = { manifest };
const nonce = document.querySelector("meta[property=\"csp-nonce\"]")?.content;
router.options.ssr = { nonce };
const matches = router.matchRoutes(router.stores.location.get());
const routeChunkPromise = Promise.all(matches.map((match) => router.loadRouteChunk(router.looseRoutesById[match.routeId])));
function setMatchForcePending(match) {
const pendingMinMs = router.looseRoutesById[match.routeId].options.pendingMinMs ?? router.options.defaultPendingMinMs;
if (pendingMinMs) {
const minPendingPromise = require_utils.createControlledPromise();
match._nonReactive.minPendingPromise = minPendingPromise;
match._forcePending = true;
setTimeout(() => {
minPendingPromise.resolve();
router.updateMatch(match.id, (prev) => {
prev._nonReactive.minPendingPromise = void 0;
return {
...prev,
_forcePending: void 0
};
});
}, pendingMinMs);
}
}
function setRouteSsr(match) {
const route = router.looseRoutesById[match.routeId];
if (route) route.options.ssr = match.ssr;
}
let firstNonSsrMatchIndex = void 0;
matches.forEach((match) => {
const dehydratedMatch = dehydratedRouter.matches.find((d) => d.i === match.id);
if (!dehydratedMatch) {
match._nonReactive.dehydrated = false;
match.ssr = false;
setRouteSsr(match);
return;
}
hydrateMatch(match, dehydratedMatch);
setRouteSsr(match);
match._nonReactive.dehydrated = match.ssr !== false;
if (match.ssr === "data-only" || match.ssr === false) {
if (firstNonSsrMatchIndex === void 0) {
firstNonSsrMatchIndex = match.index;
setMatchForcePending(match);
}
}
});
router.stores.setMatches(matches);
await router.options.hydrate?.(dehydratedData);
const activeMatches = router.stores.matches.get();
const location = router.stores.location.get();
await Promise.all(activeMatches.map(async (match) => {
try {
const route = router.looseRoutesById[match.routeId];
const parentContext = activeMatches[match.index - 1]?.context ?? router.options.context;
if (route.options.context) {
const contextFnContext = {
deps: match.loaderDeps,
params: match.params,
context: parentContext ?? {},
location,
navigate: (opts) => router.navigate({
...opts,
_fromLocation: location
}),
buildLocation: router.buildLocation,
cause: match.cause,
abortController: match.abortController,
preload: false,
matches,
routeId: route.id
};
match.__routeContext = route.options.context(contextFnContext) ?? void 0;
}
match.context = {
...parentContext,
...match.__routeContext,
...match.__beforeLoadContext
};
const assetContext = {
ssr: router.options.ssr,
matches: activeMatches,
match,
params: match.params,
loaderData: match.loaderData
};
const headFnContent = await route.options.head?.(assetContext);
const scripts = await route.options.scripts?.(assetContext);
match.meta = headFnContent?.meta;
match.links = headFnContent?.links;
match.headScripts = headFnContent?.scripts;
match.styles = headFnContent?.styles;
match.scripts = scripts;
} catch (err) {
if (require_not_found.isNotFound(err)) {
match.error = { isNotFound: true };
console.error(`NotFound error during hydration for routeId: ${match.routeId}`, err);
} else {
match.error = err;
console.error(`Error during hydration for route ${match.routeId}:`, err);
throw err;
}
}
}));
const isSpaMode = matches[matches.length - 1].id !== lastMatchId;
if (!matches.some((m) => m.ssr === false) && !isSpaMode) {
matches.forEach((match) => {
match._nonReactive.dehydrated = void 0;
});
router.stores.resolvedLocation.set(router.stores.location.get());
return routeChunkPromise;
}
const loadPromise = Promise.resolve().then(() => router.load()).catch((err) => {
console.error("Error during router hydration:", err);
});
if (isSpaMode) {
const match = matches[1];
if (!match) {
if (process.env.NODE_ENV !== "production") throw new Error("Invariant failed: Expected to find a match below the root match in SPA mode.");
require_invariant.invariant();
}
setMatchForcePending(match);
match._displayPending = true;
match._nonReactive.displayPendingPromise = loadPromise;
loadPromise.then(() => {
router.batch(() => {
if (router.stores.status.get() === "pending") {
router.stores.status.set("idle");
router.stores.resolvedLocation.set(router.stores.location.get());
}
router.updateMatch(match.id, (prev) => ({
...prev,
_displayPending: void 0,
displayPendingPromise: void 0
}));
});
});
}
return routeChunkPromise;
}
//#endregion
exports.hydrate = hydrate;
//# sourceMappingURL=ssr-client.cjs.map