routexiz 0.2.2 → 0.2.3-z1
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/README.md +236 -264
- package/build/index.esm.js +1 -1
- package/build/index.js +1 -1
- package/build/router/builder.d.ts +1 -1
- package/build/router/types.d.ts +24 -4
- package/build/router/utils.d.ts +4 -2
- package/build/styles.css +6 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
<a href="https://codesandbox.io/p/sandbox/2lslks" target="_blank">LIVE EXAMPLE</a>
|
|
6
6
|
|
|
7
|
-
A lightweight,
|
|
7
|
+
A lightweight, modern, and flexible React router with tree-based routing, Suspense-first data loading, and nested route support.
|
|
8
8
|
|
|
9
|
-
>
|
|
10
|
-
> Nested layouts
|
|
9
|
+
> Routes are modeled like a tree, where each route is a node and navigation resolves a path from root to leaf.
|
|
10
|
+
> Nested layouts, guards, middlewares, and per-route hooks are fully supported.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -60,53 +60,33 @@ export default function App() {
|
|
|
60
60
|
|
|
61
61
|
---
|
|
62
62
|
|
|
63
|
-
# Routing API
|
|
64
|
-
|
|
65
|
-
<b>route</b>
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
route(path, component, config?)
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
<b>builder</b>
|
|
63
|
+
# Nested Routing & Builder API
|
|
72
64
|
|
|
73
65
|
```ts
|
|
74
|
-
// /home
|
|
75
66
|
route("/", Layout, root => {
|
|
76
|
-
// /dashboard
|
|
77
67
|
root.route("/dashboard", Dashboard, dash => {
|
|
78
|
-
dash.
|
|
79
|
-
dash.
|
|
80
|
-
|
|
81
|
-
})
|
|
82
|
-
// more
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
// /admin
|
|
86
|
-
route("/admin", Admin, root => {
|
|
87
|
-
// /admin/dashboard
|
|
88
|
-
root.route("/dashboard", AdminDashboard)
|
|
89
|
-
// more
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
// more
|
|
93
|
-
|
|
94
|
-
```
|
|
68
|
+
dash.route("/stats", () => <div>Dashboard Stats Page</div>);
|
|
69
|
+
dash.route("/settings", () => <div>Dashboard Settings Page</div>);
|
|
70
|
+
});
|
|
95
71
|
|
|
96
|
-
|
|
72
|
+
root.route("/users", Users);
|
|
73
|
+
root.route("/users/:id", User, {
|
|
74
|
+
loader: async ({ params }) => ({ id: params.id, name: "User " + params.id }),
|
|
75
|
+
fallback: <div>Loading user...</div>,
|
|
76
|
+
errorBoundary: ({ error }) => <div>Error: {String(error)}</div>,
|
|
77
|
+
});
|
|
97
78
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
route("/admin/dashboard", AdminDashboard)
|
|
79
|
+
root.route("*", NotFound); // wildcard fallback
|
|
80
|
+
});
|
|
101
81
|
```
|
|
102
82
|
|
|
103
|
-
>
|
|
83
|
+
> Each node can have its own layout, children, loader, hooks, and options.
|
|
104
84
|
|
|
105
85
|
---
|
|
106
86
|
|
|
107
87
|
# Navigation
|
|
108
88
|
|
|
109
|
-
## useNavigate
|
|
89
|
+
## useNavigate / navigate
|
|
110
90
|
|
|
111
91
|
```ts
|
|
112
92
|
const navigate = useNavigate()
|
|
@@ -125,10 +105,9 @@ navigate("/users/:id", {
|
|
|
125
105
|
## redirect
|
|
126
106
|
|
|
127
107
|
```ts
|
|
128
|
-
redirect
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
})
|
|
108
|
+
import { redirect } from "routexiz";
|
|
109
|
+
|
|
110
|
+
redirect("/users/:id", { params: { id: 1 } });
|
|
132
111
|
```
|
|
133
112
|
|
|
134
113
|
---
|
|
@@ -136,24 +115,16 @@ redirect("/users/:id", {
|
|
|
136
115
|
## Link
|
|
137
116
|
|
|
138
117
|
```tsx
|
|
139
|
-
<Link
|
|
140
|
-
|
|
141
|
-
params={{ id: 1 }}
|
|
142
|
-
query={{ tab: "profile" }}
|
|
143
|
-
>
|
|
144
|
-
Open User
|
|
118
|
+
<Link to="/users/:id" params={{ id: 1 }} query={{ tab: "profile" }}>
|
|
119
|
+
User 1
|
|
145
120
|
</Link>
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## NavLink
|
|
149
121
|
|
|
150
|
-
```tsx
|
|
151
122
|
<NavLink
|
|
152
123
|
to="/users/:id"
|
|
153
124
|
params={{ id: 1 }}
|
|
154
125
|
className={({ isActive }) => isActive ? "active" : "link"}
|
|
155
126
|
>
|
|
156
|
-
|
|
127
|
+
User 1
|
|
157
128
|
</NavLink>
|
|
158
129
|
```
|
|
159
130
|
|
|
@@ -161,54 +132,76 @@ redirect("/users/:id", {
|
|
|
161
132
|
|
|
162
133
|
# Data Loading
|
|
163
134
|
|
|
135
|
+
Loader allows async data fetching per route, compatible with React Suspense:
|
|
136
|
+
|
|
164
137
|
```ts
|
|
165
|
-
route("/users/:id", User, {
|
|
166
|
-
loader: async ({ params }) => fetchUser(params.id)
|
|
167
|
-
|
|
138
|
+
root.route("/users/:id", User, {
|
|
139
|
+
loader: async ({ params }) => fetchUser(params.id),
|
|
140
|
+
fallback: <div>Loading user...</div>,
|
|
141
|
+
errorBoundary: ({ error }) => <div>Error: {String(error)}</div>
|
|
142
|
+
});
|
|
143
|
+
```
|
|
168
144
|
|
|
145
|
+
Use useLoaderData() to access loaded data inside components
|
|
146
|
+
|
|
147
|
+
```ts
|
|
169
148
|
// Access loader data:
|
|
170
149
|
const data = useLoaderData()
|
|
171
150
|
```
|
|
172
151
|
|
|
173
152
|
---
|
|
174
153
|
|
|
175
|
-
#
|
|
154
|
+
# Route Options & Hooks
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
## 1️⃣ Guard
|
|
176
158
|
|
|
177
159
|
A guard determines whether navigation is allowed.
|
|
178
160
|
|
|
179
|
-
- Runs before entering
|
|
161
|
+
- Runs before entering the route
|
|
180
162
|
- Can block navigation
|
|
181
|
-
- Returns:
|
|
182
|
-
- `true` → allow
|
|
183
|
-
- `false` → block
|
|
184
163
|
|
|
185
164
|
```ts
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
165
|
+
root.route("/dashboard", Dashboard, dash => {
|
|
166
|
+
dash.guard(({ params }) => {
|
|
167
|
+
if (!isLoggedIn()) return false; // block
|
|
168
|
+
return true; // allow
|
|
169
|
+
});
|
|
170
|
+
});
|
|
189
171
|
```
|
|
190
172
|
|
|
191
173
|
---
|
|
192
174
|
|
|
193
|
-
|
|
175
|
+
## 2️⃣ Middleware
|
|
194
176
|
|
|
195
|
-
|
|
177
|
+
Middleware runs after guards, used for side effects.
|
|
196
178
|
|
|
197
|
-
- Runs after guards
|
|
198
179
|
- Cannot block navigation
|
|
199
|
-
-
|
|
180
|
+
- Can be async
|
|
200
181
|
|
|
201
182
|
```ts
|
|
202
|
-
dash.middleware(async ({ params }) => {
|
|
203
|
-
console.log("
|
|
204
|
-
})
|
|
183
|
+
dash.middleware(async ({ params, query }) => {
|
|
184
|
+
console.log("Visited dashboard with params:", params);
|
|
185
|
+
});
|
|
205
186
|
```
|
|
206
187
|
|
|
207
188
|
---
|
|
208
189
|
|
|
209
|
-
|
|
190
|
+
## 3️⃣ onEnter / onLeave
|
|
191
|
+
|
|
192
|
+
Hooks triggered when entering or leaving a route node:
|
|
193
|
+
```ts
|
|
194
|
+
root.onEnter(({ path, params }) => console.log("Enter root:", path));
|
|
195
|
+
root.onLeave(({ path, params }) => console.log("Leave root:", path));
|
|
196
|
+
```
|
|
210
197
|
|
|
211
|
-
|
|
198
|
+
- onEnter → called when node becomes active
|
|
199
|
+
- onLeave → called when node is left
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
<b> Guards - Middleware</b>
|
|
203
|
+
|
|
204
|
+
| Creteria | Guard | Middleware |
|
|
212
205
|
| --------------- | ------------------------- | -------------------- |
|
|
213
206
|
| Purpose | Control access | Perform side effects |
|
|
214
207
|
| Can block route | ✅ Yes | ❌ No |
|
|
@@ -224,57 +217,27 @@ dash.middleware(async ({ params }) => {
|
|
|
224
217
|
|
|
225
218
|
---
|
|
226
219
|
|
|
227
|
-
|
|
220
|
+
## 4️⃣ prefetch
|
|
228
221
|
|
|
229
|
-
|
|
230
|
-
- Viewport (IntersectionObserver)
|
|
222
|
+
Prefetch loader data in advance:
|
|
231
223
|
|
|
232
224
|
```tsx
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
export default function UsersList() {
|
|
236
|
-
return (
|
|
237
|
-
<div>
|
|
238
|
-
<h2>Users</h2>
|
|
239
|
-
{/* Prefetch on hover */}
|
|
240
|
-
<Link to="/users/1" params={{ id: 1 }} query={{ tab: "profile" }}>
|
|
241
|
-
User 1 (hover to prefetch)
|
|
242
|
-
</Link>
|
|
243
|
-
|
|
244
|
-
{/* Prefetch when enters viewport */}
|
|
245
|
-
<Link to="/users/2" params={{ id: 2 }} query={{ tab: "profile" }}>
|
|
246
|
-
User 2 (prefetch on viewport)
|
|
247
|
-
</Link>
|
|
248
|
-
|
|
249
|
-
{/* disablePrefetch */}
|
|
250
|
-
<Link to="/users/2" params={{ id: 3 }} query={{ tab: "profile" }} disablePrefetch>
|
|
251
|
-
User 3
|
|
252
|
-
</Link>
|
|
253
|
-
</div>
|
|
254
|
-
)
|
|
255
|
-
}
|
|
225
|
+
<Link to="/users/:id" params={{ id: 1 }}>User 1</Link>
|
|
256
226
|
```
|
|
257
227
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
# Error Handling
|
|
263
|
-
|
|
264
|
-
```ts
|
|
265
|
-
route("/users/:id", User, {
|
|
266
|
-
errorBoundary: ({ error }) => <div>Error: {String(error)}</div>
|
|
267
|
-
})
|
|
268
|
-
```
|
|
228
|
+
- Hover → prefetch by default
|
|
229
|
+
- Viewport → prefetch when enters viewport
|
|
230
|
+
- Disable: disablePrefetch
|
|
269
231
|
|
|
270
232
|
---
|
|
271
233
|
|
|
272
|
-
|
|
234
|
+
## 5️⃣ errorBoundary / fallback
|
|
273
235
|
|
|
274
236
|
```ts
|
|
275
|
-
route("/users/:id", User, {
|
|
276
|
-
fallback: <div>Loading...</div
|
|
277
|
-
})
|
|
237
|
+
root.route("/users/:id", User, {
|
|
238
|
+
fallback: <div>Loading user...</div>,
|
|
239
|
+
errorBoundary: ({ error }) => <div>Error: {String(error)}</div>,
|
|
240
|
+
});
|
|
278
241
|
```
|
|
279
242
|
|
|
280
243
|
---
|
|
@@ -556,6 +519,129 @@ export default function App() {
|
|
|
556
519
|
|
|
557
520
|
---
|
|
558
521
|
|
|
522
|
+
# Data Preloading (SSR / Advanced)
|
|
523
|
+
|
|
524
|
+
`routexiz` provides loadRouteData() to preload all route loaders before rendering.
|
|
525
|
+
|
|
526
|
+
This is useful for:
|
|
527
|
+
- SSR (Server-Side Rendering)
|
|
528
|
+
- Prefetching with full data
|
|
529
|
+
- Avoiding loading flicker
|
|
530
|
+
|
|
531
|
+
## 1️⃣ Basic Usage
|
|
532
|
+
```ts
|
|
533
|
+
import { loadRouteData } from "routexiz"
|
|
534
|
+
|
|
535
|
+
const data = await loadRouteData("/users/1")
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
## 2️⃣ Example Route
|
|
539
|
+
|
|
540
|
+
```ts
|
|
541
|
+
route("/users/:id", User, {
|
|
542
|
+
loader: async ({ params }) => {
|
|
543
|
+
await new Promise(r => setTimeout(r, 300))
|
|
544
|
+
return { id: params.id, name: "User " + params.id }
|
|
545
|
+
}
|
|
546
|
+
})
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
## 3️⃣ Access Loaded Data
|
|
550
|
+
|
|
551
|
+
`loadRouteData` returns a map of resources (Suspense-based):
|
|
552
|
+
|
|
553
|
+
```ts
|
|
554
|
+
const data = await loadRouteData("/users/1")
|
|
555
|
+
|
|
556
|
+
const user = data["/users/:id"].get()
|
|
557
|
+
|
|
558
|
+
console.log(user)
|
|
559
|
+
// { id: "1", name: "User 1" }
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
## 4️⃣ SSR Example
|
|
563
|
+
```ts
|
|
564
|
+
import { loadRouteData } from "routexiz"
|
|
565
|
+
|
|
566
|
+
async function render(url: string) {
|
|
567
|
+
const data = await loadRouteData(url)
|
|
568
|
+
|
|
569
|
+
const user = data["/users/:id"]?.get()
|
|
570
|
+
|
|
571
|
+
return `
|
|
572
|
+
<html>
|
|
573
|
+
<body>
|
|
574
|
+
<h1>${user?.name}</h1>
|
|
575
|
+
</body>
|
|
576
|
+
</html>
|
|
577
|
+
`
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
render("/users/1")
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
## 5️⃣ Nested Routes Example
|
|
584
|
+
```ts
|
|
585
|
+
route("/", Layout, root => {
|
|
586
|
+
root.route("/dashboard", Dashboard, dash => {
|
|
587
|
+
dash.route("/users/:id", User, {
|
|
588
|
+
loader: async ({ params }) => {
|
|
589
|
+
return { id: params.id }
|
|
590
|
+
}
|
|
591
|
+
})
|
|
592
|
+
})
|
|
593
|
+
})
|
|
594
|
+
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
```ts
|
|
598
|
+
const data = await loadRouteData("/dashboard/users/1")
|
|
599
|
+
|
|
600
|
+
const user = data["/dashboard/users/:id"].get()
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
## 6️⃣ Notes
|
|
604
|
+
|
|
605
|
+
- Each route loader returns a resource (Suspense-based)
|
|
606
|
+
|
|
607
|
+
- Use .get() to read data safely
|
|
608
|
+
|
|
609
|
+
- .read() is used internally to trigger loading
|
|
610
|
+
|
|
611
|
+
## 7️⃣ Difference vs prefetch
|
|
612
|
+
|
|
613
|
+
| Function | Behavior |
|
|
614
|
+
| ----------------- | ------------------------------ |
|
|
615
|
+
| `prefetch()` | Trigger loading (non-blocking) |
|
|
616
|
+
| `loadRouteData()` | Await full data (blocking) |
|
|
617
|
+
|
|
618
|
+
## When to use
|
|
619
|
+
|
|
620
|
+
- ✅ SSR (recommended)
|
|
621
|
+
- ✅ Full preloading before navigation
|
|
622
|
+
- ✅ Testing / debugging loaders
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
# Comparison
|
|
627
|
+
|
|
628
|
+
`routexiz` focuses on **modern React routing** with Suspense-first `data loading`, `nested routes`, `guards`, `middleware`, and `prefetch/caching`.
|
|
629
|
+
It’s lightweight and flexible, designed for client-side SPAs.
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
| Criteria | routexiz | React Router | TanStack Router | Remix |
|
|
633
|
+
| ------------------------- | -------- | ------------ | --------------- | ----- |
|
|
634
|
+
| Nested routes builder API | ✅ | ✅ | ✅ | ✅ |
|
|
635
|
+
| Suspense-first loaders | ✅ | ⚠️ | ✅ | ✅ |
|
|
636
|
+
| Guards & middleware | ✅ | ⚠️ | ✅ | ⚠️ |
|
|
637
|
+
| Prefetch / caching | ✅ | ❌ | ✅ | ⚠️ |
|
|
638
|
+
| Error boundary per route | ✅ | ⚠️ | ✅ | ✅ |
|
|
639
|
+
| Transition support | ✅ | ❌ | ⚠️ | ⚠️ |
|
|
640
|
+
| Lightweight & minimal | ✅ | ⚠️ | ⚠️ | ⚠️ |
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
|
|
559
645
|
# Full Example
|
|
560
646
|
|
|
561
647
|
## Users.tsx
|
|
@@ -583,21 +669,17 @@ export function Users() {
|
|
|
583
669
|
## App.tsx
|
|
584
670
|
|
|
585
671
|
```ts
|
|
586
|
-
import React, { ReactNode } from "react";
|
|
672
|
+
import React, { ReactNode, Suspense } from "react";
|
|
587
673
|
import {
|
|
588
674
|
Link,
|
|
589
675
|
RouterProvider,
|
|
590
|
-
setGlobalFallback,
|
|
591
|
-
setGlobalError,
|
|
592
676
|
route,
|
|
593
677
|
useLoaderData,
|
|
594
678
|
useParams,
|
|
595
679
|
useQuery,
|
|
596
|
-
useNavigate,
|
|
597
|
-
navigate,
|
|
598
680
|
} from "routexiz";
|
|
599
681
|
|
|
600
|
-
import "routexiz/styles.css"
|
|
682
|
+
import "routexiz/styles.css";
|
|
601
683
|
|
|
602
684
|
/* =========================
|
|
603
685
|
DEMO PAGES
|
|
@@ -612,6 +694,7 @@ function Layout({ children }: { children?: ReactNode }) {
|
|
|
612
694
|
<Link to="/dashboard/stats">Dashboard Stats</Link> |{" "}
|
|
613
695
|
<Link to="/dashboard/settings">Dashboard Settings</Link> |{" "}
|
|
614
696
|
<Link to="/users">Users</Link> |{" "}
|
|
697
|
+
<Link to="/users/1">User 1</Link> |{" "}
|
|
615
698
|
<Link to="/about">About</Link> |{" "}
|
|
616
699
|
<Link to="/about/team">Team</Link> |{" "}
|
|
617
700
|
<Link to="/contact">Contact</Link> |{" "}
|
|
@@ -629,6 +712,7 @@ function Dashboard({ children }: { children?: ReactNode }) {
|
|
|
629
712
|
return <div>Dashboard {children}</div>;
|
|
630
713
|
}
|
|
631
714
|
|
|
715
|
+
// Lazy-loaded Users list
|
|
632
716
|
const Users = React.lazy(() => import("./Users"));
|
|
633
717
|
|
|
634
718
|
function User() {
|
|
@@ -662,28 +746,21 @@ function Contact() {
|
|
|
662
746
|
return <div>Contact Page</div>;
|
|
663
747
|
}
|
|
664
748
|
|
|
665
|
-
function Blog(
|
|
749
|
+
function Blog() {
|
|
666
750
|
const data = useLoaderData<any>();
|
|
667
|
-
return
|
|
668
|
-
<div>
|
|
669
|
-
<h3>Blog: {data.slug}</h3>
|
|
670
|
-
{children}
|
|
671
|
-
</div>
|
|
672
|
-
);
|
|
751
|
+
return <div>Blog Post: {data.slug}</div>;
|
|
673
752
|
}
|
|
674
753
|
|
|
675
754
|
function Search() {
|
|
676
|
-
const query = useQuery();
|
|
677
755
|
const params = useParams();
|
|
678
|
-
|
|
756
|
+
const query = useQuery();
|
|
679
757
|
const term = Array.isArray(query.term) ? query.term[0] : query.term;
|
|
680
758
|
|
|
681
759
|
return (
|
|
682
760
|
<div>
|
|
683
761
|
<h3>Search Page</h3>
|
|
684
|
-
<div>
|
|
685
|
-
<div>
|
|
686
|
-
<div>Full query: {JSON.stringify(query)}</div>
|
|
762
|
+
<div>Param: {params.term || "none"}</div>
|
|
763
|
+
<div>Query: {term || "none"}</div>
|
|
687
764
|
</div>
|
|
688
765
|
);
|
|
689
766
|
}
|
|
@@ -695,18 +772,26 @@ function NotFound() {
|
|
|
695
772
|
/* =========================
|
|
696
773
|
ROUTES SETUP
|
|
697
774
|
========================= */
|
|
698
|
-
|
|
699
775
|
function setupRoutes() {
|
|
700
776
|
route("/", Layout, (root) => {
|
|
777
|
+
// Dashboard + nested
|
|
701
778
|
root.route("/dashboard", Dashboard, (dash) => {
|
|
779
|
+
dash.guard(({ path }) => {
|
|
780
|
+
console.log("Guard dashboard:", path);
|
|
781
|
+
return true;
|
|
782
|
+
});
|
|
783
|
+
dash.middleware(({ path }) => console.log("Middleware dashboard:", path));
|
|
784
|
+
dash.onEnter(({ path }) => console.log("Enter dashboard:", path));
|
|
785
|
+
dash.onLeave(({ path }) => console.log("Leave dashboard:", path));
|
|
786
|
+
|
|
702
787
|
dash.route("/stats", () => <div>Dashboard Stats Page</div>);
|
|
703
788
|
dash.route("/settings", () => <div>Dashboard Settings Page</div>);
|
|
704
789
|
});
|
|
705
790
|
|
|
706
|
-
|
|
707
|
-
root.route("users
|
|
708
|
-
root.route("users/:id/posts", () => <div>User Posts Page</div>);
|
|
791
|
+
// Users list
|
|
792
|
+
root.route("/users", Users); // lazy page
|
|
709
793
|
|
|
794
|
+
// Dynamic user pages
|
|
710
795
|
root.route("/users/:id", User, {
|
|
711
796
|
loader: async ({ params }) => {
|
|
712
797
|
await new Promise((r) => setTimeout(r, 400));
|
|
@@ -717,15 +802,22 @@ function setupRoutes() {
|
|
|
717
802
|
errorBoundary: ({ error }) => <div>Error: {String(error)}</div>,
|
|
718
803
|
});
|
|
719
804
|
|
|
720
|
-
|
|
805
|
+
// Profile & posts subroutes
|
|
806
|
+
root.route("/users/:id/profile", () => <div>User Profile Page</div>);
|
|
807
|
+
root.route("/users/:id/posts", () => <div>User Posts Page</div>);
|
|
808
|
+
|
|
809
|
+
// About nested
|
|
810
|
+
root.route("/about", About, (about) => {
|
|
721
811
|
about.route("team", Team);
|
|
722
812
|
});
|
|
723
813
|
|
|
724
|
-
|
|
725
|
-
|
|
814
|
+
root.route("/contact", Contact);
|
|
815
|
+
|
|
816
|
+
// wildcard fallback
|
|
726
817
|
root.route("*", NotFound);
|
|
727
818
|
});
|
|
728
819
|
|
|
820
|
+
// Blog route with loader
|
|
729
821
|
route("/blog/:slug", Blog, {
|
|
730
822
|
loader: async ({ params }) => {
|
|
731
823
|
await new Promise((r) => setTimeout(r, 200));
|
|
@@ -733,6 +825,7 @@ function setupRoutes() {
|
|
|
733
825
|
},
|
|
734
826
|
});
|
|
735
827
|
|
|
828
|
+
// Search route
|
|
736
829
|
route("/search", Search);
|
|
737
830
|
route("/search/:term", Search);
|
|
738
831
|
}
|
|
@@ -745,136 +838,15 @@ setupRoutes();
|
|
|
745
838
|
export default function App() {
|
|
746
839
|
return (
|
|
747
840
|
<div>
|
|
748
|
-
<h1>
|
|
749
|
-
<
|
|
750
|
-
|
|
841
|
+
<h1>Full RouteXiz Demo</h1>
|
|
842
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
843
|
+
<RouterProvider />
|
|
844
|
+
</Suspense>
|
|
751
845
|
</div>
|
|
752
|
-
)
|
|
753
|
-
}
|
|
754
|
-
```
|
|
755
|
-
|
|
756
|
-
---
|
|
757
|
-
|
|
758
|
-
# Data Preloading (SSR / Advanced)
|
|
759
|
-
|
|
760
|
-
`routexiz` provides loadRouteData() to preload all route loaders before rendering.
|
|
761
|
-
|
|
762
|
-
This is useful for:
|
|
763
|
-
- SSR (Server-Side Rendering)
|
|
764
|
-
- Prefetching with full data
|
|
765
|
-
- Avoiding loading flicker
|
|
766
|
-
|
|
767
|
-
## 1️⃣ Basic Usage
|
|
768
|
-
```ts
|
|
769
|
-
import { loadRouteData } from "routexiz"
|
|
770
|
-
|
|
771
|
-
const data = await loadRouteData("/users/1")
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
## 2️⃣ Example Route
|
|
775
|
-
|
|
776
|
-
```ts
|
|
777
|
-
route("/users/:id", User, {
|
|
778
|
-
loader: async ({ params }) => {
|
|
779
|
-
await new Promise(r => setTimeout(r, 300))
|
|
780
|
-
return { id: params.id, name: "User " + params.id }
|
|
781
|
-
}
|
|
782
|
-
})
|
|
783
|
-
```
|
|
784
|
-
|
|
785
|
-
## 3️⃣ Access Loaded Data
|
|
786
|
-
|
|
787
|
-
`loadRouteData` returns a map of resources (Suspense-based):
|
|
788
|
-
|
|
789
|
-
```ts
|
|
790
|
-
const data = await loadRouteData("/users/1")
|
|
791
|
-
|
|
792
|
-
const user = data["/users/:id"].get()
|
|
793
|
-
|
|
794
|
-
console.log(user)
|
|
795
|
-
// { id: "1", name: "User 1" }
|
|
796
|
-
```
|
|
797
|
-
|
|
798
|
-
## 4️⃣ SSR Example
|
|
799
|
-
```ts
|
|
800
|
-
import { loadRouteData } from "routexiz"
|
|
801
|
-
|
|
802
|
-
async function render(url: string) {
|
|
803
|
-
const data = await loadRouteData(url)
|
|
804
|
-
|
|
805
|
-
const user = data["/users/:id"]?.get()
|
|
806
|
-
|
|
807
|
-
return `
|
|
808
|
-
<html>
|
|
809
|
-
<body>
|
|
810
|
-
<h1>${user?.name}</h1>
|
|
811
|
-
</body>
|
|
812
|
-
</html>
|
|
813
|
-
`
|
|
846
|
+
);
|
|
814
847
|
}
|
|
815
|
-
|
|
816
|
-
render("/users/1")
|
|
817
|
-
```
|
|
818
|
-
|
|
819
|
-
## 5️⃣ Nested Routes Example
|
|
820
|
-
```ts
|
|
821
|
-
route("/", Layout, root => {
|
|
822
|
-
root.route("/dashboard", Dashboard, dash => {
|
|
823
|
-
dash.route("/users/:id", User, {
|
|
824
|
-
loader: async ({ params }) => {
|
|
825
|
-
return { id: params.id }
|
|
826
|
-
}
|
|
827
|
-
})
|
|
828
|
-
})
|
|
829
|
-
})
|
|
830
|
-
|
|
831
|
-
```
|
|
832
|
-
|
|
833
|
-
```ts
|
|
834
|
-
const data = await loadRouteData("/dashboard/users/1")
|
|
835
|
-
|
|
836
|
-
const user = data["/dashboard/users/:id"].get()
|
|
837
848
|
```
|
|
838
849
|
|
|
839
|
-
## 6️⃣ Notes
|
|
840
|
-
|
|
841
|
-
- Each route loader returns a resource (Suspense-based)
|
|
842
|
-
|
|
843
|
-
- Use .get() to read data safely
|
|
844
|
-
|
|
845
|
-
- .read() is used internally to trigger loading
|
|
846
|
-
|
|
847
|
-
## 7️⃣ Difference vs prefetch
|
|
848
|
-
|
|
849
|
-
| Function | Behavior |
|
|
850
|
-
| ----------------- | ------------------------------ |
|
|
851
|
-
| `prefetch()` | Trigger loading (non-blocking) |
|
|
852
|
-
| `loadRouteData()` | Await full data (blocking) |
|
|
853
|
-
|
|
854
|
-
## When to use
|
|
855
|
-
|
|
856
|
-
- ✅ SSR (recommended)
|
|
857
|
-
- ✅ Full preloading before navigation
|
|
858
|
-
- ✅ Testing / debugging loaders
|
|
859
|
-
|
|
860
|
-
---
|
|
861
|
-
|
|
862
|
-
# Comparison
|
|
863
|
-
|
|
864
|
-
`routexiz` focuses on **modern React routing** with Suspense-first `data loading`, `nested routes`, `guards`, `middleware`, and `prefetch/caching`.
|
|
865
|
-
It’s lightweight and flexible, designed for client-side SPAs.
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
| Criteria | routexiz | React Router | TanStack Router | Remix |
|
|
869
|
-
| ------------------------- | -------- | ------------ | --------------- | ----- |
|
|
870
|
-
| Nested routes builder API | ✅ | ✅ | ✅ | ✅ |
|
|
871
|
-
| Suspense-first loaders | ✅ | ⚠️ | ✅ | ✅ |
|
|
872
|
-
| Guards & middleware | ✅ | ⚠️ | ✅ | ⚠️ |
|
|
873
|
-
| Prefetch / caching | ✅ | ❌ | ✅ | ⚠️ |
|
|
874
|
-
| Error boundary per route | ✅ | ⚠️ | ✅ | ✅ |
|
|
875
|
-
| Transition support | ✅ | ❌ | ⚠️ | ⚠️ |
|
|
876
|
-
| Lightweight & minimal | ✅ | ⚠️ | ⚠️ | ⚠️ |
|
|
877
|
-
|
|
878
850
|
---
|
|
879
851
|
|
|
880
852
|
# Architecture
|
package/build/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import n,{createContext as t,useContext as e,useState as r,useRef as o,useEffect as a,Suspense as i,forwardRef as s}from"react";import{jsx as c,Fragment as u,jsxs as l}from"react/jsx-runtime";import h from"joinclass";const d=new Map;function f(n=1/0){const t=Date.now();let e=0;for(const[r,o]of d.entries())if(o.expiry<=t&&(d.delete(r),e++,e>=n))break}function p(n,t,e,r){const o=Date.now(),a=d.get(n);if(a&&!r&&(!e||a.expiry>o))return a.resource;const i=m(t());return d.set(n,{resource:i,expiry:o+(e??0)}),i}function m(n,t){let e=t?"success":"pending",r=t??null;const o=n.then(n=>{e="success",r=n},n=>{e="error",r=n});return{read(){if("pending"===e)throw o;if("error"===e)throw r;return r},update(n){r=n,e="success"},get:()=>r}}function y(n){return n.startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function g(n){return y(n).split("/").filter(Boolean)}function w(n){const[t,e]=n.split("#"),[r,o]=t.split("?"),a={};return o&&o.split("&").forEach(n=>{const[t,e]=n.split("=");if(!t)return;const r=decodeURIComponent(t),o=decodeURIComponent(e??"");void 0!==a[r]?Array.isArray(a[r])?a[r].push(o):a[r]=[a[r],o]:a[r]=o}),{path:y(r),query:a,hash:e??""}}function v(n,t){const e=n[t];return Array.isArray(e)?e[0]:e}function P(n){const t={};if(!n)return t;if("string"==typeof n){const e=new URLSearchParams(n);e.forEach((n,r)=>{const o=e.getAll(r);t[r]=o.length>1?o:o[0]})}else for(const e in n){const r=n[e];Array.isArray(r)?t[e]=r.map(n=>String(n)):null!=r&&(t[e]=String(r))}return t}function b(n){const t=g(n);let e=10*t.length;for(const n of t)"*"===n?e+=0:n.startsWith(":")?e+=1:e+=5;return e}function q(n){return[...n].sort((n,t)=>b(t.path)-b(n.path))}function x(n,t){const{path:e,query:r,hash:o}=w(t),a=g(e),i=q(n).map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;i.length;){const{node:n,segIndex:t,params:e,chain:s}=i.pop(),c=g(n.path),u={...e};let l=0;for(;l<c.length;l++){const n=c[l],e=a[t+l];if(!e)break;if("*"===n){u["*"]=a.slice(t+l).join("/"),l=c.length;break}if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(e);else if(n!==e)break}if(l!==c.length)continue;const h=t+l,d=[...s,{node:n,params:u,query:r,hash:o}];if(h===a.length)return d;const f=q(n.children||[]);for(let n=f.length-1;n>=0;n--){const t=g(f[n].path).some(n=>n.startsWith(":"));i.push({node:f[n],segIndex:t?0:h,params:u,chain:d})}}return null}function k(n,t,e,r,o){const{path:a,query:i,hash:s}=w(n);let c=y(a);if(t)for(const n in t)c=c.replace(`:${n}`,encodeURIComponent(t[n]));const u=o?.replaceQuery?{...e??{}}:{...i,...e??{}},l=new URLSearchParams;for(const n in u){const t=u[n];Array.isArray(t)?t.forEach(t=>l.append(n,String(t))):l.append(n,String(t))}const h=l.toString();return h&&(c+=`?${h}`),r?c+=`#${r}`:s&&(c+=`#${s}`),c}function E(n){return JSON.stringify(Object.keys(n||{}).sort().reduce((t,e)=>(t[e]=n[e],t),{}))}function N(n,t){return`${n.path}|${E(t.params)}|${E(t.query)}|${t.hash}`}let S=[];function A(){S=[]}function R(){return S}function K(n){const t={guard:e=>(n.guardFns||(n.guardFns=[]),n.guardFns.push(e),t),middleware:e=>(n.middlewares||(n.middlewares=[]),n.middlewares.push(e),t),meta:e=>(n.meta={...n.meta||{},...e},t),route(t,e,r){const o={path:y(t),component:e,children:[],options:{},guardFns:[],middlewares:[],parent:n};return"function"==typeof r?K(o)(r):r&&(o.options=r),n.children.push(o),K(o).api}};return Object.assign(n=>n(t),{api:t})}function C(n,t,e){const r={path:y(n),component:t,children:[],options:{},parent:void 0};return"function"==typeof e?K(r)(e):e&&(r.options=e),S.push(r),K(r).api}const $=t(null),j=t(null),I=t(null),M=t({loading:!1,path:"",pendingPath:""}),F=t({name:"none",stage:"idle"});function L(){const n=e($);try{return n?.read()}catch{return}}function O(){const n=e(j);if(!n)throw new Error("RouteContext not found");return n}function B(){return e(I)??{}}function D(){try{const n=O();if(n?.query)return n.query}catch{}const{query:n}=w(window.location.pathname+window.location.search+window.location.hash);return n}function Q(){return e(M)}function U(){return e(F)}function W(){try{return L()}catch{return}}function T(){const{meta:n,params:t,path:e,query:r}=O();return{data:W(),params:t??{},query:r??{},path:e,meta:n}}const G=200;function J({name:n="fade",stage:t,duration:e=200,children:i}){const[s,d]=r(i),[f,p]=r(null),m=o();return a(()=>{m.current&&clearTimeout(m.current),"exiting"===t&&p(s),"entering"===t&&(d(i),m.current=window.setTimeout(()=>{p(null)},e))},[i,t,e]),"idle"===t?c(u,{children:i}):l("div",{className:h("rtx",`rtx-${n}`),children:[f&&c("div",{className:"rtx-exit",children:f}),c("div",{className:h("rtx-enter",`rtx-${t}`),children:s})]})}let z=!1,H=!1,V=c("div",{children:"Loading..."});const X=({error:n})=>l("div",{children:["Global Error: ",String(n)]});let Y=null;function Z(n,t){z=n,t&&(V=t)}function _(n,t){H=n,Y=t??null}function nn(){return z}function tn(){return H}function en(){return V}function rn(){return Y?({error:n})=>Y(n):X}class on extends n.Component{constructor(n){super(n),this.reset=()=>{this.setState(n=>({hasError:!1,error:null,resetKey:n.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(n){return{hasError:!0,error:n}}render(){if(this.state.hasError){const n=this.props.errorBoundary??(tn()?rn():void 0);return n?c(n,{error:this.state.error,params:this.props.params}):this.props.fallback??l("div",{children:["Error: ",String(this.state.error)]})}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function an({resource:t,errorBoundary:e,children:r,resetKey:o}){const[a,i]=n.useState(null);n.useEffect(()=>{i(null)},[o]);try{return t?.read(),a&&i(null),c(u,{children:r})}catch(n){if(n instanceof Promise)throw n;const t=e??(tn()?rn():void 0);if(t)return c(t,{error:n});throw n}}function sn({chain:n,data:t}){return c(u,{children:function e(r=0){const o=n[r];if(!o)return null;const a=o.node.component;if(!a)return e(r+1);const s={params:o.params,path:o.node.path,query:o.query,hash:o.hash},u=function(n,t){let e=n;for(;e;){const n=e.options?.fallback;if(n)return"function"==typeof n?n(t):n;e=e.parent}return nn()?en():null}(o.node,s),l=Object.assign({},...n.slice(0,r+1).map(n=>n.node.meta)),h=t[N(o.node,s)];return c(on,{fallback:u,errorBoundary:o.node.options?.errorBoundary,params:o.params,children:c(i,{fallback:u,children:c(j.Provider,{value:{path:o.node.path,params:o.params,query:P(o.query),hash:o.hash??"",meta:l},children:c($.Provider,{value:h,children:c(I.Provider,{value:o.params,children:c(an,{resource:h,errorBoundary:o.node.options?.errorBoundary,resetKey:o.params.id||o.params,children:c(a,{children:e(r+1)})})})})})})})}()})}function cn(n,t){const e={};for(const r of n){const n=r.node.options?.loader;if(!n)continue;const o={params:r.params,path:r.node.path,query:r.query,hash:r.hash},a=N(r.node,o),i=r.node.options?.ttl,s=t?.forceReload??r.node.options?.shouldReload?.({prev:void 0,next:o})??!0;e[a]=p(a,()=>Promise.resolve(n(o)),i,s)}return e}let un=null;function ln(){return un}function hn(n,t){const e=R(),[r,o]=n.split("?"),a={...P(o),...t?.query??{}},i=x(e,k(r,t?.params,a,t?.hash));i&&cn(i,{forceReload:!1})}function dn({transition:n,duration:t,wrapper:e}){const[i,s]=r({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:t??G}),u=o(0),l=async(e,r)=>{const o=++u.current,a=function(n){const t=R();return x(t,n)??x(t,"*")??[]}(e),i=await async function(n,t){for(const e of n)for(const n of e.node.guardFns||[]){const r=await n({params:e.params,path:e.node.path,query:e.query,hash:e.hash});if(!1===r)return!1;if("object"==typeof r&&"redirect"in r)return await t(r.redirect),!1}return!0}(a,l);if(!i)return!1;await async function(n){for(const t of n)for(const n of t.node.middlewares||[])await Promise.resolve(n({params:t.params,path:t.node.path,query:t.query,hash:t.hash})).catch(console.error)}(a);const c=function(n,t,e){if(t?.transition)return t.transition;for(let t=n.length-1;t>=0;t--){const e=n[t].node;if(e.options?.transition)return e.options.transition;if(e.meta?.transition)return e.meta.transition}return e??"none"}(a,r,n),h=function(n,t,e){if(null!=t?.duration)return t.duration;for(let t=n.length-1;t>=0;t--){const e=n[t].node;if(null!=e.options?.duration)return e.options.duration}return null!=e?e:G}(a,r,t);!function(n,t,e,r){n(n=>({...n,loading:!0,pendingPath:t,transition:{name:e,stage:"exiting"},duration:r}))}(s,e,c,h),await new Promise(n=>setTimeout(n,h));const d=r?.shallow?{}:cn(a,r);return o===u.current&&(function(n,t,e,r,o,a){n(n=>({...n,chain:t,data:{...n.data,...e},loading:!1,path:r,pendingPath:"",transition:{name:o,stage:"entering"},duration:a}))}(s,a,d??{},e,c,h),r?.shallow||window.scrollTo(0,0),!0)};a(()=>{un=l;const n=()=>window.location.pathname+window.location.search+window.location.hash;l(n());const t=()=>l(n());window.addEventListener("popstate",t);const e=()=>{const t=x(R(),n());if(!t)return;const e={};for(const n of t)n.node.options?.revalidateOnFocus&&Object.assign(e,cn([n],{forceReload:!0}));Object.keys(e).length&&s(n=>({...n,data:{...n.data,...e}}))};return window.addEventListener("focus",e),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",e),un=null}},[]);const h=c(sn,{chain:i.chain,data:i.data},i.path);let d=h;if("none"!==i.transition.name&&(d=c(J,{name:i.transition.name,stage:i.transition.stage,duration:i.duration,children:h})),e){d=c(e,{children:d})}return c(M.Provider,{value:{loading:i.loading,path:i.path,pendingPath:i.pendingPath},children:c(F.Provider,{value:i.transition,children:d})})}const fn=[],pn=[];function mn(n){fn.push(n)}function yn(n){pn.push(n)}async function gn(n,t,e=!1){const r={...t,forceReload:t?.forceReload??!0},o=k(n,r.params,r.query,r.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(n){return(await Promise.all(fn.map(t=>t(n)))).every(n=>!1!==n)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=ln();if(!r.shallow&&a){if(!await a(o,r))return console.warn("Navigation blocked by route guard",o),!1}return e?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(n){for(const t of pn)await t(n)}(o),!0}function wn(n,t){return gn(n,t,!1)}function vn(n,t){return wn(n,t)}function Pn(){const n=(n,t)=>gn(n,t);return n.replace=(n,t)=>gn(n,t,!0),n}function bn(n){return Q().path===n}function qn(n,t){return()=>hn(n,t)}function xn({to:n,transition:t,replace:e,params:r,query:i,hash:s,partialMatch:c,disablePrefetch:u,replaceQuery:l}){const h=Pn(),d=Q(),f=o(null),p=k(n,r,i,s,{replaceQuery:l}),m=c?d.path?.startsWith(p):d.path===p;return a(()=>{if(u)return;const n=f.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(hn(p),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[p,u]),{ref:f,fullPath:p,isActive:m,go:async()=>e?h.replace(n,{transition:t,params:r,query:i,hash:s}):h(n,{transition:t,params:r,query:i,hash:s})}}wn.replace=function(n,t){return gn(n,t,!0)};const kn=s(({fullPath:n,onNavigate:t,onPrefetch:e,onClick:r,onMouseEnter:o,onKeyDown:a,disablePrefetch:i,...s},u)=>c("a",{ref:u,href:n,...s,onClick:async e=>{if(r&&r(e),e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)return;e.preventDefault();await t()||console.warn("Navigation blocked:",n)},onMouseEnter:n=>{o&&o(n),i||e?.()},onKeyDown:async e=>{if("Enter"===e.key){e.preventDefault();await t()||console.warn("Navigation blocked:",n)}a&&a(e)}}));function En(n){const{to:t,transition:e,replace:r,activeClassName:o="active",onNavigate:a,partialMatch:i,params:s,query:u,hash:l,className:d,disablePrefetch:f,replaceQuery:p,...m}=n,{ref:y,fullPath:g,isActive:w,go:v}=xn({to:t,transition:e,replace:r,params:s,query:u,hash:l,partialMatch:i,disablePrefetch:f,replaceQuery:p});return c(kn,{ref:y,fullPath:g,onNavigate:async()=>{const n=await v();return a?.(g),n},onPrefetch:()=>hn(g),className:h(d,w&&o),disablePrefetch:f,...m})}function Nn(n){const{to:t,transition:e,replace:r,params:o,query:a,hash:i,partialMatch:s,className:u,style:l,disablePrefetch:d,replaceQuery:f,...p}=n,{ref:m,fullPath:y,isActive:g,go:w}=xn({to:t,transition:e,replace:r,params:o,query:a,hash:i,partialMatch:s,disablePrefetch:d,replaceQuery:f}),v="function"==typeof u?u({isActive:g}):u,P="function"==typeof l?l({isActive:g}):l;return c(kn,{ref:m,fullPath:y,onNavigate:async()=>(await w(),!0),onPrefetch:()=>hn(y),className:h(v),style:P,disablePrefetch:d,...p})}kn.displayName="LinkCore";export{G as DEFAULT_DURATION,En as Link,$ as LoaderDataContext,Nn as NavLink,I as ParamsContext,j as RouteContext,dn as RouterProvider,M as RouterStateContext,J as Transition,F as TransitionContext,yn as addAfterNavigationHook,mn as addBeforeNavigationHook,N as buildCacheKey,k as buildPath,f as cleanupCache,A as clearRoutes,m as createResource,rn as getGlobalError,en as getGlobalFallback,ln as getGlobalResolve,v as getQueryFirst,p as getResource,R as getRoutes,tn as getUseGlobalError,nn as getUseGlobalFallback,x as matchRouteChain,wn as navigate,y as normalize,P as normalizeQuery,w as parseQueryHash,hn as prefetch,vn as redirect,C as route,_ as setGlobalError,Z as setGlobalFallback,g as split,xn as useLinkCore,L as useLoaderData,Pn as useNavigate,Q as useNavigation,B as useParams,qn as usePrefetch,D as useQuery,O as useRouteContext,bn as useRouteMatch,T as useRouterData,W as useSafeLoaderData,U as useTransition};
|
|
1
|
+
import n,{createContext as t,useContext as r,useState as e,useRef as o,useEffect as a,Suspense as i,forwardRef as s}from"react";import{jsx as c,Fragment as u,jsxs as h}from"react/jsx-runtime";import l from"joinclass";const f=new Map;function p(n=1/0){const t=Date.now();let r=0;for(const[e,o]of f.entries())if(o.expiry<=t&&(f.delete(e),r++,r>=n))break}function d(n,t,r,e){const o=Date.now(),a=f.get(n);if(a&&!e&&(!r||a.expiry>o))return a.resource;const i=y(t());return f.set(n,{resource:i,expiry:o+(r??0)}),i}function y(n,t){let r=t?"success":"pending",e=t??null;const o=n.then(n=>{r="success",e=n},n=>{r="error",e=n});return{read(){if("pending"===r)throw o;if("error"===r)throw e;return e},update(n){e=n,r="success"},get:()=>e}}function m(n){return(n=n.trim()).startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function g(n){return m(n).split("/").filter(Boolean)}function w(n){const[t,r]=n.split("#"),[e,o]=t.split("?"),a={};return o&&o.split("&").forEach(n=>{const[t,r]=n.split("=");if(!t)return;const e=decodeURIComponent(t),o=decodeURIComponent(r??"");void 0!==a[e]?Array.isArray(a[e])?a[e].push(o):a[e]=[a[e],o]:a[e]=o}),{path:m(e),query:a,hash:r??""}}function v(n,t){const r=n[t];return Array.isArray(r)?r[0]:r}function q(n){const t={};if(!n)return t;if("string"==typeof n){const r=new URLSearchParams(n);r.forEach((n,e)=>{const o=r.getAll(e);t[e]=o.length>1?o:o[0]})}else for(const r in n){const e=n[r];Array.isArray(e)?t[r]=e.map(String):null!=e&&(t[r]=String(e))}return t}function P(n){const t=g(n);let r=10*t.length;for(const n of t)"*"===n?r-=100:n.startsWith(":")?r+=1:r+=5;return r}function b(n){return"*"===n}function E(n){const t=[],r=[];for(const e of n)b(e.path)?t.push(e):r.push(e);return r.sort((n,t)=>P(t.path)-P(n.path)),[...r,...t]}function k(n,t){const{path:r,query:e,hash:o}=w(t),a=g(r),i=E(n).reverse().map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;i.length;){const{node:n,segIndex:t,params:r,chain:s}=i.pop(),c=g(n.path),u={...r};let h=0;for(;h<c.length;h++){const n=c[h],r=a[t+h];if(!r)break;if("*"===n){if(h!==c.length-1)break;const n=a.slice(t+h);u["*"]=n.join("/"),u._subPath="/"+n.join("/"),h=c.length;break}if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(r);else if(n!==r)break}if(h!==c.length)continue;const l=t+h,f=[...s,{node:n,params:u,query:e,hash:o}];if(l===a.length&&!b(n.path))return f;const p=E(n.children||[]);if(0===p.length){if(l===a.length)return f;continue}const d=[],y=[];for(const n of p)"*"===n.path?y.push(n):d.push(n);for(let n=y.length-1;n>=0;n--)i.push({node:y[n],segIndex:l,params:u,chain:f});for(let n=d.length-1;n>=0;n--)i.push({node:d[n],segIndex:l,params:u,chain:f})}return null}function x(n,t,r,e,o){const{path:a,query:i,hash:s}=w(n);let c=m(a);if(t)for(const n in t)c=c.replace(`:${n}`,encodeURIComponent(t[n]));const u=o?.replaceQuery?{...r??{}}:{...i,...r??{}},h=new URLSearchParams;for(const n in u){const t=u[n];Array.isArray(t)?t.forEach(t=>h.append(n,String(t))):h.append(n,String(t))}const l=h.toString();return l&&(c+=`?${l}`),e?c+=`#${e}`:s&&(c+=`#${s}`),c}function N(n){return JSON.stringify(Object.keys(n||{}).sort().reduce((t,r)=>(t[r]=n[r],t),{}))}function S(n,t){return`${n.path}|${N(t.params)}|${N(t.query)}|${t.hash}`}function A(n){return(...t)=>Promise.resolve(n(...t))}let L=[];function R(){L=[]}function j(){return L}function K(n){const t={guard:r=>(n.guardFns||(n.guardFns=[]),n.guardFns.push(r),t),middleware:r=>(n.middlewares||(n.middlewares=[]),n.middlewares.push(r),t),meta:r=>(n.meta={...n.meta||{},...r},t),onEnter:r=>(n.onEnter||(n.onEnter=[]),n.onEnter.push(r),t),onLeave:r=>(n.onLeave||(n.onLeave=[]),n.onLeave.push(r),t),onError:r=>(n.onError||(n.onError=[]),n.onError.push(r),t),policy:r=>(n.policies||(n.policies=[]),n.policies.push(r),t),errorBoundary:r=>(n.errorBoundary=r,t),prefetch:r=>(n.prefetch=r,t),route(t,r,e){const o=m(t),a={path:o,component:r,children:[],options:{},guardFns:[],middlewares:[],parent:n};"function"==typeof e?K(a)(e):e&&(a.options=e);const i=b(o)&&function(n){return!n||""===n||"/"===n}(n?.path);if(i){n.children&&(n.children=n.children.filter(n=>!b(n.path)));const t=L.find(n=>b(n.path));return t?(Object.assign(t,{...a,parent:void 0}),K(t).api):(a.parent=void 0,L.push(a),K(a).api)}return n.children.push(a),K(a).api}};return Object.assign(n=>n(t),{api:t})}function C(n,t,r){const e={path:m(n),component:t,children:[],options:{},parent:void 0};if("function"==typeof r?K(e)(r):r&&(e.options=r),b(e.path)){const n=L.find(n=>b(n.path));if(n)return Object.assign(n,e),K(n).api}return L.push(e),K(e).api}const I=t(null),O=t(null),$=t(null),B=t({loading:!1,path:"",pendingPath:""}),M=t({name:"none",stage:"idle"});function F(){const n=r(I);try{return n?.read()}catch{return}}function D(){const n=r(O);if(!n)throw new Error("RouteContext not found");return n}function Q(){return r($)??{}}function U(){try{const n=D();if(n?.query)return n.query}catch{}const{query:n}=w(window.location.pathname+window.location.search+window.location.hash);return n}function W(){return r(B)}function T(){return r(M)}function G(){try{return F()}catch{return}}function J(){const{meta:n,params:t,path:r,query:e}=D();return{data:G(),params:t??{},query:e??{},path:r,meta:n}}const _=200;function z({name:n="fade",stage:t,duration:r=200,children:i}){const[s,f]=e(i),[p,d]=e(null),y=o();return a(()=>{y.current&&clearTimeout(y.current),"exiting"===t&&d(s),"entering"===t&&(f(i),y.current=window.setTimeout(()=>{d(null)},r))},[i,t,r]),"idle"===t?c(u,{children:i}):h("div",{className:l("rtx",`rtx-${n}`),children:[p&&c("div",{className:"rtx-exit",children:p}),c("div",{className:l("rtx-enter",`rtx-${t}`),children:s})]})}let H=!1,V=!1,X=c("div",{children:"Loading..."});const Y=({error:n})=>h("div",{children:["Global Error: ",String(n)]});let Z=null;function nn(n,t){H=n,t&&(X=t)}function tn(n,t){V=n,Z=t??null}function rn(){return H}function en(){return V}function on(){return X}function an(){return Z?({error:n})=>Z(n):Y}class sn extends n.Component{constructor(n){super(n),this.reset=()=>{this.setState(n=>({hasError:!1,error:null,resetKey:n.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(n){return{hasError:!0,error:n}}render(){if(this.state.hasError){const n=this.props.errorBoundary??(en()?an():void 0);return n?c(n,{error:this.state.error,params:this.props.params}):this.props.fallback??h("div",{children:["Error: ",String(this.state.error)]})}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function cn({resource:t,errorBoundary:r,children:e,resetKey:o}){const[a,i]=n.useState(null);n.useEffect(()=>{i(null)},[o]);try{return t?.read(),a&&i(null),c(u,{children:e})}catch(n){if(n instanceof Promise)throw n;const t=r??(en()?an():void 0);if(t)return c(t,{error:n});throw n}}function un({chain:n,data:t}){return c(u,{children:function r(e=0){const o=n[e];if(!o)return null;const a=o.node.component;if(!a)return r(e+1);const s={params:o.params,path:o.node.path,query:o.query,hash:o.hash},u=function(n,t){let r=n;for(;r;){const n=r.options?.fallback;if(n)return"function"==typeof n?n(t):n;r=r.parent}return rn()?on():null}(o.node,s),h=Object.assign({},...n.slice(0,e+1).map(n=>n.node.meta)),l=t[S(o.node,s)];return c(sn,{fallback:u,errorBoundary:o.node.options?.errorBoundary,params:o.params,children:c(i,{fallback:u,children:c(O.Provider,{value:{path:o.node.path,params:o.params,query:q(o.query),hash:o.hash??"",meta:h},children:c(I.Provider,{value:l,children:c($.Provider,{value:o.params,children:c(cn,{resource:l,errorBoundary:o.node.options?.errorBoundary,resetKey:o.params.id||o.params,children:c(a,{children:r(e+1)})})})})})})})}()})}function hn(n,t){const r={};for(const e of n){const n=e.node.options?.loader;if(!n)continue;const o={params:e.params,path:e.node.path,query:e.query,hash:e.hash},a=S(e.node,o),i=e.node.options?.ttl,s=t?.forceReload??e.node.options?.shouldReload?.({prev:void 0,next:o})??!0;r[a]=d(a,()=>Promise.resolve(n(o)),i,s)}return r}let ln=null;function fn(){return ln}function pn(n,t){const r=j(),[e,o]=n.split("?"),a={...q(o),...t?.query??{}},i=k(r,x(e,t?.params,a,t?.hash));if(i){for(const n of i)n.node.prefetch&&n.node.prefetch({params:n.params,path:n.node.path,query:n.query,hash:n.hash});hn(i,{forceReload:!1})}}function dn({transition:n,duration:t,wrapper:r}){const[i,s]=e({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:t??_}),u=o(0),h=async(r,e)=>{const o=++u.current,a=i.chain,c=function(n){const t=j();return k(t,n)??k(t,"*")??[]}(r);try{await async function(n){for(let t=n.length-1;t>=0;t--){const r=n[t];for(const n of r.node.onLeave||[])await n({params:r.params,path:r.node.path,query:r.query,hash:r.hash})}}(a);const i=await async function(n,t){for(const r of n)for(const n of r.node.policies||[]){const e=await n({params:r.params,path:r.node.path,query:r.query,hash:r.hash});if(!1===e)return!1;if("string"==typeof e)return t(e),!1}return!0}(c,h);if(!i)return!1;const l=await async function(n,t){for(const r of n)for(const n of r.node.guardFns||[]){const e=await n({params:r.params,path:r.node.path,query:r.query,hash:r.hash});if(!1===e)return!1;if("object"==typeof e&&"redirect"in e)return await t(e.redirect),!1}return!0}(c,h);if(!l)return!1;await async function(n){for(const t of n)for(const n of t.node.middlewares||[])await Promise.resolve(n({params:t.params,path:t.node.path,query:t.query,hash:t.hash})).catch(console.error)}(c);const f=function(n,t,r){if(t?.transition)return t.transition;for(let t=n.length-1;t>=0;t--){const r=n[t].node;if(r.options?.transition)return r.options.transition;if(r.meta?.transition)return r.meta.transition}return r??"none"}(c,e,n),p=function(n,t,r){if(null!=t?.duration)return t.duration;for(let t=n.length-1;t>=0;t--){const r=n[t].node;if(null!=r.options?.duration)return r.options.duration}return null!=r?r:_}(c,e,t);!function(n,t,r,e){n(n=>({...n,loading:!0,pendingPath:t,transition:{name:r,stage:"exiting"},duration:e}))}(s,r,f,p),await new Promise(n=>setTimeout(n,p)),await async function(n){for(const t of n)for(const n of t.node.onEnter||[])await n({params:t.params,path:t.node.path,query:t.query,hash:t.hash})}(c);const d=e?.shallow?{}:hn(c,e);return o===u.current&&(function(n,t,r,e,o,a){n(n=>({...n,chain:t,data:{...n.data,...r},loading:!1,path:e,pendingPath:"",transition:{name:o,stage:"entering"},duration:a}))}(s,c,d??{},r,f,p),e?.shallow||window.scrollTo(0,0),!0)}catch(n){return await async function(n,t){for(let r=t.length-1;r>=0;r--){const e=t[r];for(const t of e.node.onError||[])try{await t(n,{params:e.params,path:e.node.path,query:e.query,hash:e.hash})}catch(n){console.error("Error in onError hook",n)}}}(n,c),!1}};a(()=>{ln=h;const n=()=>window.location.pathname+window.location.search+window.location.hash;h(n());const t=()=>h(n());window.addEventListener("popstate",t);const r=()=>{const t=k(j(),n());if(!t)return;const r={};for(const n of t)n.node.options?.revalidateOnFocus&&Object.assign(r,hn([n],{forceReload:!0}));Object.keys(r).length&&s(n=>({...n,data:{...n.data,...r}}))};return window.addEventListener("focus",r),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",r),ln=null}},[]);const l=c(un,{chain:i.chain,data:i.data},i.path);let f=l;if("none"!==i.transition.name&&(f=c(z,{name:i.transition.name,stage:i.transition.stage,duration:i.duration,children:l})),r){f=c(r,{children:f})}return c(B.Provider,{value:{loading:i.loading,path:i.path,pendingPath:i.pendingPath},children:c(M.Provider,{value:i.transition,children:f})})}const yn=[],mn=[];function gn(n){yn.push(n)}function wn(n){mn.push(n)}async function vn(n,t,r=!1){const e={...t,forceReload:t?.forceReload??!0},o=x(n,e.params,e.query,e.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(n){return(await Promise.all(yn.map(t=>t(n)))).every(n=>!1!==n)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=fn();if(!e.shallow&&a){if(!await a(o,e))return console.warn("Navigation blocked by route guard",o),!1}return r?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(n){for(const t of mn)await t(n)}(o),!0}function qn(n,t){return vn(n,t,!1)}function Pn(n,t){return qn(n,t)}function bn(){const n=(n,t)=>vn(n,t);return n.replace=(n,t)=>vn(n,t,!0),n}function En(n){return W().path===n}function kn(n,t){return()=>pn(n,t)}function xn({to:n,transition:t,replace:r,params:e,query:i,hash:s,partialMatch:c,disablePrefetch:u,replaceQuery:h}){const l=bn(),f=W(),p=o(null),d=x(n,e,i,s,{replaceQuery:h}),y=c?f.path?.startsWith(d):f.path===d;return a(()=>{if(u)return;const n=p.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(pn(d),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[d,u]),{ref:p,fullPath:d,isActive:y,go:async()=>r?l.replace(n,{transition:t,params:e,query:i,hash:s}):l(n,{transition:t,params:e,query:i,hash:s})}}qn.replace=function(n,t){return vn(n,t,!0)};const Nn=s(({fullPath:n,onNavigate:t,onPrefetch:r,onClick:e,onMouseEnter:o,onKeyDown:a,disablePrefetch:i,...s},u)=>c("a",{ref:u,href:n,...s,onClick:async r=>{if(e&&e(r),r.metaKey||r.ctrlKey||r.shiftKey||r.altKey)return;r.preventDefault();await t()||console.warn("Navigation blocked:",n)},onMouseEnter:n=>{o&&o(n),i||r?.()},onKeyDown:async r=>{if("Enter"===r.key){r.preventDefault();await t()||console.warn("Navigation blocked:",n)}a&&a(r)}}));function Sn(n){const{to:t,transition:r,replace:e,activeClassName:o="active",onNavigate:a,partialMatch:i,params:s,query:u,hash:h,className:f,disablePrefetch:p,replaceQuery:d,...y}=n,{ref:m,fullPath:g,isActive:w,go:v}=xn({to:t,transition:r,replace:e,params:s,query:u,hash:h,partialMatch:i,disablePrefetch:p,replaceQuery:d});return c(Nn,{ref:m,fullPath:g,onNavigate:async()=>{const n=await v();return a?.(g),n},onPrefetch:()=>pn(g),className:l(f,w&&o),disablePrefetch:p,...y})}function An(n){const{to:t,transition:r,replace:e,params:o,query:a,hash:i,partialMatch:s,className:u,style:h,disablePrefetch:f,replaceQuery:p,...d}=n,{ref:y,fullPath:m,isActive:g,go:w}=xn({to:t,transition:r,replace:e,params:o,query:a,hash:i,partialMatch:s,disablePrefetch:f,replaceQuery:p}),v="function"==typeof u?u({isActive:g}):u,q="function"==typeof h?h({isActive:g}):h;return c(Nn,{ref:y,fullPath:m,onNavigate:async()=>(await w(),!0),onPrefetch:()=>pn(m),className:l(v),style:q,disablePrefetch:f,...d})}Nn.displayName="LinkCore";export{_ as DEFAULT_DURATION,Sn as Link,I as LoaderDataContext,An as NavLink,$ as ParamsContext,O as RouteContext,dn as RouterProvider,B as RouterStateContext,z as Transition,M as TransitionContext,wn as addAfterNavigationHook,gn as addBeforeNavigationHook,S as buildCacheKey,x as buildPath,p as cleanupCache,R as clearRoutes,y as createResource,an as getGlobalError,on as getGlobalFallback,fn as getGlobalResolve,v as getQueryFirst,d as getResource,j as getRoutes,en as getUseGlobalError,rn as getUseGlobalFallback,b as isWildcard,k as matchRouteChain,qn as navigate,m as normalize,q as normalizeQuery,w as parseQueryHash,pn as prefetch,Pn as redirect,C as route,tn as setGlobalError,nn as setGlobalFallback,g as split,xn as useLinkCore,F as useLoaderData,bn as useNavigate,W as useNavigation,Q as useParams,kn as usePrefetch,U as useQuery,D as useRouteContext,En as useRouteMatch,J as useRouterData,G as useSafeLoaderData,T as useTransition,A as wrapAsync};
|
package/build/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");const n=new Map;function o(e,t,r,o){const s=Date.now(),i=n.get(e);if(i&&!o&&(!r||i.expiry>s))return i.resource;const c=a(t());return n.set(e,{resource:c,expiry:s+(r??0)}),c}function a(e,t){let r=t?"success":"pending",n=t??null;const o=e.then(e=>{r="success",n=e},e=>{r="error",n=e});return{read(){if("pending"===r)throw o;if("error"===r)throw n;return n},update(e){n=e,r="success"},get:()=>n}}function s(e){return e.startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function i(e){return s(e).split("/").filter(Boolean)}function c(e){const[t,r]=e.split("#"),[n,o]=t.split("?"),a={};return o&&o.split("&").forEach(e=>{const[t,r]=e.split("=");if(!t)return;const n=decodeURIComponent(t),o=decodeURIComponent(r??"");void 0!==a[n]?Array.isArray(a[n])?a[n].push(o):a[n]=[a[n],o]:a[n]=o}),{path:s(n),query:a,hash:r??""}}function u(e){const t={};if(!e)return t;if("string"==typeof e){const r=new URLSearchParams(e);r.forEach((e,n)=>{const o=r.getAll(n);t[n]=o.length>1?o:o[0]})}else for(const r in e){const n=e[r];Array.isArray(n)?t[r]=n.map(e=>String(e)):null!=n&&(t[r]=String(n))}return t}function l(e){const t=i(e);let r=10*t.length;for(const e of t)"*"===e?r+=0:e.startsWith(":")?r+=1:r+=5;return r}function p(e){return[...e].sort((e,t)=>l(t.path)-l(e.path))}function h(e,t){const{path:r,query:n,hash:o}=c(t),a=i(r),s=p(e).map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:c}=s.pop(),u=i(e.path),l={...r};let h=0;for(;h<u.length;h++){const e=u[h],r=a[t+h];if(!r)break;if("*"===e){l["*"]=a.slice(t+h).join("/"),h=u.length;break}if(e.startsWith(":"))l[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(h!==u.length)continue;const d=t+h,f=[...c,{node:e,params:l,query:n,hash:o}];if(d===a.length)return f;const x=p(e.children||[]);for(let e=x.length-1;e>=0;e--){const t=i(x[e].path).some(e=>e.startsWith(":"));s.push({node:x[e],segIndex:t?0:d,params:l,chain:f})}}return null}function d(e,t,r,n,o){const{path:a,query:i,hash:u}=c(e);let l=s(a);if(t)for(const e in t)l=l.replace(`:${e}`,encodeURIComponent(t[e]));const p=o?.replaceQuery?{...r??{}}:{...i,...r??{}},h=new URLSearchParams;for(const e in p){const t=p[e];Array.isArray(t)?t.forEach(t=>h.append(e,String(t))):h.append(e,String(t))}const d=h.toString();return d&&(l+=`?${d}`),n?l+=`#${n}`:u&&(l+=`#${u}`),l}function f(e){return JSON.stringify(Object.keys(e||{}).sort().reduce((t,r)=>(t[r]=e[r],t),{}))}function x(e,t){return`${e.path}|${f(t.params)}|${f(t.query)}|${t.hash}`}let m=[];function y(){return m}function g(e){const t={guard:r=>(e.guardFns||(e.guardFns=[]),e.guardFns.push(r),t),middleware:r=>(e.middlewares||(e.middlewares=[]),e.middlewares.push(r),t),meta:r=>(e.meta={...e.meta||{},...r},t),route(t,r,n){const o={path:s(t),component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};return"function"==typeof n?g(o)(n):n&&(o.options=n),e.children.push(o),g(o).api}};return Object.assign(e=>e(t),{api:t})}const w=e.createContext(null),v=e.createContext(null),b=e.createContext(null),P=e.createContext({loading:!1,path:"",pendingPath:""}),j=e.createContext({name:"none",stage:"idle"});function q(){const t=e.useContext(w);try{return t?.read()}catch{return}}function C(){const t=e.useContext(v);if(!t)throw new Error("RouteContext not found");return t}function R(){return e.useContext(P)}function k(){try{return q()}catch{return}}const E=200;function N({name:n="fade",stage:o,duration:a=200,children:s}){const[i,c]=e.useState(s),[u,l]=e.useState(null),p=e.useRef();return e.useEffect(()=>{p.current&&clearTimeout(p.current),"exiting"===o&&l(i),"entering"===o&&(c(s),p.current=window.setTimeout(()=>{l(null)},a))},[s,o,a]),"idle"===o?t.jsx(t.Fragment,{children:s}):t.jsxs("div",{className:r("rtx",`rtx-${n}`),children:[u&&t.jsx("div",{className:"rtx-exit",children:u}),t.jsx("div",{className:r("rtx-enter",`rtx-${o}`),children:i})]})}let S=!1,A=!1,F=t.jsx("div",{children:"Loading..."});const L=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});let D=null;function K(){return S}function Q(){return A}function I(){return F}function M(){return D?({error:e})=>D(e):L}class U extends e.Component{constructor(e){super(e),this.reset=()=>{this.setState(e=>({hasError:!1,error:null,resetKey:e.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}render(){if(this.state.hasError){const e=this.props.errorBoundary??(Q()?M():void 0);return e?t.jsx(e,{error:this.state.error,params:this.props.params}):this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]})}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function $({resource:r,errorBoundary:n,children:o,resetKey:a}){const[s,i]=e.useState(null);e.useEffect(()=>{i(null)},[a]);try{return r?.read(),s&&i(null),t.jsx(t.Fragment,{children:o})}catch(e){if(e instanceof Promise)throw e;const r=n??(Q()?M():void 0);if(r)return t.jsx(r,{error:e});throw e}}function O({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const s=r[a];if(!s)return null;const i=s.node.component;if(!i)return o(a+1);const c={params:s.params,path:s.node.path,query:s.query,hash:s.hash},l=function(e,t){let r=e;for(;r;){const e=r.options?.fallback;if(e)return"function"==typeof e?e(t):e;r=r.parent}return K()?I():null}(s.node,c),p=Object.assign({},...r.slice(0,a+1).map(e=>e.node.meta)),h=n[x(s.node,c)];return t.jsx(U,{fallback:l,errorBoundary:s.node.options?.errorBoundary,params:s.params,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(v.Provider,{value:{path:s.node.path,params:s.params,query:u(s.query),hash:s.hash??"",meta:p},children:t.jsx(w.Provider,{value:h,children:t.jsx(b.Provider,{value:s.params,children:t.jsx($,{resource:h,errorBoundary:s.node.options?.errorBoundary,resetKey:s.params.id||s.params,children:t.jsx(i,{children:o(a+1)})})})})})})})}()})}function T(e,t){const r={};for(const n of e){const e=n.node.options?.loader;if(!e)continue;const a={params:n.params,path:n.node.path,query:n.query,hash:n.hash},s=x(n.node,a),i=n.node.options?.ttl,c=t?.forceReload??n.node.options?.shouldReload?.({prev:void 0,next:a})??!0;r[s]=o(s,()=>Promise.resolve(e(a)),i,c)}return r}let B=null;function G(){return B}function W(e,t){const r=y(),[n,o]=e.split("?"),a={...u(o),...t?.query??{}},s=h(r,d(n,t?.params,a,t?.hash));s&&T(s,{forceReload:!1})}const H=[],z=[];async function J(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=d(e,n.params,n.query,n.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(e){return(await Promise.all(H.map(t=>t(e)))).every(e=>!1!==e)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=G();if(!n.shallow&&a){if(!await a(o,n))return console.warn("Navigation blocked by route guard",o),!1}return r?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(e){for(const t of z)await t(e)}(o),!0}function _(e,t){return J(e,t,!1)}function V(){const e=(e,t)=>J(e,t);return e.replace=(e,t)=>J(e,t,!0),e}function X({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c,replaceQuery:u}){const l=V(),p=R(),h=e.useRef(null),f=d(t,o,a,s,{replaceQuery:u}),x=i?p.path?.startsWith(f):p.path===f;return e.useEffect(()=>{if(c)return;const e=h.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(W(f),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[f,c]),{ref:h,fullPath:f,isActive:x,go:async()=>n?l.replace(t,{transition:r,params:o,query:a,hash:s}):l(t,{transition:r,params:o,query:a,hash:s})}}_.replace=function(e,t){return J(e,t,!0)};const Y=e.forwardRef(({fullPath:e,onNavigate:r,onPrefetch:n,onClick:o,onMouseEnter:a,onKeyDown:s,disablePrefetch:i,...c},u)=>t.jsx("a",{ref:u,href:e,...c,onClick:async t=>{if(o&&o(t),t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;t.preventDefault();await r()||console.warn("Navigation blocked:",e)},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:async t=>{if("Enter"===t.key){t.preventDefault();await r()||console.warn("Navigation blocked:",e)}s&&s(t)}}));Y.displayName="LinkCore",exports.DEFAULT_DURATION=E,exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:l,hash:p,className:h,disablePrefetch:d,replaceQuery:f,...x}=e,{ref:m,fullPath:y,isActive:g,go:w}=X({to:n,transition:o,replace:a,params:u,query:l,hash:p,partialMatch:c,disablePrefetch:d,replaceQuery:f});return t.jsx(Y,{ref:m,fullPath:y,onNavigate:async()=>{const e=await w();return i?.(y),e},onPrefetch:()=>W(y),className:r(h,g&&s),disablePrefetch:d,...x})},exports.LoaderDataContext=w,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:l,style:p,disablePrefetch:h,replaceQuery:d,...f}=e,{ref:x,fullPath:m,isActive:y,go:g}=X({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:h,replaceQuery:d}),w="function"==typeof l?l({isActive:y}):l,v="function"==typeof p?p({isActive:y}):p;return t.jsx(Y,{ref:x,fullPath:m,onNavigate:async()=>(await g(),!0),onPrefetch:()=>W(m),className:r(w),style:v,disablePrefetch:h,...f})},exports.ParamsContext=b,exports.RouteContext=v,exports.RouterProvider=function({transition:r,duration:n,wrapper:o}){const[a,s]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:n??E}),i=e.useRef(0),c=async(e,t)=>{const o=++i.current,a=function(e){const t=y();return h(t,e)??h(t,"*")??[]}(e),u=await async function(e,t){for(const r of e)for(const e of r.node.guardFns||[]){const n=await e({params:r.params,path:r.node.path,query:r.query,hash:r.hash});if(!1===n)return!1;if("object"==typeof n&&"redirect"in n)return await t(n.redirect),!1}return!0}(a,c);if(!u)return!1;await async function(e){for(const t of e)for(const e of t.node.middlewares||[])await Promise.resolve(e({params:t.params,path:t.node.path,query:t.query,hash:t.hash})).catch(console.error)}(a);const l=function(e,t,r){if(t?.transition)return t.transition;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(r.options?.transition)return r.options.transition;if(r.meta?.transition)return r.meta.transition}return r??"none"}(a,t,r),p=function(e,t,r){if(null!=t?.duration)return t.duration;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(null!=r.options?.duration)return r.options.duration}return null!=r?r:E}(a,t,n);!function(e,t,r,n){e(e=>({...e,loading:!0,pendingPath:t,transition:{name:r,stage:"exiting"},duration:n}))}(s,e,l,p),await new Promise(e=>setTimeout(e,p));const d=t?.shallow?{}:T(a,t);return o===i.current&&(function(e,t,r,n,o,a){e(e=>({...e,chain:t,data:{...e.data,...r},loading:!1,path:n,pendingPath:"",transition:{name:o,stage:"entering"},duration:a}))}(s,a,d??{},e,l,p),t?.shallow||window.scrollTo(0,0),!0)};e.useEffect(()=>{B=c;const e=()=>window.location.pathname+window.location.search+window.location.hash;c(e());const t=()=>c(e());window.addEventListener("popstate",t);const r=()=>{const t=h(y(),e());if(!t)return;const r={};for(const e of t)e.node.options?.revalidateOnFocus&&Object.assign(r,T([e],{forceReload:!0}));Object.keys(r).length&&s(e=>({...e,data:{...e.data,...r}}))};return window.addEventListener("focus",r),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",r),B=null}},[]);const u=t.jsx(O,{chain:a.chain,data:a.data},a.path);let l=u;if("none"!==a.transition.name&&(l=t.jsx(N,{name:a.transition.name,stage:a.transition.stage,duration:a.duration,children:u})),o){const e=o;l=t.jsx(e,{children:l})}return t.jsx(P.Provider,{value:{loading:a.loading,path:a.path,pendingPath:a.pendingPath},children:t.jsx(j.Provider,{value:a.transition,children:l})})},exports.RouterStateContext=P,exports.Transition=N,exports.TransitionContext=j,exports.addAfterNavigationHook=function(e){z.push(e)},exports.addBeforeNavigationHook=function(e){H.push(e)},exports.buildCacheKey=x,exports.buildPath=d,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[o,a]of n.entries())if(a.expiry<=t&&(n.delete(o),r++,r>=e))break},exports.clearRoutes=function(){m=[]},exports.createResource=a,exports.getGlobalError=M,exports.getGlobalFallback=I,exports.getGlobalResolve=G,exports.getQueryFirst=function(e,t){const r=e[t];return Array.isArray(r)?r[0]:r},exports.getResource=o,exports.getRoutes=y,exports.getUseGlobalError=Q,exports.getUseGlobalFallback=K,exports.matchRouteChain=h,exports.navigate=_,exports.normalize=s,exports.normalizeQuery=u,exports.parseQueryHash=c,exports.prefetch=W,exports.redirect=function(e,t){return _(e,t)},exports.route=function(e,t,r){const n={path:s(e),component:t,children:[],options:{},parent:void 0};return"function"==typeof r?g(n)(r):r&&(n.options=r),m.push(n),g(n).api},exports.setGlobalError=function(e,t){A=e,D=t??null},exports.setGlobalFallback=function(e,t){S=e,t&&(F=t)},exports.split=i,exports.useLinkCore=X,exports.useLoaderData=q,exports.useNavigate=V,exports.useNavigation=R,exports.useParams=function(){return e.useContext(b)??{}},exports.usePrefetch=function(e,t){return()=>W(e,t)},exports.useQuery=function(){try{const e=C();if(e?.query)return e.query}catch{}const{query:e}=c(window.location.pathname+window.location.search+window.location.hash);return e},exports.useRouteContext=C,exports.useRouteMatch=function(e){return R().path===e},exports.useRouterData=function(){const{meta:e,params:t,path:r,query:n}=C();return{data:k(),params:t??{},query:n??{},path:r,meta:e}},exports.useSafeLoaderData=k,exports.useTransition=function(){return e.useContext(j)};
|
|
1
|
+
"use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");const n=new Map;function o(e,t,r,o){const s=Date.now(),i=n.get(e);if(i&&!o&&(!r||i.expiry>s))return i.resource;const c=a(t());return n.set(e,{resource:c,expiry:s+(r??0)}),c}function a(e,t){let r=t?"success":"pending",n=t??null;const o=e.then(e=>{r="success",n=e},e=>{r="error",n=e});return{read(){if("pending"===r)throw o;if("error"===r)throw n;return n},update(e){n=e,r="success"},get:()=>n}}function s(e){return(e=e.trim()).startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function i(e){return s(e).split("/").filter(Boolean)}function c(e){const[t,r]=e.split("#"),[n,o]=t.split("?"),a={};return o&&o.split("&").forEach(e=>{const[t,r]=e.split("=");if(!t)return;const n=decodeURIComponent(t),o=decodeURIComponent(r??"");void 0!==a[n]?Array.isArray(a[n])?a[n].push(o):a[n]=[a[n],o]:a[n]=o}),{path:s(n),query:a,hash:r??""}}function u(e){const t={};if(!e)return t;if("string"==typeof e){const r=new URLSearchParams(e);r.forEach((e,n)=>{const o=r.getAll(n);t[n]=o.length>1?o:o[0]})}else for(const r in e){const n=e[r];Array.isArray(n)?t[r]=n.map(String):null!=n&&(t[r]=String(n))}return t}function l(e){const t=i(e);let r=10*t.length;for(const e of t)"*"===e?r-=100:e.startsWith(":")?r+=1:r+=5;return r}function h(e){return"*"===e}function p(e){const t=[],r=[];for(const n of e)h(n.path)?t.push(n):r.push(n);return r.sort((e,t)=>l(t.path)-l(e.path)),[...r,...t]}function f(e,t){const{path:r,query:n,hash:o}=c(t),a=i(r),s=p(e).reverse().map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:c}=s.pop(),u=i(e.path),l={...r};let f=0;for(;f<u.length;f++){const e=u[f],r=a[t+f];if(!r)break;if("*"===e){if(f!==u.length-1)break;const e=a.slice(t+f);l["*"]=e.join("/"),l._subPath="/"+e.join("/"),f=u.length;break}if(e.startsWith(":"))l[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(f!==u.length)continue;const d=t+f,y=[...c,{node:e,params:l,query:n,hash:o}];if(d===a.length&&!h(e.path))return y;const m=p(e.children||[]);if(0===m.length){if(d===a.length)return y;continue}const x=[],g=[];for(const e of m)"*"===e.path?g.push(e):x.push(e);for(let e=g.length-1;e>=0;e--)s.push({node:g[e],segIndex:d,params:l,chain:y});for(let e=x.length-1;e>=0;e--)s.push({node:x[e],segIndex:d,params:l,chain:y})}return null}function d(e,t,r,n,o){const{path:a,query:i,hash:u}=c(e);let l=s(a);if(t)for(const e in t)l=l.replace(`:${e}`,encodeURIComponent(t[e]));const h=o?.replaceQuery?{...r??{}}:{...i,...r??{}},p=new URLSearchParams;for(const e in h){const t=h[e];Array.isArray(t)?t.forEach(t=>p.append(e,String(t))):p.append(e,String(t))}const f=p.toString();return f&&(l+=`?${f}`),n?l+=`#${n}`:u&&(l+=`#${u}`),l}function y(e){return JSON.stringify(Object.keys(e||{}).sort().reduce((t,r)=>(t[r]=e[r],t),{}))}function m(e,t){return`${e.path}|${y(t.params)}|${y(t.query)}|${t.hash}`}let x=[];function g(){return x}function w(e){const t={guard:r=>(e.guardFns||(e.guardFns=[]),e.guardFns.push(r),t),middleware:r=>(e.middlewares||(e.middlewares=[]),e.middlewares.push(r),t),meta:r=>(e.meta={...e.meta||{},...r},t),onEnter:r=>(e.onEnter||(e.onEnter=[]),e.onEnter.push(r),t),onLeave:r=>(e.onLeave||(e.onLeave=[]),e.onLeave.push(r),t),onError:r=>(e.onError||(e.onError=[]),e.onError.push(r),t),policy:r=>(e.policies||(e.policies=[]),e.policies.push(r),t),errorBoundary:r=>(e.errorBoundary=r,t),prefetch:r=>(e.prefetch=r,t),route(t,r,n){const o=s(t),a={path:o,component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};"function"==typeof n?w(a)(n):n&&(a.options=n);const i=h(o)&&function(e){return!e||""===e||"/"===e}(e?.path);if(i){e.children&&(e.children=e.children.filter(e=>!h(e.path)));const t=x.find(e=>h(e.path));return t?(Object.assign(t,{...a,parent:void 0}),w(t).api):(a.parent=void 0,x.push(a),w(a).api)}return e.children.push(a),w(a).api}};return Object.assign(e=>e(t),{api:t})}const v=e.createContext(null),b=e.createContext(null),q=e.createContext(null),P=e.createContext({loading:!1,path:"",pendingPath:""}),j=e.createContext({name:"none",stage:"idle"});function E(){const t=e.useContext(v);try{return t?.read()}catch{return}}function C(){const t=e.useContext(b);if(!t)throw new Error("RouteContext not found");return t}function R(){return e.useContext(P)}function k(){try{return E()}catch{return}}const N=200;function S({name:n="fade",stage:o,duration:a=200,children:s}){const[i,c]=e.useState(s),[u,l]=e.useState(null),h=e.useRef();return e.useEffect(()=>{h.current&&clearTimeout(h.current),"exiting"===o&&l(i),"entering"===o&&(c(s),h.current=window.setTimeout(()=>{l(null)},a))},[s,o,a]),"idle"===o?t.jsx(t.Fragment,{children:s}):t.jsxs("div",{className:r("rtx",`rtx-${n}`),children:[u&&t.jsx("div",{className:"rtx-exit",children:u}),t.jsx("div",{className:r("rtx-enter",`rtx-${o}`),children:i})]})}let L=!1,A=!1,F=t.jsx("div",{children:"Loading..."});const D=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});let K=null;function I(){return L}function O(){return A}function Q(){return F}function B(){return K?({error:e})=>K(e):D}class M extends e.Component{constructor(e){super(e),this.reset=()=>{this.setState(e=>({hasError:!1,error:null,resetKey:e.resetKey+1}))},this.state={hasError:!1,error:null,resetKey:0}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}render(){if(this.state.hasError){const e=this.props.errorBoundary??(O()?B():void 0);return e?t.jsx(e,{error:this.state.error,params:this.props.params}):this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]})}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function U({resource:r,errorBoundary:n,children:o,resetKey:a}){const[s,i]=e.useState(null);e.useEffect(()=>{i(null)},[a]);try{return r?.read(),s&&i(null),t.jsx(t.Fragment,{children:o})}catch(e){if(e instanceof Promise)throw e;const r=n??(O()?B():void 0);if(r)return t.jsx(r,{error:e});throw e}}function $({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const s=r[a];if(!s)return null;const i=s.node.component;if(!i)return o(a+1);const c={params:s.params,path:s.node.path,query:s.query,hash:s.hash},l=function(e,t){let r=e;for(;r;){const e=r.options?.fallback;if(e)return"function"==typeof e?e(t):e;r=r.parent}return I()?Q():null}(s.node,c),h=Object.assign({},...r.slice(0,a+1).map(e=>e.node.meta)),p=n[m(s.node,c)];return t.jsx(M,{fallback:l,errorBoundary:s.node.options?.errorBoundary,params:s.params,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(b.Provider,{value:{path:s.node.path,params:s.params,query:u(s.query),hash:s.hash??"",meta:h},children:t.jsx(v.Provider,{value:p,children:t.jsx(q.Provider,{value:s.params,children:t.jsx(U,{resource:p,errorBoundary:s.node.options?.errorBoundary,resetKey:s.params.id||s.params,children:t.jsx(i,{children:o(a+1)})})})})})})})}()})}function T(e,t){const r={};for(const n of e){const e=n.node.options?.loader;if(!e)continue;const a={params:n.params,path:n.node.path,query:n.query,hash:n.hash},s=m(n.node,a),i=n.node.options?.ttl,c=t?.forceReload??n.node.options?.shouldReload?.({prev:void 0,next:a})??!0;r[s]=o(s,()=>Promise.resolve(e(a)),i,c)}return r}let G=null;function W(){return G}function H(e,t){const r=g(),[n,o]=e.split("?"),a={...u(o),...t?.query??{}},s=f(r,d(n,t?.params,a,t?.hash));if(s){for(const e of s)e.node.prefetch&&e.node.prefetch({params:e.params,path:e.node.path,query:e.query,hash:e.hash});T(s,{forceReload:!1})}}const z=[],_=[];async function J(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=d(e,n.params,n.query,n.hash);if(window.location.pathname+window.location.search+window.location.hash===o)return!0;if(!await async function(e){return(await Promise.all(z.map(t=>t(e)))).every(e=>!1!==e)}(o))return console.warn("Navigation blocked by beforeNavigation hook",o),!1;const a=W();if(!n.shallow&&a){if(!await a(o,n))return console.warn("Navigation blocked by route guard",o),!1}return r?window.history.replaceState({},"",o):window.history.pushState({},"",o),async function(e){for(const t of _)await t(e)}(o),!0}function V(e,t){return J(e,t,!1)}function X(){const e=(e,t)=>J(e,t);return e.replace=(e,t)=>J(e,t,!0),e}function Y({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c,replaceQuery:u}){const l=X(),h=R(),p=e.useRef(null),f=d(t,o,a,s,{replaceQuery:u}),y=i?h.path?.startsWith(f):h.path===f;return e.useEffect(()=>{if(c)return;const e=p.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(H(f),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[f,c]),{ref:p,fullPath:f,isActive:y,go:async()=>n?l.replace(t,{transition:r,params:o,query:a,hash:s}):l(t,{transition:r,params:o,query:a,hash:s})}}V.replace=function(e,t){return J(e,t,!0)};const Z=e.forwardRef(({fullPath:e,onNavigate:r,onPrefetch:n,onClick:o,onMouseEnter:a,onKeyDown:s,disablePrefetch:i,...c},u)=>t.jsx("a",{ref:u,href:e,...c,onClick:async t=>{if(o&&o(t),t.metaKey||t.ctrlKey||t.shiftKey||t.altKey)return;t.preventDefault();await r()||console.warn("Navigation blocked:",e)},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:async t=>{if("Enter"===t.key){t.preventDefault();await r()||console.warn("Navigation blocked:",e)}s&&s(t)}}));Z.displayName="LinkCore",exports.DEFAULT_DURATION=N,exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:l,hash:h,className:p,disablePrefetch:f,replaceQuery:d,...y}=e,{ref:m,fullPath:x,isActive:g,go:w}=Y({to:n,transition:o,replace:a,params:u,query:l,hash:h,partialMatch:c,disablePrefetch:f,replaceQuery:d});return t.jsx(Z,{ref:m,fullPath:x,onNavigate:async()=>{const e=await w();return i?.(x),e},onPrefetch:()=>H(x),className:r(p,g&&s),disablePrefetch:f,...y})},exports.LoaderDataContext=v,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:l,style:h,disablePrefetch:p,replaceQuery:f,...d}=e,{ref:y,fullPath:m,isActive:x,go:g}=Y({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:p,replaceQuery:f}),w="function"==typeof l?l({isActive:x}):l,v="function"==typeof h?h({isActive:x}):h;return t.jsx(Z,{ref:y,fullPath:m,onNavigate:async()=>(await g(),!0),onPrefetch:()=>H(m),className:r(w),style:v,disablePrefetch:p,...d})},exports.ParamsContext=q,exports.RouteContext=b,exports.RouterProvider=function({transition:r,duration:n,wrapper:o}){const[a,s]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"none",stage:"idle"},duration:n??N}),i=e.useRef(0),c=async(e,t)=>{const o=++i.current,u=a.chain,l=function(e){const t=g();return f(t,e)??f(t,"*")??[]}(e);try{await async function(e){for(let t=e.length-1;t>=0;t--){const r=e[t];for(const e of r.node.onLeave||[])await e({params:r.params,path:r.node.path,query:r.query,hash:r.hash})}}(u);const a=await async function(e,t){for(const r of e)for(const e of r.node.policies||[]){const n=await e({params:r.params,path:r.node.path,query:r.query,hash:r.hash});if(!1===n)return!1;if("string"==typeof n)return t(n),!1}return!0}(l,c);if(!a)return!1;const h=await async function(e,t){for(const r of e)for(const e of r.node.guardFns||[]){const n=await e({params:r.params,path:r.node.path,query:r.query,hash:r.hash});if(!1===n)return!1;if("object"==typeof n&&"redirect"in n)return await t(n.redirect),!1}return!0}(l,c);if(!h)return!1;await async function(e){for(const t of e)for(const e of t.node.middlewares||[])await Promise.resolve(e({params:t.params,path:t.node.path,query:t.query,hash:t.hash})).catch(console.error)}(l);const p=function(e,t,r){if(t?.transition)return t.transition;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(r.options?.transition)return r.options.transition;if(r.meta?.transition)return r.meta.transition}return r??"none"}(l,t,r),f=function(e,t,r){if(null!=t?.duration)return t.duration;for(let t=e.length-1;t>=0;t--){const r=e[t].node;if(null!=r.options?.duration)return r.options.duration}return null!=r?r:N}(l,t,n);!function(e,t,r,n){e(e=>({...e,loading:!0,pendingPath:t,transition:{name:r,stage:"exiting"},duration:n}))}(s,e,p,f),await new Promise(e=>setTimeout(e,f)),await async function(e){for(const t of e)for(const e of t.node.onEnter||[])await e({params:t.params,path:t.node.path,query:t.query,hash:t.hash})}(l);const d=t?.shallow?{}:T(l,t);return o===i.current&&(function(e,t,r,n,o,a){e(e=>({...e,chain:t,data:{...e.data,...r},loading:!1,path:n,pendingPath:"",transition:{name:o,stage:"entering"},duration:a}))}(s,l,d??{},e,p,f),t?.shallow||window.scrollTo(0,0),!0)}catch(e){return await async function(e,t){for(let r=t.length-1;r>=0;r--){const n=t[r];for(const t of n.node.onError||[])try{await t(e,{params:n.params,path:n.node.path,query:n.query,hash:n.hash})}catch(e){console.error("Error in onError hook",e)}}}(e,l),!1}};e.useEffect(()=>{G=c;const e=()=>window.location.pathname+window.location.search+window.location.hash;c(e());const t=()=>c(e());window.addEventListener("popstate",t);const r=()=>{const t=f(g(),e());if(!t)return;const r={};for(const e of t)e.node.options?.revalidateOnFocus&&Object.assign(r,T([e],{forceReload:!0}));Object.keys(r).length&&s(e=>({...e,data:{...e.data,...r}}))};return window.addEventListener("focus",r),()=>{window.removeEventListener("popstate",t),window.removeEventListener("focus",r),G=null}},[]);const u=t.jsx($,{chain:a.chain,data:a.data},a.path);let l=u;if("none"!==a.transition.name&&(l=t.jsx(S,{name:a.transition.name,stage:a.transition.stage,duration:a.duration,children:u})),o){const e=o;l=t.jsx(e,{children:l})}return t.jsx(P.Provider,{value:{loading:a.loading,path:a.path,pendingPath:a.pendingPath},children:t.jsx(j.Provider,{value:a.transition,children:l})})},exports.RouterStateContext=P,exports.Transition=S,exports.TransitionContext=j,exports.addAfterNavigationHook=function(e){_.push(e)},exports.addBeforeNavigationHook=function(e){z.push(e)},exports.buildCacheKey=m,exports.buildPath=d,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[o,a]of n.entries())if(a.expiry<=t&&(n.delete(o),r++,r>=e))break},exports.clearRoutes=function(){x=[]},exports.createResource=a,exports.getGlobalError=B,exports.getGlobalFallback=Q,exports.getGlobalResolve=W,exports.getQueryFirst=function(e,t){const r=e[t];return Array.isArray(r)?r[0]:r},exports.getResource=o,exports.getRoutes=g,exports.getUseGlobalError=O,exports.getUseGlobalFallback=I,exports.isWildcard=h,exports.matchRouteChain=f,exports.navigate=V,exports.normalize=s,exports.normalizeQuery=u,exports.parseQueryHash=c,exports.prefetch=H,exports.redirect=function(e,t){return V(e,t)},exports.route=function(e,t,r){const n={path:s(e),component:t,children:[],options:{},parent:void 0};if("function"==typeof r?w(n)(r):r&&(n.options=r),h(n.path)){const e=x.find(e=>h(e.path));if(e)return Object.assign(e,n),w(e).api}return x.push(n),w(n).api},exports.setGlobalError=function(e,t){A=e,K=t??null},exports.setGlobalFallback=function(e,t){L=e,t&&(F=t)},exports.split=i,exports.useLinkCore=Y,exports.useLoaderData=E,exports.useNavigate=X,exports.useNavigation=R,exports.useParams=function(){return e.useContext(q)??{}},exports.usePrefetch=function(e,t){return()=>H(e,t)},exports.useQuery=function(){try{const e=C();if(e?.query)return e.query}catch{}const{query:e}=c(window.location.pathname+window.location.search+window.location.hash);return e},exports.useRouteContext=C,exports.useRouteMatch=function(e){return R().path===e},exports.useRouterData=function(){const{meta:e,params:t,path:r,query:n}=C();return{data:k(),params:t??{},query:n??{},path:r,meta:e}},exports.useSafeLoaderData=k,exports.useTransition=function(){return e.useContext(j)},exports.wrapAsync=function(e){return(...t)=>Promise.resolve(e(...t))};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BuilderRouteAPI, ExtractParams, RouteComponent, RouteNode, RouteOptions } from './types';
|
|
2
2
|
export declare function clearRoutes(): void;
|
|
3
3
|
export declare function getRoutes(): RouteNode[];
|
|
4
|
-
export declare function route<Path extends string, R = any>(path: Path, component?: RouteComponent, options?: ((api: BuilderRouteAPI<ExtractParams<Path>>) => void) | RouteOptions<ExtractParams<Path>, R>): BuilderRouteAPI<ExtractParams<Path>>;
|
|
4
|
+
export declare function route<Path extends string, R = any>(path: Path, component?: RouteComponent, options?: ((api: BuilderRouteAPI<ExtractParams<Path>>) => void) | RouteOptions<ExtractParams<Path>, R>): BuilderRouteAPI<any> | BuilderRouteAPI<ExtractParams<Path>>;
|
|
5
5
|
export declare function loadRouteData(path: string): Promise<Record<string, any>>;
|
package/build/router/types.d.ts
CHANGED
|
@@ -10,6 +10,11 @@ export type LoaderContext<P = any> = {
|
|
|
10
10
|
hash?: string;
|
|
11
11
|
};
|
|
12
12
|
export type LoaderFn<P, R> = (context: LoaderContext<P>) => Promise<R> | R;
|
|
13
|
+
export type OnEnterFn<P = any> = (context: LoaderContext<P>) => void | Promise<void>;
|
|
14
|
+
export type OnLeaveFn<P = any> = (context: LoaderContext<P>) => void | Promise<void>;
|
|
15
|
+
export type OnErrorFn<P = any> = (error: unknown, context: LoaderContext<P>) => void | boolean | Promise<void | boolean>;
|
|
16
|
+
export type PolicyFn<P = any> = (context: LoaderContext<P>) => boolean | Promise<boolean>;
|
|
17
|
+
export type PrefetchFn<P = any> = (context: LoaderContext<P>) => Promise<any>;
|
|
13
18
|
export type RouteOptions<P = any, R = any> = {
|
|
14
19
|
loader?: LoaderFn<P, R>;
|
|
15
20
|
ttl?: number;
|
|
@@ -33,12 +38,12 @@ export type RouteOptions<P = any, R = any> = {
|
|
|
33
38
|
type GuardResult = boolean | {
|
|
34
39
|
redirect: string;
|
|
35
40
|
};
|
|
36
|
-
export type GuardFn<P = any> = (
|
|
41
|
+
export type GuardFn<P = any> = (context: LoaderContext<P>) => GuardResult | Promise<GuardResult>;
|
|
37
42
|
export type MiddlewareFn<P = any> = (context: LoaderContext<P>) => void | Promise<void>;
|
|
38
|
-
export type RouteComponent = React.ComponentType<{
|
|
43
|
+
export type RouteComponent<P = any> = React.ComponentType<P & {
|
|
39
44
|
children?: ReactNode;
|
|
40
45
|
}> | (() => Promise<{
|
|
41
|
-
default: React.ComponentType<{
|
|
46
|
+
default: React.ComponentType<P & {
|
|
42
47
|
children?: ReactNode;
|
|
43
48
|
}>;
|
|
44
49
|
}>);
|
|
@@ -46,11 +51,17 @@ export type RouteNode<P = any, R = any> = {
|
|
|
46
51
|
path: string;
|
|
47
52
|
component?: RouteComponent;
|
|
48
53
|
children: RouteNode[];
|
|
54
|
+
parent?: RouteNode;
|
|
49
55
|
options: RouteOptions<P, R>;
|
|
50
56
|
guardFns?: GuardFn<P>[];
|
|
51
57
|
middlewares?: MiddlewareFn<P>[];
|
|
52
58
|
meta?: Record<string, any>;
|
|
53
|
-
|
|
59
|
+
onEnter?: OnEnterFn<P>[];
|
|
60
|
+
onLeave?: OnLeaveFn<P>[];
|
|
61
|
+
onError?: OnErrorFn<P>[];
|
|
62
|
+
policies?: PolicyFn<P>[];
|
|
63
|
+
prefetch?: PrefetchFn<P>;
|
|
64
|
+
errorBoundary?: RouteComponent;
|
|
54
65
|
};
|
|
55
66
|
export type InternalRouteMatch<P = any> = {
|
|
56
67
|
node: RouteNode<P>;
|
|
@@ -67,6 +78,15 @@ export type BuilderRouteAPI<Params = any> = {
|
|
|
67
78
|
guard: (fn: GuardFn<Params>) => BuilderRouteAPI<Params>;
|
|
68
79
|
middleware: (fn: MiddlewareFn<Params>) => BuilderRouteAPI<Params>;
|
|
69
80
|
meta: (obj: Record<string, any>) => BuilderRouteAPI<Params>;
|
|
81
|
+
onEnter: (fn: OnEnterFn<Params>) => BuilderRouteAPI<Params>;
|
|
82
|
+
onLeave: (fn: OnLeaveFn<Params>) => BuilderRouteAPI<Params>;
|
|
83
|
+
onError: (fn: OnErrorFn<Params>) => BuilderRouteAPI<Params>;
|
|
84
|
+
policy: (fn: PolicyFn<Params>) => BuilderRouteAPI<Params>;
|
|
85
|
+
prefetch: (fn: PrefetchFn<Params>) => BuilderRouteAPI<Params>;
|
|
86
|
+
errorBoundary: (component: RouteComponent<{
|
|
87
|
+
error: any;
|
|
88
|
+
params?: Params;
|
|
89
|
+
}>) => BuilderRouteAPI<Params>;
|
|
70
90
|
route: <Path extends string, R = any>(path: Path, component?: RouteComponent, options?: ((api: BuilderRouteAPI<Params & ExtractParams<Path>>) => void) | RouteOptions<Params & ExtractParams<Path>, R>) => BuilderRouteAPI<Params & ExtractParams<Path>>;
|
|
71
91
|
};
|
|
72
92
|
export type InferParams<T> = T extends InternalRouteMatch<infer P> ? P : never;
|
package/build/router/utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Query, RouteNode } from './types';
|
|
1
|
+
import type { InternalRouteMatch, Query, RouteNode } from './types';
|
|
2
2
|
export declare function normalize(path: string): string;
|
|
3
3
|
export declare function split(path: string): string[];
|
|
4
4
|
export declare function parseQueryHash(pathname: string): {
|
|
@@ -8,8 +8,10 @@ export declare function parseQueryHash(pathname: string): {
|
|
|
8
8
|
};
|
|
9
9
|
export declare function getQueryFirst(q: Query, key: string): string | number;
|
|
10
10
|
export declare function normalizeQuery(input?: string | Record<string, any>): Record<string, string | string[]>;
|
|
11
|
-
export declare function
|
|
11
|
+
export declare function isWildcard(path: string): path is "*";
|
|
12
|
+
export declare function matchRouteChain(routes: RouteNode[], pathname: string): InternalRouteMatch[] | null;
|
|
12
13
|
export declare function buildPath(path: string, params?: Record<string, any>, query?: Record<string, any>, hash?: string, options?: {
|
|
13
14
|
replaceQuery?: boolean;
|
|
14
15
|
}): string;
|
|
15
16
|
export declare function buildCacheKey(node: RouteNode, context: any): string;
|
|
17
|
+
export declare function wrapAsync<T extends (...args: any[]) => any>(fn: T): (...args: Parameters<T>) => Promise<any>;
|
package/build/styles.css
CHANGED
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
FADE
|
|
18
18
|
========================= */
|
|
19
19
|
.rtx-fade .rtx-entering {
|
|
20
|
-
animation: rtxFadeIn 0.
|
|
20
|
+
animation: rtxFadeIn 0.20s ease forwards;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
.rtx-fade .rtx-exit {
|
|
24
|
-
animation: rtxFadeOut 0.
|
|
24
|
+
animation: rtxFadeOut 0.20s ease forwards;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
@keyframes rtxFadeIn {
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
SLIDE (from right)
|
|
39
39
|
========================= */
|
|
40
40
|
.rtx-slide .rtx-entering {
|
|
41
|
-
animation: rtxSlideIn 0.
|
|
41
|
+
animation: rtxSlideIn 0.20s ease forwards;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
.rtx-slide .rtx-exit {
|
|
45
|
-
animation: rtxSlideOut 0.
|
|
45
|
+
animation: rtxSlideOut 0.20s ease forwards;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
@keyframes rtxSlideIn {
|
|
@@ -59,11 +59,11 @@
|
|
|
59
59
|
SCALE (zoom)
|
|
60
60
|
========================= */
|
|
61
61
|
.rtx-scale .rtx-entering {
|
|
62
|
-
animation: rtxScaleIn 0.
|
|
62
|
+
animation: rtxScaleIn 0.20s ease forwards;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
.rtx-scale .rtx-exit {
|
|
66
|
-
animation: rtxScaleOut 0.
|
|
66
|
+
animation: rtxScaleOut 0.20s ease forwards;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
@keyframes rtxScaleIn {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "routexiz",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3-z1",
|
|
4
4
|
"description": "A modern tree-based router for React with intent-driven navigation, Suspense-first data loading, and built-in guards, middleware, and caching.",
|
|
5
5
|
"author": "Delpi.Kye",
|
|
6
6
|
"license": "MIT",
|