UNPKG

@tanstack/solid-router

Version:

Modern and scalable routing for Solid applications

137 lines (136 loc) • 4.72 kB
import { useRouter } from "./useRouter.js"; import { describeInitial, describeNavigation, takeRequest } from "./observe.js"; import { getLocationChangeInfo, trimPathRight } from "@tanstack/router-core"; import * as Solid from "solid-js"; import { isServer } from "@tanstack/router-core/isServer"; //#region src/Transitioner.tsx /** The history change was the arrival's own canonicalization, not a request. */ var ARRIVAL = Symbol(); /** * `offerPending`'s publish: a match still `pending` at or above the * not-found boundary. The matches below a not-found never load, and stay * `pending` in the publish that lands; an offer whose pending match is * below one already shows the destination, the not-found. */ function isPendingOffer(matches) { for (const match of matches) { if (match.status === "pending") return true; if (match._notFound) return false; } return false; } function getResolvedLocation(router) { const resolvedLocation = router.stores.resolvedLocation.get(); if (resolvedLocation?.href === router.latestLocation.href && resolvedLocation.state.__TSR_key === router.latestLocation.state.__TSR_key) return resolvedLocation; } function Transitioner() { const router = useRouter(); const acks = []; let committed; const isCommitted = (expected) => !!committed && committed.length === expected.length && expected.every((match, index) => committed[index] === match); let request; let canonicalizing = false; let arrival; router.startTransition = (fn, expectedMatches) => { if (isServer ?? router.isServer) { fn(); return Promise.resolve(true); } return new Promise((resolve) => { const ack = [expectedMatches, resolve]; acks.push(ack); let publish = fn; if (Solid.OBSERVE !== void 0 && !isPendingOffer(expectedMatches)) { const answered = request; request = void 0; const refs = answered === void 0 || answered === ARRIVAL ? void 0 : describeNavigation(router, answered, router.stores.resolvedLocation.get() ?? arrival, answered.hops); if (refs !== void 0) { const observe = Solid.OBSERVE; publish = refs.reduceRight((inner, ref) => () => observe.attribution.withOrigin(ref, inner), fn); } } Solid.runWithOwner(null, publish); try { Solid.flush(); } catch {} if (acks.includes(ack) && isCommitted(expectedMatches)) { acks.splice(acks.indexOf(ack), 1); resolve(true); } }); }; Solid.createEffect(() => router.stores.matches.get(), (current) => { committed = current; if (acks.length) for (const [expected, resolve] of acks.splice(0)) resolve(isCommitted(expected)); }); Solid.onSettled(() => { const unsub = router.history.subscribe(({ location, action }) => { if (Solid.OBSERVE !== void 0) { const write = action.type === "PUSH" || action.type === "REPLACE"; const noted = write ? takeRequest(router) : void 0; if (canonicalizing) request ??= ARRIVAL; else if (write && request !== void 0 && request !== ARRIVAL) { request.hops.push({ location: router.parseLocation(request.location), at: noted?.at ?? performance.now() }); request.location = location; } else request = { ...noted ?? { at: performance.now(), interaction: Solid.OBSERVE.attribution.currentOrigin() }, location, hops: [] }; } queueMicrotask(() => router.load().catch(console.error)); }); const establish = () => { router.updateLatestLocation(); const nextLocation = router.buildLocation({ to: router.latestLocation.pathname, search: true, params: true, hash: true, state: true, _includeValidateSearch: true }); if (trimPathRight(router.latestLocation.publicHref) !== trimPathRight(nextLocation.publicHref)) { arrival = nextLocation; canonicalizing = true; try { router.commitLocation({ ...nextLocation, replace: true, ignoreBlocker: true }); } finally { canonicalizing = false; } return; } arrival = router.latestLocation; if (!getResolvedLocation(router) && !router._tx) queueMicrotask(() => router.load().catch(console.error)); }; const observe = Solid.OBSERVE; if (observe !== void 0) observe.attribution.withOrigin(describeInitial(router, () => arrival ?? router.latestLocation), establish); else establish(); return unsub; }); return null; } function Rendered() { const router = useRouter(); Solid.onSettled(() => { const resolvedLocation = getResolvedLocation(router); if (resolvedLocation) router.emit({ type: "onRendered", ...getLocationChangeInfo(resolvedLocation, resolvedLocation) }); }); return null; } //#endregion export { Rendered, Transitioner }; //# sourceMappingURL=Transitioner.js.map