@tanstack/solid-router
Version:
Modern and scalable routing for Solid applications
1 lines • 8.18 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 type { NavigationRef } from 'solid-js'\nimport type { AnyRouteMatch } from '@tanstack/router-core'\n\n/**\n * Solid's observe tier (`OBSERVE` is defined on the dev and observe builds,\n * undefined in production) attributes what the user waited on to the\n * navigation that caused it. The rule for every router is the same: wrap the\n * write whose landing is the destination showing, and pass `at` when the\n * request predates that write. Here that write is the match publish inside\n * `startTransition` — the loaders were awaited in router-core before it —\n * so the ref names the destination route from the expected matches and\n * dates from the history change that started the load. The pending offer\n * (`offerPending`, a match with `status: 'pending'`) is not the destination\n * and is published undeclared; the initial load and a same-location reload\n * are not navigations.\n */\nfunction describeNavigation(\n router: ReturnType<typeof useRouter>,\n expected: Array<AnyRouteMatch>,\n at: number | undefined,\n): NavigationRef | undefined {\n if (expected.some((match) => match.status === 'pending')) return\n const to = router.latestLocation\n const from = router.stores.resolvedLocation.get()\n // Nothing shown yet (the initial load), or a reload of what is shown.\n if (!from || from.href === to.href) return\n const leaf = expected[expected.length - 1]\n const ref: NavigationRef = {\n kind: 'navigation',\n name: leaf?.fullPath || to.pathname,\n to: to.pathname,\n from: from.pathname,\n }\n if (leaf && Object.keys(leaf.params).length) ref.params = leaf.params\n if (at !== undefined) ref.at = at\n return ref\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 // When the history changed since the last declared publish: the moment\n // the user asked, which is where the navigation's wait starts.\n let requestedAt: number | 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 if (Solid.OBSERVE !== undefined) {\n const ref = describeNavigation(router, expectedMatches, requestedAt)\n if (ref !== undefined) {\n requestedAt = undefined\n const observe = Solid.OBSERVE\n publish = () => observe.attribution.withOrigin(ref, fn)\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 requestedAt ??= performance.now()\n queueMicrotask(() => router.load().catch(console.error))\n })\n\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 router.commitLocation({\n ...nextLocation,\n replace: true,\n ignoreBlocker: true,\n })\n return unsub\n }\n\n if (!getResolvedLocation(router) && !router._tx) {\n queueMicrotask(() => router.load().catch(console.error))\n }\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":";;;;;;;;;;;;;;;;;;;;AAoBA,SAAS,mBACP,QACA,UACA,IAC2B;CAC3B,IAAI,SAAS,MAAM,UAAU,MAAM,WAAW,SAAS,GAAG;CAC1D,MAAM,KAAK,OAAO;CAClB,MAAM,OAAO,OAAO,OAAO,iBAAiB,IAAI;CAEhD,IAAI,CAAC,QAAQ,KAAK,SAAS,GAAG,MAAM;CACpC,MAAM,OAAO,SAAS,SAAS,SAAS;CACxC,MAAM,MAAqB;EACzB,MAAM;EACN,MAAM,MAAM,YAAY,GAAG;EAC3B,IAAI,GAAG;EACP,MAAM,KAAK;CACb;CACA,IAAI,QAAQ,OAAO,KAAK,KAAK,MAAM,EAAE,QAAQ,IAAI,SAAS,KAAK;CAC/D,IAAI,OAAO,KAAA,GAAW,IAAI,KAAK;CAC/B,OAAO;AACT;AAEA,SAAS,oBAAoB,QAAsC;CACjE,MAAM,mBAAmB,OAAO,OAAO,iBAAiB,IAAI;CAC5D,IACE,kBAAkB,SAAS,OAAO,eAAe,QACjD,iBAAiB,MAAM,cAAc,OAAO,eAAe,MAAM,WAEjE,OAAO;AAGX;AAEA,SAAgB,eAAe;CAC7B,MAAM,SAAS,kBAAA,UAAU;CAMzB,MAAM,OAAmB,CAAC;CAC1B,IAAI;CAEJ,MAAM,eAAe,aACnB,CAAC,CAAC,aACF,UAAU,WAAW,SAAS,UAC9B,SAAS,OAAO,OAAO,UAAU,UAAW,WAAW,KAAK;CAI9D,IAAI;CAIJ,OAAO,mBAAmB,IAAI,oBAAoB;EAChD,IAAI,+BAAA,YAAY,OAAO,UAAU;GAC/B,GAAG;GACH,OAAO,QAAQ,QAAQ,IAAI;EAC7B;EACA,OAAO,IAAI,SAAS,YAAY;GAC9B,MAAM,MAAW,CAAC,iBAAiB,OAAO;GAC1C,KAAK,KAAK,GAAG;GACb,IAAI,UAAU;GACd,IAAI,SAAM,YAAY,KAAA,GAAW;IAC/B,MAAM,MAAM,mBAAmB,QAAQ,iBAAiB,WAAW;IACnE,IAAI,QAAQ,KAAA,GAAW;KACrB,cAAc,KAAA;KACd,MAAM,UAAU,SAAM;KACtB,gBAAgB,QAAQ,YAAY,WAAW,KAAK,EAAE;IACxD;GACF;GACA,SAAM,aAAa,MAAM,OAAO;GAChC,IAAI;IACF,SAAM,MAAM;GACd,QAAQ,CAER;GAEA,IAAI,KAAK,SAAS,GAAG,KAAK,YAAY,eAAe,GAAG;IACtD,KAAK,OAAO,KAAK,QAAQ,GAAG,GAAG,CAAC;IAChC,QAAQ,IAAI;GACd;EACF,CAAC;CACH;CAKA,SAAM,mBACE,OAAO,OAAO,QAAQ,IAAI,IAC/B,YAAY;EACX,YAAY;EACZ,IAAI,KAAK,QACP,KAAK,MAAM,CAAC,UAAU,YAAY,KAAK,OAAO,CAAC,GAC7C,QAAQ,YAAY,QAAQ,CAAC;CAGnC,CACF;CAEA,SAAM,gBAAgB;EACpB,MAAM,QAAQ,OAAO,QAAQ,gBAAgB;GAC3C,gBAAgB,YAAY,IAAI;GAChC,qBAAqB,OAAO,KAAK,EAAE,MAAM,QAAQ,KAAK,CAAC;EACzD,CAAC;EAGD,OAAO,qBAAqB;EAC5B,MAAM,eAAe,OAAO,cAAc;GACxC,IAAI,OAAO,eAAe;GAC1B,QAAQ;GACR,QAAQ;GACR,MAAM;GACN,OAAO;GACP,wBAAwB;EAC1B,CAAC;EAED,KAAA,GAAA,sBAAA,eACgB,OAAO,eAAe,UAAU,OAAA,GAAA,sBAAA,eAChC,aAAa,UAAU,GACrC;GACA,OAAO,eAAe;IACpB,GAAG;IACH,SAAS;IACT,eAAe;GACjB,CAAC;GACD,OAAO;EACT;EAEA,IAAI,CAAC,oBAAoB,MAAM,KAAK,CAAC,OAAO,KAC1C,qBAAqB,OAAO,KAAK,EAAE,MAAM,QAAQ,KAAK,CAAC;EAGzD,OAAO;CACT,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,WAAW;CACzB,MAAM,SAAS,kBAAA,UAAU;CACzB,SAAM,gBAAgB;EACpB,MAAM,mBAAmB,oBAAoB,MAAM;EACnD,IAAI,kBACF,OAAO,KAAK;GACV,MAAM;GACN,IAAA,GAAA,sBAAA,uBAAyB,kBAAkB,gBAAgB;EAC7D,CAAC;CAEL,CAAC;CACD,OAAO;AACT"}