@tanstack/solid-router
Version:
Modern and scalable routing for Solid applications
1 lines • 11.8 kB
Source Map (JSON)
{"version":3,"file":"Transitioner.cjs","names":[],"sources":["../../src/Transitioner.tsx"],"sourcesContent":["import * as Solid from 'solid-js'\nimport { getLocationChangeInfo, trimPathRight } from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport { useRouter } from './useRouter'\nimport { describeInitial, describeNavigation, takeRequest } from './observe'\nimport type { NavigationHop, NavigationRequest } from './observe'\nimport type { HistoryLocation, RouterHistory } from '@tanstack/history'\nimport type { AnyRouteMatch, ParsedLocation } from '@tanstack/router-core'\n\n/** What the history tells its subscribers. */\ntype HistoryChange = Parameters<Parameters<RouterHistory['subscribe']>[0]>[0]\n\n/** The history change was the arrival's own canonicalization, not a request. */\nconst ARRIVAL = Symbol()\n\n/** A navigation requested and not yet published. */\ninterface PendingNavigation extends NavigationRequest {\n /** Where it is headed now. */\n location: HistoryLocation\n /** The destinations it was sent on from, in order. */\n hops: Array<NavigationHop>\n}\n\n/**\n * `offerPending`'s publish: a match still `pending` at or above the\n * not-found boundary. The matches below a not-found never load, and stay\n * `pending` in the publish that lands; an offer whose pending match is\n * below one already shows the destination, the not-found.\n */\nfunction isPendingOffer(matches: Array<AnyRouteMatch>) {\n for (const match of matches) {\n if (match.status === 'pending') return true\n if (match._notFound) return false\n }\n return false\n}\n\nfunction getResolvedLocation(router: ReturnType<typeof useRouter>) {\n const resolvedLocation = router.stores.resolvedLocation.get()\n if (\n resolvedLocation?.href === router.latestLocation.href &&\n resolvedLocation.state.__TSR_key === router.latestLocation.state.__TSR_key\n ) {\n return resolvedLocation\n }\n return\n}\n\nexport function Transitioner() {\n const router = useRouter()\n\n type Ack = [\n expected: Array<AnyRouteMatch>,\n resolve: (rendered: boolean) => void,\n ]\n const acks: Array<Ack> = []\n let committed: Array<AnyRouteMatch> | undefined\n\n const isCommitted = (expected: Array<AnyRouteMatch>) =>\n !!committed &&\n committed.length === expected.length &&\n expected.every((match, index) => committed![index] === match)\n\n // Solid's observe tier attributes what the user waited on to the\n // navigation that caused it: wrap the write whose landing is the\n // destination showing — here the match publish, after router-core awaited\n // the loaders — and describe the request, which predates it. The first\n // history change since the last publish is that request: when the user\n // asked, and the interaction they asked in, gone from the stack by the time\n // the publish runs. A push or replace while it is pending (a redirect, or\n // another navigation) sends it elsewhere, as `@solidjs/router` folds one:\n // a hop. `ARRIVAL` when the change was the arrival being canonicalized\n // (below): the initial declaration already covers it.\n let request: PendingNavigation | typeof ARRIVAL | undefined\n let canonicalizing = false\n // The location the initial declaration names, canonical: what a navigation\n // is from until the first publish resolves one (a redirect while it loads).\n let arrival: ParsedLocation | undefined\n\n // Ack when the commit's transition settles (the atomic swap), not when the\n // flush parks it; superseded or rolled-back commits resolve false.\n router.startTransition = (fn, expectedMatches) => {\n if (isServer ?? router.isServer) {\n fn()\n return Promise.resolve(true)\n }\n return new Promise((resolve) => {\n const ack: Ack = [expectedMatches, resolve]\n acks.push(ack)\n let publish = fn\n // The pending offer (`offerPending`) is not the destination: published\n // undeclared, the request kept for the publish that lands. Every other\n // publish answers the request.\n if (Solid.OBSERVE !== undefined && !isPendingOffer(expectedMatches)) {\n const answered = request\n request = undefined\n const refs =\n answered === undefined || answered === ARRIVAL\n ? undefined\n : describeNavigation(\n router,\n answered,\n router.stores.resolvedLocation.get() ?? arrival,\n answered.hops,\n )\n if (refs !== undefined) {\n const observe = Solid.OBSERVE\n publish = refs.reduceRight<() => void>(\n (inner, ref) => () => observe.attribution.withOrigin(ref, inner),\n fn,\n )\n }\n }\n Solid.runWithOwner(null, publish)\n try {\n Solid.flush()\n } catch {\n // Solid auto-flushes when this is called from a reactive context.\n }\n // A commit that changed nothing produces no settlement to observe.\n if (acks.includes(ack) && isCommitted(expectedMatches)) {\n acks.splice(acks.indexOf(ack), 1)\n resolve(true)\n }\n })\n }\n\n // No server early-return here or below: Solid 2 derives hydration keys\n // from the reactive owner tree, so the server must register the same slots\n // as the client. The callbacks themselves never run on the server.\n Solid.createEffect(\n () => router.stores.matches.get(),\n (current) => {\n committed = current\n if (acks.length) {\n for (const [expected, resolve] of acks.splice(0)) {\n resolve(isCommitted(expected))\n }\n }\n },\n )\n\n Solid.onSettled(() => {\n const unsub = router.history.subscribe(\n ({ location, action }: HistoryChange) => {\n if (Solid.OBSERVE !== undefined) {\n // A push or replace was requested through `commitLocation`, maybe\n // before an `await` on the blockers; anything else is the browser\n // moving, requested now.\n const write = action.type === 'PUSH' || action.type === 'REPLACE'\n const noted = write ? takeRequest(router) : undefined\n if (canonicalizing) request ??= ARRIVAL\n else if (write && request !== undefined && request !== ARRIVAL) {\n request.hops.push({\n location: router.parseLocation(request.location),\n at: noted?.at ?? performance.now(),\n })\n request.location = location\n } else {\n // A new request: the browser moving (back, forward) supersedes a\n // pending one rather than redirecting it, as in `@solidjs/router`.\n request = {\n ...(noted ?? {\n at: performance.now(),\n interaction: Solid.OBSERVE.attribution.currentOrigin(),\n }),\n location,\n hops: [],\n }\n }\n }\n queueMicrotask(() => router.load().catch(console.error))\n },\n )\n\n // The route the document arrived on is declared around establishing it,\n // canonicalization included, as `@solidjs/router` does: the record\n // names the canonical location, and the commit is the arrival's.\n const establish = () => {\n // The URL may have changed synchronously between render and settlement.\n router.updateLatestLocation()\n const nextLocation = router.buildLocation({\n to: router.latestLocation.pathname,\n search: true,\n params: true,\n hash: true,\n state: true,\n _includeValidateSearch: true,\n })\n\n if (\n trimPathRight(router.latestLocation.publicHref) !==\n trimPathRight(nextLocation.publicHref)\n ) {\n arrival = nextLocation\n // The history notifies synchronously inside the commit (no blocker\n // to await), so the subscriber above sees the flag.\n canonicalizing = true\n try {\n router.commitLocation({\n ...nextLocation,\n replace: true,\n ignoreBlocker: true,\n })\n } finally {\n canonicalizing = false\n }\n return\n }\n\n arrival = router.latestLocation\n if (!getResolvedLocation(router) && !router._tx) {\n queueMicrotask(() => router.load().catch(console.error))\n }\n }\n\n const observe = Solid.OBSERVE\n if (observe !== undefined) {\n observe.attribution.withOrigin(\n describeInitial(router, () => arrival ?? router.latestLocation),\n establish,\n )\n } else establish()\n\n return unsub\n })\n\n return null\n}\n\nexport function Rendered() {\n const router = useRouter()\n Solid.onSettled(() => {\n const resolvedLocation = getResolvedLocation(router)\n if (resolvedLocation) {\n router.emit({\n type: 'onRendered',\n ...getLocationChangeInfo(resolvedLocation, resolvedLocation),\n })\n }\n })\n return null\n}\n"],"mappings":"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"}