kiru 0.54.0-preview.1 → 0.54.1

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 (204) hide show
  1. package/dist/components/derive.d.ts +1 -1
  2. package/dist/components/derive.d.ts.map +1 -1
  3. package/dist/components/derive.js +2 -3
  4. package/dist/components/derive.js.map +1 -1
  5. package/dist/components/memo.d.ts +1 -3
  6. package/dist/components/memo.d.ts.map +1 -1
  7. package/dist/components/memo.js +2 -2
  8. package/dist/components/memo.js.map +1 -1
  9. package/dist/context.d.ts.map +1 -1
  10. package/dist/context.js +1 -23
  11. package/dist/context.js.map +1 -1
  12. package/dist/dom.d.ts.map +1 -1
  13. package/dist/dom.js +111 -78
  14. package/dist/dom.js.map +1 -1
  15. package/dist/error.d.ts.map +1 -1
  16. package/dist/error.js +2 -4
  17. package/dist/error.js.map +1 -1
  18. package/dist/form/index.d.ts.map +1 -1
  19. package/dist/form/index.js +6 -10
  20. package/dist/form/index.js.map +1 -1
  21. package/dist/globals.d.ts +1 -1
  22. package/dist/globals.d.ts.map +1 -1
  23. package/dist/globals.js.map +1 -1
  24. package/dist/hmr.d.ts +1 -0
  25. package/dist/hmr.d.ts.map +1 -1
  26. package/dist/hmr.js +11 -3
  27. package/dist/hmr.js.map +1 -1
  28. package/dist/hooks/useEffectEvent.d.ts.map +1 -1
  29. package/dist/hooks/useEffectEvent.js.map +1 -1
  30. package/dist/hooks/usePromise.d.ts +1 -2
  31. package/dist/hooks/usePromise.d.ts.map +1 -1
  32. package/dist/hooks/usePromise.js +62 -31
  33. package/dist/hooks/usePromise.js.map +1 -1
  34. package/dist/hooks/utils.d.ts.map +1 -1
  35. package/dist/hooks/utils.js +10 -10
  36. package/dist/hooks/utils.js.map +1 -1
  37. package/dist/hydration.d.ts +6 -13
  38. package/dist/hydration.d.ts.map +1 -1
  39. package/dist/hydration.js +20 -50
  40. package/dist/hydration.js.map +1 -1
  41. package/dist/index.js +2 -1
  42. package/dist/index.js.map +1 -1
  43. package/dist/reconciler.d.ts.map +1 -1
  44. package/dist/reconciler.js +3 -6
  45. package/dist/reconciler.js.map +1 -1
  46. package/dist/recursiveRender.d.ts.map +1 -1
  47. package/dist/recursiveRender.js +9 -8
  48. package/dist/recursiveRender.js.map +1 -1
  49. package/dist/renderToString.d.ts.map +1 -1
  50. package/dist/renderToString.js.map +1 -1
  51. package/dist/router/client/index.d.ts +2 -4
  52. package/dist/router/client/index.d.ts.map +1 -1
  53. package/dist/router/client/index.js +13 -59
  54. package/dist/router/client/index.js.map +1 -1
  55. package/dist/router/context.d.ts +5 -2
  56. package/dist/router/context.d.ts.map +1 -1
  57. package/dist/router/context.js +1 -5
  58. package/dist/router/context.js.map +1 -1
  59. package/dist/router/fileRouter.d.ts.map +1 -1
  60. package/dist/router/fileRouter.js +2 -4
  61. package/dist/router/fileRouter.js.map +1 -1
  62. package/dist/router/fileRouterController.d.ts +2 -2
  63. package/dist/router/fileRouterController.d.ts.map +1 -1
  64. package/dist/router/fileRouterController.js +135 -214
  65. package/dist/router/fileRouterController.js.map +1 -1
  66. package/dist/router/globals.d.ts +0 -3
  67. package/dist/router/globals.d.ts.map +1 -1
  68. package/dist/router/globals.js +0 -3
  69. package/dist/router/globals.js.map +1 -1
  70. package/dist/router/head.d.ts.map +1 -1
  71. package/dist/router/head.js +7 -5
  72. package/dist/router/head.js.map +1 -1
  73. package/dist/router/index.d.ts +1 -2
  74. package/dist/router/index.d.ts.map +1 -1
  75. package/dist/router/index.js +1 -2
  76. package/dist/router/index.js.map +1 -1
  77. package/dist/router/link.js +3 -3
  78. package/dist/router/link.js.map +1 -1
  79. package/dist/router/{ssg → server}/index.d.ts +4 -4
  80. package/dist/router/server/index.d.ts.map +1 -0
  81. package/dist/router/{ssg → server}/index.js +7 -9
  82. package/dist/router/server/index.js.map +1 -0
  83. package/dist/router/types.d.ts +16 -42
  84. package/dist/router/types.d.ts.map +1 -1
  85. package/dist/router/types.internal.d.ts +0 -4
  86. package/dist/router/types.internal.d.ts.map +1 -1
  87. package/dist/router/utils/index.d.ts +3 -8
  88. package/dist/router/utils/index.d.ts.map +1 -1
  89. package/dist/router/utils/index.js +8 -40
  90. package/dist/router/utils/index.js.map +1 -1
  91. package/dist/scheduler.d.ts +3 -14
  92. package/dist/scheduler.d.ts.map +1 -1
  93. package/dist/scheduler.js +64 -56
  94. package/dist/scheduler.js.map +1 -1
  95. package/dist/signals/base.d.ts +0 -2
  96. package/dist/signals/base.d.ts.map +1 -1
  97. package/dist/signals/base.js +0 -6
  98. package/dist/signals/base.js.map +1 -1
  99. package/dist/signals/computed.d.ts +3 -0
  100. package/dist/signals/computed.d.ts.map +1 -1
  101. package/dist/signals/computed.js +29 -20
  102. package/dist/signals/computed.js.map +1 -1
  103. package/dist/signals/for.d.ts +3 -3
  104. package/dist/signals/for.d.ts.map +1 -1
  105. package/dist/signals/for.js +2 -1
  106. package/dist/signals/for.js.map +1 -1
  107. package/dist/signals/utils.d.ts.map +1 -1
  108. package/dist/signals/utils.js +2 -1
  109. package/dist/signals/utils.js.map +1 -1
  110. package/dist/signals/watch.d.ts.map +1 -1
  111. package/dist/signals/watch.js +18 -22
  112. package/dist/signals/watch.js.map +1 -1
  113. package/dist/ssr/client.d.ts +1 -1
  114. package/dist/ssr/client.d.ts.map +1 -1
  115. package/dist/ssr/client.js +0 -2
  116. package/dist/ssr/client.js.map +1 -1
  117. package/dist/ssr/server.d.ts +3 -9
  118. package/dist/ssr/server.d.ts.map +1 -1
  119. package/dist/ssr/server.js +30 -37
  120. package/dist/ssr/server.js.map +1 -1
  121. package/dist/types.d.ts +0 -7
  122. package/dist/types.d.ts.map +1 -1
  123. package/dist/types.dom.d.ts +3 -3
  124. package/dist/types.dom.d.ts.map +1 -1
  125. package/dist/utils/format.d.ts +1 -2
  126. package/dist/utils/format.d.ts.map +1 -1
  127. package/dist/utils/format.js +1 -4
  128. package/dist/utils/format.js.map +1 -1
  129. package/dist/utils/index.d.ts +1 -1
  130. package/dist/utils/index.d.ts.map +1 -1
  131. package/dist/utils/index.js +1 -1
  132. package/dist/utils/index.js.map +1 -1
  133. package/dist/utils/promise.d.ts +0 -2
  134. package/dist/utils/promise.d.ts.map +1 -1
  135. package/dist/utils/promise.js +1 -45
  136. package/dist/utils/promise.js.map +1 -1
  137. package/dist/utils/runtime.d.ts +3 -2
  138. package/dist/utils/runtime.d.ts.map +1 -1
  139. package/dist/utils/runtime.js +5 -2
  140. package/dist/utils/runtime.js.map +1 -1
  141. package/dist/utils/vdom.d.ts.map +1 -1
  142. package/dist/utils/vdom.js +2 -2
  143. package/dist/utils/vdom.js.map +1 -1
  144. package/package.json +4 -8
  145. package/src/components/derive.ts +3 -5
  146. package/src/components/memo.ts +3 -11
  147. package/src/context.ts +1 -24
  148. package/src/dom.ts +146 -101
  149. package/src/error.ts +2 -4
  150. package/src/form/index.ts +6 -9
  151. package/src/globals.ts +1 -1
  152. package/src/hmr.ts +14 -5
  153. package/src/hooks/useEffectEvent.ts +0 -1
  154. package/src/hooks/usePromise.ts +77 -58
  155. package/src/hooks/utils.ts +12 -12
  156. package/src/hydration.ts +21 -57
  157. package/src/index.ts +1 -1
  158. package/src/reconciler.ts +2 -6
  159. package/src/recursiveRender.ts +10 -9
  160. package/src/renderToString.ts +0 -1
  161. package/src/router/client/index.ts +16 -114
  162. package/src/router/context.ts +6 -7
  163. package/src/router/fileRouter.ts +2 -6
  164. package/src/router/fileRouterController.ts +161 -324
  165. package/src/router/globals.ts +0 -4
  166. package/src/router/head.ts +7 -5
  167. package/src/router/index.ts +1 -12
  168. package/src/router/link.ts +3 -3
  169. package/src/router/{ssg → server}/index.ts +13 -18
  170. package/src/router/types.internal.ts +0 -5
  171. package/src/router/types.ts +16 -53
  172. package/src/router/utils/index.ts +16 -79
  173. package/src/scheduler.ts +85 -89
  174. package/src/signals/base.ts +0 -8
  175. package/src/signals/computed.ts +30 -18
  176. package/src/signals/for.ts +15 -10
  177. package/src/signals/utils.ts +2 -1
  178. package/src/signals/watch.ts +27 -22
  179. package/src/ssr/client.ts +1 -4
  180. package/src/ssr/server.ts +34 -59
  181. package/src/types.dom.ts +4 -5
  182. package/src/types.ts +0 -10
  183. package/src/utils/format.ts +0 -5
  184. package/src/utils/index.ts +1 -1
  185. package/src/utils/promise.ts +1 -70
  186. package/src/utils/runtime.ts +6 -2
  187. package/src/utils/vdom.ts +2 -7
  188. package/dist/router/constants.d.ts +0 -2
  189. package/dist/router/constants.d.ts.map +0 -1
  190. package/dist/router/constants.js +0 -2
  191. package/dist/router/constants.js.map +0 -1
  192. package/dist/router/guard.d.ts +0 -17
  193. package/dist/router/guard.d.ts.map +0 -1
  194. package/dist/router/guard.js +0 -45
  195. package/dist/router/guard.js.map +0 -1
  196. package/dist/router/ssg/index.d.ts.map +0 -1
  197. package/dist/router/ssg/index.js.map +0 -1
  198. package/dist/router/ssr/index.d.ts +0 -20
  199. package/dist/router/ssr/index.d.ts.map +0 -1
  200. package/dist/router/ssr/index.js +0 -163
  201. package/dist/router/ssr/index.js.map +0 -1
  202. package/src/router/constants.ts +0 -1
  203. package/src/router/guard.ts +0 -72
  204. package/src/router/ssr/index.ts +0 -252
@@ -2,15 +2,9 @@ import { Signal } from "../signals/base.js"
2
2
  import { watch } from "../signals/watch.js"
3
3
  import { __DEV__ } from "../env.js"
4
4
  import { flushSync, nextIdle } from "../scheduler.js"
5
- import { toArray } from "../utils/format.js"
6
5
  import { ReloadOptions, type FileRouterContextType } from "./context.js"
7
6
  import { FileRouterDataLoadError } from "./errors.js"
8
- import {
9
- fileRouterInstance,
10
- fileRouterRoute,
11
- requestContext,
12
- routerCache,
13
- } from "./globals.js"
7
+ import { fileRouterInstance, fileRouterRoute, routerCache } from "./globals.js"
14
8
  import type {
15
9
  FileRouterConfig,
16
10
  PageConfig,
@@ -23,22 +17,17 @@ import type {
23
17
  CurrentPage,
24
18
  DevtoolsInterface,
25
19
  FormattedViteImportMap,
26
- GuardModule,
27
20
  PageModule,
28
21
  ViteImportMap,
29
22
  } from "./types.internal.js"
30
23
  import {
31
24
  formatViteImportMap,
32
- matchModules,
25
+ matchLayouts,
33
26
  matchRoute,
34
27
  match404Route,
35
28
  normalizePrefixPath,
36
29
  parseQuery,
37
30
  wrapWithLayouts,
38
- runAfterEachGuards,
39
- runBeforeEachGuards,
40
- runBeforeEnterHooks,
41
- runBeforeLeaveHooks,
42
31
  } from "./utils/index.js"
43
32
  import { RouterCache, type CacheKey } from "./cache.js"
44
33
  import { scrollStack } from "./scrollStack.js"
@@ -47,19 +36,6 @@ interface PageConfigWithLoader<T = unknown> extends PageConfig {
47
36
  loader: PageDataLoaderConfig<T>
48
37
  }
49
38
 
50
- interface LoadRouteOptions {
51
- path?: string
52
- transition?: boolean
53
- isStatic404?: boolean
54
- onPaint?: () => void
55
- }
56
-
57
- let transitionId = 0
58
- let currentTransition = null as null | {
59
- transition: ViewTransition
60
- id: number
61
- }
62
-
63
39
  export class FileRouterController {
64
40
  public contextValue: FileRouterContextType
65
41
  public devtools?: DevtoolsInterface
@@ -79,9 +55,9 @@ export class FileRouterController {
79
55
  private historyIndex: number
80
56
  private layouts: FormattedViteImportMap
81
57
  private pages: FormattedViteImportMap<PageModule>
82
- private guards: FormattedViteImportMap<GuardModule>
83
58
  private pageRouteToConfig?: Map<string, PageConfig>
84
59
  private state: RouterState
60
+ private baseUrl = "/"
85
61
 
86
62
  constructor() {
87
63
  routerCache.current ??= new RouterCache()
@@ -93,7 +69,6 @@ export class FileRouterController {
93
69
  this.historyIndex = 0
94
70
  this.layouts = {}
95
71
  this.pages = {}
96
- this.guards = {}
97
72
  this.state = {
98
73
  pathname: window.location.pathname,
99
74
  hash: window.location.hash,
@@ -101,27 +76,7 @@ export class FileRouterController {
101
76
  query: {},
102
77
  signal: this.abortController.signal,
103
78
  }
104
- const __this = this
105
- this.contextValue = {
106
- invalidate: async (...paths: string[]) => {
107
- if (this.invalidate(...paths)) {
108
- return this.loadRoute()
109
- }
110
- },
111
- get state() {
112
- return { ...__this.state }
113
- },
114
- navigate: this.navigate.bind(this),
115
- prefetchRouteModules: this.prefetchRouteModules.bind(this),
116
- reload: async (options?: ReloadOptions) => {
117
- if (options?.invalidate ?? true) {
118
- this.invalidate(this.state.pathname)
119
- }
120
- return this.loadRoute({ transition: options?.transition })
121
- },
122
- setQuery: this.setQuery.bind(this),
123
- setHash: this.setHash.bind(this),
124
- }
79
+ this.contextValue = this.createContextValue()
125
80
  if (__DEV__) {
126
81
  this.filePathToPageRoute = new Map()
127
82
  this.pageRouteToConfig = new Map()
@@ -138,11 +93,12 @@ export class FileRouterController {
138
93
  if (curPage?.route === existing.route && loader) {
139
94
  const p = this.currentPageProps.value
140
95
  const transition =
141
- (!loader.static && loader.transition) ?? this.enableTransitions
96
+ (loader.mode !== "static" && loader.transition) ??
97
+ this.enableTransitions
142
98
 
143
99
  // Check cache first if caching is enabled
144
100
  let cachedData = null
145
- if (!loader.static && loader.cache) {
101
+ if (loader.mode !== "static" && loader.cache) {
146
102
  const cacheKey: CacheKey = {
147
103
  path: this.state.pathname,
148
104
  params: this.state.params,
@@ -159,11 +115,9 @@ export class FileRouterController {
159
115
  error: null,
160
116
  loading: false,
161
117
  }
162
- handleStateTransition(
163
- transition,
164
- transitionId,
165
- () => (this.currentPageProps.value = props)
166
- )
118
+ handleStateTransition(this.state.signal, transition, () => {
119
+ this.currentPageProps.value = props
120
+ })
167
121
  } else {
168
122
  // No cached data - show loading state and load data
169
123
  const props = {
@@ -172,14 +126,13 @@ export class FileRouterController {
172
126
  data: null,
173
127
  error: null,
174
128
  }
175
- handleStateTransition(
176
- transition,
177
- transitionId,
178
- () => (this.currentPageProps.value = props)
179
- )
129
+ handleStateTransition(this.state.signal, transition, () => {
130
+ this.currentPageProps.value = props
131
+ })
180
132
 
181
133
  this.loadRouteData(
182
134
  config as PageConfigWithLoader,
135
+ props,
183
136
  this.state,
184
137
  transition
185
138
  )
@@ -215,7 +168,6 @@ export class FileRouterController {
215
168
  const {
216
169
  pages,
217
170
  layouts,
218
- guards,
219
171
  dir = "/pages",
220
172
  baseUrl = "/",
221
173
  transition,
@@ -226,39 +178,14 @@ export class FileRouterController {
226
178
  normalizePrefixPath(dir),
227
179
  normalizePrefixPath(baseUrl),
228
180
  ]
181
+ this.baseUrl = normalizedBaseUrl.slice(0, -1)
229
182
 
230
- if (!preloaded) {
231
- this.pages = formatViteImportMap(
232
- pages as ViteImportMap,
233
- normalizedDir,
234
- normalizedBaseUrl
235
- )
236
-
237
- this.layouts = formatViteImportMap(
238
- layouts as ViteImportMap,
239
- normalizedDir,
240
- normalizedBaseUrl
241
- )
242
- this.guards = !guards
243
- ? {}
244
- : (formatViteImportMap(
245
- guards as ViteImportMap,
246
- normalizedDir,
247
- normalizedBaseUrl
248
- ) as unknown as FormattedViteImportMap<GuardModule>)
249
-
250
- if (__DEV__) {
251
- validateRoutes(this.pages)
252
- }
253
- this.loadRoute()
254
- } else {
183
+ if (preloaded) {
255
184
  const {
256
185
  pages,
257
186
  layouts,
258
- guards,
259
187
  page,
260
188
  pageProps,
261
- pagePropsPromise,
262
189
  pageLayouts,
263
190
  route,
264
191
  params,
@@ -281,49 +208,57 @@ export class FileRouterController {
281
208
  this.currentLayouts.value = pageLayouts.map((l) => l.default)
282
209
  this.pages = pages
283
210
  this.layouts = layouts
284
- this.guards = (guards ??
285
- {}) as unknown as FormattedViteImportMap<GuardModule>
286
211
  if (__DEV__) {
287
- validateRoutes(this.pages)
288
212
  if (page.config) {
289
213
  this.dev_onPageConfigDefined!(route, page.config)
290
214
  }
291
215
  }
292
-
216
+ if (__DEV__) {
217
+ validateRoutes(this.pages)
218
+ }
293
219
  const loader = page.config?.loader
294
- const transition =
295
- (!loader?.static && loader?.transition) ?? this.enableTransitions
296
-
297
- if (loader && pagePropsPromise) {
298
- const prevState = this.state
299
- pagePropsPromise.then(({ data, error }) => {
300
- if (this.state !== prevState) return
301
-
302
- handleStateTransition(
303
- transition,
304
- transitionId,
305
- () =>
306
- (this.currentPageProps.value = { loading: false, data, error })
307
- )
308
- })
309
- } else if (
220
+ if (
310
221
  loader &&
311
- ((!loader.static && pageProps.loading === true) || __DEV__)
222
+ ((loader.mode !== "static" && pageProps.loading === true) || __DEV__)
312
223
  ) {
313
224
  if (cacheData === null) {
314
- this.loadRouteData(page.config as PageConfigWithLoader, this.state)
225
+ this.loadRouteData(
226
+ page.config as PageConfigWithLoader,
227
+ pageProps,
228
+ this.state
229
+ )
315
230
  } else {
316
231
  nextIdle(() => {
317
- handleStateTransition(transition, transitionId, () => {
318
- this.currentPageProps.value = {
319
- data: cacheData.value,
320
- error: null,
321
- loading: false,
322
- }
232
+ const props = {
233
+ ...pageProps,
234
+ data: cacheData.value,
235
+ error: null,
236
+ loading: false,
237
+ }
238
+ // @ts-ignore
239
+ const transition = loader.transition ?? this.enableTransitions
240
+ handleStateTransition(this.state.signal, transition, () => {
241
+ this.currentPageProps.value = props
323
242
  })
324
243
  })
325
244
  }
326
245
  }
246
+ } else {
247
+ this.pages = formatViteImportMap(
248
+ pages as ViteImportMap,
249
+ normalizedDir,
250
+ normalizedBaseUrl
251
+ )
252
+
253
+ this.layouts = formatViteImportMap(
254
+ layouts as ViteImportMap,
255
+ normalizedDir,
256
+ normalizedBaseUrl
257
+ )
258
+ if (__DEV__) {
259
+ validateRoutes(this.pages)
260
+ }
261
+ this.loadRoute()
327
262
  }
328
263
 
329
264
  window.history.scrollRestoration = "manual"
@@ -362,74 +297,22 @@ export class FileRouterController {
362
297
  window.history.scrollRestoration = "auto"
363
298
  })
364
299
 
365
- let ignorePopState = false
366
-
367
300
  window.addEventListener("popstate", (e) => {
368
301
  e.preventDefault()
369
-
370
- if (
371
- !ignorePopState &&
372
- this.onBeforeLeave(window.location.pathname) === false
373
- ) {
374
- ignorePopState = true
375
- if (e.state !== null) {
376
- if (e.state.index > this.historyIndex) {
377
- window.history.go(-1)
378
- } else if (e.state.index < this.historyIndex) {
379
- window.history.go(1)
380
- }
381
- }
382
- return
383
- }
384
- if (ignorePopState) {
385
- ignorePopState = false
386
- return
387
- }
388
-
389
302
  scrollStack.replace(this.historyIndex, window.scrollX, window.scrollY)
390
303
 
391
- // prep 'on painted' callback for scroll-to-offset action
392
- // this will fire once the page has rendered but before (loader?) kicks off.
393
- let onPaint
394
- if (e.state != null) {
395
- onPaint = () => {
304
+ this.loadRoute().then(() => {
305
+ if (e.state != null) {
396
306
  this.historyIndex = e.state.index
397
307
  const offset = scrollStack.getItem(e.state.index)
398
308
  if (offset !== undefined) {
399
309
  window.scrollTo(...offset)
400
310
  }
401
311
  }
402
- }
403
-
404
- this.loadRoute({ onPaint })
312
+ })
405
313
  })
406
314
  }
407
315
 
408
- private onBeforeLeave(to: string) {
409
- const currentPage = this.currentPage.peek()
410
- if (!currentPage) {
411
- return true
412
- }
413
-
414
- let config = currentPage.config ?? ({} as PageConfig)
415
- if (__DEV__) {
416
- if (this.pageRouteToConfig?.has(currentPage.route)) {
417
- config = this.pageRouteToConfig.get(currentPage.route)!
418
- }
419
- }
420
-
421
- const onBeforeLeave = config.hooks?.onBeforeLeave
422
- if (onBeforeLeave) {
423
- return runBeforeLeaveHooks(
424
- toArray(onBeforeLeave),
425
- { ...requestContext.current },
426
- to,
427
- this.state.pathname
428
- )
429
- }
430
- return true
431
- }
432
-
433
316
  public getChildren() {
434
317
  const page = this.currentPage.value
435
318
  if (!page) return null
@@ -450,14 +333,12 @@ export class FileRouterController {
450
333
  fileRouterInstance.current = null
451
334
  }
452
335
 
453
- private async loadRoute(options?: LoadRouteOptions): Promise<void> {
454
- const {
455
- transition: enableTransition = this.enableTransitions,
456
- isStatic404 = false,
457
- path = window.location.pathname,
458
- onPaint,
459
- } = options ?? {}
460
-
336
+ private async loadRoute(
337
+ path: string = window.location.pathname,
338
+ props: Record<string, unknown> = {},
339
+ enableTransition = this.enableTransitions,
340
+ isStatic404 = false
341
+ ): Promise<void> {
461
342
  this.abortController?.abort()
462
343
  const signal = (this.abortController = new AbortController()).signal
463
344
 
@@ -482,38 +363,10 @@ See https://kirujs.dev/docs/api/file-router#404 for more information.`
482
363
 
483
364
  const { route, pageEntry, params, routeSegments } = routeMatch
484
365
 
485
- // Apply beforeEach guards before loading route
486
- const guardEntries = matchModules(
487
- this.guards as unknown as FormattedViteImportMap,
488
- routeSegments
489
- )
490
- const guardModules = await Promise.all(
491
- guardEntries.map(
492
- (entry) => entry.load() as unknown as Promise<GuardModule>
493
- )
494
- )
495
-
496
- const fromPath = this.state.pathname
497
- const redirectPath = await runBeforeEachGuards(
498
- guardModules,
499
- { ...requestContext.current },
500
- path,
501
- fromPath
502
- )
503
-
504
- // If redirect was requested, navigate to that path instead
505
- if (redirectPath !== null) {
506
- this.state.pathname = path
507
- return this.navigate(redirectPath, {
508
- replace: true,
509
- transition: enableTransition,
510
- })
511
- }
512
-
513
366
  fileRouterRoute.current = route
514
367
  const pagePromise = pageEntry.load()
515
368
 
516
- const layoutPromises = matchModules(this.layouts, routeSegments).map(
369
+ const layoutPromises = matchLayouts(this.layouts, routeSegments).map(
517
370
  (layoutEntry) => layoutEntry.load()
518
371
  )
519
372
 
@@ -547,77 +400,64 @@ See https://kirujs.dev/docs/api/file-router#404 for more information.`
547
400
  }
548
401
  }
549
402
 
550
- const { loader, hooks } = config
403
+ const { loader } = config
551
404
 
552
- if (hooks?.onBeforeEnter) {
553
- const redirectPath = await runBeforeEnterHooks(
554
- toArray(hooks.onBeforeEnter),
555
- requestContext,
556
- path,
557
- fromPath
558
- )
559
- if (redirectPath !== null) {
560
- this.state.pathname = path
561
- return this.navigate(redirectPath, {
562
- replace: true,
563
- transition: enableTransition,
564
- })
565
- }
566
- }
567
-
568
- let props: Record<string, unknown> = {}
569
- if (!!loader) {
570
- props = {
571
- data: null,
572
- error: null,
573
- loading: true,
574
- }
575
- }
576
-
577
- if (loader?.static && !__DEV__) {
578
- const staticProps = page.__KIRU_STATIC_PROPS__?.[path]
579
- if (!staticProps) {
580
- // 404
581
- return this.loadRoute({
582
- path,
583
- transition: enableTransition,
584
- isStatic404: true,
585
- })
586
- }
587
- const { data, error } = staticProps
588
- props = error
589
- ? {
590
- data: null,
591
- error: new FileRouterDataLoadError(error),
592
- loading: false,
405
+ if (loader) {
406
+ if (loader.mode !== "static" || __DEV__) {
407
+ // Check cache first if caching is enabled
408
+ let cachedData = null
409
+ if (loader.mode !== "static" && loader.cache) {
410
+ const cacheKey: CacheKey = {
411
+ path: routerState.pathname,
412
+ params: routerState.params,
413
+ query: routerState.query,
593
414
  }
594
- : {
595
- data: data,
415
+ cachedData = routerCache.current!.get(cacheKey, loader.cache)
416
+ }
417
+
418
+ if (cachedData !== null) {
419
+ // Use cached data immediately - no loading state needed
420
+ props = {
421
+ ...props,
422
+ data: cachedData.value,
596
423
  error: null,
597
424
  loading: false,
598
- }
599
- } else if (!loader?.static && loader?.cache) {
600
- const cacheKey: CacheKey = {
601
- path: routerState.pathname,
602
- params: routerState.params,
603
- query: routerState.query,
604
- }
605
- const cachedData = routerCache.current!.get(cacheKey, loader.cache)
425
+ } satisfies PageProps<PageConfig<unknown>>
426
+ } else {
427
+ // No cached data - show loading state and load data
428
+ props = {
429
+ ...props,
430
+ loading: true,
431
+ data: null,
432
+ error: null,
433
+ } satisfies PageProps<PageConfig<unknown>>
606
434
 
607
- if (cachedData !== null) {
435
+ this.loadRouteData(
436
+ config as PageConfigWithLoader,
437
+ props,
438
+ routerState,
439
+ enableTransition
440
+ )
441
+ }
442
+ } else {
443
+ const staticProps = page.__KIRU_STATIC_PROPS__?.[path]
444
+ if (!staticProps) {
445
+ return this.loadRoute(path, props, enableTransition, true)
446
+ }
447
+
448
+ const { data, error } = staticProps
608
449
  props = {
609
- data: cachedData.value,
610
- error: null,
450
+ ...props,
451
+ data: data,
452
+ error: error ? new FileRouterDataLoadError(error) : null,
611
453
  loading: false,
612
- } satisfies PageProps<PageConfig<unknown>>
454
+ } as PageProps<PageConfig<unknown>>
613
455
  }
614
456
  }
615
457
 
616
- // loader transition must use the same id as page transition in order to prevent skipping it.
617
- let tId = transitionId++
618
-
619
- return await handleStateTransition(enableTransition, tId, () => {
458
+ return handleStateTransition(signal, enableTransition, () => {
620
459
  this.state = routerState
460
+ this.contextValue = this.createContextValue()
621
461
  this.currentPage.value = {
622
462
  component: page.default,
623
463
  config,
@@ -627,25 +467,6 @@ See https://kirujs.dev/docs/api/file-router#404 for more information.`
627
467
  this.currentLayouts.value = layouts
628
468
  .filter((m) => typeof m.default === "function")
629
469
  .map((m) => m.default)
630
-
631
- nextIdle(() => {
632
- runAfterEachGuards(
633
- guardModules,
634
- { ...requestContext.current },
635
- path,
636
- fromPath
637
- )
638
- if (props.loading) {
639
- this.loadRouteData(
640
- config as PageConfigWithLoader,
641
- routerState,
642
- enableTransition,
643
- tId
644
- ).then(() => signal.aborted || onPaint?.())
645
- } else {
646
- onPaint?.()
647
- }
648
- })
649
470
  })
650
471
  } catch (error) {
651
472
  console.error("[kiru/router]: Failed to load route component:", error)
@@ -655,19 +476,19 @@ See https://kirujs.dev/docs/api/file-router#404 for more information.`
655
476
 
656
477
  private async loadRouteData(
657
478
  config: PageConfigWithLoader,
479
+ props: Record<string, unknown>,
658
480
  routerState: RouterState,
659
- enableTransition = this.enableTransitions,
660
- id = transitionId
481
+ enableTransition = this.enableTransitions
661
482
  ) {
662
483
  const { loader } = config
663
484
 
664
485
  // Load data from loader (cache check is now done earlier in loadRoute)
665
- return loader
666
- .load({ ...routerState, context: { ...requestContext.current } })
486
+ loader
487
+ .load(routerState)
667
488
  .then(
668
489
  (data) => {
669
490
  // Cache the data if caching is enabled
670
- if (!loader.static && loader.cache) {
491
+ if (loader.mode !== "static" && loader.cache) {
671
492
  const cacheKey: CacheKey = {
672
493
  path: routerState.pathname,
673
494
  params: routerState.params,
@@ -687,19 +508,20 @@ See https://kirujs.dev/docs/api/file-router#404 for more information.`
687
508
  data: null,
688
509
  error: new FileRouterDataLoadError(error),
689
510
  loading: false,
690
- } satisfies PageProps<PageConfig<unknown>>)
511
+ }) satisfies PageProps<PageConfig<unknown>>
691
512
  )
692
513
  .then((state) => {
693
514
  if (routerState.signal.aborted) return
694
515
 
695
516
  const transition =
696
- (!loader.static && loader.transition) ?? enableTransition
517
+ (loader.mode !== "static" && loader.transition) ?? enableTransition
697
518
 
698
- return handleStateTransition(
699
- transition,
700
- id,
701
- () => (this.currentPageProps.value = state)
702
- )
519
+ handleStateTransition(routerState.signal, transition, () => {
520
+ this.currentPageProps.value = {
521
+ ...props,
522
+ ...state,
523
+ } satisfies PageProps<PageConfig<unknown>>
524
+ })
703
525
  })
704
526
  }
705
527
 
@@ -727,17 +549,17 @@ See https://kirujs.dev/docs/api/file-router#404 for more information.`
727
549
  const url = new URL(path, "http://localhost")
728
550
  const { hash: nextHash, pathname: nextPath } = url
729
551
  const { hash: prevHash, pathname: prevPath } = this.state
730
- if (
731
- (nextHash === prevHash && nextPath === prevPath) ||
732
- this.onBeforeLeave(prevPath) === false
733
- ) {
552
+ if (nextHash === prevHash && nextPath === prevPath) {
734
553
  return
735
554
  }
736
555
 
737
556
  this.updateHistoryState(path, options)
738
557
 
739
- const transition = options?.transition ?? this.enableTransitions
740
- this.loadRoute({ transition }).then(() => {
558
+ this.loadRoute(
559
+ void 0,
560
+ void 0,
561
+ options?.transition ?? this.enableTransitions
562
+ ).then(() => {
741
563
  if (nextHash !== prevHash) {
742
564
  window.dispatchEvent(new HashChangeEvent("hashchange"))
743
565
  }
@@ -764,7 +586,7 @@ See https://kirujs.dev/docs/api/file-router#404 for more information.`
764
586
  const { pageEntry, route } = routeMatch
765
587
  fileRouterRoute.current = route
766
588
  const pagePromise = pageEntry.load()
767
- const layoutPromises = matchModules(this.layouts, route.split("/")).map(
589
+ const layoutPromises = matchLayouts(this.layouts, route.split("/")).map(
768
590
  (layoutEntry) => layoutEntry.load()
769
591
  )
770
592
  await Promise.all([pagePromise, ...layoutPromises])
@@ -810,15 +632,13 @@ See https://kirujs.dev/docs/api/file-router#404 for more information.`
810
632
  path
811
633
  )
812
634
  } else {
813
- const current = scrollStack.get()
814
-
815
635
  // if we've gone back and are now going forward, we need to
816
636
  // truncate the scroll stack so it doesn't just permanently grow.
817
637
  // this should keep it at the same length as the history stack.
638
+ const current = scrollStack.get()
818
639
  if (this.historyIndex < window.history.length - 1) {
819
640
  current.length = this.historyIndex
820
641
  }
821
-
822
642
  scrollStack.save([...current, [window.scrollX, window.scrollY]])
823
643
  window.history.pushState(
824
644
  { ...window.history.state, index: ++this.historyIndex },
@@ -827,6 +647,32 @@ See https://kirujs.dev/docs/api/file-router#404 for more information.`
827
647
  )
828
648
  }
829
649
  }
650
+ private createContextValue() {
651
+ const __this = this
652
+ return {
653
+ get baseUrl() {
654
+ return __this.baseUrl
655
+ },
656
+ invalidate: async (...paths: string[]) => {
657
+ if (this.invalidate(...paths)) {
658
+ return this.loadRoute(void 0, void 0, true)
659
+ }
660
+ },
661
+ get state() {
662
+ return { ...__this.state }
663
+ },
664
+ navigate: this.navigate.bind(this),
665
+ prefetchRouteModules: this.prefetchRouteModules.bind(this),
666
+ reload: async (options?: ReloadOptions) => {
667
+ if (options?.invalidate ?? true) {
668
+ this.invalidate(this.state.pathname)
669
+ }
670
+ return this.loadRoute(void 0, void 0, options?.transition)
671
+ },
672
+ setQuery: this.setQuery.bind(this),
673
+ setHash: this.setHash.bind(this),
674
+ }
675
+ }
830
676
  }
831
677
 
832
678
  function buildQueryString(
@@ -848,32 +694,23 @@ function buildQueryString(
848
694
  }
849
695
 
850
696
  async function handleStateTransition(
697
+ signal: AbortSignal,
851
698
  enableTransition: boolean,
852
- id: number,
853
699
  callback: () => void
854
700
  ) {
855
- if (currentTransition) {
856
- const { id: currentId, transition } = currentTransition
857
- // for cross-page navigations, we skip any existing transitions.
858
- // otherwise (eg. loaders), we wait for the existing transition to finish
859
- if (id !== currentId) {
860
- transition.skipTransition()
861
- }
862
- await transition.finished
863
- }
864
701
  if (!enableTransition || typeof document.startViewTransition !== "function") {
865
702
  return new Promise<void>((resolve) => {
866
703
  callback()
867
704
  nextIdle(resolve)
868
705
  })
869
706
  }
870
- const transition = document.startViewTransition(() => {
707
+ const vt = document.startViewTransition(() => {
871
708
  callback()
872
709
  flushSync()
873
710
  })
874
- currentTransition = { id, transition }
875
- await transition.finished
876
- currentTransition = null
711
+
712
+ signal.addEventListener("abort", () => vt.skipTransition())
713
+ await vt.ready
877
714
  }
878
715
 
879
716
  function validateRoutes(pageMap: FormattedViteImportMap) {