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