@buzola/router 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/define/create-page.d.ts +7 -6
- package/dist/define/create-page.d.ts.map +1 -1
- package/dist/define/create-page.js +33 -120
- package/dist/define/create-page.js.map +1 -1
- package/dist/engine/loader-state.d.ts +68 -0
- package/dist/engine/loader-state.d.ts.map +1 -0
- package/dist/engine/loader-state.js +135 -0
- package/dist/engine/loader-state.js.map +1 -0
- package/dist/engine/route-tree.d.ts +1 -0
- package/dist/engine/route-tree.d.ts.map +1 -1
- package/dist/engine/route-tree.js +5 -2
- package/dist/engine/route-tree.js.map +1 -1
- package/dist/engine/router.d.ts +6 -0
- package/dist/engine/router.d.ts.map +1 -1
- package/dist/engine/router.js +19 -1
- package/dist/engine/router.js.map +1 -1
- package/dist/engine/types.d.ts +6 -0
- package/dist/engine/types.d.ts.map +1 -1
- package/dist/react/hooks.d.ts +2 -0
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/hooks.js +5 -1
- package/dist/react/hooks.js.map +1 -1
- package/dist/react/link.d.ts +7 -0
- package/dist/react/link.d.ts.map +1 -1
- package/dist/react/link.js +33 -6
- package/dist/react/link.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/loader-state.test.ts +339 -0
- package/src/__tests__/matcher.test.ts +29 -0
- package/src/__tests__/router.test.ts +58 -0
- package/src/__tests__/type-safety.test.tsx +24 -0
- package/src/define/create-page.tsx +49 -140
- package/src/engine/loader-state.ts +178 -0
- package/src/engine/route-tree.ts +6 -1
- package/src/engine/router.ts +19 -1
- package/src/engine/types.ts +6 -0
- package/src/react/hooks.ts +5 -1
- package/src/react/link.tsx +49 -5
package/src/engine/types.ts
CHANGED
|
@@ -63,6 +63,8 @@ export interface RouteConfig {
|
|
|
63
63
|
component?: RouteComponent
|
|
64
64
|
/** Schema for validating search params (Standard Schema compatible). */
|
|
65
65
|
searchSchema?: StandardSchema
|
|
66
|
+
/** Preload function — triggers lazy component import ahead of navigation. */
|
|
67
|
+
preload?: () => void
|
|
66
68
|
/** Child routes. */
|
|
67
69
|
children?: RouteConfig[]
|
|
68
70
|
/** Whether this is a layout route (has outlet but no own path segment). */
|
|
@@ -103,6 +105,8 @@ export interface RouteNode {
|
|
|
103
105
|
component?: RouteComponent
|
|
104
106
|
/** Search params schema. */
|
|
105
107
|
searchSchema?: StandardSchema
|
|
108
|
+
/** Preload function — triggers lazy component import ahead of navigation. */
|
|
109
|
+
preload?: () => void
|
|
106
110
|
/** Child nodes. */
|
|
107
111
|
children: RouteNode[]
|
|
108
112
|
/** Parent node reference. */
|
|
@@ -123,6 +127,8 @@ export interface RouterState {
|
|
|
123
127
|
matches: RouteMatch[]
|
|
124
128
|
/** Whether a navigation is in progress. */
|
|
125
129
|
isPending: boolean
|
|
130
|
+
/** The target URL during a pending navigation. Undefined when not navigating. */
|
|
131
|
+
pendingLocation?: URL
|
|
126
132
|
/** Navigation state from Navigation API. */
|
|
127
133
|
navigationState?: unknown
|
|
128
134
|
}
|
package/src/react/hooks.ts
CHANGED
|
@@ -115,9 +115,13 @@ export function useRoute() {
|
|
|
115
115
|
depth,
|
|
116
116
|
/** Whether a navigation is pending. */
|
|
117
117
|
isPending: state.isPending,
|
|
118
|
+
/** The target URL during a pending navigation (with base path stripped). Undefined when not navigating. */
|
|
119
|
+
pendingLocation: state.pendingLocation
|
|
120
|
+
? router.stripBasePath(state.pendingLocation.pathname)
|
|
121
|
+
: undefined,
|
|
118
122
|
/** Current pathname (with base path stripped). */
|
|
119
123
|
pathname: router.stripBasePath(state.location.pathname),
|
|
120
|
-
}), [params, matches, depth, state.isPending, state.location.pathname, router])
|
|
124
|
+
}), [params, matches, depth, state.isPending, state.pendingLocation, state.location.pathname, router])
|
|
121
125
|
}
|
|
122
126
|
|
|
123
127
|
/**
|
package/src/react/link.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback } from 'react'
|
|
1
|
+
import React, { useCallback, useEffect, useRef } from 'react'
|
|
2
2
|
import type { EffectivePageParams, NavigateOptions, RegisteredPage } from '../engine/types.js'
|
|
3
3
|
import { useRouter, useRouterState } from './hooks.js'
|
|
4
4
|
|
|
@@ -27,6 +27,13 @@ type LinkPropsBase = Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'>
|
|
|
27
27
|
* When `true`, clones the single child element and merges `href`, `onClick`, and `data-active` props into it.
|
|
28
28
|
*/
|
|
29
29
|
asChild?: boolean
|
|
30
|
+
/**
|
|
31
|
+
* Prefetch strategy for lazy-loaded route code.
|
|
32
|
+
* - `'intent'` — preload on mouseEnter/focus (default)
|
|
33
|
+
* - `'render'` — preload as soon as the Link renders
|
|
34
|
+
* - `'none'` — disable prefetching
|
|
35
|
+
*/
|
|
36
|
+
prefetch?: 'intent' | 'render' | 'none'
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
export type LinkProps<P extends RegisteredPage = RegisteredPage> = [keyof EffectivePageParams<P>] extends [never]
|
|
@@ -53,13 +60,17 @@ export function Link<P extends RegisteredPage>(props: LinkProps<P>): React.React
|
|
|
53
60
|
resetScroll,
|
|
54
61
|
activeExact = true,
|
|
55
62
|
asChild,
|
|
63
|
+
prefetch = 'intent',
|
|
56
64
|
onClick,
|
|
65
|
+
onMouseEnter,
|
|
66
|
+
onFocus,
|
|
57
67
|
children,
|
|
58
68
|
...rest
|
|
59
69
|
} = props as LinkPropsBase & { to: string; params?: Record<string, string> }
|
|
60
70
|
|
|
61
71
|
const router = useRouter()
|
|
62
72
|
const routerState = useRouterState()
|
|
73
|
+
const preloadedRef = useRef(false)
|
|
63
74
|
|
|
64
75
|
const href = router.buildPagePath(to, params)
|
|
65
76
|
const needsProgrammaticNav = state !== undefined || replace || viewTransition || resetScroll === false
|
|
@@ -71,6 +82,33 @@ export function Link<P extends RegisteredPage>(props: LinkProps<P>): React.React
|
|
|
71
82
|
const isPrefix = targetPath !== '/' && currentPath.startsWith(targetPath + '/')
|
|
72
83
|
const isActive = isExact || (!activeExact && isPrefix)
|
|
73
84
|
|
|
85
|
+
const doPreload = useCallback(() => {
|
|
86
|
+
if (preloadedRef.current) return
|
|
87
|
+
preloadedRef.current = true
|
|
88
|
+
router.preload(to, params)
|
|
89
|
+
}, [router, to, params])
|
|
90
|
+
|
|
91
|
+
// Prefetch on render
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (prefetch === 'render') doPreload()
|
|
94
|
+
}, [prefetch, doPreload])
|
|
95
|
+
|
|
96
|
+
const handleMouseEnter = useCallback(
|
|
97
|
+
(e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
98
|
+
onMouseEnter?.(e)
|
|
99
|
+
if (prefetch === 'intent') doPreload()
|
|
100
|
+
},
|
|
101
|
+
[onMouseEnter, prefetch, doPreload],
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
const handleFocus = useCallback(
|
|
105
|
+
(e: React.FocusEvent<HTMLAnchorElement>) => {
|
|
106
|
+
onFocus?.(e)
|
|
107
|
+
if (prefetch === 'intent') doPreload()
|
|
108
|
+
},
|
|
109
|
+
[onFocus, prefetch, doPreload],
|
|
110
|
+
)
|
|
111
|
+
|
|
74
112
|
const handleClick = useCallback(
|
|
75
113
|
(e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
76
114
|
onClick?.(e)
|
|
@@ -91,18 +129,24 @@ export function Link<P extends RegisteredPage>(props: LinkProps<P>): React.React
|
|
|
91
129
|
[onClick, needsProgrammaticNav, router, to, params, replace, state, viewTransition, resetScroll],
|
|
92
130
|
)
|
|
93
131
|
|
|
132
|
+
const commonProps = {
|
|
133
|
+
href,
|
|
134
|
+
onClick: handleClick,
|
|
135
|
+
onMouseEnter: handleMouseEnter,
|
|
136
|
+
onFocus: handleFocus,
|
|
137
|
+
'data-active': isActive ? '' : undefined,
|
|
138
|
+
}
|
|
139
|
+
|
|
94
140
|
if (asChild) {
|
|
95
141
|
const child = React.Children.only(children) as React.ReactElement<any>
|
|
96
142
|
return React.cloneElement(child, {
|
|
97
|
-
|
|
98
|
-
onClick: handleClick,
|
|
99
|
-
'data-active': isActive ? '' : undefined,
|
|
143
|
+
...commonProps,
|
|
100
144
|
...rest,
|
|
101
145
|
})
|
|
102
146
|
}
|
|
103
147
|
|
|
104
148
|
return (
|
|
105
|
-
<a
|
|
149
|
+
<a {...commonProps} {...rest}>
|
|
106
150
|
{children}
|
|
107
151
|
</a>
|
|
108
152
|
)
|