@tanstack/solid-router 2.0.0-alpha.5 → 2.0.0-alpha.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.
Files changed (219) hide show
  1. package/dist/cjs/Asset.cjs +2 -2
  2. package/dist/cjs/Asset.cjs.map +1 -1
  3. package/dist/cjs/HeadContent.cjs +11 -1
  4. package/dist/cjs/HeadContent.cjs.map +1 -1
  5. package/dist/cjs/HeadContent.dev.cjs +11 -1
  6. package/dist/cjs/HeadContent.dev.cjs.map +1 -1
  7. package/dist/cjs/Match.cjs +265 -248
  8. package/dist/cjs/Match.cjs.map +1 -1
  9. package/dist/cjs/Match.d.cts +1 -3
  10. package/dist/cjs/Matches.cjs +35 -34
  11. package/dist/cjs/Matches.cjs.map +1 -1
  12. package/dist/cjs/RouterProvider.cjs +12 -8
  13. package/dist/cjs/RouterProvider.cjs.map +1 -1
  14. package/dist/cjs/RouterProvider.d.cts +1 -1
  15. package/dist/cjs/Scripts.cjs +23 -12
  16. package/dist/cjs/Scripts.cjs.map +1 -1
  17. package/dist/cjs/Scripts.d.cts +2 -1
  18. package/dist/cjs/Transitioner.cjs +57 -36
  19. package/dist/cjs/Transitioner.cjs.map +1 -1
  20. package/dist/cjs/headContentUtils.cjs +26 -23
  21. package/dist/cjs/headContentUtils.cjs.map +1 -1
  22. package/dist/cjs/headContentUtils.d.cts +2 -1
  23. package/dist/cjs/index.cjs +1 -1
  24. package/dist/cjs/index.dev.cjs +1 -1
  25. package/dist/cjs/link.cjs +143 -101
  26. package/dist/cjs/link.cjs.map +1 -1
  27. package/dist/cjs/matchContext.cjs +7 -5
  28. package/dist/cjs/matchContext.cjs.map +1 -1
  29. package/dist/cjs/matchContext.d.cts +8 -2
  30. package/dist/cjs/not-found.cjs +8 -4
  31. package/dist/cjs/not-found.cjs.map +1 -1
  32. package/dist/cjs/not-found.d.cts +1 -1
  33. package/dist/cjs/router.cjs +2 -1
  34. package/dist/cjs/router.cjs.map +1 -1
  35. package/dist/cjs/routerStores.cjs +75 -0
  36. package/dist/cjs/routerStores.cjs.map +1 -0
  37. package/dist/cjs/routerStores.d.cts +10 -0
  38. package/dist/cjs/ssr/RouterClient.cjs +1 -1
  39. package/dist/cjs/ssr/RouterClient.cjs.map +1 -1
  40. package/dist/cjs/ssr/renderRouterToStream.cjs +1 -1
  41. package/dist/cjs/ssr/renderRouterToStream.cjs.map +1 -1
  42. package/dist/cjs/ssr/renderRouterToString.cjs +1 -1
  43. package/dist/cjs/ssr/renderRouterToString.cjs.map +1 -1
  44. package/dist/cjs/useBlocker.cjs +12 -3
  45. package/dist/cjs/useBlocker.cjs.map +1 -1
  46. package/dist/cjs/useCanGoBack.cjs +6 -2
  47. package/dist/cjs/useCanGoBack.cjs.map +1 -1
  48. package/dist/cjs/useCanGoBack.d.cts +2 -1
  49. package/dist/cjs/useLoaderDeps.cjs +2 -3
  50. package/dist/cjs/useLoaderDeps.cjs.map +1 -1
  51. package/dist/cjs/useLocation.cjs +13 -2
  52. package/dist/cjs/useLocation.cjs.map +1 -1
  53. package/dist/cjs/useMatch.cjs +27 -15
  54. package/dist/cjs/useMatch.cjs.map +1 -1
  55. package/dist/cjs/useParams.cjs +1 -1
  56. package/dist/cjs/useParams.cjs.map +1 -1
  57. package/dist/cjs/useRouterState.cjs +12 -30
  58. package/dist/cjs/useRouterState.cjs.map +1 -1
  59. package/dist/cjs/useSearch.cjs +2 -1
  60. package/dist/cjs/useSearch.cjs.map +1 -1
  61. package/dist/cjs/utils.cjs +3 -17
  62. package/dist/cjs/utils.cjs.map +1 -1
  63. package/dist/cjs/utils.d.cts +0 -5
  64. package/dist/esm/Asset.js +6 -6
  65. package/dist/esm/Asset.js.map +1 -1
  66. package/dist/esm/HeadContent.dev.js +12 -2
  67. package/dist/esm/HeadContent.dev.js.map +1 -1
  68. package/dist/esm/HeadContent.js +12 -2
  69. package/dist/esm/HeadContent.js.map +1 -1
  70. package/dist/esm/Match.d.ts +1 -3
  71. package/dist/esm/Match.js +267 -250
  72. package/dist/esm/Match.js.map +1 -1
  73. package/dist/esm/Matches.js +40 -39
  74. package/dist/esm/Matches.js.map +1 -1
  75. package/dist/esm/RouterProvider.d.ts +1 -1
  76. package/dist/esm/RouterProvider.js +10 -7
  77. package/dist/esm/RouterProvider.js.map +1 -1
  78. package/dist/esm/ScriptOnce.js +2 -2
  79. package/dist/esm/ScriptOnce.js.map +1 -1
  80. package/dist/esm/Scripts.d.ts +2 -1
  81. package/dist/esm/Scripts.js +21 -11
  82. package/dist/esm/Scripts.js.map +1 -1
  83. package/dist/esm/Transitioner.js +58 -37
  84. package/dist/esm/Transitioner.js.map +1 -1
  85. package/dist/esm/headContentUtils.d.ts +2 -1
  86. package/dist/esm/headContentUtils.js +26 -23
  87. package/dist/esm/headContentUtils.js.map +1 -1
  88. package/dist/esm/index.dev.js +1 -1
  89. package/dist/esm/index.js +1 -1
  90. package/dist/esm/link.js +146 -104
  91. package/dist/esm/link.js.map +1 -1
  92. package/dist/esm/matchContext.d.ts +8 -2
  93. package/dist/esm/matchContext.js +7 -4
  94. package/dist/esm/matchContext.js.map +1 -1
  95. package/dist/esm/not-found.d.ts +1 -1
  96. package/dist/esm/not-found.js +6 -3
  97. package/dist/esm/not-found.js.map +1 -1
  98. package/dist/esm/router.js +2 -1
  99. package/dist/esm/router.js.map +1 -1
  100. package/dist/esm/routerStores.d.ts +10 -0
  101. package/dist/esm/routerStores.js +73 -0
  102. package/dist/esm/routerStores.js.map +1 -0
  103. package/dist/esm/scroll-restoration.js +2 -2
  104. package/dist/esm/scroll-restoration.js.map +1 -1
  105. package/dist/esm/ssr/RouterClient.js +1 -1
  106. package/dist/esm/ssr/RouterClient.js.map +1 -1
  107. package/dist/esm/ssr/renderRouterToStream.js +1 -1
  108. package/dist/esm/ssr/renderRouterToStream.js.map +1 -1
  109. package/dist/esm/ssr/renderRouterToString.js +1 -1
  110. package/dist/esm/ssr/renderRouterToString.js.map +1 -1
  111. package/dist/esm/useBlocker.js +12 -3
  112. package/dist/esm/useBlocker.js.map +1 -1
  113. package/dist/esm/useCanGoBack.d.ts +2 -1
  114. package/dist/esm/useCanGoBack.js +4 -2
  115. package/dist/esm/useCanGoBack.js.map +1 -1
  116. package/dist/esm/useLoaderDeps.js +2 -3
  117. package/dist/esm/useLoaderDeps.js.map +1 -1
  118. package/dist/esm/useLocation.js +11 -2
  119. package/dist/esm/useLocation.js.map +1 -1
  120. package/dist/esm/useMatch.js +28 -16
  121. package/dist/esm/useMatch.js.map +1 -1
  122. package/dist/esm/useParams.js +1 -1
  123. package/dist/esm/useParams.js.map +1 -1
  124. package/dist/esm/useRouterState.js +11 -30
  125. package/dist/esm/useRouterState.js.map +1 -1
  126. package/dist/esm/useSearch.js +2 -1
  127. package/dist/esm/useSearch.js.map +1 -1
  128. package/dist/esm/utils.d.ts +0 -5
  129. package/dist/esm/utils.js +4 -17
  130. package/dist/esm/utils.js.map +1 -1
  131. package/dist/source/Asset.jsx +3 -3
  132. package/dist/source/Asset.jsx.map +1 -1
  133. package/dist/source/HeadContent.dev.jsx +5 -1
  134. package/dist/source/HeadContent.dev.jsx.map +1 -1
  135. package/dist/source/HeadContent.jsx +5 -1
  136. package/dist/source/HeadContent.jsx.map +1 -1
  137. package/dist/source/Match.d.ts +1 -3
  138. package/dist/source/Match.jsx +260 -264
  139. package/dist/source/Match.jsx.map +1 -1
  140. package/dist/source/Matches.jsx +46 -46
  141. package/dist/source/Matches.jsx.map +1 -1
  142. package/dist/source/RouterProvider.d.ts +1 -1
  143. package/dist/source/RouterProvider.jsx +13 -9
  144. package/dist/source/RouterProvider.jsx.map +1 -1
  145. package/dist/source/Scripts.d.ts +2 -1
  146. package/dist/source/Scripts.jsx +46 -47
  147. package/dist/source/Scripts.jsx.map +1 -1
  148. package/dist/source/Transitioner.jsx +80 -44
  149. package/dist/source/Transitioner.jsx.map +1 -1
  150. package/dist/source/headContentUtils.d.ts +2 -1
  151. package/dist/source/headContentUtils.jsx +79 -80
  152. package/dist/source/headContentUtils.jsx.map +1 -1
  153. package/dist/source/link.jsx +145 -112
  154. package/dist/source/link.jsx.map +1 -1
  155. package/dist/source/matchContext.d.ts +8 -2
  156. package/dist/source/matchContext.jsx +7 -3
  157. package/dist/source/matchContext.jsx.map +1 -1
  158. package/dist/source/not-found.d.ts +1 -1
  159. package/dist/source/not-found.jsx +6 -5
  160. package/dist/source/not-found.jsx.map +1 -1
  161. package/dist/source/router.js +2 -1
  162. package/dist/source/router.js.map +1 -1
  163. package/dist/source/routerStores.d.ts +10 -0
  164. package/dist/source/routerStores.js +82 -0
  165. package/dist/source/routerStores.js.map +1 -0
  166. package/dist/source/ssr/RouterClient.jsx +1 -1
  167. package/dist/source/ssr/RouterClient.jsx.map +1 -1
  168. package/dist/source/ssr/renderRouterToStream.jsx +1 -1
  169. package/dist/source/ssr/renderRouterToStream.jsx.map +1 -1
  170. package/dist/source/ssr/renderRouterToString.jsx +1 -1
  171. package/dist/source/ssr/renderRouterToString.jsx.map +1 -1
  172. package/dist/source/useBlocker.jsx +19 -8
  173. package/dist/source/useBlocker.jsx.map +1 -1
  174. package/dist/source/useCanGoBack.d.ts +2 -1
  175. package/dist/source/useCanGoBack.js +4 -2
  176. package/dist/source/useCanGoBack.js.map +1 -1
  177. package/dist/source/useLoaderDeps.jsx +2 -3
  178. package/dist/source/useLoaderDeps.jsx.map +1 -1
  179. package/dist/source/useLocation.jsx +13 -3
  180. package/dist/source/useLocation.jsx.map +1 -1
  181. package/dist/source/useMatch.jsx +33 -23
  182. package/dist/source/useMatch.jsx.map +1 -1
  183. package/dist/source/useParams.jsx +1 -1
  184. package/dist/source/useParams.jsx.map +1 -1
  185. package/dist/source/useRouterState.jsx +14 -55
  186. package/dist/source/useRouterState.jsx.map +1 -1
  187. package/dist/source/useSearch.jsx +2 -1
  188. package/dist/source/useSearch.jsx.map +1 -1
  189. package/dist/source/utils.d.ts +0 -5
  190. package/dist/source/utils.js +2 -15
  191. package/dist/source/utils.js.map +1 -1
  192. package/package.json +7 -7
  193. package/skills/solid-router/SKILL.md +2 -0
  194. package/src/Asset.tsx +3 -3
  195. package/src/HeadContent.dev.tsx +10 -1
  196. package/src/HeadContent.tsx +10 -1
  197. package/src/Match.tsx +395 -349
  198. package/src/Matches.tsx +55 -54
  199. package/src/RouterProvider.tsx +13 -10
  200. package/src/Scripts.tsx +55 -54
  201. package/src/Transitioner.tsx +103 -60
  202. package/src/headContentUtils.tsx +104 -96
  203. package/src/link.tsx +188 -146
  204. package/src/matchContext.tsx +16 -7
  205. package/src/not-found.tsx +6 -6
  206. package/src/router.ts +2 -1
  207. package/src/routerStores.ts +119 -0
  208. package/src/ssr/RouterClient.tsx +1 -1
  209. package/src/ssr/renderRouterToStream.tsx +1 -1
  210. package/src/ssr/renderRouterToString.tsx +1 -1
  211. package/src/useBlocker.tsx +80 -63
  212. package/src/useCanGoBack.ts +6 -2
  213. package/src/useLoaderDeps.tsx +2 -3
  214. package/src/useLocation.tsx +18 -5
  215. package/src/useMatch.tsx +37 -38
  216. package/src/useParams.tsx +2 -3
  217. package/src/useRouterState.tsx +21 -67
  218. package/src/useSearch.tsx +2 -1
  219. package/src/utils.ts +2 -24
@@ -5,100 +5,116 @@ import { createControlledPromise, getLocationChangeInfo, isNotFound, isRedirect,
5
5
  import { isServer } from '@tanstack/router-core/isServer';
6
6
  import { Dynamic } from '@solidjs/web';
7
7
  import { CatchBoundary, ErrorComponent } from './CatchBoundary';
8
- import { useRouterState } from './useRouterState';
9
8
  import { useRouter } from './useRouter';
10
9
  import { CatchNotFound } from './not-found';
11
- import { matchContext } from './matchContext';
10
+ import { nearestMatchContext } from './matchContext';
12
11
  import { SafeFragment } from './SafeFragment';
13
12
  import { renderRouteNotFound } from './renderRouteNotFound';
14
13
  import { ScrollRestoration } from './scroll-restoration';
15
- const MatchContext = matchContext;
14
+ const NearestMatchContext = nearestMatchContext;
16
15
  export const Match = (props) => {
17
16
  const router = useRouter();
18
- const matchState = useRouterState({
19
- select: (s) => {
20
- const match = s.matches.find((d) => d.id === props.matchId);
21
- // During navigation transitions, matches can be temporarily removed
22
- // Return null to avoid errors - the component will handle this gracefully
23
- if (!match) {
24
- return null;
25
- }
26
- return {
27
- routeId: match.routeId,
28
- ssr: match.ssr,
29
- _displayPending: match._displayPending,
30
- };
31
- },
17
+ const match = Solid.createMemo(() => {
18
+ const id = props.matchId;
19
+ if (!id)
20
+ return undefined;
21
+ return router.stores.activeMatchStoresById.get(id)?.state;
32
22
  });
33
- // If match doesn't exist yet, return null (component is being unmounted or not ready)
34
- if (!Solid.untrack(matchState))
35
- return null;
36
- const route = () => router.routesById[matchState().routeId];
37
- const resolvePendingComponent = () => route().options.pendingComponent ?? router.options.defaultPendingComponent;
38
- const routeErrorComponent = () => route().options.errorComponent ?? router.options.defaultErrorComponent;
39
- const routeOnCatch = () => route().options.onCatch ?? router.options.defaultOnCatch;
40
- const routeNotFoundComponent = () => route().isRoot
41
- ? // If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component
42
- (route().options.notFoundComponent ??
43
- router.options.notFoundRoute?.options.component)
44
- : route().options.notFoundComponent;
45
- const resolvedNoSsr = Solid.createMemo(() => matchState().ssr === false || matchState().ssr === 'data-only');
46
- const ResolvedSuspenseBoundary = () => resolvedNoSsr() ? SafeFragment : Solid.Loading;
47
- const ResolvedCatchBoundary = () => routeErrorComponent() ? CatchBoundary : SafeFragment;
48
- const ResolvedNotFoundBoundary = () => routeNotFoundComponent() ? CatchNotFound : SafeFragment;
49
- const resetKey = useRouterState({
50
- select: (s) => s.loadedAt,
23
+ const rawMatchState = Solid.createMemo(() => {
24
+ const currentMatch = match();
25
+ if (!currentMatch) {
26
+ return null;
27
+ }
28
+ const routeId = currentMatch.routeId;
29
+ const parentRouteId = router.routesById[routeId]?.parentRoute
30
+ ?.id;
31
+ return {
32
+ matchId: currentMatch.id,
33
+ routeId,
34
+ ssr: currentMatch.ssr,
35
+ _displayPending: currentMatch._displayPending,
36
+ parentRouteId: parentRouteId,
37
+ };
51
38
  });
52
- const parentRouteId = useRouterState({
53
- select: (s) => {
54
- const index = s.matches.findIndex((d) => d.id === props.matchId);
55
- return s.matches[index - 1]?.routeId;
56
- },
39
+ const hasPendingMatch = Solid.createMemo(() => {
40
+ const currentRouteId = rawMatchState()?.routeId;
41
+ return currentRouteId
42
+ ? Boolean(router.stores.pendingRouteIds.state[currentRouteId])
43
+ : false;
57
44
  });
58
- const ShellComponent = Solid.createMemo(() => route().isRoot
59
- ? (route().options.shellComponent ?? SafeFragment)
60
- : SafeFragment);
61
- return (<Dynamic component={ShellComponent()}>
62
- <MatchContext value={() => props.matchId}>
63
- <Dynamic component={ResolvedSuspenseBoundary()} fallback={
64
- // Don't show fallback on server when using no-ssr mode to avoid hydration mismatch
65
- (isServer ?? router.isServer) || resolvedNoSsr() ? undefined : (<Dynamic component={resolvePendingComponent()}/>)}>
66
- <Dynamic component={ResolvedCatchBoundary()} getResetKey={() => resetKey()} errorComponent={routeErrorComponent() || ErrorComponent} onCatch={(error) => {
67
- // Forward not found errors (we don't want to show the error component for these)
68
- if (isNotFound(error))
69
- throw error;
70
- warning(false, `Error in route match: ${Solid.untrack(matchState).routeId}`);
71
- routeOnCatch()?.(error);
72
- }}>
73
- <Dynamic component={ResolvedNotFoundBoundary()} fallback={(error) => {
74
- // If the current not found handler doesn't exist or it has a
75
- // route ID which doesn't match the current route, rethrow the error
76
- if (!routeNotFoundComponent() ||
77
- (error.routeId && error.routeId !== matchState().routeId) ||
78
- (!error.routeId && !route().isRoot))
79
- throw error;
80
- return (<Dynamic component={routeNotFoundComponent()} {...error}/>);
81
- }}>
82
- <Solid.Switch>
83
- <Solid.Match when={resolvedNoSsr()}>
84
- <Solid.Show when={!(isServer ?? router.isServer)} fallback={<Dynamic component={resolvePendingComponent()}/>}>
85
- <MatchInner matchId={props.matchId}/>
86
- </Solid.Show>
87
- </Solid.Match>
88
- <Solid.Match when={!resolvedNoSsr()}>
89
- <MatchInner matchId={props.matchId}/>
90
- </Solid.Match>
91
- </Solid.Switch>
92
- </Dynamic>
93
- </Dynamic>
94
- </Dynamic>
95
- </MatchContext>
45
+ const nearestMatch = {
46
+ matchId: () => rawMatchState()?.matchId,
47
+ routeId: () => rawMatchState()?.routeId,
48
+ match,
49
+ hasPending: hasPendingMatch,
50
+ };
51
+ return (<Solid.Show when={rawMatchState()}>
52
+ {(currentMatchState) => {
53
+ const route = Solid.createMemo(() => router.routesById[currentMatchState().routeId]);
54
+ const resolvePendingComponent = Solid.createMemo(() => route().options.pendingComponent ??
55
+ router.options.defaultPendingComponent);
56
+ const routeErrorComponent = Solid.createMemo(() => route().options.errorComponent ??
57
+ router.options.defaultErrorComponent);
58
+ const routeOnCatch = Solid.createMemo(() => route().options.onCatch ?? router.options.defaultOnCatch);
59
+ const routeNotFoundComponent = Solid.createMemo(() => route().isRoot
60
+ ? // If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component
61
+ (route().options.notFoundComponent ??
62
+ router.options.notFoundRoute?.options.component)
63
+ : route().options.notFoundComponent);
64
+ const resolvedNoSsr = Solid.createMemo(() => currentMatchState().ssr === false ||
65
+ currentMatchState().ssr === 'data-only');
66
+ const ResolvedSuspenseBoundary = Solid.createMemo(() => resolvedNoSsr() ? SafeFragment : Solid.Loading);
67
+ const ResolvedCatchBoundary = Solid.createMemo(() => routeErrorComponent() ? CatchBoundary : SafeFragment);
68
+ const ResolvedNotFoundBoundary = Solid.createMemo(() => routeNotFoundComponent() ? CatchNotFound : SafeFragment);
69
+ const ShellComponent = Solid.createMemo(() => route().isRoot
70
+ ? (route().options.shellComponent ??
71
+ SafeFragment)
72
+ : SafeFragment);
73
+ return (<Dynamic component={ShellComponent()}>
74
+ <NearestMatchContext value={nearestMatch}>
75
+ <Dynamic component={ResolvedSuspenseBoundary()} fallback={
76
+ // Don't show fallback on server when using no-ssr mode to avoid hydration mismatch
77
+ (isServer ?? router.isServer) &&
78
+ resolvedNoSsr() ? undefined : (<Dynamic component={resolvePendingComponent()}/>)}>
79
+ <Dynamic component={ResolvedCatchBoundary()} getResetKey={() => router.stores.loadedAt.state} errorComponent={routeErrorComponent() || ErrorComponent} onCatch={(error) => {
80
+ // Forward not found errors (we don't want to show the error component for these)
81
+ if (isNotFound(error))
82
+ throw error;
83
+ warning(false, `Error in route match: ${currentMatchState().routeId}`);
84
+ routeOnCatch()?.(error);
85
+ }}>
86
+ <Dynamic component={ResolvedNotFoundBoundary()} fallback={(error) => {
87
+ // If the current not found handler doesn't exist or it has a
88
+ // route ID which doesn't match the current route, rethrow the error
89
+ if (!routeNotFoundComponent() ||
90
+ (error.routeId &&
91
+ error.routeId !== currentMatchState().routeId) ||
92
+ (!error.routeId && !route().isRoot))
93
+ throw error;
94
+ return (<Dynamic component={routeNotFoundComponent()} {...error}/>);
95
+ }}>
96
+ <Solid.Switch>
97
+ <Solid.Match when={resolvedNoSsr()}>
98
+ <Solid.Show when={!(isServer ?? router.isServer)} fallback={<Dynamic component={resolvePendingComponent()}/>}>
99
+ <MatchInner />
100
+ </Solid.Show>
101
+ </Solid.Match>
102
+ <Solid.Match when={!resolvedNoSsr()}>
103
+ <MatchInner />
104
+ </Solid.Match>
105
+ </Solid.Switch>
106
+ </Dynamic>
107
+ </Dynamic>
108
+ </Dynamic>
109
+ </NearestMatchContext>
96
110
 
97
- {parentRouteId() === rootRouteId ? (<>
98
- <OnRendered />
99
- <ScrollRestoration />
100
- </>) : null}
101
- </Dynamic>);
111
+ {currentMatchState().parentRouteId === rootRouteId ? (<>
112
+ <OnRendered />
113
+ <ScrollRestoration />
114
+ </>) : null}
115
+ </Dynamic>);
116
+ }}
117
+ </Solid.Show>);
102
118
  };
103
119
  // On Rendered can't happen above the root layout because it actually
104
120
  // renders a dummy dom element to track the rendered state of the app.
@@ -116,12 +132,10 @@ export const Match = (props) => {
116
132
  const lastOnRenderedKey = new WeakMap();
117
133
  function OnRendered() {
118
134
  const router = useRouter();
119
- const location = useRouterState({
120
- select: (s) => {
121
- return s.resolvedLocation?.state.__TSR_key;
122
- },
123
- });
124
- Solid.createEffect(() => [location()], ([location]) => {
135
+ const location = Solid.createMemo(() => router.stores.resolvedLocation.state?.state.__TSR_key);
136
+ const locationState = Solid.createMemo(() => router.stores.location.state);
137
+ const resolvedLocationState = Solid.createMemo(() => router.stores.resolvedLocation.state);
138
+ Solid.createEffect(() => [location(), locationState(), resolvedLocationState()], ([location, currentLocationState, currentResolvedLocationState]) => {
125
139
  if (!location)
126
140
  return;
127
141
  if (lastOnRenderedKey.get(router) === location)
@@ -129,201 +143,183 @@ function OnRendered() {
129
143
  lastOnRenderedKey.set(router, location);
130
144
  router.emit({
131
145
  type: 'onRendered',
132
- ...getLocationChangeInfo(router.state),
146
+ ...getLocationChangeInfo(currentLocationState, currentResolvedLocationState),
133
147
  });
134
148
  });
135
149
  return null;
136
150
  }
137
- export const MatchInner = (props) => {
151
+ export const MatchInner = () => {
138
152
  const router = useRouter();
139
- const matchState = useRouterState({
140
- select: (s) => {
141
- const match = s.matches.find((d) => d.id === props.matchId);
142
- // During navigation transitions, matches can be temporarily removed
143
- if (!match) {
144
- return null;
145
- }
146
- const routeId = match.routeId;
147
- const remountFn = router.routesById[routeId].options.remountDeps ??
148
- router.options.defaultRemountDeps;
149
- const remountDeps = remountFn?.({
150
- routeId,
151
- loaderDeps: match.loaderDeps,
152
- params: match._strictParams,
153
- search: match._strictSearch,
154
- });
155
- const key = remountDeps ? JSON.stringify(remountDeps) : undefined;
156
- return {
157
- key,
158
- routeId,
159
- match: {
160
- id: match.id,
161
- status: match.status,
162
- error: match.error,
163
- _forcePending: match._forcePending,
164
- _displayPending: match._displayPending,
165
- },
166
- };
167
- },
168
- });
169
- if (!Solid.untrack(matchState))
170
- return null;
171
- const route = () => router.routesById[matchState().routeId];
172
- const match = () => matchState().match;
173
- const componentKey = () => matchState().key ?? matchState().match.id;
174
- const out = () => {
175
- const currentRoute = Solid.untrack(route);
176
- const Comp = currentRoute.options.component ?? router.options.defaultComponent;
177
- if (Comp) {
178
- return <Comp />;
153
+ const match = Solid.useContext(nearestMatchContext).match;
154
+ const rawMatchState = Solid.createMemo(() => {
155
+ const currentMatch = match();
156
+ if (!currentMatch) {
157
+ return null;
179
158
  }
180
- return <Outlet />;
181
- };
182
- const keyedOut = () => (<Solid.Show when={componentKey()} keyed>
183
- {(_key) => out()}
184
- </Solid.Show>);
185
- return (<Solid.Switch>
186
- <Solid.Match when={match()._displayPending}>
187
- {(_) => {
188
- const matchId = Solid.untrack(() => match().id);
189
- const displayPendingResult = Solid.createMemo(() => router.getMatch(matchId)?._nonReactive.displayPendingPromise);
190
- return <>{displayPendingResult()}</>;
191
- }}
192
- </Solid.Match>
193
- <Solid.Match when={match()._forcePending}>
194
- {(_) => {
195
- const matchId = Solid.untrack(() => match().id);
196
- const minPendingResult = Solid.createMemo(() => router.getMatch(matchId)?._nonReactive.minPendingPromise);
197
- return <>{minPendingResult()}</>;
198
- }}
199
- </Solid.Match>
200
- <Solid.Match when={match().status === 'pending'}>
201
- {(_) => {
202
- const currentMatch = Solid.untrack(match);
203
- const currentRoute = Solid.untrack(route);
204
- const pendingMinMs = currentRoute.options.pendingMinMs ??
205
- router.options.defaultPendingMinMs;
206
- if (pendingMinMs) {
207
- const routerMatch = router.getMatch(currentMatch.id);
208
- if (routerMatch && !routerMatch._nonReactive.minPendingPromise) {
209
- // Create a promise that will resolve after the minPendingMs
210
- if (!(isServer ?? router.isServer)) {
211
- const minPendingPromise = createControlledPromise();
212
- routerMatch._nonReactive.minPendingPromise = minPendingPromise;
213
- setTimeout(() => {
214
- minPendingPromise.resolve();
215
- // We've handled the minPendingPromise, so we can delete it
216
- routerMatch._nonReactive.minPendingPromise = undefined;
217
- }, pendingMinMs);
218
- }
219
- }
220
- }
221
- const loaderResult = Solid.createMemo(async () => {
222
- await new Promise((r) => setTimeout(r, 0));
223
- return router.getMatch(currentMatch.id)?._nonReactive.loadPromise;
224
- });
225
- const FallbackComponent = currentRoute.options.pendingComponent ??
226
- router.options.defaultPendingComponent;
227
- return (<>
228
- {FallbackComponent && pendingMinMs > 0 ? (<Dynamic component={FallbackComponent}/>) : null}
229
- {loaderResult()}
230
- </>);
231
- }}
232
- </Solid.Match>
233
- <Solid.Match when={match().status === 'notFound'}>
234
- {(_) => {
235
- const currentMatch = Solid.untrack(match);
236
- const currentRoute = Solid.untrack(route);
237
- const currentRouteId = Solid.untrack(() => matchState().routeId);
238
- invariant(isNotFound(currentMatch.error), 'Expected a notFound error');
239
- // Use Show with keyed to ensure re-render when routeId changes
240
- return (<Solid.Show when={currentRouteId} keyed>
241
- {(_routeId) => renderRouteNotFound(router, currentRoute, currentMatch.error)}
242
- </Solid.Show>);
243
- }}
244
- </Solid.Match>
245
- <Solid.Match when={match().status === 'redirected'}>
246
- {(_) => {
247
- const matchId = Solid.untrack(() => match().id);
248
- invariant(isRedirect(Solid.untrack(match).error), 'Expected a redirect error');
249
- const loaderResult = Solid.createMemo(async () => {
250
- await new Promise((r) => setTimeout(r, 0));
251
- return router.getMatch(matchId)?._nonReactive.loadPromise;
159
+ const routeId = currentMatch.routeId;
160
+ const remountFn = router.routesById[routeId].options.remountDeps ??
161
+ router.options.defaultRemountDeps;
162
+ const remountDeps = remountFn?.({
163
+ routeId,
164
+ loaderDeps: currentMatch.loaderDeps,
165
+ params: currentMatch._strictParams,
166
+ search: currentMatch._strictSearch,
167
+ });
168
+ const key = remountDeps ? JSON.stringify(remountDeps) : undefined;
169
+ return {
170
+ key,
171
+ routeId,
172
+ match: {
173
+ id: currentMatch.id,
174
+ status: currentMatch.status,
175
+ error: currentMatch.error,
176
+ _forcePending: currentMatch._forcePending ?? false,
177
+ _displayPending: currentMatch._displayPending ?? false,
178
+ },
179
+ };
180
+ });
181
+ return (<Solid.Show when={rawMatchState()}>
182
+ {(currentMatchState) => {
183
+ const route = Solid.createMemo(() => router.routesById[currentMatchState().routeId]);
184
+ const currentMatch = Solid.createMemo(() => currentMatchState().match);
185
+ const componentKey = Solid.createMemo(() => currentMatchState().key ?? currentMatchState().match.id);
186
+ const Comp = Solid.createMemo(() => route().options.component ?? router.options.defaultComponent);
187
+ const OutComponent = Solid.createMemo(() => {
188
+ const C = Comp();
189
+ return C || Outlet;
252
190
  });
253
- return <>{loaderResult()}</>;
254
- }}
255
- </Solid.Match>
256
- <Solid.Match when={match().status === 'error'}>
257
- {(_) => {
258
- if (isServer ?? router.isServer) {
259
- const currentMatch = Solid.untrack(match);
260
- const RouteErrorComponent = (Solid.untrack(route).options.errorComponent ??
261
- router.options.defaultErrorComponent) ||
262
- ErrorComponent;
263
- return (<RouteErrorComponent error={currentMatch.error} info={{
264
- componentStack: '',
265
- }}/>);
266
- }
267
- throw Solid.untrack(match).error;
191
+ const RenderOut = () => <Dynamic component={OutComponent()}/>;
192
+ const keyedOut = () => (<Solid.Show when={componentKey()} keyed>
193
+ {(_key) => <RenderOut />}
194
+ </Solid.Show>);
195
+ return (<Solid.Switch>
196
+ <Solid.Match when={currentMatch()._displayPending}>
197
+ {(_) => {
198
+ const displayPendingResult = Solid.createMemo(() => router.getMatch(currentMatch().id)?._nonReactive
199
+ .displayPendingPromise);
200
+ return <>{displayPendingResult()}</>;
201
+ }}
202
+ </Solid.Match>
203
+ <Solid.Match when={currentMatch()._forcePending}>
204
+ {(_) => {
205
+ const minPendingResult = Solid.createMemo(() => router.getMatch(currentMatch().id)?._nonReactive
206
+ .minPendingPromise);
207
+ return <>{minPendingResult()}</>;
208
+ }}
209
+ </Solid.Match>
210
+ <Solid.Match when={currentMatch().status === 'pending'}>
211
+ {(_) => {
212
+ const pendingMinMs = Solid.untrack(() => route().options.pendingMinMs ??
213
+ router.options.defaultPendingMinMs);
214
+ if (pendingMinMs) {
215
+ const routerMatch = Solid.untrack(() => router.getMatch(currentMatch().id));
216
+ if (routerMatch &&
217
+ !routerMatch._nonReactive.minPendingPromise) {
218
+ // Create a promise that will resolve after the minPendingMs
219
+ if (!(isServer ?? router.isServer)) {
220
+ const minPendingPromise = createControlledPromise();
221
+ routerMatch._nonReactive.minPendingPromise =
222
+ minPendingPromise;
223
+ setTimeout(() => {
224
+ minPendingPromise.resolve();
225
+ // We've handled the minPendingPromise, so we can delete it
226
+ routerMatch._nonReactive.minPendingPromise = undefined;
227
+ }, pendingMinMs);
228
+ }
229
+ }
230
+ }
231
+ const loaderResult = Solid.createMemo(async () => {
232
+ await new Promise((r) => setTimeout(r, 0));
233
+ return router.getMatch(currentMatch().id)?._nonReactive
234
+ .loadPromise;
235
+ });
236
+ const FallbackComponent = Solid.untrack(() => route().options.pendingComponent ??
237
+ router.options.defaultPendingComponent);
238
+ return (<>
239
+ {FallbackComponent && pendingMinMs > 0 ? (<Dynamic component={FallbackComponent}/>) : null}
240
+ {loaderResult()}
241
+ </>);
242
+ }}
243
+ </Solid.Match>
244
+ <Solid.Match when={currentMatch().status === 'notFound'}>
245
+ {(_) => {
246
+ const matchError = Solid.untrack(() => currentMatch().error);
247
+ invariant(isNotFound(matchError), 'Expected a notFound error');
248
+ // Use Show with keyed to ensure re-render when routeId changes
249
+ return (<Solid.Show when={currentMatchState().routeId} keyed>
250
+ {(_routeId) => Solid.untrack(() => renderRouteNotFound(router, route(), matchError))}
251
+ </Solid.Show>);
252
+ }}
253
+ </Solid.Match>
254
+ <Solid.Match when={currentMatch().status === 'redirected'}>
255
+ {(_) => {
256
+ const matchError = Solid.untrack(() => currentMatch().error);
257
+ invariant(isRedirect(matchError), 'Expected a redirect error');
258
+ return null;
259
+ }}
260
+ </Solid.Match>
261
+ <Solid.Match when={currentMatch().status === 'error'}>
262
+ {(_) => {
263
+ const matchError = Solid.untrack(() => currentMatch().error);
264
+ if (isServer ?? router.isServer) {
265
+ const RouteErrorComponent = (route().options.errorComponent ??
266
+ router.options.defaultErrorComponent) ||
267
+ ErrorComponent;
268
+ return (<RouteErrorComponent error={matchError} info={{
269
+ componentStack: '',
270
+ }}/>);
271
+ }
272
+ throw matchError;
273
+ }}
274
+ </Solid.Match>
275
+ <Solid.Match when={currentMatch().status === 'success'}>
276
+ {keyedOut()}
277
+ </Solid.Match>
278
+ </Solid.Switch>);
268
279
  }}
269
- </Solid.Match>
270
- <Solid.Match when={match().status === 'success'}>
271
- {keyedOut()}
272
- </Solid.Match>
273
- </Solid.Switch>);
280
+ </Solid.Show>);
274
281
  };
275
282
  export const Outlet = () => {
276
283
  const router = useRouter();
277
- const matchId = Solid.useContext(matchContext);
278
- const routeId = useRouterState({
279
- select: (s) => s.matches.find((d) => d.id === matchId())?.routeId,
280
- });
281
- const route = () => router.routesById[routeId()];
282
- const parentGlobalNotFound = useRouterState({
283
- select: (s) => {
284
- const matches = s.matches;
285
- const parentMatch = matches.find((d) => d.id === matchId());
286
- // During navigation transitions, parent match can be temporarily removed
287
- // Return false to avoid errors - the component will handle this gracefully
288
- if (!parentMatch) {
289
- return false;
290
- }
291
- return parentMatch.globalNotFound;
292
- },
284
+ const nearestParentMatch = Solid.useContext(nearestMatchContext);
285
+ const parentMatch = nearestParentMatch.match;
286
+ const routeId = nearestParentMatch.routeId;
287
+ const route = Solid.createMemo(() => routeId() ? router.routesById[routeId()] : undefined);
288
+ const parentGlobalNotFound = Solid.createMemo(() => parentMatch()?.globalNotFound ?? false);
289
+ const childMatchId = Solid.createMemo(() => {
290
+ const currentRouteId = routeId();
291
+ return currentRouteId
292
+ ? router.stores.childMatchIdByRouteId.state[currentRouteId]
293
+ : undefined;
293
294
  });
294
- const childMatchId = useRouterState({
295
- select: (s) => {
296
- const matches = s.matches;
297
- const index = matches.findIndex((d) => d.id === matchId());
298
- const v = matches[index + 1]?.id;
299
- return v;
300
- },
295
+ const childRouteId = Solid.createMemo(() => {
296
+ const id = childMatchId();
297
+ if (!id)
298
+ return undefined;
299
+ return router.stores.activeMatchStoresById.get(id)?.state.routeId;
301
300
  });
302
- const childRouteId = useRouterState({
303
- select: (s) => {
304
- const matches = s.matches;
305
- const index = matches.findIndex((d) => d.id === matchId());
306
- return matches[index + 1]?.routeId;
307
- },
301
+ const childRoute = Solid.createMemo(() => {
302
+ const id = childRouteId();
303
+ return id ? router.routesById[id] : undefined;
308
304
  });
309
- const childMatchStatus = useRouterState({
310
- select: (s) => {
311
- const matches = s.matches;
312
- const index = matches.findIndex((d) => d.id === matchId());
313
- return matches[index + 1]?.status;
314
- },
305
+ const childPendingComponent = Solid.createMemo(() => childRoute()?.options.pendingComponent ??
306
+ router.options.defaultPendingComponent);
307
+ const childMatchStatus = Solid.createMemo(() => {
308
+ const id = childMatchId();
309
+ if (!id)
310
+ return undefined;
311
+ return router.stores.activeMatchStoresById.get(id)?.state.status;
315
312
  });
316
313
  // Only show not-found if we're not in a redirected state
317
314
  const shouldShowNotFound = () => childMatchStatus() !== 'redirected' && parentGlobalNotFound();
318
- return (<Solid.Show when={!shouldShowNotFound() && childMatchId()} fallback={<Solid.Show when={shouldShowNotFound()}>
319
- {renderRouteNotFound(router, route(), undefined)}
315
+ return (<Solid.Show when={!shouldShowNotFound() && childMatchId()} fallback={<Solid.Show when={shouldShowNotFound() && route()}>
316
+ {(resolvedRoute) => Solid.untrack(() => renderRouteNotFound(router, resolvedRoute(), undefined))}
320
317
  </Solid.Show>}>
321
- {(matchIdAccessor) => {
322
- // Use a memo to avoid stale accessor errors while keeping reactivity
323
- const currentMatchId = Solid.createMemo(() => matchIdAccessor());
318
+ {(childMatchIdAccessor) => {
319
+ const currentMatchId = Solid.createMemo(() => childMatchIdAccessor());
324
320
  return (<Solid.Show when={routeId() === rootRouteId} fallback={<Match matchId={currentMatchId()}/>}>
325
321
  <Solid.Show when={childRouteId()} keyed>
326
- {(_routeId) => (<Solid.Loading fallback={<Dynamic component={router.options.defaultPendingComponent}/>}>
322
+ {(_routeId) => (<Solid.Loading fallback={childPendingComponent() ? (<Dynamic component={childPendingComponent()}/>) : null}>
327
323
  <Match matchId={currentMatchId()}/>
328
324
  </Solid.Loading>)}
329
325
  </Solid.Show>