one 1.10.1 → 1.10.3

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 (189) hide show
  1. package/devtools/source-inspector.mjs +157 -25
  2. package/dist/cjs/cli/build.cjs +3 -5
  3. package/dist/cjs/cli/build.js +3 -5
  4. package/dist/cjs/cli/build.js.map +1 -1
  5. package/dist/cjs/cli/build.native.js +3 -5
  6. package/dist/cjs/cli/build.native.js.map +1 -1
  7. package/dist/cjs/cli/buildPage.cjs +8 -8
  8. package/dist/cjs/cli/buildPage.js +5 -6
  9. package/dist/cjs/cli/buildPage.js.map +1 -1
  10. package/dist/cjs/cli/buildPage.native.js +6 -8
  11. package/dist/cjs/cli/buildPage.native.js.map +1 -1
  12. package/dist/cjs/cli/daemon.cjs +2 -4
  13. package/dist/cjs/cli/daemon.js +2 -2
  14. package/dist/cjs/cli/daemon.js.map +1 -1
  15. package/dist/cjs/cli/daemon.native.js +2 -4
  16. package/dist/cjs/cli/daemon.native.js.map +1 -1
  17. package/dist/cjs/createHandleRequest.cjs +2 -2
  18. package/dist/cjs/createHandleRequest.js +2 -2
  19. package/dist/cjs/createHandleRequest.js.map +1 -1
  20. package/dist/cjs/createHandleRequest.native.js +2 -2
  21. package/dist/cjs/createHandleRequest.native.js.map +1 -1
  22. package/dist/cjs/daemon/server.cjs +8 -14
  23. package/dist/cjs/daemon/server.js +8 -14
  24. package/dist/cjs/daemon/server.js.map +1 -1
  25. package/dist/cjs/daemon/server.native.js +10 -16
  26. package/dist/cjs/daemon/server.native.js.map +1 -1
  27. package/dist/cjs/daemon/tui.cjs +7 -10
  28. package/dist/cjs/daemon/tui.js +12 -24
  29. package/dist/cjs/daemon/tui.js.map +1 -1
  30. package/dist/cjs/daemon/tui.native.js +5 -7
  31. package/dist/cjs/daemon/tui.native.js.map +1 -1
  32. package/dist/cjs/router/getRoutes.cjs +2 -2
  33. package/dist/cjs/router/getRoutes.js +2 -2
  34. package/dist/cjs/router/getRoutes.js.map +1 -1
  35. package/dist/cjs/router/getRoutes.native.js +2 -2
  36. package/dist/cjs/router/getRoutes.native.js.map +1 -1
  37. package/dist/cjs/router/interceptRoutes.cjs +5 -8
  38. package/dist/cjs/router/interceptRoutes.js +5 -9
  39. package/dist/cjs/router/interceptRoutes.js.map +1 -1
  40. package/dist/cjs/router/interceptRoutes.native.js +5 -8
  41. package/dist/cjs/router/interceptRoutes.native.js.map +1 -1
  42. package/dist/cjs/router/router.js.map +1 -1
  43. package/dist/cjs/router/router.native.js.map +1 -1
  44. package/dist/cjs/server/oneServe.cjs +41 -57
  45. package/dist/cjs/server/oneServe.js +41 -45
  46. package/dist/cjs/server/oneServe.js.map +1 -1
  47. package/dist/cjs/server/oneServe.native.js +54 -73
  48. package/dist/cjs/server/oneServe.native.js.map +1 -1
  49. package/dist/cjs/useLoader.cjs +5 -10
  50. package/dist/cjs/useLoader.js +10 -16
  51. package/dist/cjs/useLoader.js.map +1 -1
  52. package/dist/cjs/useLoader.native.js +5 -10
  53. package/dist/cjs/useLoader.native.js.map +1 -1
  54. package/dist/cjs/utils/redirect.cjs +1 -1
  55. package/dist/cjs/utils/redirect.js +1 -1
  56. package/dist/cjs/utils/redirect.js.map +1 -1
  57. package/dist/cjs/utils/redirect.native.js +1 -1
  58. package/dist/cjs/utils/redirect.native.js.map +1 -1
  59. package/dist/cjs/views/Navigator.cjs +1 -1
  60. package/dist/cjs/views/Navigator.js +1 -1
  61. package/dist/cjs/views/Navigator.js.map +1 -1
  62. package/dist/cjs/views/Navigator.native.js +1 -3
  63. package/dist/cjs/views/Navigator.native.js.map +1 -1
  64. package/dist/cjs/vite/one.cjs +1 -19
  65. package/dist/cjs/vite/one.js +1 -20
  66. package/dist/cjs/vite/one.js.map +1 -1
  67. package/dist/cjs/vite/one.native.js +1 -17
  68. package/dist/cjs/vite/one.native.js.map +1 -1
  69. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.cjs +2 -7
  70. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.js +3 -7
  71. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.js.map +1 -1
  72. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js +2 -44
  73. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js.map +1 -1
  74. package/dist/esm/cli/build.js +3 -5
  75. package/dist/esm/cli/build.js.map +1 -1
  76. package/dist/esm/cli/build.mjs +3 -5
  77. package/dist/esm/cli/build.mjs.map +1 -1
  78. package/dist/esm/cli/build.native.js +3 -5
  79. package/dist/esm/cli/build.native.js.map +1 -1
  80. package/dist/esm/cli/buildPage.js +5 -6
  81. package/dist/esm/cli/buildPage.js.map +1 -1
  82. package/dist/esm/cli/buildPage.mjs +8 -8
  83. package/dist/esm/cli/buildPage.mjs.map +1 -1
  84. package/dist/esm/cli/buildPage.native.js +6 -8
  85. package/dist/esm/cli/buildPage.native.js.map +1 -1
  86. package/dist/esm/cli/daemon.js +2 -2
  87. package/dist/esm/cli/daemon.js.map +1 -1
  88. package/dist/esm/cli/daemon.mjs +2 -4
  89. package/dist/esm/cli/daemon.mjs.map +1 -1
  90. package/dist/esm/cli/daemon.native.js +2 -4
  91. package/dist/esm/cli/daemon.native.js.map +1 -1
  92. package/dist/esm/createHandleRequest.js +2 -2
  93. package/dist/esm/createHandleRequest.js.map +1 -1
  94. package/dist/esm/createHandleRequest.mjs +2 -2
  95. package/dist/esm/createHandleRequest.mjs.map +1 -1
  96. package/dist/esm/createHandleRequest.native.js +2 -2
  97. package/dist/esm/createHandleRequest.native.js.map +1 -1
  98. package/dist/esm/daemon/server.js +8 -14
  99. package/dist/esm/daemon/server.js.map +1 -1
  100. package/dist/esm/daemon/server.mjs +8 -14
  101. package/dist/esm/daemon/server.mjs.map +1 -1
  102. package/dist/esm/daemon/server.native.js +10 -16
  103. package/dist/esm/daemon/server.native.js.map +1 -1
  104. package/dist/esm/daemon/tui.js +12 -24
  105. package/dist/esm/daemon/tui.js.map +1 -1
  106. package/dist/esm/daemon/tui.mjs +7 -10
  107. package/dist/esm/daemon/tui.mjs.map +1 -1
  108. package/dist/esm/daemon/tui.native.js +6 -8
  109. package/dist/esm/daemon/tui.native.js.map +1 -1
  110. package/dist/esm/router/getRoutes.js +2 -2
  111. package/dist/esm/router/getRoutes.js.map +1 -1
  112. package/dist/esm/router/getRoutes.mjs +2 -2
  113. package/dist/esm/router/getRoutes.mjs.map +1 -1
  114. package/dist/esm/router/getRoutes.native.js +2 -2
  115. package/dist/esm/router/getRoutes.native.js.map +1 -1
  116. package/dist/esm/router/interceptRoutes.js +5 -9
  117. package/dist/esm/router/interceptRoutes.js.map +1 -1
  118. package/dist/esm/router/interceptRoutes.mjs +6 -8
  119. package/dist/esm/router/interceptRoutes.mjs.map +1 -1
  120. package/dist/esm/router/interceptRoutes.native.js +6 -8
  121. package/dist/esm/router/interceptRoutes.native.js.map +1 -1
  122. package/dist/esm/router/router.js.map +1 -1
  123. package/dist/esm/router/router.mjs.map +1 -1
  124. package/dist/esm/router/router.native.js.map +1 -1
  125. package/dist/esm/server/oneServe.js +41 -45
  126. package/dist/esm/server/oneServe.js.map +1 -1
  127. package/dist/esm/server/oneServe.mjs +41 -57
  128. package/dist/esm/server/oneServe.mjs.map +1 -1
  129. package/dist/esm/server/oneServe.native.js +54 -73
  130. package/dist/esm/server/oneServe.native.js.map +1 -1
  131. package/dist/esm/useLoader.js +10 -16
  132. package/dist/esm/useLoader.js.map +1 -1
  133. package/dist/esm/useLoader.mjs +5 -10
  134. package/dist/esm/useLoader.mjs.map +1 -1
  135. package/dist/esm/useLoader.native.js +5 -10
  136. package/dist/esm/useLoader.native.js.map +1 -1
  137. package/dist/esm/utils/redirect.js +1 -1
  138. package/dist/esm/utils/redirect.js.map +1 -1
  139. package/dist/esm/utils/redirect.mjs +1 -1
  140. package/dist/esm/utils/redirect.mjs.map +1 -1
  141. package/dist/esm/utils/redirect.native.js +1 -1
  142. package/dist/esm/utils/redirect.native.js.map +1 -1
  143. package/dist/esm/views/Navigator.js +1 -1
  144. package/dist/esm/views/Navigator.js.map +1 -1
  145. package/dist/esm/views/Navigator.mjs +1 -1
  146. package/dist/esm/views/Navigator.mjs.map +1 -1
  147. package/dist/esm/views/Navigator.native.js +1 -3
  148. package/dist/esm/views/Navigator.native.js.map +1 -1
  149. package/dist/esm/vite/one.js +1 -20
  150. package/dist/esm/vite/one.js.map +1 -1
  151. package/dist/esm/vite/one.mjs +1 -19
  152. package/dist/esm/vite/one.mjs.map +1 -1
  153. package/dist/esm/vite/one.native.js +1 -17
  154. package/dist/esm/vite/one.native.js.map +1 -1
  155. package/dist/esm/vite/plugins/fileSystemRouterPlugin.js +3 -7
  156. package/dist/esm/vite/plugins/fileSystemRouterPlugin.js.map +1 -1
  157. package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs +2 -7
  158. package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs.map +1 -1
  159. package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js +2 -44
  160. package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js.map +1 -1
  161. package/package.json +9 -9
  162. package/src/cli/build.ts +3 -20
  163. package/src/cli/buildPage.ts +5 -7
  164. package/src/cli/daemon.ts +2 -2
  165. package/src/createHandleRequest.ts +2 -1
  166. package/src/daemon/server.ts +11 -23
  167. package/src/daemon/tui.ts +11 -29
  168. package/src/router/getRoutes.ts +3 -4
  169. package/src/router/interceptRoutes.ts +5 -30
  170. package/src/router/router.ts +0 -3
  171. package/src/server/oneServe.ts +67 -108
  172. package/src/useLoader.ts +17 -27
  173. package/src/utils/redirect.ts +1 -1
  174. package/src/views/Navigator.tsx +1 -3
  175. package/src/vite/one.ts +2 -100
  176. package/src/vite/plugins/fileSystemRouterPlugin.tsx +3 -50
  177. package/types/cli/build.d.ts.map +1 -1
  178. package/types/createHandleRequest.d.ts.map +1 -1
  179. package/types/daemon/server.d.ts.map +1 -1
  180. package/types/daemon/tui.d.ts.map +1 -1
  181. package/types/router/getRoutes.d.ts.map +1 -1
  182. package/types/router/interceptRoutes.d.ts +0 -1
  183. package/types/router/interceptRoutes.d.ts.map +1 -1
  184. package/types/router/router.d.ts.map +1 -1
  185. package/types/server/oneServe.d.ts.map +1 -1
  186. package/types/useLoader.d.ts.map +1 -1
  187. package/types/views/Navigator.d.ts.map +1 -1
  188. package/types/vite/one.d.ts.map +1 -1
  189. package/types/vite/plugins/fileSystemRouterPlugin.d.ts.map +1 -1
package/src/daemon/tui.ts CHANGED
@@ -52,6 +52,14 @@ interface TUIState {
52
52
  popup: { message: string; timeout: NodeJS.Timeout } | null
53
53
  }
54
54
 
55
+ const CABLE_COLORS = [
56
+ colors.green,
57
+ colors.cyan,
58
+ colors.magenta,
59
+ colors.blue,
60
+ colors.yellow,
61
+ ]
62
+
55
63
  const ESC = '\x1b'
56
64
  const CSI = `${ESC}[`
57
65
 
@@ -447,8 +455,6 @@ async function refreshData(): Promise<void> {
447
455
  tuiState.simulators = newSims
448
456
  tuiState.servers = newServers
449
457
 
450
- const isDragging = tuiState.draggingSimIndex !== null
451
-
452
458
  // get actual simulator -> server mappings from routing state
453
459
  const simMappings = getSimulatorMappings()
454
460
 
@@ -558,15 +564,7 @@ function render(): void {
558
564
  const isSelected = tuiState.selectedCol === 0 && tuiState.selectedRow === row
559
565
  const cable = tuiState.cables.get(row)
560
566
  const hasConnection = cable?.serverIndex !== null
561
- // unique color per cable
562
- const cableColors = [
563
- colors.green,
564
- colors.cyan,
565
- colors.magenta,
566
- colors.blue,
567
- colors.yellow,
568
- ]
569
- const cableColor = cableColors[row % cableColors.length]
567
+ const cableColor = CABLE_COLORS[row % CABLE_COLORS.length]
570
568
  const plug = hasConnection ? cableColor('●') : colors.dim('○')
571
569
  const name = truncate(sim.name, simEndX - 5)
572
570
  simText = `${name} ${plug}`
@@ -591,18 +589,10 @@ function render(): void {
591
589
  let srvRight = ''
592
590
  if (server) {
593
591
  const isSelected = tuiState.selectedCol === 1 && tuiState.selectedRow === row
594
- // check which cables are connected to this server and get their colors
595
- const cableColors = [
596
- colors.green,
597
- colors.cyan,
598
- colors.magenta,
599
- colors.blue,
600
- colors.yellow,
601
- ]
602
592
  let connectedColor: ((s: string) => string) | null = null
603
593
  for (const [simIndex, cable] of tuiState.cables) {
604
594
  if (cable.serverIndex === row) {
605
- connectedColor = cableColors[simIndex % cableColors.length]
595
+ connectedColor = CABLE_COLORS[simIndex % CABLE_COLORS.length]
606
596
  break
607
597
  }
608
598
  }
@@ -695,15 +685,7 @@ function getCableCharAt(x: number, y: number): string | null {
695
685
 
696
686
  if (px === x && py === y) {
697
687
  const connected = cable.serverIndex !== null
698
- // unique color per cable based on sim index
699
- const cableColors = [
700
- colors.green,
701
- colors.cyan,
702
- colors.magenta,
703
- colors.blue,
704
- colors.yellow,
705
- ]
706
- const baseColor = cableColors[simIndex % cableColors.length]
688
+ const baseColor = CABLE_COLORS[simIndex % CABLE_COLORS.length]
707
689
  const color = connected ? baseColor : colors.dim
708
690
 
709
691
  // determine character based on curve direction
@@ -536,7 +536,7 @@ function getFileMeta(
536
536
  const isLayout = filenameWithoutExtensions.startsWith('_layout')
537
537
  const isMiddleware = filenameWithoutExtensions.startsWith('_middleware')
538
538
 
539
- const [_fullname, renderModeFound] =
539
+ const [, renderModeFound] =
540
540
  filename.match(/\+(api|ssg|ssr|spa)\.(\w+\.)?[jt]sx?$/) || []
541
541
  const fileRenderMode = renderModeFound as 'api' | One.RouteRenderMode | undefined
542
542
 
@@ -656,6 +656,7 @@ function getFileMeta(
656
656
  }
657
657
 
658
658
  function getMostSpecific(routes: RouteNode[]) {
659
+ // holey array: .length includes holes, so last element is most specific
659
660
  const route = routes[routes.length - 1]
660
661
 
661
662
  if (!routes[0]) {
@@ -664,9 +665,7 @@ function getMostSpecific(routes: RouteNode[]) {
664
665
  )
665
666
  }
666
667
 
667
- // This works even tho routes is holey array (e.g it might have index 0 and 2 but not 1)
668
- // `.length` includes the holes in its count
669
- return routes[routes.length - 1]
668
+ return route
670
669
  }
671
670
 
672
671
  export function getIgnoreList(options?: Options) {
@@ -27,10 +27,6 @@ export function isHardNavigation(): boolean {
27
27
  return navigationMode === 'hard'
28
28
  }
29
29
 
30
- export function isSoftNavigation(): boolean {
31
- return navigationMode === 'soft'
32
- }
33
-
34
30
  // ============================================
35
31
  // Intercept Route Matching
36
32
  // ============================================
@@ -241,8 +237,7 @@ function findMatchingInterceptInSlot(
241
237
  const resolvedTargetPath = resolveInterceptTargetPath(
242
238
  interceptTargetPath,
243
239
  levels,
244
- layoutNode,
245
- currentPath
240
+ layoutNode
246
241
  )
247
242
 
248
243
  // Try to match the target path against the resolved intercept path
@@ -273,24 +268,11 @@ function findMatchingInterceptInSlot(
273
268
  function resolveInterceptTargetPath(
274
269
  interceptTargetPath: string,
275
270
  levels: number,
276
- layoutNode: RouteNode,
277
- currentPath: string
271
+ layoutNode: RouteNode
278
272
  ): string {
279
- // Get the base path for resolution
280
- // contextKey is like './app/_layout.tsx' or './_layout.tsx'
281
- // We need to extract just the route path portion
282
- let layoutPath = layoutNode.contextKey
283
- .replace(/^\.\//, '') // Remove leading ./
284
- .replace(/\/?_layout.*$/, '') // Remove _layout and extension (with optional leading /)
285
- .replace(/^app\/?/, '') // Remove 'app/' prefix since routes are relative to app dir
286
-
287
- // Strip route groups like (app), (tabs) since they don't appear in URLs
288
- layoutPath = stripGroupSegmentsFromPath(layoutPath)
289
-
290
- // Normalize: empty string or '/' means root
291
- if (!layoutPath || layoutPath === '/') {
292
- layoutPath = ''
293
- }
273
+ // reuse getLayoutPath and strip leading slash for internal resolution
274
+ const fullLayoutPath = getLayoutPath(layoutNode)
275
+ let layoutPath = fullLayoutPath === '/' ? '' : fullLayoutPath.slice(1)
294
276
 
295
277
  if (levels === Infinity) {
296
278
  // (...) matches from root - target path is absolute
@@ -468,13 +450,6 @@ export function isReturningFromIntercept(): boolean {
468
450
  return returningFromIntercept
469
451
  }
470
452
 
471
- // Store intercept state info for forward navigation restoration
472
- interface StoredInterceptState {
473
- slotName: string
474
- routeContextKey: string
475
- params: Record<string, string>
476
- }
477
-
478
453
  // Callback to set slot state - set from Navigator.tsx to avoid circular deps
479
454
  let setSlotStateCallback:
480
455
  | ((
@@ -475,7 +475,6 @@ export function subscribeToStore(subscriber: () => void) {
475
475
  }
476
476
  }
477
477
 
478
- // Subscription functions
479
478
  export function subscribeToLoadingState(subscriber: OneRouter.LoadingStateListener) {
480
479
  loadingStateSubscribers.add(subscriber)
481
480
  return () => {
@@ -1163,8 +1162,6 @@ export async function linkTo(
1163
1162
  }, 1000)
1164
1163
  }
1165
1164
  })
1166
-
1167
- return
1168
1165
  }
1169
1166
 
1170
1167
  const hashes: Record<string, string> = {}
@@ -22,6 +22,19 @@ import { getFetchStaticHtml } from './staticHtmlFetcher'
22
22
 
23
23
  const debugRouter = process.env.ONE_DEBUG_ROUTER
24
24
 
25
+ async function readStaticHtml(htmlPath: string): Promise<string | null> {
26
+ const fetchStaticHtml = getFetchStaticHtml()
27
+ if (fetchStaticHtml) {
28
+ const html = await fetchStaticHtml(htmlPath)
29
+ if (html) return html
30
+ }
31
+ try {
32
+ return await readFile(join('dist/client', htmlPath), 'utf-8')
33
+ } catch {
34
+ return null
35
+ }
36
+ }
37
+
25
38
  /**
26
39
  * Lazy import functions for route modules.
27
40
  * Modules are loaded on-demand when a route is matched, not all upfront.
@@ -102,6 +115,43 @@ export async function oneServe(
102
115
 
103
116
  const apiCJS = oneOptions.build?.api?.outputFormat === 'cjs'
104
117
 
118
+ // shared helper to import a route module and run its loader
119
+ async function importAndRunLoader(
120
+ routeId: string,
121
+ serverPath: string | undefined,
122
+ lazyKey: string | undefined,
123
+ loaderProps: any
124
+ ): Promise<{ loaderData: unknown; routeId: string; isEnoent?: boolean }> {
125
+ if (!serverPath && !lazyKey) {
126
+ return { loaderData: undefined, routeId }
127
+ }
128
+
129
+ try {
130
+ const pathToResolve = serverPath || lazyKey || ''
131
+ const resolvedPath = pathToResolve.includes('dist/server')
132
+ ? pathToResolve
133
+ : join('./', 'dist/server', pathToResolve)
134
+
135
+ const routeExported = lazyKey
136
+ ? options?.lazyRoutes?.pages?.[lazyKey]
137
+ ? await options.lazyRoutes.pages[lazyKey]()
138
+ : await import(toAbsolute(resolvedPath))
139
+ : await import(toAbsolute(serverPath!))
140
+
141
+ const loaderData = await routeExported?.loader?.(loaderProps)
142
+ return { loaderData, routeId }
143
+ } catch (err) {
144
+ if (isResponse(err)) {
145
+ throw err
146
+ }
147
+ if ((err as any)?.code === 'ENOENT') {
148
+ return { loaderData: undefined, routeId, isEnoent: true }
149
+ }
150
+ console.error(`[one] Error running loader for ${routeId}:`, err)
151
+ return { loaderData: undefined, routeId }
152
+ }
153
+ }
154
+
105
155
  // Lazy load server entry only when needed for SSR
106
156
  let render: ((props: RenderAppProps) => any) | null = null
107
157
  async function getRender() {
@@ -211,57 +261,24 @@ export async function oneServe(
211
261
  }
212
262
 
213
263
  try {
214
- // helper to import and run a single loader
215
- async function runLoader(
216
- routeId: string,
217
- serverPath: string | undefined,
218
- lazyKey?: string
219
- ): Promise<{ loaderData: unknown; routeId: string; isEnoent?: boolean }> {
220
- if (!serverPath && !lazyKey) {
221
- return { loaderData: undefined, routeId }
222
- }
223
-
224
- try {
225
- // serverPath may already include dist/server if it came from buildInfo.serverJsPath
226
- const pathToResolve = serverPath || lazyKey || ''
227
- const resolvedPath = pathToResolve.includes('dist/server')
228
- ? pathToResolve
229
- : join('./', 'dist/server', pathToResolve)
230
-
231
- const routeExported = lazyKey
232
- ? options?.lazyRoutes?.pages?.[lazyKey]
233
- ? await options.lazyRoutes.pages[lazyKey]()
234
- : await import(toAbsolute(resolvedPath))
235
- : await import(toAbsolute(serverPath!))
236
-
237
- const loaderData = await routeExported?.loader?.(loaderProps)
238
- return { loaderData, routeId }
239
- } catch (err) {
240
- // if a loader throws a Response (redirect), re-throw it
241
- if (isResponse(err)) {
242
- throw err
243
- }
244
- if ((err as any)?.code === 'ENOENT') {
245
- return { loaderData: undefined, routeId, isEnoent: true }
246
- }
247
- console.error(`[one] Error running loader for ${routeId}:`, err)
248
- return { loaderData: undefined, routeId }
249
- }
250
- }
251
-
252
264
  // collect layout loaders to run in parallel
253
265
  const layoutRoutes = route.layouts || []
254
266
  const layoutLoaderPromises = layoutRoutes.map((layout: any) => {
255
- // layouts may have loaderServerPath set from build, or we can try contextKey
256
267
  const serverPath = layout.loaderServerPath || layout.contextKey
257
- return runLoader(layout.contextKey, serverPath, layout.contextKey)
268
+ return importAndRunLoader(
269
+ layout.contextKey,
270
+ serverPath,
271
+ layout.contextKey,
272
+ loaderProps
273
+ )
258
274
  })
259
275
 
260
276
  // run page loader
261
- const pageLoaderPromise = runLoader(
277
+ const pageLoaderPromise = importAndRunLoader(
262
278
  route.file,
263
279
  buildInfo.serverJsPath,
264
- route.file
280
+ route.file,
281
+ loaderProps
265
282
  )
266
283
 
267
284
  // wait for all loaders in parallel
@@ -375,43 +392,16 @@ url: ${url}`)
375
392
 
376
393
  if (needsSpaShell) {
377
394
  try {
378
- // helper to import and run a single loader
379
- async function runShellLoader(
380
- routeId: string,
381
- serverPath: string | undefined,
382
- lazyKey?: string
383
- ): Promise<{ loaderData: unknown; routeId: string }> {
384
- if (!serverPath && !lazyKey) {
385
- return { loaderData: undefined, routeId }
386
- }
387
- try {
388
- const pathToResolve = serverPath || lazyKey || ''
389
- const resolvedPath = pathToResolve.includes('dist/server')
390
- ? pathToResolve
391
- : join('./', 'dist/server', pathToResolve)
392
-
393
- const routeExported = lazyKey
394
- ? options?.lazyRoutes?.pages?.[lazyKey]
395
- ? await options.lazyRoutes.pages[lazyKey]()
396
- : await import(toAbsolute(resolvedPath))
397
- : await import(toAbsolute(serverPath!))
398
-
399
- const loaderData = await routeExported?.loader?.(loaderProps)
400
- return { loaderData, routeId }
401
- } catch (err) {
402
- if (isResponse(err)) {
403
- throw err
404
- }
405
- console.error(`[one] Error running shell loader for ${routeId}:`, err)
406
- return { loaderData: undefined, routeId }
407
- }
408
- }
409
-
410
395
  // run layout loaders only (page content is client-rendered)
411
396
  const layoutResults = await Promise.all(
412
397
  layoutRoutes.map((layout: any) => {
413
398
  const serverPath = layout.loaderServerPath || layout.contextKey
414
- return runShellLoader(layout.contextKey, serverPath, layout.contextKey)
399
+ return importAndRunLoader(
400
+ layout.contextKey,
401
+ serverPath,
402
+ layout.contextKey,
403
+ loaderProps
404
+ )
415
405
  })
416
406
  )
417
407
 
@@ -477,22 +467,7 @@ url: ${url}`)
477
467
  : routeMap[url.pathname] || routeMap[buildInfo?.cleanPath]
478
468
 
479
469
  if (htmlPath) {
480
- // Try Worker ASSETS binding first (for Cloudflare Workers), fall back to filesystem
481
- const fetchStaticHtml = getFetchStaticHtml()
482
- let html: string | null = null
483
-
484
- if (fetchStaticHtml) {
485
- html = await fetchStaticHtml(htmlPath)
486
- }
487
-
488
- if (!html) {
489
- // Fall back to filesystem (Node.js)
490
- try {
491
- html = await readFile(join('dist/client', htmlPath), 'utf-8')
492
- } catch {
493
- // File not found
494
- }
495
- }
470
+ const html = await readStaticHtml(htmlPath)
496
471
 
497
472
  if (html) {
498
473
  const headers = new Headers()
@@ -511,23 +486,7 @@ url: ${url}`)
511
486
  const notFoundHtmlPath = routeMap[notFoundRoute]
512
487
 
513
488
  if (notFoundHtmlPath) {
514
- const fetchStaticHtml = getFetchStaticHtml()
515
- let notFoundHtml: string | null = null
516
-
517
- if (fetchStaticHtml) {
518
- notFoundHtml = await fetchStaticHtml(notFoundHtmlPath)
519
- }
520
-
521
- if (!notFoundHtml) {
522
- try {
523
- notFoundHtml = await readFile(
524
- join('dist/client', notFoundHtmlPath),
525
- 'utf-8'
526
- )
527
- } catch {
528
- // File not found
529
- }
530
- }
489
+ const notFoundHtml = await readStaticHtml(notFoundHtmlPath)
531
490
 
532
491
  if (notFoundHtml) {
533
492
  // inject 404 marker so client knows this is a 404 response
package/src/useLoader.ts CHANGED
@@ -398,8 +398,7 @@ export function useLoaderState<
398
398
  if (
399
399
  !loaderStateEntry.data &&
400
400
  !loaderStateEntry.promise &&
401
- !loaderStateEntry.hasLoadedOnce &&
402
- loader
401
+ !loaderStateEntry.hasLoadedOnce
403
402
  ) {
404
403
  // check for already-resolved preloaded data first (synchronous)
405
404
  // use != null to also exclude null values (which indicate preload failures)
@@ -641,33 +640,24 @@ export function useLoaderState<
641
640
  }
642
641
  }
643
642
 
644
- // handle errors and suspension
645
- if (loader) {
646
- // only throw error on initial load
647
- if (loaderStateEntry.error && !loaderStateEntry.hasLoadedOnce) {
648
- throw loaderStateEntry.error
649
- }
650
-
651
- // only throw promise for suspension on initial load
652
- if (
653
- loaderStateEntry.data === undefined &&
654
- loaderStateEntry.promise &&
655
- !loaderStateEntry.hasLoadedOnce
656
- ) {
657
- throw loaderStateEntry.promise
658
- }
643
+ // handle errors and suspension (only on initial load)
644
+ if (loaderStateEntry.error && !loaderStateEntry.hasLoadedOnce) {
645
+ throw loaderStateEntry.error
646
+ }
659
647
 
660
- return {
661
- data: loaderStateEntry.data,
662
- refetch,
663
- state: loaderStateEntry.state,
664
- } as any
665
- } else {
666
- return {
667
- refetch,
668
- state: loaderStateEntry.state,
669
- } as any
648
+ if (
649
+ loaderStateEntry.data === undefined &&
650
+ loaderStateEntry.promise &&
651
+ !loaderStateEntry.hasLoadedOnce
652
+ ) {
653
+ throw loaderStateEntry.promise
670
654
  }
655
+
656
+ return {
657
+ data: loaderStateEntry.data,
658
+ refetch,
659
+ state: loaderStateEntry.state,
660
+ } as any
671
661
  }
672
662
 
673
663
  /**
@@ -6,7 +6,7 @@ export const redirect = (
6
6
  path: '__branded__' extends keyof OneRouter.Href ? string : OneRouter.Href,
7
7
  status?: number
8
8
  ) => {
9
- if (process.env.VITE_ENVIRONMENT === 'client') {
9
+ if (process.env.VITE_ENVIRONMENT !== 'ssr') {
10
10
  router.navigate(path)
11
11
  return
12
12
  }
@@ -240,9 +240,7 @@ export function useSlot() {
240
240
  return null
241
241
  }
242
242
 
243
- const current = state.routes.find((route, i) => {
244
- return state.index === i
245
- })
243
+ const current = state.routes[state.index]
246
244
 
247
245
  if (!current) {
248
246
  return null
package/src/vite/one.ts CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  } from '@vxrn/vite-plugin-metro'
8
8
  import events from 'node:events'
9
9
  import path from 'node:path'
10
- import { mergeConfig, type Plugin, type PluginOption } from 'vite'
10
+ import { type Plugin, type PluginOption } from 'vite'
11
11
  import tsconfigPaths from 'vite-tsconfig-paths'
12
12
  import { autoDepOptimizePlugin, getOptionsFilled, loadEnv } from 'vxrn'
13
13
  import vxrnVitePlugin from 'vxrn/vite-plugin'
@@ -252,8 +252,6 @@ export function one(options: One.PluginOptions = {}): PluginOption {
252
252
  enforce: 'pre',
253
253
 
254
254
  config() {
255
- // const forkPath = dirname(resolvePath('one'))
256
-
257
255
  let tslibLitePath = ''
258
256
 
259
257
  try {
@@ -279,22 +277,6 @@ export function one(options: One.PluginOptions = {}): PluginOption {
279
277
  tslib: tslibLitePath,
280
278
  }),
281
279
  },
282
-
283
- // [
284
- // {
285
- // find: /tslib/,
286
- // replacement: resolvePath('@vxrn/tslib-lite'),
287
- // },
288
- // // not working but would save ~30Kb stat
289
- // // {
290
- // // find: /@react-navigation\/core.*\/getStateFromPath/,
291
- // // replacement: join(forkPath, 'fork', 'getStateFromPath.mjs'),
292
- // // },
293
- // // {
294
- // // find: /@react-navigation\/core.*\/getPathFromState/,
295
- // // replacement: join(forkPath, 'fork', 'getPathFromState.mjs'),
296
- // // },
297
- // ],
298
280
  },
299
281
  }
300
282
  },
@@ -526,88 +508,8 @@ export function one(options: One.PluginOptions = {}): PluginOption {
526
508
  },
527
509
  ] satisfies Plugin[]
528
510
 
529
- // leaving this as a good example of an option that loads a library conditionally
530
- // // react scan
531
- // const scan = options.react?.scan
532
-
533
- // const reactScanPlugin = {
534
- // name: `one:react-scan`,
535
- // config() {
536
- // return reactScanConfig
537
- // },
538
- // }
539
- // devAndProdPlugins.push(reactScanPlugin)
540
-
541
- // // do it here because it gets called a few times
542
- // const reactScanConfig = ((): UserConfig => {
543
- // const stringify = (obj: Object) => JSON.stringify(JSON.stringify(obj))
544
-
545
- // const configs = {
546
- // disabled: {
547
- // define: {
548
- // 'process.env.ONE_ENABLE_REACT_SCAN': '""',
549
- // },
550
- // },
551
- // enabled: {
552
- // define: {
553
- // 'process.env.ONE_ENABLE_REACT_SCAN': stringify({
554
- // enabled: true,
555
- // animationSpeed: 'slow',
556
- // showToolbar: false,
557
- // }),
558
- // },
559
- // },
560
- // } satisfies Record<string, UserConfig>
561
-
562
- // const getConfigFor = (platform: 'ios' | 'android' | 'client'): UserConfig => {
563
- // if (process.env.NODE_ENV === 'production') {
564
- // return configs.disabled
565
- // }
566
- // if (!scan) {
567
- // return configs.disabled
568
- // }
569
- // if (scan === true) {
570
- // return configs.enabled
571
- // }
572
- // if (typeof scan === 'string') {
573
- // if (scan === 'native' && platform === 'client') {
574
- // return configs.disabled
575
- // }
576
- // if (scan === 'web' && platform !== 'client') {
577
- // return configs.disabled
578
- // }
579
- // return configs.enabled
580
- // }
581
-
582
- // const defaultConfig = scan.options || configs.enabled
583
- // const perPlatformConfig =
584
- // platform === 'ios' || platform === 'android' ? scan.native : scan.web
585
-
586
- // return {
587
- // define: {
588
- // 'process.env.ONE_ENABLE_REACT_SCAN': stringify({
589
- // ...defaultConfig,
590
- // ...perPlatformConfig,
591
- // }),
592
- // },
593
- // }
594
- // }
595
-
596
- // return {
597
- // environments: {
598
- // client: getConfigFor('client'),
599
- // ios: getConfigFor('ios'),
600
- // android: getConfigFor('android'),
601
- // },
602
- // }
603
- // })()
604
-
605
511
  // TODO move to single config and through environments
606
- const nativeWebDevAndProdPlugsin: Plugin[] = [
607
- clientTreeShakePlugin(),
608
- //
609
- // reactScanPlugin
610
- ]
512
+ const nativeWebDevAndProdPlugsin: Plugin[] = [clientTreeShakePlugin()]
611
513
 
612
514
  // TODO make this passed into vxrn through real API
613
515
  globalThis.__vxrnAddNativePlugins = nativeWebDevAndProdPlugsin