@tanstack/vue-router 1.141.4 → 1.141.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/esm/ClientOnly.js +33 -0
- package/dist/esm/ClientOnly.js.map +1 -0
- package/dist/esm/Match.js +66 -39
- package/dist/esm/Match.js.map +1 -1
- package/dist/esm/Transitioner.js +14 -5
- package/dist/esm/Transitioner.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lazyRouteComponent.d.ts +0 -6
- package/dist/esm/lazyRouteComponent.js +5 -24
- package/dist/esm/lazyRouteComponent.js.map +1 -1
- package/dist/esm/link.d.ts +20 -22
- package/dist/esm/link.js +3 -2
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/route.d.ts +6 -1
- package/dist/esm/route.js +25 -0
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/ssr/renderRouterToStream.js +1 -1
- package/dist/esm/ssr/renderRouterToStream.js.map +1 -1
- package/dist/source/Match.jsx +90 -63
- package/dist/source/Match.jsx.map +1 -1
- package/dist/source/Transitioner.jsx +12 -5
- package/dist/source/Transitioner.jsx.map +1 -1
- package/dist/source/index.d.ts +2 -1
- package/dist/source/index.jsx +1 -0
- package/dist/source/index.jsx.map +1 -1
- package/dist/source/lazyRouteComponent.d.ts +0 -6
- package/dist/source/lazyRouteComponent.jsx +3 -23
- package/dist/source/lazyRouteComponent.jsx.map +1 -1
- package/dist/source/link.d.ts +20 -22
- package/dist/source/link.jsx +3 -2
- package/dist/source/link.jsx.map +1 -1
- package/dist/source/route.d.ts +6 -1
- package/dist/source/route.js +13 -0
- package/dist/source/route.js.map +1 -1
- package/dist/source/ssr/renderRouterToStream.jsx +1 -1
- package/dist/source/ssr/renderRouterToStream.jsx.map +1 -1
- package/package.json +3 -2
- package/src/Match.tsx +115 -73
- package/src/Transitioner.tsx +15 -6
- package/src/index.tsx +2 -0
- package/src/lazyRouteComponent.tsx +10 -32
- package/src/link.tsx +87 -59
- package/src/route.ts +33 -1
- package/src/ssr/renderRouterToStream.tsx +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# TanStack Vue Router
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|
|
|
7
7
|
🤖 Type-safe router w/ built-in caching & URL state management for Vue!
|
|
8
8
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as Vue from "vue";
|
|
2
|
+
const ClientOnly = Vue.defineComponent({
|
|
3
|
+
name: "ClientOnly",
|
|
4
|
+
props: {
|
|
5
|
+
fallback: {
|
|
6
|
+
type: Object,
|
|
7
|
+
default: null
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
setup(props, {
|
|
11
|
+
slots
|
|
12
|
+
}) {
|
|
13
|
+
const hydrated = useHydrated();
|
|
14
|
+
return () => {
|
|
15
|
+
if (hydrated.value) {
|
|
16
|
+
return slots.default?.();
|
|
17
|
+
}
|
|
18
|
+
return props.fallback ?? null;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
function useHydrated() {
|
|
23
|
+
const hydrated = Vue.ref(false);
|
|
24
|
+
Vue.onMounted(() => {
|
|
25
|
+
hydrated.value = true;
|
|
26
|
+
});
|
|
27
|
+
return hydrated;
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
ClientOnly,
|
|
31
|
+
useHydrated
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=ClientOnly.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClientOnly.js","sources":["../../src/ClientOnly.tsx"],"sourcesContent":["import * as Vue from 'vue'\n\nexport interface ClientOnlyProps {\n /**\n * The children to render when the JS is loaded.\n */\n children?: Vue.VNode\n /**\n * The fallback component to render if the JS is not yet loaded.\n */\n fallback?: Vue.VNode\n}\n\n/**\n * Render the children only after the JS has loaded client-side. Use an optional\n * fallback component if the JS is not yet loaded.\n *\n * @example\n * Render a Chart component if JS loads, renders a simple FakeChart\n * component server-side or if there is no JS. The FakeChart can have only the\n * UI without the behavior or be a loading spinner or skeleton.\n *\n * ```tsx\n * return (\n * <ClientOnly fallback={<FakeChart />}>\n * <Chart />\n * </ClientOnly>\n * )\n * ```\n */\nexport const ClientOnly = Vue.defineComponent({\n name: 'ClientOnly',\n props: {\n fallback: {\n type: Object as Vue.PropType<Vue.VNode>,\n default: null,\n },\n },\n setup(props, { slots }) {\n const hydrated = useHydrated()\n return () => {\n if (hydrated.value) {\n return slots.default?.()\n }\n return props.fallback ?? null\n }\n },\n})\n\n/**\n * Return a boolean indicating if the JS has been hydrated already.\n * When doing Server-Side Rendering, the result will always be false.\n * When doing Client-Side Rendering, the result will always be false on the\n * first render and true from then on. Even if a new component renders it will\n * always start with true.\n *\n * @example\n * ```tsx\n * // Disable a button that needs JS to work.\n * const hydrated = useHydrated()\n * return (\n * <button type=\"button\" disabled={!hydrated.value} onClick={doSomethingCustom}>\n * Click me\n * </button>\n * )\n * ```\n * @returns True if the JS has been hydrated already, false otherwise.\n */\nexport function useHydrated(): Vue.Ref<boolean> {\n const hydrated = Vue.ref(false)\n Vue.onMounted(() => {\n hydrated.value = true\n })\n return hydrated\n}\n"],"names":["ClientOnly","Vue","defineComponent","name","props","fallback","type","Object","default","setup","slots","hydrated","useHydrated","value","ref","onMounted"],"mappings":";MA8BaA,aAAaC,IAAIC,gBAAgB;AAAA,EAC5CC,MAAM;AAAA,EACNC,OAAO;AAAA,IACLC,UAAU;AAAA,MACRC,MAAMC;AAAAA,MACNC,SAAS;AAAA,IACX;AAAA;EAEFC,MAAML,OAAO;AAAA,IAAEM;AAAAA,EAAM,GAAG;AACtB,UAAMC,WAAWC,YAAW;AAC5B,WAAO,MAAM;AACX,UAAID,SAASE,OAAO;AAClB,eAAOH,MAAMF,UAAO;AAAA,MACtB;AACA,aAAOJ,MAAMC,YAAY;AAAA,IAC3B;AAAA,EACF;AACF,CAAC;AAqBM,SAASO,cAAgC;AAC9C,QAAMD,WAAWV,IAAIa,IAAI,KAAK;AAC9Bb,MAAIc,UAAU,MAAM;AAClBJ,aAASE,QAAQ;AAAA,EACnB,CAAC;AACD,SAAOF;AACT;"}
|
package/dist/esm/Match.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as Vue from "vue";
|
|
2
2
|
import invariant from "tiny-invariant";
|
|
3
3
|
import warning from "tiny-warning";
|
|
4
|
-
import {
|
|
4
|
+
import { rootRouteId, isNotFound, isRedirect, createControlledPromise, getLocationChangeInfo } from "@tanstack/router-core";
|
|
5
5
|
import { CatchBoundary, ErrorComponent } from "./CatchBoundary.js";
|
|
6
|
+
import { ClientOnly } from "./ClientOnly.js";
|
|
6
7
|
import { useRouterState } from "./useRouterState.js";
|
|
7
8
|
import { useRouter } from "./useRouter.js";
|
|
8
9
|
import { CatchNotFound } from "./not-found.js";
|
|
@@ -40,19 +41,27 @@ const Match = Vue.defineComponent({
|
|
|
40
41
|
// Return the actual matchId (may differ from props.matchId)
|
|
41
42
|
routeId,
|
|
42
43
|
parentRouteId,
|
|
43
|
-
loadedAt: s.loadedAt
|
|
44
|
+
loadedAt: s.loadedAt,
|
|
45
|
+
ssr: match.ssr,
|
|
46
|
+
_displayPending: match._displayPending
|
|
44
47
|
};
|
|
45
48
|
}
|
|
46
49
|
});
|
|
47
50
|
invariant(matchData.value, `Could not find routeId for matchId "${props.matchId}". Please file an issue!`);
|
|
48
51
|
const route = Vue.computed(() => matchData.value ? router.routesById[matchData.value.routeId] : null);
|
|
49
52
|
const PendingComponent = Vue.computed(() => route.value?.options?.pendingComponent ?? router?.options?.defaultPendingComponent);
|
|
53
|
+
const pendingElement = Vue.computed(() => PendingComponent.value ? Vue.h(PendingComponent.value) : void 0);
|
|
50
54
|
const routeErrorComponent = Vue.computed(() => route.value?.options?.errorComponent ?? router?.options?.defaultErrorComponent);
|
|
51
55
|
const routeOnCatch = Vue.computed(() => route.value?.options?.onCatch ?? router?.options?.defaultOnCatch);
|
|
52
56
|
const routeNotFoundComponent = Vue.computed(() => route.value?.isRoot ? (
|
|
53
57
|
// If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component
|
|
54
58
|
route.value?.options?.notFoundComponent ?? router?.options?.notFoundRoute?.options?.component
|
|
55
59
|
) : route.value?.options?.notFoundComponent);
|
|
60
|
+
const hasShellComponent = Vue.computed(() => {
|
|
61
|
+
if (!route.value?.isRoot) return false;
|
|
62
|
+
return !!route.value.options.shellComponent;
|
|
63
|
+
});
|
|
64
|
+
const ShellComponent = Vue.computed(() => hasShellComponent.value ? route.value.options.shellComponent : null);
|
|
56
65
|
const matchIdRef = Vue.ref(matchData.value?.matchId ?? props.matchId);
|
|
57
66
|
Vue.watch([() => props.matchId, () => matchData.value?.matchId], ([propsMatchId, dataMatchId]) => {
|
|
58
67
|
matchIdRef.value = dataMatchId ?? propsMatchId;
|
|
@@ -62,43 +71,52 @@ const Match = Vue.defineComponent({
|
|
|
62
71
|
Vue.provide(matchContext, matchIdRef);
|
|
63
72
|
return () => {
|
|
64
73
|
const actualMatchId = matchData.value?.matchId ?? props.matchId;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
fallback: (error) => {
|
|
71
|
-
if (!routeNotFoundComponent.value || error.routeId && error.routeId !== matchData.value?.routeId || !error.routeId && route.value && !route.value.isRoot) throw error;
|
|
72
|
-
return Vue.h(routeNotFoundComponent.value, error);
|
|
73
|
-
},
|
|
74
|
-
children: content
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
if (routeErrorComponent.value) {
|
|
78
|
-
content = CatchBoundary({
|
|
79
|
-
getResetKey: () => matchData.value?.loadedAt ?? 0,
|
|
80
|
-
errorComponent: routeErrorComponent.value || ErrorComponent,
|
|
81
|
-
onCatch: (error) => {
|
|
82
|
-
if (isNotFound(error)) throw error;
|
|
83
|
-
warning(false, `Error in route match: ${actualMatchId}`);
|
|
84
|
-
routeOnCatch.value?.(error);
|
|
85
|
-
},
|
|
86
|
-
children: content
|
|
74
|
+
const resolvedNoSsr = matchData.value?.ssr === false || matchData.value?.ssr === "data-only";
|
|
75
|
+
const shouldClientOnly = resolvedNoSsr || !!matchData.value?._displayPending;
|
|
76
|
+
const renderMatchContent = () => {
|
|
77
|
+
const matchInner = Vue.h(MatchInner, {
|
|
78
|
+
matchId: actualMatchId
|
|
87
79
|
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (needsSuspense) {
|
|
91
|
-
content = Vue.h(Vue.Suspense, {
|
|
92
|
-
fallback: PendingComponent.value ? Vue.h(PendingComponent.value) : null
|
|
80
|
+
let content = shouldClientOnly ? Vue.h(ClientOnly, {
|
|
81
|
+
fallback: pendingElement.value
|
|
93
82
|
}, {
|
|
94
|
-
default: () =>
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
83
|
+
default: () => matchInner
|
|
84
|
+
}) : matchInner;
|
|
85
|
+
if (routeNotFoundComponent.value) {
|
|
86
|
+
content = Vue.h(CatchNotFound, {
|
|
87
|
+
fallback: (error) => {
|
|
88
|
+
if (!routeNotFoundComponent.value || error.routeId && error.routeId !== matchData.value?.routeId || !error.routeId && route.value && !route.value.isRoot) throw error;
|
|
89
|
+
return Vue.h(routeNotFoundComponent.value, error);
|
|
90
|
+
},
|
|
91
|
+
children: content
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (routeErrorComponent.value) {
|
|
95
|
+
content = CatchBoundary({
|
|
96
|
+
getResetKey: () => matchData.value?.loadedAt ?? 0,
|
|
97
|
+
errorComponent: routeErrorComponent.value || ErrorComponent,
|
|
98
|
+
onCatch: (error) => {
|
|
99
|
+
if (isNotFound(error)) throw error;
|
|
100
|
+
warning(false, `Error in route match: ${actualMatchId}`);
|
|
101
|
+
routeOnCatch.value?.(error);
|
|
102
|
+
},
|
|
103
|
+
children: content
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const withScrollRestoration = [content, matchData.value?.parentRouteId === rootRouteId && router.options.scrollRestoration ? Vue.h(Vue.Fragment, null, [Vue.h(OnRendered), Vue.h(ScrollRestoration)]) : null].filter(Boolean);
|
|
107
|
+
if (withScrollRestoration.length === 1) {
|
|
108
|
+
return withScrollRestoration[0];
|
|
109
|
+
}
|
|
110
|
+
return Vue.h(Vue.Fragment, null, withScrollRestoration);
|
|
111
|
+
};
|
|
112
|
+
if (!hasShellComponent.value) {
|
|
113
|
+
return renderMatchContent();
|
|
100
114
|
}
|
|
101
|
-
return Vue.h(
|
|
115
|
+
return Vue.h(ShellComponent.value, null, {
|
|
116
|
+
// Important: return a fresh VNode on each slot invocation so that shell
|
|
117
|
+
// components can re-render without reusing a cached VNode instance.
|
|
118
|
+
default: () => renderMatchContent()
|
|
119
|
+
});
|
|
102
120
|
};
|
|
103
121
|
}
|
|
104
122
|
});
|
|
@@ -164,7 +182,10 @@ const MatchInner = Vue.defineComponent({
|
|
|
164
182
|
match: {
|
|
165
183
|
id: match2.id,
|
|
166
184
|
status: match2.status,
|
|
167
|
-
error: match2.error
|
|
185
|
+
error: match2.error,
|
|
186
|
+
ssr: match2.ssr,
|
|
187
|
+
_forcePending: match2._forcePending,
|
|
188
|
+
_displayPending: match2._displayPending
|
|
168
189
|
},
|
|
169
190
|
remountKey: remountKey2
|
|
170
191
|
};
|
|
@@ -177,8 +198,14 @@ const MatchInner = Vue.defineComponent({
|
|
|
177
198
|
const match = Vue.computed(() => combinedState.value?.match);
|
|
178
199
|
const remountKey = Vue.computed(() => combinedState.value?.remountKey);
|
|
179
200
|
return () => {
|
|
180
|
-
if (!combinedState.value || !match.value || !route.value)
|
|
181
|
-
|
|
201
|
+
if (!combinedState.value || !match.value || !route.value) return null;
|
|
202
|
+
if (match.value._displayPending) {
|
|
203
|
+
const PendingComponent = route.value.options.pendingComponent ?? router.options.defaultPendingComponent;
|
|
204
|
+
return PendingComponent ? Vue.h(PendingComponent) : null;
|
|
205
|
+
}
|
|
206
|
+
if (match.value._forcePending) {
|
|
207
|
+
const PendingComponent = route.value.options.pendingComponent ?? router.options.defaultPendingComponent;
|
|
208
|
+
return PendingComponent ? Vue.h(PendingComponent) : null;
|
|
182
209
|
}
|
|
183
210
|
if (match.value.status === "notFound") {
|
|
184
211
|
invariant(isNotFound(match.value.error), "Expected a notFound error");
|
package/dist/esm/Match.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Match.js","sources":["../../src/Match.tsx"],"sourcesContent":["import * as Vue from 'vue'\nimport invariant from 'tiny-invariant'\nimport warning from 'tiny-warning'\nimport {\n createControlledPromise,\n getLocationChangeInfo,\n isNotFound,\n isRedirect,\n rootRouteId,\n} from '@tanstack/router-core'\nimport { CatchBoundary, ErrorComponent } from './CatchBoundary'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport { CatchNotFound } from './not-found'\nimport { matchContext } from './matchContext'\nimport { renderRouteNotFound } from './renderRouteNotFound'\nimport { ScrollRestoration } from './scroll-restoration'\nimport type { VNode } from 'vue'\nimport type { AnyRoute } from '@tanstack/router-core'\n\nexport const Match = Vue.defineComponent({\n name: 'Match',\n props: {\n matchId: {\n type: String,\n required: true,\n },\n },\n setup(props) {\n const router = useRouter()\n\n // Track the last known routeId to handle stale props during same-route transitions\n let lastKnownRouteId: string | null = null\n\n // Combined selector that returns all needed data including the actual matchId\n // This handles stale props.matchId during same-route transitions\n const matchData = useRouterState({\n select: (s) => {\n // First try to find match by props.matchId\n let match = s.matches.find((d) => d.id === props.matchId)\n let matchIndex = match\n ? s.matches.findIndex((d) => d.id === props.matchId)\n : -1\n\n // If match found, update lastKnownRouteId\n if (match) {\n lastKnownRouteId = match.routeId as string\n } else if (lastKnownRouteId) {\n // Match not found - props.matchId might be stale during a same-route transition\n // Try to find the NEW match by routeId\n match = s.matches.find((d) => d.routeId === lastKnownRouteId)\n matchIndex = match\n ? s.matches.findIndex((d) => d.routeId === lastKnownRouteId)\n : -1\n }\n\n if (!match) {\n return null\n }\n\n const routeId = match.routeId as string\n const parentRouteId =\n matchIndex > 0 ? (s.matches[matchIndex - 1]?.routeId as string) : null\n\n return {\n matchId: match.id, // Return the actual matchId (may differ from props.matchId)\n routeId,\n parentRouteId,\n loadedAt: s.loadedAt,\n }\n },\n })\n\n invariant(\n matchData.value,\n `Could not find routeId for matchId \"${props.matchId}\". Please file an issue!`,\n )\n\n const route = Vue.computed(() =>\n matchData.value ? router.routesById[matchData.value.routeId] : null,\n )\n\n const PendingComponent = Vue.computed(\n () =>\n route.value?.options?.pendingComponent ??\n router?.options?.defaultPendingComponent,\n )\n\n const routeErrorComponent = Vue.computed(\n () =>\n route.value?.options?.errorComponent ??\n router?.options?.defaultErrorComponent,\n )\n\n const routeOnCatch = Vue.computed(\n () => route.value?.options?.onCatch ?? router?.options?.defaultOnCatch,\n )\n\n const routeNotFoundComponent = Vue.computed(() =>\n route.value?.isRoot\n ? // If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component\n (route.value?.options?.notFoundComponent ??\n router?.options?.notFoundRoute?.options?.component)\n : route.value?.options?.notFoundComponent,\n )\n\n // Create a ref for the current matchId that we provide to child components\n // This ref is updated to the ACTUAL matchId found (which may differ from props during transitions)\n const matchIdRef = Vue.ref(matchData.value?.matchId ?? props.matchId)\n\n // Watch both props.matchId and matchData to keep matchIdRef in sync\n // This ensures Outlet gets the correct matchId even during transitions\n Vue.watch(\n [() => props.matchId, () => matchData.value?.matchId],\n ([propsMatchId, dataMatchId]) => {\n // Prefer the matchId from matchData (which handles fallback)\n // Fall back to props.matchId if matchData is null\n matchIdRef.value = dataMatchId ?? propsMatchId\n },\n { immediate: true },\n )\n\n // Provide the matchId to child components\n Vue.provide(matchContext, matchIdRef)\n\n return (): VNode => {\n // Use the actual matchId from matchData, not props (which may be stale)\n const actualMatchId = matchData.value?.matchId ?? props.matchId\n\n // Determine which components to render\n let content: VNode = Vue.h(MatchInner, { matchId: actualMatchId })\n\n // Wrap in NotFound boundary if needed\n if (routeNotFoundComponent.value) {\n content = Vue.h(CatchNotFound, {\n fallback: (error: any) => {\n // If the current not found handler doesn't exist or it has a\n // route ID which doesn't match the current route, rethrow the error\n if (\n !routeNotFoundComponent.value ||\n (error.routeId && error.routeId !== matchData.value?.routeId) ||\n (!error.routeId && route.value && !route.value.isRoot)\n )\n throw error\n\n return Vue.h(routeNotFoundComponent.value, error)\n },\n children: content,\n })\n }\n\n // Wrap in error boundary if needed\n if (routeErrorComponent.value) {\n content = CatchBoundary({\n getResetKey: () => matchData.value?.loadedAt ?? 0,\n errorComponent: routeErrorComponent.value || ErrorComponent,\n onCatch: (error: Error) => {\n // Forward not found errors (we don't want to show the error component for these)\n if (isNotFound(error)) throw error\n warning(false, `Error in route match: ${actualMatchId}`)\n routeOnCatch.value?.(error)\n },\n children: content,\n })\n }\n\n // Wrap in suspense if needed\n // Root routes should also wrap in Suspense if they have a pendingComponent\n const needsSuspense =\n route.value &&\n (route.value?.options?.wrapInSuspense ??\n PendingComponent.value ??\n false)\n\n if (needsSuspense) {\n content = Vue.h(\n Vue.Suspense,\n {\n fallback: PendingComponent.value\n ? Vue.h(PendingComponent.value)\n : null,\n },\n {\n default: () => content,\n },\n )\n }\n\n // Add scroll restoration if needed\n const withScrollRestoration: Array<VNode> = [\n content,\n matchData.value?.parentRouteId === rootRouteId &&\n router.options.scrollRestoration\n ? Vue.h(Vue.Fragment, null, [\n Vue.h(OnRendered),\n Vue.h(ScrollRestoration),\n ])\n : null,\n ].filter(Boolean) as Array<VNode>\n\n // Return single child directly to avoid Fragment wrapper that causes hydration mismatch\n if (withScrollRestoration.length === 1) {\n return withScrollRestoration[0]!\n }\n return Vue.h(Vue.Fragment, null, withScrollRestoration)\n }\n },\n})\n\n// On Rendered can't happen above the root layout because it actually\n// renders a dummy dom element to track the rendered state of the app.\n// We render a script tag with a key that changes based on the current\n// location state.key. Also, because it's below the root layout, it\n// allows us to fire onRendered events even after a hydration mismatch\n// error that occurred above the root layout (like bad head/link tags,\n// which is common).\nconst OnRendered = Vue.defineComponent({\n name: 'OnRendered',\n setup() {\n const router = useRouter()\n\n const location = useRouterState({\n select: (s) => {\n return s.resolvedLocation?.state.key\n },\n })\n\n Vue.watchEffect(() => {\n if (location.value) {\n router.emit({\n type: 'onRendered',\n ...getLocationChangeInfo(router.state),\n })\n }\n })\n\n return () => null\n },\n})\n\nexport const MatchInner = Vue.defineComponent({\n name: 'MatchInner',\n props: {\n matchId: {\n type: String,\n required: true,\n },\n },\n setup(props) {\n const router = useRouter()\n\n // Track the last known routeId to handle stale props during same-route transitions\n // This is stored outside the selector so it persists across selector calls\n let lastKnownRouteId: string | null = null\n\n // Combined selector for match state AND remount key\n // This ensures both are computed in the same selector call with consistent data\n const combinedState = useRouterState({\n select: (s) => {\n // First try to find match by props.matchId\n let match = s.matches.find((d) => d.id === props.matchId)\n\n // If match found, update lastKnownRouteId\n if (match) {\n lastKnownRouteId = match.routeId as string\n } else if (lastKnownRouteId) {\n // Match not found - props.matchId might be stale during a same-route transition\n // (matchId changed due to loaderDepsHash but props haven't updated yet)\n // Try to find the NEW match by routeId and use that instead\n const sameRouteMatch = s.matches.find(\n (d) => d.routeId === lastKnownRouteId,\n )\n if (sameRouteMatch) {\n match = sameRouteMatch\n }\n }\n\n if (!match) {\n // Route no longer exists - truly navigating away\n return null\n }\n\n const routeId = match.routeId as string\n\n // Compute remount key\n const remountFn =\n (router.routesById[routeId] as AnyRoute).options.remountDeps ??\n router.options.defaultRemountDeps\n\n let remountKey: string | undefined\n if (remountFn) {\n const remountDeps = remountFn({\n routeId,\n loaderDeps: match.loaderDeps,\n params: match._strictParams,\n search: match._strictSearch,\n })\n remountKey = remountDeps ? JSON.stringify(remountDeps) : undefined\n }\n\n return {\n routeId,\n match: {\n id: match.id,\n status: match.status,\n error: match.error,\n },\n remountKey,\n }\n },\n })\n\n const route = Vue.computed(() => {\n if (!combinedState.value) return null\n return router.routesById[combinedState.value.routeId]!\n })\n\n const match = Vue.computed(() => combinedState.value?.match)\n const remountKey = Vue.computed(() => combinedState.value?.remountKey)\n\n return (): VNode | null => {\n // If match doesn't exist, return null (component is being unmounted or not ready)\n if (!combinedState.value || !match.value || !route.value) {\n return null\n }\n\n // Handle different match statuses\n if (match.value.status === 'notFound') {\n invariant(isNotFound(match.value.error), 'Expected a notFound error')\n return renderRouteNotFound(router, route.value, match.value.error)\n }\n\n if (match.value.status === 'redirected') {\n invariant(isRedirect(match.value.error), 'Expected a redirect error')\n throw router.getMatch(match.value.id)?._nonReactive.loadPromise\n }\n\n if (match.value.status === 'error') {\n // Check if this route or any parent has an error component\n const RouteErrorComponent =\n route.value.options.errorComponent ??\n router.options.defaultErrorComponent\n\n // If this route has an error component, render it directly\n // This is more reliable than relying on Vue's error boundary\n if (RouteErrorComponent) {\n return Vue.h(RouteErrorComponent, {\n error: match.value.error,\n reset: () => {\n router.invalidate()\n },\n info: {\n componentStack: '',\n },\n })\n }\n\n // If there's no error component for this route, throw the error\n // so it can bubble up to the nearest parent with an error component\n throw match.value.error\n }\n\n if (match.value.status === 'pending') {\n const pendingMinMs =\n route.value.options.pendingMinMs ?? router.options.defaultPendingMinMs\n\n const routerMatch = router.getMatch(match.value.id)\n if (\n pendingMinMs &&\n routerMatch &&\n !routerMatch._nonReactive.minPendingPromise\n ) {\n // Create a promise that will resolve after the minPendingMs\n if (!router.isServer) {\n const minPendingPromise = createControlledPromise<void>()\n\n routerMatch._nonReactive.minPendingPromise = minPendingPromise\n\n setTimeout(() => {\n minPendingPromise.resolve()\n // We've handled the minPendingPromise, so we can delete it\n routerMatch._nonReactive.minPendingPromise = undefined\n }, pendingMinMs)\n }\n }\n\n // In Vue, we render the pending component directly instead of throwing a promise\n // because Vue's Suspense doesn't catch thrown promises like React does\n const PendingComponent =\n route.value.options.pendingComponent ??\n router.options.defaultPendingComponent\n\n if (PendingComponent) {\n return Vue.h(PendingComponent)\n }\n\n // If no pending component, return null while loading\n return null\n }\n\n // Success status - render the component with remount key\n const Comp =\n route.value.options.component ?? router.options.defaultComponent\n const key = remountKey.value\n\n if (Comp) {\n // Pass key as a prop - Vue.h properly handles 'key' as a special prop\n return Vue.h(Comp, key !== undefined ? { key } : undefined)\n }\n\n return Vue.h(Outlet, key !== undefined ? { key } : undefined)\n }\n },\n})\n\nexport const Outlet = Vue.defineComponent({\n name: 'Outlet',\n setup() {\n const router = useRouter()\n const matchId = Vue.inject(matchContext)\n const safeMatchId = Vue.computed(() => matchId?.value || '')\n\n const routeId = useRouterState({\n select: (s) =>\n s.matches.find((d) => d.id === safeMatchId.value)?.routeId as string,\n })\n\n const route = Vue.computed(() => router.routesById[routeId.value]!)\n\n const parentGlobalNotFound = useRouterState({\n select: (s) => {\n const matches = s.matches\n const parentMatch = matches.find((d) => d.id === safeMatchId.value)\n\n // During navigation transitions, parent match can be temporarily removed\n // Return false to avoid errors - the component will handle this gracefully\n if (!parentMatch) {\n return false\n }\n\n return parentMatch.globalNotFound\n },\n })\n\n const childMatchData = useRouterState({\n select: (s) => {\n const matches = s.matches\n const index = matches.findIndex((d) => d.id === safeMatchId.value)\n const child = matches[index + 1]\n if (!child) return null\n return {\n id: child.id,\n // Key based on routeId + params only (not loaderDeps)\n // This ensures component recreates when params change,\n // but NOT when only loaderDeps change\n paramsKey: child.routeId + JSON.stringify(child._strictParams),\n }\n },\n })\n\n return (): VNode | null => {\n if (parentGlobalNotFound.value) {\n return renderRouteNotFound(router, route.value, undefined)\n }\n\n if (!childMatchData.value) {\n return null\n }\n\n const nextMatch = Vue.h(Match, {\n matchId: childMatchData.value.id,\n key: childMatchData.value.paramsKey,\n })\n\n if (safeMatchId.value === rootRouteId) {\n return Vue.h(\n Vue.Suspense,\n {\n fallback: router.options.defaultPendingComponent\n ? Vue.h(router.options.defaultPendingComponent)\n : null,\n },\n {\n default: () => nextMatch,\n },\n )\n }\n\n return nextMatch\n }\n },\n})\n"],"names":["Match","Vue","defineComponent","name","props","matchId","type","String","required","setup","router","useRouter","lastKnownRouteId","matchData","useRouterState","select","s","match","matches","find","d","id","matchIndex","findIndex","routeId","parentRouteId","loadedAt","invariant","value","route","computed","routesById","PendingComponent","options","pendingComponent","defaultPendingComponent","routeErrorComponent","errorComponent","defaultErrorComponent","routeOnCatch","onCatch","defaultOnCatch","routeNotFoundComponent","isRoot","notFoundComponent","notFoundRoute","component","matchIdRef","ref","watch","propsMatchId","dataMatchId","immediate","provide","matchContext","actualMatchId","content","h","MatchInner","CatchNotFound","fallback","error","children","CatchBoundary","getResetKey","ErrorComponent","isNotFound","warning","needsSuspense","wrapInSuspense","Suspense","default","withScrollRestoration","rootRouteId","scrollRestoration","Fragment","OnRendered","ScrollRestoration","filter","Boolean","length","location","resolvedLocation","state","key","watchEffect","emit","getLocationChangeInfo","combinedState","sameRouteMatch","remountFn","remountDeps","defaultRemountDeps","remountKey","loaderDeps","params","_strictParams","search","_strictSearch","JSON","stringify","undefined","status","renderRouteNotFound","isRedirect","getMatch","_nonReactive","loadPromise","RouteErrorComponent","reset","invalidate","info","componentStack","pendingMinMs","defaultPendingMinMs","routerMatch","minPendingPromise","isServer","createControlledPromise","setTimeout","resolve","Comp","defaultComponent","Outlet","inject","safeMatchId","parentGlobalNotFound","parentMatch","globalNotFound","childMatchData","index","child","paramsKey","nextMatch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Match.js","sources":["../../src/Match.tsx"],"sourcesContent":["import * as Vue from 'vue'\nimport invariant from 'tiny-invariant'\nimport warning from 'tiny-warning'\nimport {\n createControlledPromise,\n getLocationChangeInfo,\n isNotFound,\n isRedirect,\n rootRouteId,\n} from '@tanstack/router-core'\nimport { CatchBoundary, ErrorComponent } from './CatchBoundary'\nimport { ClientOnly } from './ClientOnly'\nimport { useRouterState } from './useRouterState'\nimport { useRouter } from './useRouter'\nimport { CatchNotFound } from './not-found'\nimport { matchContext } from './matchContext'\nimport { renderRouteNotFound } from './renderRouteNotFound'\nimport { ScrollRestoration } from './scroll-restoration'\nimport type { VNode } from 'vue'\nimport type { AnyRoute, RootRouteOptions } from '@tanstack/router-core'\n\nexport const Match = Vue.defineComponent({\n name: 'Match',\n props: {\n matchId: {\n type: String,\n required: true,\n },\n },\n setup(props) {\n const router = useRouter()\n\n // Track the last known routeId to handle stale props during same-route transitions\n let lastKnownRouteId: string | null = null\n\n // Combined selector that returns all needed data including the actual matchId\n // This handles stale props.matchId during same-route transitions\n const matchData = useRouterState({\n select: (s) => {\n // First try to find match by props.matchId\n let match = s.matches.find((d) => d.id === props.matchId)\n let matchIndex = match\n ? s.matches.findIndex((d) => d.id === props.matchId)\n : -1\n\n // If match found, update lastKnownRouteId\n if (match) {\n lastKnownRouteId = match.routeId as string\n } else if (lastKnownRouteId) {\n // Match not found - props.matchId might be stale during a same-route transition\n // Try to find the NEW match by routeId\n match = s.matches.find((d) => d.routeId === lastKnownRouteId)\n matchIndex = match\n ? s.matches.findIndex((d) => d.routeId === lastKnownRouteId)\n : -1\n }\n\n if (!match) {\n return null\n }\n\n const routeId = match.routeId as string\n const parentRouteId =\n matchIndex > 0 ? (s.matches[matchIndex - 1]?.routeId as string) : null\n\n return {\n matchId: match.id, // Return the actual matchId (may differ from props.matchId)\n routeId,\n parentRouteId,\n loadedAt: s.loadedAt,\n ssr: match.ssr,\n _displayPending: match._displayPending,\n }\n },\n })\n\n invariant(\n matchData.value,\n `Could not find routeId for matchId \"${props.matchId}\". Please file an issue!`,\n )\n\n const route = Vue.computed(() =>\n matchData.value ? router.routesById[matchData.value.routeId] : null,\n )\n\n const PendingComponent = Vue.computed(\n () =>\n route.value?.options?.pendingComponent ??\n router?.options?.defaultPendingComponent,\n )\n\n const pendingElement = Vue.computed(() =>\n PendingComponent.value ? Vue.h(PendingComponent.value) : undefined,\n )\n\n const routeErrorComponent = Vue.computed(\n () =>\n route.value?.options?.errorComponent ??\n router?.options?.defaultErrorComponent,\n )\n\n const routeOnCatch = Vue.computed(\n () => route.value?.options?.onCatch ?? router?.options?.defaultOnCatch,\n )\n\n const routeNotFoundComponent = Vue.computed(() =>\n route.value?.isRoot\n ? // If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component\n (route.value?.options?.notFoundComponent ??\n router?.options?.notFoundRoute?.options?.component)\n : route.value?.options?.notFoundComponent,\n )\n\n const hasShellComponent = Vue.computed(() => {\n if (!route.value?.isRoot) return false\n return !!(route.value.options as RootRouteOptions).shellComponent\n })\n\n const ShellComponent = Vue.computed(() =>\n hasShellComponent.value\n ? ((route.value!.options as RootRouteOptions).shellComponent as any)\n : null,\n )\n\n // Create a ref for the current matchId that we provide to child components\n // This ref is updated to the ACTUAL matchId found (which may differ from props during transitions)\n const matchIdRef = Vue.ref(matchData.value?.matchId ?? props.matchId)\n\n // Watch both props.matchId and matchData to keep matchIdRef in sync\n // This ensures Outlet gets the correct matchId even during transitions\n Vue.watch(\n [() => props.matchId, () => matchData.value?.matchId],\n ([propsMatchId, dataMatchId]) => {\n // Prefer the matchId from matchData (which handles fallback)\n // Fall back to props.matchId if matchData is null\n matchIdRef.value = dataMatchId ?? propsMatchId\n },\n { immediate: true },\n )\n\n // Provide the matchId to child components\n Vue.provide(matchContext, matchIdRef)\n\n return (): VNode => {\n // Use the actual matchId from matchData, not props (which may be stale)\n const actualMatchId = matchData.value?.matchId ?? props.matchId\n\n const resolvedNoSsr =\n matchData.value?.ssr === false || matchData.value?.ssr === 'data-only'\n const shouldClientOnly =\n resolvedNoSsr || !!matchData.value?._displayPending\n\n const renderMatchContent = (): VNode => {\n const matchInner = Vue.h(MatchInner, { matchId: actualMatchId })\n\n let content: VNode = shouldClientOnly\n ? Vue.h(\n ClientOnly,\n {\n fallback: pendingElement.value,\n },\n {\n default: () => matchInner,\n },\n )\n : matchInner\n\n // Wrap in NotFound boundary if needed\n if (routeNotFoundComponent.value) {\n content = Vue.h(CatchNotFound, {\n fallback: (error: any) => {\n // If the current not found handler doesn't exist or it has a\n // route ID which doesn't match the current route, rethrow the error\n if (\n !routeNotFoundComponent.value ||\n (error.routeId && error.routeId !== matchData.value?.routeId) ||\n (!error.routeId && route.value && !route.value.isRoot)\n )\n throw error\n\n return Vue.h(routeNotFoundComponent.value, error)\n },\n children: content,\n })\n }\n\n // Wrap in error boundary if needed\n if (routeErrorComponent.value) {\n content = CatchBoundary({\n getResetKey: () => matchData.value?.loadedAt ?? 0,\n errorComponent: routeErrorComponent.value || ErrorComponent,\n onCatch: (error: Error) => {\n // Forward not found errors (we don't want to show the error component for these)\n if (isNotFound(error)) throw error\n warning(false, `Error in route match: ${actualMatchId}`)\n routeOnCatch.value?.(error)\n },\n children: content,\n })\n }\n\n // Add scroll restoration if needed\n const withScrollRestoration: Array<VNode> = [\n content,\n matchData.value?.parentRouteId === rootRouteId &&\n router.options.scrollRestoration\n ? Vue.h(Vue.Fragment, null, [\n Vue.h(OnRendered),\n Vue.h(ScrollRestoration),\n ])\n : null,\n ].filter(Boolean) as Array<VNode>\n\n // Return single child directly to avoid Fragment wrapper that causes hydration mismatch\n if (withScrollRestoration.length === 1) {\n return withScrollRestoration[0]!\n }\n\n return Vue.h(Vue.Fragment, null, withScrollRestoration)\n }\n\n if (!hasShellComponent.value) {\n return renderMatchContent()\n }\n\n return Vue.h(ShellComponent.value, null, {\n // Important: return a fresh VNode on each slot invocation so that shell\n // components can re-render without reusing a cached VNode instance.\n default: () => renderMatchContent(),\n })\n }\n },\n})\n\n// On Rendered can't happen above the root layout because it actually\n// renders a dummy dom element to track the rendered state of the app.\n// We render a script tag with a key that changes based on the current\n// location state.key. Also, because it's below the root layout, it\n// allows us to fire onRendered events even after a hydration mismatch\n// error that occurred above the root layout (like bad head/link tags,\n// which is common).\nconst OnRendered = Vue.defineComponent({\n name: 'OnRendered',\n setup() {\n const router = useRouter()\n\n const location = useRouterState({\n select: (s) => {\n return s.resolvedLocation?.state.key\n },\n })\n\n Vue.watchEffect(() => {\n if (location.value) {\n router.emit({\n type: 'onRendered',\n ...getLocationChangeInfo(router.state),\n })\n }\n })\n\n return () => null\n },\n})\n\nexport const MatchInner = Vue.defineComponent({\n name: 'MatchInner',\n props: {\n matchId: {\n type: String,\n required: true,\n },\n },\n setup(props) {\n const router = useRouter()\n\n // Track the last known routeId to handle stale props during same-route transitions\n // This is stored outside the selector so it persists across selector calls\n let lastKnownRouteId: string | null = null\n\n // Combined selector for match state AND remount key\n // This ensures both are computed in the same selector call with consistent data\n const combinedState = useRouterState({\n select: (s) => {\n // First try to find match by props.matchId\n let match = s.matches.find((d) => d.id === props.matchId)\n\n // If match found, update lastKnownRouteId\n if (match) {\n lastKnownRouteId = match.routeId as string\n } else if (lastKnownRouteId) {\n // Match not found - props.matchId might be stale during a same-route transition\n // (matchId changed due to loaderDepsHash but props haven't updated yet)\n // Try to find the NEW match by routeId and use that instead\n const sameRouteMatch = s.matches.find(\n (d) => d.routeId === lastKnownRouteId,\n )\n if (sameRouteMatch) {\n match = sameRouteMatch\n }\n }\n\n if (!match) {\n // Route no longer exists - truly navigating away\n return null\n }\n\n const routeId = match.routeId as string\n\n // Compute remount key\n const remountFn =\n (router.routesById[routeId] as AnyRoute).options.remountDeps ??\n router.options.defaultRemountDeps\n\n let remountKey: string | undefined\n if (remountFn) {\n const remountDeps = remountFn({\n routeId,\n loaderDeps: match.loaderDeps,\n params: match._strictParams,\n search: match._strictSearch,\n })\n remountKey = remountDeps ? JSON.stringify(remountDeps) : undefined\n }\n\n return {\n routeId,\n match: {\n id: match.id,\n status: match.status,\n error: match.error,\n ssr: match.ssr,\n _forcePending: match._forcePending,\n _displayPending: match._displayPending,\n },\n remountKey,\n }\n },\n })\n\n const route = Vue.computed(() => {\n if (!combinedState.value) return null\n return router.routesById[combinedState.value.routeId]!\n })\n\n const match = Vue.computed(() => combinedState.value?.match)\n const remountKey = Vue.computed(() => combinedState.value?.remountKey)\n\n return (): VNode | null => {\n // If match doesn't exist, return null (component is being unmounted or not ready)\n if (!combinedState.value || !match.value || !route.value) return null\n\n // Handle different match statuses\n if (match.value._displayPending) {\n const PendingComponent =\n route.value.options.pendingComponent ??\n router.options.defaultPendingComponent\n\n return PendingComponent ? Vue.h(PendingComponent) : null\n }\n\n if (match.value._forcePending) {\n const PendingComponent =\n route.value.options.pendingComponent ??\n router.options.defaultPendingComponent\n\n return PendingComponent ? Vue.h(PendingComponent) : null\n }\n\n if (match.value.status === 'notFound') {\n invariant(isNotFound(match.value.error), 'Expected a notFound error')\n return renderRouteNotFound(router, route.value, match.value.error)\n }\n\n if (match.value.status === 'redirected') {\n invariant(isRedirect(match.value.error), 'Expected a redirect error')\n throw router.getMatch(match.value.id)?._nonReactive.loadPromise\n }\n\n if (match.value.status === 'error') {\n // Check if this route or any parent has an error component\n const RouteErrorComponent =\n route.value.options.errorComponent ??\n router.options.defaultErrorComponent\n\n // If this route has an error component, render it directly\n // This is more reliable than relying on Vue's error boundary\n if (RouteErrorComponent) {\n return Vue.h(RouteErrorComponent, {\n error: match.value.error,\n reset: () => {\n router.invalidate()\n },\n info: {\n componentStack: '',\n },\n })\n }\n\n // If there's no error component for this route, throw the error\n // so it can bubble up to the nearest parent with an error component\n throw match.value.error\n }\n\n if (match.value.status === 'pending') {\n const pendingMinMs =\n route.value.options.pendingMinMs ?? router.options.defaultPendingMinMs\n\n const routerMatch = router.getMatch(match.value.id)\n if (\n pendingMinMs &&\n routerMatch &&\n !routerMatch._nonReactive.minPendingPromise\n ) {\n // Create a promise that will resolve after the minPendingMs\n if (!router.isServer) {\n const minPendingPromise = createControlledPromise<void>()\n\n routerMatch._nonReactive.minPendingPromise = minPendingPromise\n\n setTimeout(() => {\n minPendingPromise.resolve()\n // We've handled the minPendingPromise, so we can delete it\n routerMatch._nonReactive.minPendingPromise = undefined\n }, pendingMinMs)\n }\n }\n\n // In Vue, we render the pending component directly instead of throwing a promise\n // because Vue's Suspense doesn't catch thrown promises like React does\n const PendingComponent =\n route.value.options.pendingComponent ??\n router.options.defaultPendingComponent\n\n if (PendingComponent) {\n return Vue.h(PendingComponent)\n }\n\n // If no pending component, return null while loading\n return null\n }\n\n // Success status - render the component with remount key\n const Comp =\n route.value.options.component ?? router.options.defaultComponent\n const key = remountKey.value\n\n if (Comp) {\n // Pass key as a prop - Vue.h properly handles 'key' as a special prop\n return Vue.h(Comp, key !== undefined ? { key } : undefined)\n }\n\n return Vue.h(Outlet, key !== undefined ? { key } : undefined)\n }\n },\n})\n\nexport const Outlet = Vue.defineComponent({\n name: 'Outlet',\n setup() {\n const router = useRouter()\n const matchId = Vue.inject(matchContext)\n const safeMatchId = Vue.computed(() => matchId?.value || '')\n\n const routeId = useRouterState({\n select: (s) =>\n s.matches.find((d) => d.id === safeMatchId.value)?.routeId as string,\n })\n\n const route = Vue.computed(() => router.routesById[routeId.value]!)\n\n const parentGlobalNotFound = useRouterState({\n select: (s) => {\n const matches = s.matches\n const parentMatch = matches.find((d) => d.id === safeMatchId.value)\n\n // During navigation transitions, parent match can be temporarily removed\n // Return false to avoid errors - the component will handle this gracefully\n if (!parentMatch) {\n return false\n }\n\n return parentMatch.globalNotFound\n },\n })\n\n const childMatchData = useRouterState({\n select: (s) => {\n const matches = s.matches\n const index = matches.findIndex((d) => d.id === safeMatchId.value)\n const child = matches[index + 1]\n if (!child) return null\n return {\n id: child.id,\n // Key based on routeId + params only (not loaderDeps)\n // This ensures component recreates when params change,\n // but NOT when only loaderDeps change\n paramsKey: child.routeId + JSON.stringify(child._strictParams),\n }\n },\n })\n\n return (): VNode | null => {\n if (parentGlobalNotFound.value) {\n return renderRouteNotFound(router, route.value, undefined)\n }\n\n if (!childMatchData.value) {\n return null\n }\n\n const nextMatch = Vue.h(Match, {\n matchId: childMatchData.value.id,\n key: childMatchData.value.paramsKey,\n })\n\n if (safeMatchId.value === rootRouteId) {\n return Vue.h(\n Vue.Suspense,\n {\n fallback: router.options.defaultPendingComponent\n ? Vue.h(router.options.defaultPendingComponent)\n : null,\n },\n {\n default: () => nextMatch,\n },\n )\n }\n\n return nextMatch\n }\n },\n})\n"],"names":["Match","Vue","defineComponent","name","props","matchId","type","String","required","setup","router","useRouter","lastKnownRouteId","matchData","useRouterState","select","s","match","matches","find","d","id","matchIndex","findIndex","routeId","parentRouteId","loadedAt","ssr","_displayPending","invariant","value","route","computed","routesById","PendingComponent","options","pendingComponent","defaultPendingComponent","pendingElement","h","undefined","routeErrorComponent","errorComponent","defaultErrorComponent","routeOnCatch","onCatch","defaultOnCatch","routeNotFoundComponent","isRoot","notFoundComponent","notFoundRoute","component","hasShellComponent","shellComponent","ShellComponent","matchIdRef","ref","watch","propsMatchId","dataMatchId","immediate","provide","matchContext","actualMatchId","resolvedNoSsr","shouldClientOnly","renderMatchContent","matchInner","MatchInner","content","ClientOnly","fallback","default","CatchNotFound","error","children","CatchBoundary","getResetKey","ErrorComponent","isNotFound","warning","withScrollRestoration","rootRouteId","scrollRestoration","Fragment","OnRendered","ScrollRestoration","filter","Boolean","length","location","resolvedLocation","state","key","watchEffect","emit","getLocationChangeInfo","combinedState","sameRouteMatch","remountFn","remountDeps","defaultRemountDeps","remountKey","loaderDeps","params","_strictParams","search","_strictSearch","JSON","stringify","status","_forcePending","renderRouteNotFound","isRedirect","getMatch","_nonReactive","loadPromise","RouteErrorComponent","reset","invalidate","info","componentStack","pendingMinMs","defaultPendingMinMs","routerMatch","minPendingPromise","isServer","createControlledPromise","setTimeout","resolve","Comp","defaultComponent","Outlet","inject","safeMatchId","parentGlobalNotFound","parentMatch","globalNotFound","childMatchData","index","child","paramsKey","nextMatch","Suspense"],"mappings":"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"}
|
package/dist/esm/Transitioner.js
CHANGED
|
@@ -79,6 +79,13 @@ function useTransitionerSetup() {
|
|
|
79
79
|
const isMounted = Vue.ref(false);
|
|
80
80
|
Vue.onMounted(() => {
|
|
81
81
|
isMounted.value = true;
|
|
82
|
+
if (!isAnyPending.value) {
|
|
83
|
+
router.__store.setState((s) => s.status === "pending" ? {
|
|
84
|
+
...s,
|
|
85
|
+
status: "idle",
|
|
86
|
+
resolvedLocation: s.location
|
|
87
|
+
} : s);
|
|
88
|
+
}
|
|
82
89
|
});
|
|
83
90
|
Vue.onUnmounted(() => {
|
|
84
91
|
isMounted.value = false;
|
|
@@ -130,17 +137,19 @@ function useTransitionerSetup() {
|
|
|
130
137
|
Vue.watch(isAnyPending, (newValue) => {
|
|
131
138
|
if (!isMounted.value) return;
|
|
132
139
|
try {
|
|
140
|
+
if (!newValue && router.__store.state.status === "pending") {
|
|
141
|
+
router.__store.setState((s) => ({
|
|
142
|
+
...s,
|
|
143
|
+
status: "idle",
|
|
144
|
+
resolvedLocation: s.location
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
133
147
|
if (previousIsAnyPending.value.previous && !newValue) {
|
|
134
148
|
const changeInfo = getLocationChangeInfo(router.state);
|
|
135
149
|
router.emit({
|
|
136
150
|
type: "onResolved",
|
|
137
151
|
...changeInfo
|
|
138
152
|
});
|
|
139
|
-
router.__store.setState((s) => ({
|
|
140
|
-
...s,
|
|
141
|
-
status: "idle",
|
|
142
|
-
resolvedLocation: s.location
|
|
143
|
-
}));
|
|
144
153
|
if (changeInfo.hrefChanged) {
|
|
145
154
|
handleHashScroll(router);
|
|
146
155
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Transitioner.js","sources":["../../src/Transitioner.tsx"],"sourcesContent":["import * as Vue from 'vue'\nimport {\n getLocationChangeInfo,\n handleHashScroll,\n trimPathRight,\n} from '@tanstack/router-core'\nimport { useRouter } from './useRouter'\nimport { useRouterState } from './useRouterState'\nimport { usePrevious } from './utils'\n\n// Track mount state per router to avoid double-loading\nlet mountLoadForRouter = { router: null as any, mounted: false }\n\n/**\n * Composable that sets up router transition logic.\n * This is called from MatchesContent to set up:\n * - router.startTransition\n * - router.startViewTransition\n * - History subscription\n * - Router event watchers\n *\n * Must be called during component setup phase.\n */\nexport function useTransitionerSetup() {\n const router = useRouter()\n\n // Skip on server - no transitions needed\n if (router.isServer) {\n return\n }\n\n const isLoading = useRouterState({\n select: ({ isLoading }) => isLoading,\n })\n\n // Track if we're in a transition - using a ref to track async transitions\n const isTransitioning = Vue.ref(false)\n\n // Track pending state changes\n const hasPendingMatches = useRouterState({\n select: (s) => s.matches.some((d) => d.status === 'pending'),\n })\n\n const previousIsLoading = usePrevious(() => isLoading.value)\n\n const isAnyPending = Vue.computed(\n () => isLoading.value || isTransitioning.value || hasPendingMatches.value,\n )\n const previousIsAnyPending = usePrevious(() => isAnyPending.value)\n\n const isPagePending = Vue.computed(\n () => isLoading.value || hasPendingMatches.value,\n )\n const previousIsPagePending = usePrevious(() => isPagePending.value)\n\n // Implement startTransition similar to React/Solid\n // Vue doesn't have a native useTransition like React 18, so we simulate it\n // We also update the router state's isTransitioning flag so useMatch can check it\n router.startTransition = (fn: () => void | Promise<void>) => {\n isTransitioning.value = true\n // Also update the router state so useMatch knows we're transitioning\n try {\n router.__store.setState((s) => ({ ...s, isTransitioning: true }))\n } catch {\n // Ignore errors if component is unmounted\n }\n\n // Helper to end the transition\n const endTransition = () => {\n // Use nextTick to ensure Vue has processed all reactive updates\n Vue.nextTick(() => {\n try {\n isTransitioning.value = false\n router.__store.setState((s) => ({ ...s, isTransitioning: false }))\n } catch {\n // Ignore errors if component is unmounted\n }\n })\n }\n\n // Execute the function synchronously\n // The function internally may call startViewTransition which schedules async work\n // via document.startViewTransition, but we don't need to wait for it here\n // because Vue's reactivity will trigger re-renders when state changes\n fn()\n\n // End the transition on next tick to allow Vue to process reactive updates\n endTransition()\n }\n\n // Vue updates DOM asynchronously (next tick). The View Transitions API expects the\n // update callback promise to resolve only after the DOM has been updated.\n // Wrap the router-core implementation to await a Vue flush before resolving.\n const originalStartViewTransition:\n | undefined\n | ((fn: () => Promise<void>) => void) =\n (router as any).__tsrOriginalStartViewTransition ??\n router.startViewTransition\n\n ;(router as any).__tsrOriginalStartViewTransition =\n originalStartViewTransition\n\n router.startViewTransition = (fn: () => Promise<void>) => {\n return originalStartViewTransition?.(async () => {\n await fn()\n await Vue.nextTick()\n })\n }\n\n // Subscribe to location changes\n // and try to load the new location\n let unsubscribe: (() => void) | undefined\n\n Vue.onMounted(() => {\n unsubscribe = router.history.subscribe(router.load)\n\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.href) !==\n trimPathRight(nextLocation.href)\n ) {\n router.commitLocation({ ...nextLocation, replace: true })\n }\n })\n\n // Track if component is mounted to prevent updates after unmount\n const isMounted = Vue.ref(false)\n\n Vue.onMounted(() => {\n isMounted.value = true\n })\n\n Vue.onUnmounted(() => {\n isMounted.value = false\n if (unsubscribe) {\n unsubscribe()\n }\n })\n\n // Try to load the initial location\n Vue.onMounted(() => {\n if (\n (typeof window !== 'undefined' && router.ssr) ||\n (mountLoadForRouter.router === router && mountLoadForRouter.mounted)\n ) {\n return\n }\n mountLoadForRouter = { router, mounted: true }\n const tryLoad = async () => {\n try {\n await router.load()\n } catch (err) {\n console.error(err)\n }\n }\n tryLoad()\n })\n\n // Setup watchers for emitting events\n // All watchers check isMounted to prevent updates after unmount\n Vue.watch(\n () => isLoading.value,\n (newValue) => {\n if (!isMounted.value) return\n try {\n if (previousIsLoading.value.previous && !newValue) {\n router.emit({\n type: 'onLoad',\n ...getLocationChangeInfo(router.state),\n })\n }\n } catch {\n // Ignore errors if component is unmounted\n }\n },\n )\n\n Vue.watch(isPagePending, (newValue) => {\n if (!isMounted.value) return\n try {\n // emit onBeforeRouteMount\n if (previousIsPagePending.value.previous && !newValue) {\n router.emit({\n type: 'onBeforeRouteMount',\n ...getLocationChangeInfo(router.state),\n })\n }\n } catch {\n // Ignore errors if component is unmounted\n }\n })\n\n Vue.watch(isAnyPending, (newValue) => {\n if (!isMounted.value) return\n try {\n // The router was pending and now it's not\n if (previousIsAnyPending.value.previous && !newValue) {\n const changeInfo = getLocationChangeInfo(router.state)\n router.emit({\n type: 'onResolved',\n ...changeInfo,\n })\n\n router.__store.setState((s) => ({\n ...s,\n status: 'idle',\n resolvedLocation: s.location,\n }))\n\n if (changeInfo.hrefChanged) {\n handleHashScroll(router)\n }\n }\n } catch {\n // Ignore errors if component is unmounted\n }\n })\n}\n\n/**\n * @deprecated Use useTransitionerSetup() composable instead.\n * This component is kept for backwards compatibility but the setup logic\n * has been moved to useTransitionerSetup() for better SSR hydration.\n */\nexport const Transitioner = Vue.defineComponent({\n name: 'Transitioner',\n setup() {\n useTransitionerSetup()\n return () => null\n },\n})\n"],"names":["mountLoadForRouter","router","mounted","useTransitionerSetup","useRouter","isServer","isLoading","useRouterState","select","isTransitioning","Vue","ref","hasPendingMatches","s","matches","some","d","status","previousIsLoading","usePrevious","value","isAnyPending","computed","previousIsAnyPending","isPagePending","previousIsPagePending","startTransition","fn","__store","setState","endTransition","nextTick","originalStartViewTransition","__tsrOriginalStartViewTransition","startViewTransition","unsubscribe","onMounted","history","subscribe","load","nextLocation","buildLocation","to","latestLocation","pathname","search","params","hash","state","_includeValidateSearch","trimPathRight","href","commitLocation","replace","isMounted","onUnmounted","window","ssr","tryLoad","err","console","error","watch","newValue","previous","emit","type","getLocationChangeInfo","changeInfo","resolvedLocation","location","hrefChanged","handleHashScroll","defineComponent","name","setup"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Transitioner.js","sources":["../../src/Transitioner.tsx"],"sourcesContent":["import * as Vue from 'vue'\nimport {\n getLocationChangeInfo,\n handleHashScroll,\n trimPathRight,\n} from '@tanstack/router-core'\nimport { useRouter } from './useRouter'\nimport { useRouterState } from './useRouterState'\nimport { usePrevious } from './utils'\n\n// Track mount state per router to avoid double-loading\nlet mountLoadForRouter = { router: null as any, mounted: false }\n\n/**\n * Composable that sets up router transition logic.\n * This is called from MatchesContent to set up:\n * - router.startTransition\n * - router.startViewTransition\n * - History subscription\n * - Router event watchers\n *\n * Must be called during component setup phase.\n */\nexport function useTransitionerSetup() {\n const router = useRouter()\n\n // Skip on server - no transitions needed\n if (router.isServer) {\n return\n }\n\n const isLoading = useRouterState({\n select: ({ isLoading }) => isLoading,\n })\n\n // Track if we're in a transition - using a ref to track async transitions\n const isTransitioning = Vue.ref(false)\n\n // Track pending state changes\n const hasPendingMatches = useRouterState({\n select: (s) => s.matches.some((d) => d.status === 'pending'),\n })\n\n const previousIsLoading = usePrevious(() => isLoading.value)\n\n const isAnyPending = Vue.computed(\n () => isLoading.value || isTransitioning.value || hasPendingMatches.value,\n )\n const previousIsAnyPending = usePrevious(() => isAnyPending.value)\n\n const isPagePending = Vue.computed(\n () => isLoading.value || hasPendingMatches.value,\n )\n const previousIsPagePending = usePrevious(() => isPagePending.value)\n\n // Implement startTransition similar to React/Solid\n // Vue doesn't have a native useTransition like React 18, so we simulate it\n // We also update the router state's isTransitioning flag so useMatch can check it\n router.startTransition = (fn: () => void | Promise<void>) => {\n isTransitioning.value = true\n // Also update the router state so useMatch knows we're transitioning\n try {\n router.__store.setState((s) => ({ ...s, isTransitioning: true }))\n } catch {\n // Ignore errors if component is unmounted\n }\n\n // Helper to end the transition\n const endTransition = () => {\n // Use nextTick to ensure Vue has processed all reactive updates\n Vue.nextTick(() => {\n try {\n isTransitioning.value = false\n router.__store.setState((s) => ({ ...s, isTransitioning: false }))\n } catch {\n // Ignore errors if component is unmounted\n }\n })\n }\n\n // Execute the function synchronously\n // The function internally may call startViewTransition which schedules async work\n // via document.startViewTransition, but we don't need to wait for it here\n // because Vue's reactivity will trigger re-renders when state changes\n fn()\n\n // End the transition on next tick to allow Vue to process reactive updates\n endTransition()\n }\n\n // Vue updates DOM asynchronously (next tick). The View Transitions API expects the\n // update callback promise to resolve only after the DOM has been updated.\n // Wrap the router-core implementation to await a Vue flush before resolving.\n const originalStartViewTransition:\n | undefined\n | ((fn: () => Promise<void>) => void) =\n (router as any).__tsrOriginalStartViewTransition ??\n router.startViewTransition\n\n ;(router as any).__tsrOriginalStartViewTransition =\n originalStartViewTransition\n\n router.startViewTransition = (fn: () => Promise<void>) => {\n return originalStartViewTransition?.(async () => {\n await fn()\n await Vue.nextTick()\n })\n }\n\n // Subscribe to location changes\n // and try to load the new location\n let unsubscribe: (() => void) | undefined\n\n Vue.onMounted(() => {\n unsubscribe = router.history.subscribe(router.load)\n\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.href) !==\n trimPathRight(nextLocation.href)\n ) {\n router.commitLocation({ ...nextLocation, replace: true })\n }\n })\n\n // Track if component is mounted to prevent updates after unmount\n const isMounted = Vue.ref(false)\n\n Vue.onMounted(() => {\n isMounted.value = true\n if (!isAnyPending.value) {\n router.__store.setState((s) =>\n s.status === 'pending'\n ? { ...s, status: 'idle', resolvedLocation: s.location }\n : s,\n )\n }\n })\n\n Vue.onUnmounted(() => {\n isMounted.value = false\n if (unsubscribe) {\n unsubscribe()\n }\n })\n\n // Try to load the initial location\n Vue.onMounted(() => {\n if (\n (typeof window !== 'undefined' && router.ssr) ||\n (mountLoadForRouter.router === router && mountLoadForRouter.mounted)\n ) {\n return\n }\n mountLoadForRouter = { router, mounted: true }\n const tryLoad = async () => {\n try {\n await router.load()\n } catch (err) {\n console.error(err)\n }\n }\n tryLoad()\n })\n\n // Setup watchers for emitting events\n // All watchers check isMounted to prevent updates after unmount\n Vue.watch(\n () => isLoading.value,\n (newValue) => {\n if (!isMounted.value) return\n try {\n if (previousIsLoading.value.previous && !newValue) {\n router.emit({\n type: 'onLoad',\n ...getLocationChangeInfo(router.state),\n })\n }\n } catch {\n // Ignore errors if component is unmounted\n }\n },\n )\n\n Vue.watch(isPagePending, (newValue) => {\n if (!isMounted.value) return\n try {\n // emit onBeforeRouteMount\n if (previousIsPagePending.value.previous && !newValue) {\n router.emit({\n type: 'onBeforeRouteMount',\n ...getLocationChangeInfo(router.state),\n })\n }\n } catch {\n // Ignore errors if component is unmounted\n }\n })\n\n Vue.watch(isAnyPending, (newValue) => {\n if (!isMounted.value) return\n try {\n if (!newValue && router.__store.state.status === 'pending') {\n router.__store.setState((s) => ({\n ...s,\n status: 'idle',\n resolvedLocation: s.location,\n }))\n }\n\n // The router was pending and now it's not\n if (previousIsAnyPending.value.previous && !newValue) {\n const changeInfo = getLocationChangeInfo(router.state)\n router.emit({\n type: 'onResolved',\n ...changeInfo,\n })\n\n if (changeInfo.hrefChanged) {\n handleHashScroll(router)\n }\n }\n } catch {\n // Ignore errors if component is unmounted\n }\n })\n}\n\n/**\n * @deprecated Use useTransitionerSetup() composable instead.\n * This component is kept for backwards compatibility but the setup logic\n * has been moved to useTransitionerSetup() for better SSR hydration.\n */\nexport const Transitioner = Vue.defineComponent({\n name: 'Transitioner',\n setup() {\n useTransitionerSetup()\n return () => null\n },\n})\n"],"names":["mountLoadForRouter","router","mounted","useTransitionerSetup","useRouter","isServer","isLoading","useRouterState","select","isTransitioning","Vue","ref","hasPendingMatches","s","matches","some","d","status","previousIsLoading","usePrevious","value","isAnyPending","computed","previousIsAnyPending","isPagePending","previousIsPagePending","startTransition","fn","__store","setState","endTransition","nextTick","originalStartViewTransition","__tsrOriginalStartViewTransition","startViewTransition","unsubscribe","onMounted","history","subscribe","load","nextLocation","buildLocation","to","latestLocation","pathname","search","params","hash","state","_includeValidateSearch","trimPathRight","href","commitLocation","replace","isMounted","resolvedLocation","location","onUnmounted","window","ssr","tryLoad","err","console","error","watch","newValue","previous","emit","type","getLocationChangeInfo","changeInfo","hrefChanged","handleHashScroll","defineComponent","name","setup"],"mappings":"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"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export * from './history.js';
|
|
|
10
10
|
export { lazyRouteComponent } from './lazyRouteComponent.js';
|
|
11
11
|
export { useLinkProps, createLink, Link, linkOptions } from './link.js';
|
|
12
12
|
export type { InferDescendantToPaths, RelativeToPath, RelativeToParentPath, RelativeToCurrentPath, AbsoluteToPath, RelativeToPathAutoComplete, NavigateOptions, ToOptions, ToMaskOptions, ToSubOptions, ResolveRoute, SearchParamOptions, PathParamOptions, ToPathOption, LinkOptions, MakeOptionalPathParams, FileRouteTypes, RouteContextParameter, BeforeLoadContextParameter, ResolveAllContext, ResolveAllParamsFromParent, ResolveFullSearchSchema, ResolveFullSearchSchemaInput, RouteIds, NavigateFn, BuildLocationFn, FullSearchSchemaOption, MakeRemountDepsOptionsUnion, RemountDepsOptions, ResolveFullPath, AnyRouteWithContext, AnyRouterWithContext, CommitLocationOptions, MatchLocation, UseNavigateResult, AnyRedirect, Redirect, RedirectOptions, ResolvedRedirect, MakeRouteMatch, MakeRouteMatchUnion, RouteMatch, AnyRouteMatch, RouteContextFn, RouteContextOptions, BeforeLoadContextOptions, ContextOptions, RouteOptions, FileBaseRouteOptions, BaseRouteOptions, UpdatableRouteOptions, RouteLoaderFn, LoaderFnContext, LazyRouteOptions, AnyRouter, RegisteredRouter, RouterContextOptions, ControllablePromise, InjectedHtmlEntry, RouterOptions, RouterState, ListenerFn, BuildNextOptions, RouterConstructorOptions, RouterEvents, RouterEvent, RouterListener, RouteConstraints, RouteMask, MatchRouteOptions, CreateLazyFileRoute, } from '@tanstack/router-core';
|
|
13
|
-
export type { UseLinkPropsOptions, ActiveLinkOptions, LinkProps, LinkComponent, LinkComponentProps, CreateLinkProps, } from './link.js';
|
|
13
|
+
export type { UseLinkPropsOptions, ActiveLinkOptions, LinkProps, LinkComponent, LinkComponentRoute, LinkComponentProps, CreateLinkProps, } from './link.js';
|
|
14
14
|
export { Matches, useMatchRoute, MatchRoute, useMatches, useParentMatches, useChildMatches, } from './Matches.js';
|
|
15
15
|
export type { UseMatchRouteOptions, MakeMatchRouteOptions } from './Matches.js';
|
|
16
16
|
export { matchContext } from './matchContext.js';
|
|
@@ -51,3 +51,4 @@ export { Body } from './Body.js';
|
|
|
51
51
|
export { Html } from './Html.js';
|
|
52
52
|
export { composeRewrites } from '@tanstack/router-core';
|
|
53
53
|
export type { LocationRewrite, LocationRewriteFunction, } from '@tanstack/router-core';
|
|
54
|
+
export { ClientOnly } from './ClientOnly.js';
|
package/dist/esm/index.js
CHANGED
|
@@ -33,6 +33,7 @@ import { HeadContent } from "./HeadContent.js";
|
|
|
33
33
|
import { Scripts } from "./Scripts.js";
|
|
34
34
|
import { Body } from "./Body.js";
|
|
35
35
|
import { Html } from "./Html.js";
|
|
36
|
+
import { ClientOnly } from "./ClientOnly.js";
|
|
36
37
|
export {
|
|
37
38
|
Asset,
|
|
38
39
|
Await,
|
|
@@ -40,6 +41,7 @@ export {
|
|
|
40
41
|
Body,
|
|
41
42
|
CatchBoundary,
|
|
42
43
|
CatchNotFound,
|
|
44
|
+
ClientOnly,
|
|
43
45
|
DefaultGlobalNotFound,
|
|
44
46
|
ErrorComponent,
|
|
45
47
|
FileRoute,
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,8 +1,2 @@
|
|
|
1
1
|
import { AsyncRouteComponent } from './route.js';
|
|
2
|
-
import * as Vue from 'vue';
|
|
3
|
-
export declare function ClientOnly(props: {
|
|
4
|
-
children?: any;
|
|
5
|
-
fallback?: Vue.VNode;
|
|
6
|
-
}): () => any;
|
|
7
|
-
export declare function useHydrated(): Vue.Ref<boolean, boolean> | Vue.ComputedRef<boolean>;
|
|
8
2
|
export declare function lazyRouteComponent<T extends Record<string, any>, TKey extends keyof T = 'default'>(importer: () => Promise<T>, exportName?: TKey, ssr?: () => boolean): T[TKey] extends (props: infer TProps) => any ? AsyncRouteComponent<TProps> : never;
|
|
@@ -1,28 +1,10 @@
|
|
|
1
1
|
import * as Vue from "vue";
|
|
2
2
|
import { Outlet } from "./Match.js";
|
|
3
|
+
import { ClientOnly } from "./ClientOnly.js";
|
|
3
4
|
function isModuleNotFoundError(error) {
|
|
4
5
|
if (typeof error?.message !== "string") return false;
|
|
5
6
|
return error.message.startsWith("Failed to fetch dynamically imported module") || error.message.startsWith("error loading dynamically imported module") || error.message.startsWith("Importing a module script failed");
|
|
6
7
|
}
|
|
7
|
-
function ClientOnly(props) {
|
|
8
|
-
const hydrated = useHydrated();
|
|
9
|
-
return () => {
|
|
10
|
-
if (hydrated.value) {
|
|
11
|
-
return props.children;
|
|
12
|
-
}
|
|
13
|
-
return props.fallback || null;
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
function useHydrated() {
|
|
17
|
-
const hydrated = Vue.ref(false);
|
|
18
|
-
if (typeof window === "undefined") {
|
|
19
|
-
return Vue.computed(() => false);
|
|
20
|
-
}
|
|
21
|
-
Vue.onMounted(() => {
|
|
22
|
-
hydrated.value = true;
|
|
23
|
-
});
|
|
24
|
-
return hydrated;
|
|
25
|
-
}
|
|
26
8
|
function lazyRouteComponent(importer, exportName, ssr) {
|
|
27
9
|
let loadPromise;
|
|
28
10
|
let comp = null;
|
|
@@ -87,8 +69,9 @@ function lazyRouteComponent(importer, exportName, ssr) {
|
|
|
87
69
|
}
|
|
88
70
|
if (ssr?.() === false) {
|
|
89
71
|
return Vue.h(ClientOnly, {
|
|
90
|
-
fallback: Vue.h(Outlet)
|
|
91
|
-
|
|
72
|
+
fallback: Vue.h(Outlet)
|
|
73
|
+
}, {
|
|
74
|
+
default: () => Vue.h(component.value, props)
|
|
92
75
|
});
|
|
93
76
|
}
|
|
94
77
|
return Vue.h(component.value, props);
|
|
@@ -99,8 +82,6 @@ function lazyRouteComponent(importer, exportName, ssr) {
|
|
|
99
82
|
return lazyComp;
|
|
100
83
|
}
|
|
101
84
|
export {
|
|
102
|
-
|
|
103
|
-
lazyRouteComponent,
|
|
104
|
-
useHydrated
|
|
85
|
+
lazyRouteComponent
|
|
105
86
|
};
|
|
106
87
|
//# sourceMappingURL=lazyRouteComponent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lazyRouteComponent.js","sources":["../../src/lazyRouteComponent.tsx"],"sourcesContent":["import * as Vue from 'vue'\nimport { Outlet } from './Match'\nimport type { AsyncRouteComponent } from './route'\n\n// If the load fails due to module not found, it may mean a new version of\n// the build was deployed and the user's browser is still using an old version.\n// If this happens, the old version in the user's browser would have an outdated\n// URL to the lazy module.\n// In that case, we want to attempt one window refresh to get the latest.\nfunction isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function
|
|
1
|
+
{"version":3,"file":"lazyRouteComponent.js","sources":["../../src/lazyRouteComponent.tsx"],"sourcesContent":["import * as Vue from 'vue'\nimport { Outlet } from './Match'\nimport { ClientOnly } from './ClientOnly'\nimport type { AsyncRouteComponent } from './route'\n\n// If the load fails due to module not found, it may mean a new version of\n// the build was deployed and the user's browser is still using an old version.\n// If this happens, the old version in the user's browser would have an outdated\n// URL to the lazy module.\n// In that case, we want to attempt one window refresh to get the latest.\nfunction isModuleNotFoundError(error: any): boolean {\n // chrome: \"Failed to fetch dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // firefox: \"error loading dynamically imported module: http://localhost:5173/src/routes/posts.index.tsx?tsr-split\"\n // safari: \"Importing a module script failed.\"\n if (typeof error?.message !== 'string') return false\n return (\n error.message.startsWith('Failed to fetch dynamically imported module') ||\n error.message.startsWith('error loading dynamically imported module') ||\n error.message.startsWith('Importing a module script failed')\n )\n}\n\nexport function lazyRouteComponent<\n T extends Record<string, any>,\n TKey extends keyof T = 'default',\n>(\n importer: () => Promise<T>,\n exportName?: TKey,\n ssr?: () => boolean,\n): T[TKey] extends (props: infer TProps) => any\n ? AsyncRouteComponent<TProps>\n : never {\n let loadPromise: Promise<any> | undefined\n let comp: T[TKey] | T['default'] | null = null\n let error: any = null\n let attemptedReload = false\n\n const load = () => {\n // If we're on the server and SSR is disabled for this component\n if (typeof document === 'undefined' && ssr?.() === false) {\n comp = (() => null) as any\n return Promise.resolve(comp)\n }\n\n // Use existing promise or create new one\n if (!loadPromise) {\n loadPromise = importer()\n .then((res) => {\n loadPromise = undefined\n comp = res[exportName ?? 'default']\n return comp\n })\n .catch((err) => {\n error = err\n loadPromise = undefined\n\n // If it's a module not found error, we'll try to handle it in the component\n if (isModuleNotFoundError(error)) {\n return null\n }\n\n throw err\n })\n }\n\n return loadPromise\n }\n\n // Create a lazy component wrapper using defineComponent so it works in Vue SFC templates\n const lazyComp = Vue.defineComponent({\n name: 'LazyRouteComponent',\n setup(props: any) {\n // Create refs to track component state\n // Use shallowRef for component to avoid making it reactive (Vue warning)\n const component = Vue.shallowRef<any>(comp ? Vue.markRaw(comp) : comp)\n const errorState = Vue.ref<any>(error)\n const loading = Vue.ref(!component.value && !errorState.value)\n\n // Setup effect to load the component when this component is used\n Vue.onMounted(() => {\n if (!component.value && !errorState.value) {\n loading.value = true\n\n load()\n .then((result) => {\n // Use markRaw to prevent Vue from making the component reactive\n component.value = result ? Vue.markRaw(result) : result\n loading.value = false\n })\n .catch((err) => {\n errorState.value = err\n loading.value = false\n })\n }\n })\n\n // Handle module not found error with reload attempt\n if (\n errorState.value &&\n isModuleNotFoundError(errorState.value) &&\n !attemptedReload\n ) {\n if (\n typeof window !== 'undefined' &&\n typeof sessionStorage !== 'undefined'\n ) {\n // Try to reload once on module not found error\n const storageKey = `tanstack_router_reload:${errorState.value.message}`\n if (!sessionStorage.getItem(storageKey)) {\n sessionStorage.setItem(storageKey, '1')\n attemptedReload = true\n window.location.reload()\n return () => null // Return empty while reloading\n }\n }\n }\n\n // If we have a non-module-not-found error, throw it\n if (errorState.value && !isModuleNotFoundError(errorState.value)) {\n throw errorState.value\n }\n\n // Return a render function\n return () => {\n // If we're still loading or don't have a component yet, use a suspense pattern\n if (loading.value || !component.value) {\n return Vue.h('div', null) // Empty div while loading\n }\n\n // If SSR is disabled for this component\n if (ssr?.() === false) {\n return Vue.h(\n ClientOnly,\n {\n fallback: Vue.h(Outlet),\n },\n {\n default: () => Vue.h(component.value, props),\n },\n )\n }\n\n // Regular render with the loaded component\n return Vue.h(component.value, props)\n }\n },\n })\n\n // Add preload method\n ;(lazyComp as any).preload = load\n\n return lazyComp as any\n}\n"],"names":["isModuleNotFoundError","error","message","startsWith","lazyRouteComponent","importer","exportName","ssr","loadPromise","comp","attemptedReload","load","document","Promise","resolve","then","res","undefined","catch","err","lazyComp","Vue","defineComponent","name","setup","props","component","shallowRef","markRaw","errorState","ref","loading","value","onMounted","result","window","sessionStorage","storageKey","getItem","setItem","location","reload","h","ClientOnly","fallback","Outlet","default","preload"],"mappings":"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"}
|