@tanstack/vue-router 1.167.4 → 1.168.0

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.
Files changed (95) hide show
  1. package/dist/esm/Match.js +55 -61
  2. package/dist/esm/Match.js.map +1 -1
  3. package/dist/esm/Matches.js +8 -15
  4. package/dist/esm/Matches.js.map +1 -1
  5. package/dist/esm/Scripts.js +7 -6
  6. package/dist/esm/Scripts.js.map +1 -1
  7. package/dist/esm/Transitioner.js +18 -24
  8. package/dist/esm/Transitioner.js.map +1 -1
  9. package/dist/esm/headContentUtils.js +13 -15
  10. package/dist/esm/headContentUtils.js.map +1 -1
  11. package/dist/esm/index.dev.js +6 -6
  12. package/dist/esm/index.js +6 -6
  13. package/dist/esm/link.js +242 -178
  14. package/dist/esm/link.js.map +1 -1
  15. package/dist/esm/matchContext.d.ts +8 -14
  16. package/dist/esm/matchContext.js +11 -9
  17. package/dist/esm/matchContext.js.map +1 -1
  18. package/dist/esm/not-found.js +6 -3
  19. package/dist/esm/not-found.js.map +1 -1
  20. package/dist/esm/router.js +2 -1
  21. package/dist/esm/router.js.map +1 -1
  22. package/dist/esm/routerStores.d.ts +13 -0
  23. package/dist/esm/routerStores.js +33 -0
  24. package/dist/esm/routerStores.js.map +1 -0
  25. package/dist/esm/ssr/RouterClient.js +1 -1
  26. package/dist/esm/ssr/RouterClient.js.map +1 -1
  27. package/dist/esm/ssr/renderRouterToStream.js +2 -2
  28. package/dist/esm/ssr/renderRouterToStream.js.map +1 -1
  29. package/dist/esm/ssr/renderRouterToString.js +1 -1
  30. package/dist/esm/ssr/renderRouterToString.js.map +1 -1
  31. package/dist/esm/useCanGoBack.d.ts +1 -1
  32. package/dist/esm/useCanGoBack.js +3 -2
  33. package/dist/esm/useCanGoBack.js.map +1 -1
  34. package/dist/esm/useLocation.js +3 -2
  35. package/dist/esm/useLocation.js.map +1 -1
  36. package/dist/esm/useMatch.js +29 -19
  37. package/dist/esm/useMatch.js.map +1 -1
  38. package/dist/esm/useRouterState.js +4 -4
  39. package/dist/esm/useRouterState.js.map +1 -1
  40. package/dist/source/Match.jsx +121 -159
  41. package/dist/source/Match.jsx.map +1 -1
  42. package/dist/source/Matches.jsx +11 -28
  43. package/dist/source/Matches.jsx.map +1 -1
  44. package/dist/source/Scripts.jsx +32 -35
  45. package/dist/source/Scripts.jsx.map +1 -1
  46. package/dist/source/Transitioner.jsx +19 -21
  47. package/dist/source/Transitioner.jsx.map +1 -1
  48. package/dist/source/headContentUtils.jsx +51 -61
  49. package/dist/source/headContentUtils.jsx.map +1 -1
  50. package/dist/source/link.jsx +298 -249
  51. package/dist/source/link.jsx.map +1 -1
  52. package/dist/source/matchContext.d.ts +8 -14
  53. package/dist/source/matchContext.jsx +17 -23
  54. package/dist/source/matchContext.jsx.map +1 -1
  55. package/dist/source/not-found.jsx +6 -5
  56. package/dist/source/not-found.jsx.map +1 -1
  57. package/dist/source/router.js +2 -1
  58. package/dist/source/router.js.map +1 -1
  59. package/dist/source/routerStores.d.ts +13 -0
  60. package/dist/source/routerStores.js +37 -0
  61. package/dist/source/routerStores.js.map +1 -0
  62. package/dist/source/ssr/RouterClient.jsx +1 -1
  63. package/dist/source/ssr/RouterClient.jsx.map +1 -1
  64. package/dist/source/ssr/renderRouterToStream.jsx +2 -2
  65. package/dist/source/ssr/renderRouterToStream.jsx.map +1 -1
  66. package/dist/source/ssr/renderRouterToString.jsx +1 -1
  67. package/dist/source/ssr/renderRouterToString.jsx.map +1 -1
  68. package/dist/source/useCanGoBack.d.ts +1 -1
  69. package/dist/source/useCanGoBack.js +4 -2
  70. package/dist/source/useCanGoBack.js.map +1 -1
  71. package/dist/source/useLocation.jsx +4 -4
  72. package/dist/source/useLocation.jsx.map +1 -1
  73. package/dist/source/useMatch.jsx +60 -38
  74. package/dist/source/useMatch.jsx.map +1 -1
  75. package/dist/source/useRouterState.jsx +4 -4
  76. package/dist/source/useRouterState.jsx.map +1 -1
  77. package/package.json +2 -2
  78. package/skills/vue-router/SKILL.md +3 -0
  79. package/src/Match.tsx +168 -180
  80. package/src/Matches.tsx +18 -31
  81. package/src/Scripts.tsx +40 -40
  82. package/src/Transitioner.tsx +35 -23
  83. package/src/headContentUtils.tsx +101 -107
  84. package/src/link.tsx +445 -300
  85. package/src/matchContext.tsx +23 -25
  86. package/src/not-found.tsx +9 -5
  87. package/src/router.ts +2 -1
  88. package/src/routerStores.ts +54 -0
  89. package/src/ssr/RouterClient.tsx +1 -1
  90. package/src/ssr/renderRouterToStream.tsx +2 -2
  91. package/src/ssr/renderRouterToString.tsx +1 -1
  92. package/src/useCanGoBack.ts +7 -2
  93. package/src/useLocation.tsx +8 -5
  94. package/src/useMatch.tsx +95 -49
  95. package/src/useRouterState.tsx +6 -4
@@ -1,9 +1,9 @@
1
1
  import { useRouter } from "./useRouter.js";
2
- import { useRouterState } from "./useRouterState.js";
3
2
  import { usePrevious } from "./utils.js";
4
3
  import { getLocationChangeInfo, handleHashScroll, trimPathRight } from "@tanstack/router-core";
5
4
  import * as Vue from "vue";
6
5
  import { isServer } from "@tanstack/router-core/isServer";
6
+ import { batch, useStore } from "@tanstack/vue-store";
7
7
  //#region src/Transitioner.tsx
8
8
  var mountLoadForRouter = {
9
9
  router: null,
@@ -22,9 +22,9 @@ var mountLoadForRouter = {
22
22
  function useTransitionerSetup() {
23
23
  const router = useRouter();
24
24
  if (isServer ?? router.isServer) return;
25
- const isLoading = useRouterState({ select: ({ isLoading }) => isLoading });
25
+ const isLoading = useStore(router.stores.isLoading, (value) => value);
26
26
  const isTransitioning = Vue.ref(false);
27
- const hasPendingMatches = useRouterState({ select: (s) => s.matches.some((d) => d.status === "pending") });
27
+ const hasPendingMatches = useStore(router.stores.hasPendingMatches, (value) => value);
28
28
  const previousIsLoading = usePrevious(() => isLoading.value);
29
29
  const isAnyPending = Vue.computed(() => isLoading.value || isTransitioning.value || hasPendingMatches.value);
30
30
  const previousIsAnyPending = usePrevious(() => isAnyPending.value);
@@ -33,19 +33,13 @@ function useTransitionerSetup() {
33
33
  router.startTransition = (fn) => {
34
34
  isTransitioning.value = true;
35
35
  try {
36
- router.__store.setState((s) => ({
37
- ...s,
38
- isTransitioning: true
39
- }));
36
+ router.stores.isTransitioning.setState(() => true);
40
37
  } catch {}
41
38
  const endTransition = () => {
42
39
  Vue.nextTick(() => {
43
40
  try {
44
41
  isTransitioning.value = false;
45
- router.__store.setState((s) => ({
46
- ...s,
47
- isTransitioning: false
48
- }));
42
+ router.stores.isTransitioning.setState(() => false);
49
43
  } catch {}
50
44
  });
51
45
  };
@@ -79,11 +73,12 @@ function useTransitionerSetup() {
79
73
  const isMounted = Vue.ref(false);
80
74
  Vue.onMounted(() => {
81
75
  isMounted.value = true;
82
- if (!isAnyPending.value) router.__store.setState((s) => s.status === "pending" ? {
83
- ...s,
84
- status: "idle",
85
- resolvedLocation: s.location
86
- } : s);
76
+ if (!isAnyPending.value) {
77
+ if (router.stores.status.state === "pending") batch(() => {
78
+ router.stores.status.setState(() => "idle");
79
+ router.stores.resolvedLocation.setState(() => router.stores.location.state);
80
+ });
81
+ }
87
82
  });
88
83
  Vue.onUnmounted(() => {
89
84
  isMounted.value = false;
@@ -109,7 +104,7 @@ function useTransitionerSetup() {
109
104
  try {
110
105
  if (previousIsLoading.value.previous && !newValue) router.emit({
111
106
  type: "onLoad",
112
- ...getLocationChangeInfo(router.state)
107
+ ...getLocationChangeInfo(router.stores.location.state, router.stores.resolvedLocation.state)
113
108
  });
114
109
  } catch {}
115
110
  });
@@ -118,20 +113,19 @@ function useTransitionerSetup() {
118
113
  try {
119
114
  if (previousIsPagePending.value.previous && !newValue) router.emit({
120
115
  type: "onBeforeRouteMount",
121
- ...getLocationChangeInfo(router.state)
116
+ ...getLocationChangeInfo(router.stores.location.state, router.stores.resolvedLocation.state)
122
117
  });
123
118
  } catch {}
124
119
  });
125
120
  Vue.watch(isAnyPending, (newValue) => {
126
121
  if (!isMounted.value) return;
127
122
  try {
128
- if (!newValue && router.__store.state.status === "pending") router.__store.setState((s) => ({
129
- ...s,
130
- status: "idle",
131
- resolvedLocation: s.location
132
- }));
123
+ if (!newValue && router.stores.status.state === "pending") batch(() => {
124
+ router.stores.status.setState(() => "idle");
125
+ router.stores.resolvedLocation.setState(() => router.stores.location.state);
126
+ });
133
127
  if (previousIsAnyPending.value.previous && !newValue) {
134
- const changeInfo = getLocationChangeInfo(router.state);
128
+ const changeInfo = getLocationChangeInfo(router.stores.location.state, router.stores.resolvedLocation.state);
135
129
  router.emit({
136
130
  type: "onResolved",
137
131
  ...changeInfo
@@ -1 +1 @@
1
- {"version":3,"file":"Transitioner.js","names":["Vue","getLocationChangeInfo","handleHashScroll","trimPathRight","isServer","useRouter","useRouterState","usePrevious","mountLoadForRouter","router","mounted","useTransitionerSetup","isLoading","select","isTransitioning","ref","hasPendingMatches","s","matches","some","d","status","previousIsLoading","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","publicHref","commitLocation","replace","isMounted","resolvedLocation","location","onUnmounted","window","ssr","tryLoad","err","console","error","watch","newValue","previous","emit","type","changeInfo","hrefChanged","Transitioner","defineComponent","name","setup"],"sources":["../../src/Transitioner.tsx"],"sourcesContent":["import * as Vue from 'vue'\nimport {\n getLocationChangeInfo,\n handleHashScroll,\n trimPathRight,\n} from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\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 (isServer ?? 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 // Check if the current URL matches the canonical form.\n // Compare publicHref (browser-facing URL) for consistency with\n // the server-side redirect check in router.beforeLoad.\n if (\n trimPathRight(router.latestLocation.publicHref) !==\n trimPathRight(nextLocation.publicHref)\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"],"mappings":"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"}
1
+ {"version":3,"file":"Transitioner.js","names":["Vue","getLocationChangeInfo","handleHashScroll","trimPathRight","isServer","batch","useStore","useRouter","usePrevious","mountLoadForRouter","router","mounted","useTransitionerSetup","isLoading","stores","value","isTransitioning","ref","hasPendingMatches","previousIsLoading","isAnyPending","computed","previousIsAnyPending","isPagePending","previousIsPagePending","startTransition","fn","setState","endTransition","nextTick","originalStartViewTransition","__tsrOriginalStartViewTransition","startViewTransition","unsubscribe","onMounted","history","subscribe","load","nextLocation","buildLocation","to","latestLocation","pathname","search","params","hash","state","_includeValidateSearch","publicHref","commitLocation","replace","isMounted","status","resolvedLocation","location","onUnmounted","window","ssr","tryLoad","err","console","error","watch","newValue","previous","emit","type","changeInfo","hrefChanged","Transitioner","defineComponent","name","setup"],"sources":["../../src/Transitioner.tsx"],"sourcesContent":["import * as Vue from 'vue'\nimport {\n getLocationChangeInfo,\n handleHashScroll,\n trimPathRight,\n} from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport { batch, useStore } from '@tanstack/vue-store'\nimport { useRouter } from './useRouter'\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 (isServer ?? router.isServer) {\n return\n }\n\n const isLoading = useStore(router.stores.isLoading, (value) => value)\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 = useStore(\n router.stores.hasPendingMatches,\n (value) => value,\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.stores.isTransitioning.setState(() => 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.stores.isTransitioning.setState(() => 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 // Check if the current URL matches the canonical form.\n // Compare publicHref (browser-facing URL) for consistency with\n // the server-side redirect check in router.beforeLoad.\n if (\n trimPathRight(router.latestLocation.publicHref) !==\n trimPathRight(nextLocation.publicHref)\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 if (router.stores.status.state === 'pending') {\n batch(() => {\n router.stores.status.setState(() => 'idle')\n router.stores.resolvedLocation.setState(\n () => router.stores.location.state,\n )\n })\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(\n router.stores.location.state,\n router.stores.resolvedLocation.state,\n ),\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(\n router.stores.location.state,\n router.stores.resolvedLocation.state,\n ),\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.stores.status.state === 'pending') {\n batch(() => {\n router.stores.status.setState(() => 'idle')\n router.stores.resolvedLocation.setState(\n () => router.stores.location.state,\n )\n })\n }\n\n // The router was pending and now it's not\n if (previousIsAnyPending.value.previous && !newValue) {\n const changeInfo = getLocationChangeInfo(\n router.stores.location.state,\n router.stores.resolvedLocation.state,\n )\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"],"mappings":"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"}
@@ -1,18 +1,16 @@
1
1
  import { useRouter } from "./useRouter.js";
2
- import { useRouterState } from "./useRouterState.js";
3
2
  import { escapeHtml } from "@tanstack/router-core";
4
3
  import * as Vue from "vue";
4
+ import { useStore } from "@tanstack/vue-store";
5
5
  //#region src/headContentUtils.tsx
6
6
  var useTags = () => {
7
7
  const router = useRouter();
8
- const routeMeta = useRouterState({ select: (state) => {
9
- return state.matches.map((match) => match.meta).filter(Boolean);
10
- } });
8
+ const matches = useStore(router.stores.activeMatchesSnapshot, (value) => value);
11
9
  const meta = Vue.computed(() => {
12
10
  const resultMeta = [];
13
11
  const metaByAttribute = {};
14
12
  let title;
15
- [...routeMeta.value].reverse().forEach((metas) => {
13
+ [...matches.value.map((match) => match.meta).filter(Boolean)].reverse().forEach((metas) => {
16
14
  [...metas].reverse().forEach((m) => {
17
15
  if (!m) return;
18
16
  if (m.title) {
@@ -43,13 +41,13 @@ var useTags = () => {
43
41
  resultMeta.reverse();
44
42
  return resultMeta;
45
43
  });
46
- const links = useRouterState({ select: (state) => state.matches.map((match) => match.links).filter(Boolean).flat(1).map((link) => ({
44
+ const links = Vue.computed(() => matches.value.map((match) => match.links).filter(Boolean).flat(1).map((link) => ({
47
45
  tag: "link",
48
46
  attrs: { ...link }
49
- })) });
50
- const preloadMeta = useRouterState({ select: (state) => {
47
+ })));
48
+ const preloadMeta = Vue.computed(() => {
51
49
  const preloadMeta = [];
52
- state.matches.map((match) => router.looseRoutesById[match.routeId]).forEach((route) => router.ssr?.manifest?.routes[route.id]?.preloads?.filter(Boolean).forEach((preload) => {
50
+ matches.value.map((match) => router.looseRoutesById[match.routeId]).forEach((route) => router.ssr?.manifest?.routes[route.id]?.preloads?.filter(Boolean).forEach((preload) => {
53
51
  preloadMeta.push({
54
52
  tag: "link",
55
53
  attrs: {
@@ -59,19 +57,19 @@ var useTags = () => {
59
57
  });
60
58
  }));
61
59
  return preloadMeta;
62
- } });
63
- const headScripts = useRouterState({ select: (state) => state.matches.map((match) => match.headScripts).flat(1).filter(Boolean).map(({ children, ...script }) => ({
60
+ });
61
+ const headScripts = Vue.computed(() => matches.value.map((match) => match.headScripts).flat(1).filter(Boolean).map(({ children, ...script }) => ({
64
62
  tag: "script",
65
63
  attrs: { ...script },
66
64
  children
67
- })) });
68
- const manifestAssets = useRouterState({ select: (state) => {
65
+ })));
66
+ const manifestAssets = Vue.computed(() => {
69
67
  const manifest = router.ssr?.manifest;
70
- return state.matches.map((match) => manifest?.routes[match.routeId]?.assets ?? []).filter(Boolean).flat(1).filter((asset) => asset.tag === "link").map((asset) => ({
68
+ return matches.value.map((match) => manifest?.routes[match.routeId]?.assets ?? []).filter(Boolean).flat(1).filter((asset) => asset.tag === "link").map((asset) => ({
71
69
  tag: "link",
72
70
  attrs: { ...asset.attrs }
73
71
  }));
74
- } });
72
+ });
75
73
  return () => uniqBy([
76
74
  ...manifestAssets.value,
77
75
  ...meta.value,
@@ -1 +1 @@
1
- {"version":3,"file":"headContentUtils.js","names":["Vue","escapeHtml","useRouter","useRouterState","useTags","router","routeMeta","select","state","matches","map","match","meta","filter","Boolean","computed","resultMeta","metaByAttribute","title","value","reverse","forEach","metas","m","tag","children","json","JSON","stringify","push","attrs","type","attribute","name","property","links","flat","link","preloadMeta","looseRoutesById","routeId","route","ssr","manifest","routes","id","preloads","preload","rel","href","headScripts","script","manifestAssets","assets","asset","uniqBy","d","arr","fn","seen","Set","item","key","has","add"],"sources":["../../src/headContentUtils.tsx"],"sourcesContent":["import * as Vue from 'vue'\n\nimport { escapeHtml } from '@tanstack/router-core'\nimport { useRouter } from './useRouter'\nimport { useRouterState } from './useRouterState'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\nexport const useTags = () => {\n const router = useRouter()\n\n const routeMeta = useRouterState({\n select: (state) => {\n return state.matches.map((match) => match.meta!).filter(Boolean)\n },\n })\n\n const meta: Vue.Ref<Array<RouterManagedTag>> = Vue.computed(() => {\n const resultMeta: Array<RouterManagedTag> = []\n const metaByAttribute: Record<string, true> = {}\n let title: RouterManagedTag | undefined\n ;[...routeMeta.value].reverse().forEach((metas) => {\n ;[...metas].reverse().forEach((m) => {\n if (!m) return\n\n if (m.title) {\n if (!title) {\n title = {\n tag: 'title',\n children: m.title,\n }\n }\n } else if ('script:ld+json' in m) {\n // Handle JSON-LD structured data\n // Content is HTML-escaped to prevent XSS when injected via innerHTML\n try {\n const json = JSON.stringify(m['script:ld+json'])\n resultMeta.push({\n tag: 'script',\n attrs: {\n type: 'application/ld+json',\n },\n children: escapeHtml(json),\n })\n } catch {\n // Skip invalid JSON-LD objects\n }\n } else {\n const attribute = m.name ?? m.property\n if (attribute) {\n if (metaByAttribute[attribute]) {\n return\n } else {\n metaByAttribute[attribute] = true\n }\n }\n\n resultMeta.push({\n tag: 'meta',\n attrs: {\n ...m,\n },\n })\n }\n })\n })\n\n if (title) {\n resultMeta.push(title)\n }\n\n resultMeta.reverse()\n\n return resultMeta\n })\n\n const links = useRouterState({\n select: (state) =>\n state.matches\n .map((match) => match.links!)\n .filter(Boolean)\n .flat(1)\n .map((link) => ({\n tag: 'link',\n attrs: {\n ...link,\n },\n })) as Array<RouterManagedTag>,\n })\n\n const preloadMeta = useRouterState({\n select: (state) => {\n const preloadMeta: Array<RouterManagedTag> = []\n\n state.matches\n .map((match) => router.looseRoutesById[match.routeId]!)\n .forEach((route) =>\n router.ssr?.manifest?.routes[route.id]?.preloads\n ?.filter(Boolean)\n .forEach((preload) => {\n preloadMeta.push({\n tag: 'link',\n attrs: {\n rel: 'modulepreload',\n href: preload,\n },\n })\n }),\n )\n\n return preloadMeta\n },\n })\n\n const headScripts = useRouterState({\n select: (state) =>\n (\n state.matches\n .map((match) => match.headScripts!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...script }) => ({\n tag: 'script',\n attrs: {\n ...script,\n },\n children,\n })),\n })\n\n const manifestAssets = useRouterState({\n select: (state) => {\n const manifest = router.ssr?.manifest\n\n const assets = state.matches\n .map((match) => manifest?.routes[match.routeId]?.assets ?? [])\n .filter(Boolean)\n .flat(1)\n .filter((asset) => asset.tag === 'link')\n .map(\n (asset) =>\n ({\n tag: 'link',\n attrs: { ...asset.attrs },\n }) satisfies RouterManagedTag,\n )\n\n return assets\n },\n })\n\n return () =>\n uniqBy(\n [\n ...manifestAssets.value,\n ...meta.value,\n ...preloadMeta.value,\n ...links.value,\n ...headScripts.value,\n ] as Array<RouterManagedTag>,\n (d) => {\n return JSON.stringify(d)\n },\n )\n}\n\nexport function uniqBy<T>(arr: Array<T>, fn: (item: T) => string) {\n const seen = new Set<string>()\n return arr.filter((item) => {\n const key = fn(item)\n if (seen.has(key)) {\n return false\n }\n seen.add(key)\n return true\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"headContentUtils.js","names":["Vue","escapeHtml","useStore","useRouter","useTags","router","matches","stores","activeMatchesSnapshot","value","meta","computed","resultMeta","metaByAttribute","title","map","match","filter","Boolean","reverse","forEach","metas","m","tag","children","json","JSON","stringify","push","attrs","type","attribute","name","property","links","flat","link","preloadMeta","looseRoutesById","routeId","route","ssr","manifest","routes","id","preloads","preload","rel","href","headScripts","script","manifestAssets","assets","asset","uniqBy","d","arr","fn","seen","Set","item","key","has","add"],"sources":["../../src/headContentUtils.tsx"],"sourcesContent":["import * as Vue from 'vue'\nimport { escapeHtml } from '@tanstack/router-core'\nimport { useStore } from '@tanstack/vue-store'\nimport { useRouter } from './useRouter'\nimport type { RouterManagedTag } from '@tanstack/router-core'\n\nexport const useTags = () => {\n const router = useRouter()\n const matches = useStore(\n router.stores.activeMatchesSnapshot,\n (value) => value,\n )\n\n const meta = Vue.computed<Array<RouterManagedTag>>(() => {\n const resultMeta: Array<RouterManagedTag> = []\n const metaByAttribute: Record<string, true> = {}\n let title: RouterManagedTag | undefined\n ;[...matches.value.map((match) => match.meta!).filter(Boolean)]\n .reverse()\n .forEach((metas) => {\n ;[...metas].reverse().forEach((m) => {\n if (!m) return\n\n if (m.title) {\n if (!title) {\n title = {\n tag: 'title',\n children: m.title,\n }\n }\n } else if ('script:ld+json' in m) {\n // Handle JSON-LD structured data\n // Content is HTML-escaped to prevent XSS when injected via innerHTML\n try {\n const json = JSON.stringify(m['script:ld+json'])\n resultMeta.push({\n tag: 'script',\n attrs: {\n type: 'application/ld+json',\n },\n children: escapeHtml(json),\n })\n } catch {\n // Skip invalid JSON-LD objects\n }\n } else {\n const attribute = m.name ?? m.property\n if (attribute) {\n if (metaByAttribute[attribute]) {\n return\n } else {\n metaByAttribute[attribute] = true\n }\n }\n\n resultMeta.push({\n tag: 'meta',\n attrs: {\n ...m,\n },\n })\n }\n })\n })\n\n if (title) {\n resultMeta.push(title)\n }\n\n resultMeta.reverse()\n\n return resultMeta\n })\n\n const links = Vue.computed<Array<RouterManagedTag>>(\n () =>\n matches.value\n .map((match) => match.links!)\n .filter(Boolean)\n .flat(1)\n .map((link) => ({\n tag: 'link',\n attrs: {\n ...link,\n },\n })) as Array<RouterManagedTag>,\n )\n\n const preloadMeta = Vue.computed<Array<RouterManagedTag>>(() => {\n const preloadMeta: Array<RouterManagedTag> = []\n\n matches.value\n .map((match) => router.looseRoutesById[match.routeId]!)\n .forEach((route) =>\n router.ssr?.manifest?.routes[route.id]?.preloads\n ?.filter(Boolean)\n .forEach((preload) => {\n preloadMeta.push({\n tag: 'link',\n attrs: {\n rel: 'modulepreload',\n href: preload,\n },\n })\n }),\n )\n\n return preloadMeta\n })\n\n const headScripts = Vue.computed<Array<RouterManagedTag>>(() =>\n (\n matches.value\n .map((match) => match.headScripts!)\n .flat(1)\n .filter(Boolean) as Array<RouterManagedTag>\n ).map(({ children, ...script }) => ({\n tag: 'script',\n attrs: {\n ...script,\n },\n children,\n })),\n )\n\n const manifestAssets = Vue.computed<Array<RouterManagedTag>>(() => {\n const manifest = router.ssr?.manifest\n\n const assets = matches.value\n .map((match) => manifest?.routes[match.routeId]?.assets ?? [])\n .filter(Boolean)\n .flat(1)\n .filter((asset) => asset.tag === 'link')\n .map(\n (asset) =>\n ({\n tag: 'link',\n attrs: { ...asset.attrs },\n }) satisfies RouterManagedTag,\n )\n\n return assets\n })\n\n return () =>\n uniqBy(\n [\n ...manifestAssets.value,\n ...meta.value,\n ...preloadMeta.value,\n ...links.value,\n ...headScripts.value,\n ] as Array<RouterManagedTag>,\n (d) => {\n return JSON.stringify(d)\n },\n )\n}\n\nexport function uniqBy<T>(arr: Array<T>, fn: (item: T) => string) {\n const seen = new Set<string>()\n return arr.filter((item) => {\n const key = fn(item)\n if (seen.has(key)) {\n return false\n }\n seen.add(key)\n return true\n })\n}\n"],"mappings":"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"}
@@ -1,12 +1,6 @@
1
1
  import { Await, useAwaited } from "./awaited.js";
2
2
  import { CatchBoundary, ErrorComponent } from "./CatchBoundary.js";
3
3
  import { useRouter } from "./useRouter.js";
4
- import { useRouterState } from "./useRouterState.js";
5
- import { ClientOnly } from "./ClientOnly.js";
6
- import { CatchNotFound, DefaultGlobalNotFound } from "./not-found.js";
7
- import { ScriptOnce } from "./ScriptOnce.js";
8
- import { Match, Outlet } from "./Match.js";
9
- import { MatchRoute, Matches, useChildMatches, useMatchRoute, useMatches, useParentMatches } from "./Matches.js";
10
4
  import { Link, createLink, linkOptions, useLinkProps } from "./link.js";
11
5
  import { useMatch } from "./useMatch.js";
12
6
  import { useLoaderData } from "./useLoaderData.js";
@@ -17,11 +11,17 @@ import { Navigate, useNavigate } from "./useNavigate.js";
17
11
  import { useRouteContext } from "./useRouteContext.js";
18
12
  import { NotFoundRoute, RootRoute, Route, RouteApi, createRootRoute, createRootRouteWithContext, createRoute, createRouteMask, getRouteApi, rootRouteWithContext } from "./route.js";
19
13
  import { FileRoute, FileRouteLoader, LazyRoute, createFileRoute, createLazyFileRoute, createLazyRoute } from "./fileRoute.js";
14
+ import { ClientOnly } from "./ClientOnly.js";
15
+ import { CatchNotFound, DefaultGlobalNotFound } from "./not-found.js";
16
+ import { ScriptOnce } from "./ScriptOnce.js";
17
+ import { Match, Outlet } from "./Match.js";
20
18
  import { lazyRouteComponent } from "./lazyRouteComponent.js";
19
+ import { MatchRoute, Matches, useChildMatches, useMatchRoute, useMatches, useParentMatches } from "./Matches.js";
21
20
  import { Router, createRouter } from "./router.js";
22
21
  import { RouterContextProvider, RouterProvider } from "./RouterProvider.js";
23
22
  import { ScrollRestoration, useElementScrollRestoration } from "./ScrollRestoration.js";
24
23
  import { Block, useBlocker } from "./useBlocker.js";
24
+ import { useRouterState } from "./useRouterState.js";
25
25
  import { useLocation } from "./useLocation.js";
26
26
  import { useCanGoBack } from "./useCanGoBack.js";
27
27
  import { Asset } from "./Asset.js";
package/dist/esm/index.js CHANGED
@@ -1,12 +1,6 @@
1
1
  import { Await, useAwaited } from "./awaited.js";
2
2
  import { CatchBoundary, ErrorComponent } from "./CatchBoundary.js";
3
3
  import { useRouter } from "./useRouter.js";
4
- import { useRouterState } from "./useRouterState.js";
5
- import { ClientOnly } from "./ClientOnly.js";
6
- import { CatchNotFound, DefaultGlobalNotFound } from "./not-found.js";
7
- import { ScriptOnce } from "./ScriptOnce.js";
8
- import { Match, Outlet } from "./Match.js";
9
- import { MatchRoute, Matches, useChildMatches, useMatchRoute, useMatches, useParentMatches } from "./Matches.js";
10
4
  import { Link, createLink, linkOptions, useLinkProps } from "./link.js";
11
5
  import { useMatch } from "./useMatch.js";
12
6
  import { useLoaderData } from "./useLoaderData.js";
@@ -17,11 +11,17 @@ import { Navigate, useNavigate } from "./useNavigate.js";
17
11
  import { useRouteContext } from "./useRouteContext.js";
18
12
  import { NotFoundRoute, RootRoute, Route, RouteApi, createRootRoute, createRootRouteWithContext, createRoute, createRouteMask, getRouteApi, rootRouteWithContext } from "./route.js";
19
13
  import { FileRoute, FileRouteLoader, LazyRoute, createFileRoute, createLazyFileRoute, createLazyRoute } from "./fileRoute.js";
14
+ import { ClientOnly } from "./ClientOnly.js";
15
+ import { CatchNotFound, DefaultGlobalNotFound } from "./not-found.js";
16
+ import { ScriptOnce } from "./ScriptOnce.js";
17
+ import { Match, Outlet } from "./Match.js";
20
18
  import { lazyRouteComponent } from "./lazyRouteComponent.js";
19
+ import { MatchRoute, Matches, useChildMatches, useMatchRoute, useMatches, useParentMatches } from "./Matches.js";
21
20
  import { Router, createRouter } from "./router.js";
22
21
  import { RouterContextProvider, RouterProvider } from "./RouterProvider.js";
23
22
  import { ScrollRestoration, useElementScrollRestoration } from "./ScrollRestoration.js";
24
23
  import { Block, useBlocker } from "./useBlocker.js";
24
+ import { useRouterState } from "./useRouterState.js";
25
25
  import { useLocation } from "./useLocation.js";
26
26
  import { useCanGoBack } from "./useCanGoBack.js";
27
27
  import { Asset } from "./Asset.js";