routexiz 0.0.21 → 0.1.0-z2
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 +108 -6
- package/build/index.esm.js +1 -1
- package/build/index.js +1 -1
- package/build/router/contexts.d.ts +6 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
A lightweight, powerful, and modern router for React.
|
|
8
8
|
|
|
9
|
-
> Tree-based routing —
|
|
9
|
+
> Tree-based routing — modeled like a TreeView, where each route is a node and navigation resolves a single path from root to leaf.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -78,20 +78,28 @@ route("/", Layout, root => {
|
|
|
78
78
|
dash.middleware(fn)
|
|
79
79
|
dash.meta({ title: "Dashboard" })
|
|
80
80
|
})
|
|
81
|
+
|
|
82
|
+
// more
|
|
81
83
|
})
|
|
82
84
|
|
|
83
|
-
// admin
|
|
85
|
+
// /admin
|
|
84
86
|
// admin/dashboard
|
|
85
|
-
route("/
|
|
87
|
+
route("/admin", Admin, root => {
|
|
86
88
|
root.route("/dashboard", AdminDashboard, dash => {
|
|
87
89
|
dash.guard(fn)
|
|
88
90
|
dash.middleware(fn)
|
|
89
91
|
dash.meta({ title: "Dashboard" })
|
|
90
92
|
})
|
|
93
|
+
|
|
94
|
+
// more
|
|
91
95
|
})
|
|
92
96
|
|
|
97
|
+
// more
|
|
98
|
+
|
|
93
99
|
```
|
|
94
100
|
|
|
101
|
+
> Supports multiple route trees and deeply nested routing via a flexible builder API.
|
|
102
|
+
|
|
95
103
|
---
|
|
96
104
|
|
|
97
105
|
# Navigation
|
|
@@ -150,6 +158,14 @@ const data = useLoaderData()
|
|
|
150
158
|
|
|
151
159
|
# Guards
|
|
152
160
|
|
|
161
|
+
A guard determines whether navigation is allowed.
|
|
162
|
+
|
|
163
|
+
- Runs before entering a route
|
|
164
|
+
- Can block navigation
|
|
165
|
+
- Returns:
|
|
166
|
+
- true → allow
|
|
167
|
+
- false → block
|
|
168
|
+
|
|
153
169
|
```ts
|
|
154
170
|
dash.guard(({ params }) => {
|
|
155
171
|
if (!isLoggedIn()) return false
|
|
@@ -160,6 +176,12 @@ dash.guard(({ params }) => {
|
|
|
160
176
|
|
|
161
177
|
# Middleware
|
|
162
178
|
|
|
179
|
+
A middleware performs side effects during navigation.
|
|
180
|
+
|
|
181
|
+
- Runs after guards
|
|
182
|
+
- Cannot block navigation
|
|
183
|
+
- Used for processing, not decision-making
|
|
184
|
+
|
|
163
185
|
```ts
|
|
164
186
|
dash.middleware(async ({ params }) => {
|
|
165
187
|
console.log("run side effects")
|
|
@@ -168,6 +190,24 @@ dash.middleware(async ({ params }) => {
|
|
|
168
190
|
|
|
169
191
|
---
|
|
170
192
|
|
|
193
|
+
# Key Differences Guards - Middleware
|
|
194
|
+
|
|
195
|
+
| Feature | Guard | Middleware |
|
|
196
|
+
| --------------- | ------------------------- | -------------------- |
|
|
197
|
+
| Purpose | Control access | Perform side effects |
|
|
198
|
+
| Can block route | ✅ Yes | ❌ No |
|
|
199
|
+
| Return value | `true / false / redirect` | Ignored |
|
|
200
|
+
| Execution order | First | After guards |
|
|
201
|
+
| Async support | ✅ Yes | ✅ Yes |
|
|
202
|
+
|
|
203
|
+
<br />
|
|
204
|
+
|
|
205
|
+
<b>Mental Model</b>
|
|
206
|
+
- Guard = "Can we enter?"
|
|
207
|
+
- Middleware = "Do something while entering"
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
171
211
|
# Prefetch
|
|
172
212
|
|
|
173
213
|
- Hover
|
|
@@ -189,6 +229,11 @@ export default function UsersList() {
|
|
|
189
229
|
<Link to="/users/2" params={{ id: 2 }} query={{ tab: "profile" }}>
|
|
190
230
|
User 2 (prefetch on viewport)
|
|
191
231
|
</Link>
|
|
232
|
+
|
|
233
|
+
{/* disablePrefetch */}
|
|
234
|
+
<Link to="/users/2" params={{ id: 2 }} query={{ tab: "profile" }} disablePrefetch>
|
|
235
|
+
User 2 (prefetch on viewport)
|
|
236
|
+
</Link>
|
|
192
237
|
</div>
|
|
193
238
|
)
|
|
194
239
|
}
|
|
@@ -418,6 +463,20 @@ function PageTransition() {
|
|
|
418
463
|
}
|
|
419
464
|
```
|
|
420
465
|
|
|
466
|
+
```css
|
|
467
|
+
.fade-entering {
|
|
468
|
+
opacity: 0;
|
|
469
|
+
transition: opacity 0.3s ease;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.fade-exiting {
|
|
473
|
+
opacity: 1;
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
> You are responsible for defining CSS transitions for each stage.
|
|
478
|
+
|
|
479
|
+
|
|
421
480
|
## 6️⃣ useRouterData()
|
|
422
481
|
|
|
423
482
|
```ts
|
|
@@ -425,7 +484,7 @@ import React from "react"
|
|
|
425
484
|
import { useRouterData } from "./hooks"
|
|
426
485
|
|
|
427
486
|
export function UserPage() {
|
|
428
|
-
const { data, params, query, path } = useRouterData<{ id: string; name: string }>()
|
|
487
|
+
const { data, params, query, path, meta } = useRouterData<{ id: string; name: string }>()
|
|
429
488
|
|
|
430
489
|
return (
|
|
431
490
|
<div>
|
|
@@ -434,6 +493,7 @@ export function UserPage() {
|
|
|
434
493
|
<div>Params: {JSON.stringify(params)}</div>
|
|
435
494
|
<div>Query: {JSON.stringify(query)}</div>
|
|
436
495
|
<div>Data: {JSON.stringify(data)}</div>
|
|
496
|
+
<div>Meta: {JSON.stringify(meta)}</div>
|
|
437
497
|
</div>
|
|
438
498
|
)
|
|
439
499
|
}
|
|
@@ -501,9 +561,10 @@ route("/", Home, root => {
|
|
|
501
561
|
|
|
502
562
|
root.route("/dashboard", Dashboard, dash => {
|
|
503
563
|
|
|
504
|
-
|
|
564
|
+
// child
|
|
565
|
+
dash.route("/users", Users)
|
|
505
566
|
|
|
506
|
-
dash.route("/
|
|
567
|
+
dash.route("/users/:id", User, {
|
|
507
568
|
|
|
508
569
|
loader: async ({ params }) => {
|
|
509
570
|
await new Promise(r => setTimeout(r, 400))
|
|
@@ -730,6 +791,47 @@ render tree
|
|
|
730
791
|
|
|
731
792
|
---
|
|
732
793
|
|
|
794
|
+
# Duplicate Routes
|
|
795
|
+
|
|
796
|
+
`routexiz` does not support duplicate route paths.
|
|
797
|
+
|
|
798
|
+
Each route must resolve to a unique full path in the route tree.
|
|
799
|
+
|
|
800
|
+
## Why?
|
|
801
|
+
|
|
802
|
+
Because routing is tree-based:
|
|
803
|
+
|
|
804
|
+
- Navigation resolves a single path from root to leaf
|
|
805
|
+
- Duplicate paths create ambiguous matches
|
|
806
|
+
- This leads to unpredictable behavior
|
|
807
|
+
|
|
808
|
+
## Important
|
|
809
|
+
|
|
810
|
+
⚠️ Duplicate routes are NOT validated automatically.
|
|
811
|
+
|
|
812
|
+
The router will NOT throw an error or warning.
|
|
813
|
+
|
|
814
|
+
## Requirement
|
|
815
|
+
|
|
816
|
+
Ensure all routes are unique:
|
|
817
|
+
|
|
818
|
+
```ts
|
|
819
|
+
route("/", Layout, root => {
|
|
820
|
+
root.route("/dashboard", Dashboard)
|
|
821
|
+
root.route("/dashboard/users", Users)
|
|
822
|
+
|
|
823
|
+
// ❌ BAD: duplicate
|
|
824
|
+
root.route("/dashboard/users", AnotherUsers)
|
|
825
|
+
})
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
## Recommendation
|
|
829
|
+
- Keep route paths explicit and unique
|
|
830
|
+
- Avoid defining the same full path in multiple branches
|
|
831
|
+
- Use nesting correctly to prevent duplication
|
|
832
|
+
|
|
833
|
+
---
|
|
834
|
+
|
|
733
835
|
# License
|
|
734
836
|
|
|
735
837
|
MIT
|
package/build/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import n,{createContext as t,useContext as e,Suspense as r,useState as o,useEffect as a,forwardRef as s,useRef as i}from"react";import{jsx as c,jsxs as h,Fragment as u}from"react/jsx-runtime";import l from"joinclass";let d=[];function p(){d=[]}function f(){return d}function m(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=e,t),route(t,e,r){const o={path:t,component:e,children:[],options:{},guardFns:[],middlewares:[],parent:n};"function"==typeof r?m(o)(r):r&&(o.options=r),n.children.push(o);const a={guard:n=>(o.guardFns?.push(n),a),middleware:n=>(o.middlewares?.push(n),a),meta:n=>(o.meta=n,a),route(n,t,e){const r={path:n,component:t,children:[],options:{},guardFns:[],middlewares:[],parent:o};return"function"==typeof e?m(r)(e):e&&(r.options=e),o.children.push(r),a}};return a}};return n=>{n(t)}}function y(n,t,e){const r={path:n,component:t,children:[],options:{}};"function"==typeof e?m(r)(e):e&&(r.options=e),d.push(r)}const g=t(null),w=t(null),v=t(null),P=t({loading:!1,path:"",pendingPath:""}),q=t({name:"",stage:"idle"});function b(){const n=e(g);return n?.read()}function E(){const n=e(w);if(!n)throw new Error("RouteContext not found");return n}function k(){return e(v)??{}}function N(){return E().query??{}}function S(){return e(P)}function x(){return e(q)}function R(){const n=E(),t=k(),e=N();let r;try{r=b()}catch{}return{data:r,params:t,query:e,path:n.path}}let A=!1,C=!1,F=c("div",{children:"Loading..."}),I=({error:n})=>h("div",{children:["Global Error: ",String(n)]});function K(n,t){A=n,t&&(F=t)}function M(n,t){C=n,t&&(I=({error:n})=>t(n))}function B(){return A}function D(){return C}function U(){return F}function L(){return I}function O(n){return n.startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function W(n){return O(n).split("/").filter(Boolean)}function $(n){const[t,e]=n.split("#"),[r,o]=t.split("?"),a={};return o&&o.split("&").forEach(n=>{const[t,e]=n.split("=");t&&(a[decodeURIComponent(t)]=decodeURIComponent(e??""))}),{path:O(r),query:a,hash:e??""}}function j(n,t){const{path:e,query:r,hash:o}=$(t),a=W(e),s=n.map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:n,segIndex:t,params:e,chain:i}=s.pop(),c=W(n.path);let h=0;const u={...e};for(;h<c.length;h++){const n=c[h],e=a[t+h];if(!e)break;if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(e);else if(n!==e)break}if(h!==c.length)continue;const l=t+h,d=[...i,{node:n,params:u,query:r,hash:o}];if(l===a.length)return d;for(const t of n.children)s.push({node:t,segIndex:l,params:u,chain:d})}return null}function J(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 G(n,t,e,r){const{path:o,query:a,hash:s}=$(n);let i=O(o);if(t)for(const n in t)i=i.replace(`:${n}`,encodeURIComponent(t[n]));const c={...a,...e??{}};if(Object.keys(c).length){const n=new URLSearchParams;for(const t in c){const e=c[t];Array.isArray(e)?e.forEach(e=>n.append(t,String(e))):n.append(t,String(e))}const t=n.toString();t&&(i+=`?${t}`)}return r?i+=`#${r}`:s&&(i+=`#${s}`),i}class T 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??(D()?L():void 0),t=this.props.fallback??h("div",{children:["Error: ",String(this.state.error)]});return n?c(n,{error:this.state.error}):t}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function z({resource:n,errorBoundary:t,children:e}){try{return n?.read(),c(u,{children:e})}catch(n){if(n instanceof Promise)throw n;const e=t??(D()?L():void 0);if(e)return c(e,{error:n});throw n}}function H({chain:n,data:t}){return c(u,{children:function e(o=0){const a=n[o];if(!a)return null;const s=a.node.component,i=t[a.node.path];if(!s)return e(o+1);let h=a.node.options?.fallback,u=a.node.parent;for(;!h&&u;)h=u.options?.fallback,u=u.parent;return!h&&B()&&(h=U()),c(T,{fallback:h,errorBoundary:a.node.options?.errorBoundary,children:c(r,{fallback:h,children:c(w.Provider,{value:{path:a.node.path,params:a.params,query:J(a.node.query??window.location.search),hash:a.node.hash??""},children:c(g.Provider,{value:i,children:c(v.Provider,{value:a.params,children:c(z,{resource:i,errorBoundary:a.node.options?.errorBoundary,children:c(s,{children:e(o+1)})})})})})})})}()})}async function Q(n){const t=j(f(),n);if(!t)return{};const e={};t.forEach(n=>{const t=n.node.options?.loader;t&&(e[n.node.path]=Y(JSON.stringify({path:n.node.path,params:n.params,query:n.query,hash:n.hash}),()=>t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}),n.node.options?.ttl))});for(const n in e)e[n].read();return e}const V=new Map;function X(n=1/0){const t=Date.now();let e=0;for(const[r,o]of V.entries())if(o.expiry<=t&&(V.delete(r),e++,e>=n))break}function Y(n,t,e,r){const o=Date.now(),a=V.get(n);if(a&&!r&&(!e||a.expiry>o))return a.resource;const s=Z(t());return V.set(n,{resource:s,expiry:o+(e??0)}),s}function Z(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}}let _=null;function nn(){return _}function tn(n,t){const e=f(),[r,o]=n.split("?"),a={...J(o),...t?.query??{}},s=j(e,G(r,t?.params,a,t?.hash));s&&en(s,{forceReload:!1})}function en(n,t){const e={};return n.forEach(n=>{const r=n.node.options?.loader;if(!r)return;const o=JSON.stringify({path:n.node.path,params:n.params,query:n.query,hash:n.hash}),a=n.node.options?.ttl,s=Y(o,()=>r({params:n.params,path:n.node.path,query:n.query,hash:n.hash}),a,t?.forceReload);e[n.node.path]=s}),e}function rn(){const[n,t]=o({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"",stage:"idle"}});async function e(n,e){const r=f();let o=j(r,n)??j(r,"*")??[];for(const t of o)for(const e of t.node.guardFns||[]){if(!await e({params:t.params,path:t.node.path,query:t.query,hash:t.hash}))return console.warn("Navigation blocked by guard",n)}for(const n of o)for(const t of n.node.middlewares||[])await t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}).catch(console.error);t(t=>({...t,loading:!0,pendingPath:n,transition:{name:e?.transition||"fade",stage:"exiting"}}));const a=en(o,e);t(t=>({...t,chain:o,data:{...t.data,...a},loading:!1,path:n,pendingPath:"",transition:{name:e?.transition||"fade",stage:"entering"}})),window.scrollTo(0,0)}return a(()=>{_=e,e(window.location.pathname),window.addEventListener("popstate",()=>e(window.location.pathname))},[]),c(P.Provider,{value:{loading:n.loading,path:n.path,pendingPath:n.pendingPath},children:c(q.Provider,{value:n.transition,children:c(H,{chain:n.chain,data:n.data})})})}let on=[],an=[];function sn(n){on.push(n)}function cn(n){an.push(n)}async function hn(n,t,e=!1){const r={...t,forceReload:t?.forceReload??!0},o=G(n,r.params,r.query,r.hash),a=await async function(n){return Promise.all(on.map(t=>t(n))).then(n=>n.every(n=>!1!==n))}(o);if(!a)return console.warn("Navigation blocked by beforeNavigation hook",o);e?window.history.replaceState({},"",o):window.history.pushState({},"",o),nn()?.(o,r),async function(n){for(const t of an)await t(n)}(o)}function un(n,t){return hn(n,t,!1)}function ln(n,t){return un(n,t)}function dn(n){return S().path===n}function pn(n,t){return()=>tn(n,t)}function fn(){const n=(n,t)=>hn(G(n,t?.params,t?.query,t?.hash),t);return n.replace=(n,t)=>hn(G(n,t?.params,t?.query,t?.hash),t,!0),n}function mn({to:n,transition:t,replace:e,params:r,query:o,hash:s,partialMatch:c,disablePrefetch:h}){const u=fn(),l=S(),d=i(null),p=G(n,r,o,s),f=c?l.path?.startsWith(p):l.path===p;return a(()=>{if(h)return;const n=d.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(tn(p),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[p,h]),{ref:d,fullPath:p,isActive:f,go:()=>{e?u.replace(n,{transition:t,params:r,query:o,hash:s}):u(n,{transition:t,params:r,query:o,hash:s})}}}un.replace=(n,t)=>hn(n,t,!0);const yn=s(({fullPath:n,onNavigate:t,onPrefetch:e,onClick:r,onMouseEnter:o,onKeyDown:a,disablePrefetch:s,...i},h)=>c("a",{ref:h,href:n,...i,onClick:n=>{r&&r(n),n.metaKey||n.ctrlKey||n.shiftKey||(n.preventDefault(),t())},onMouseEnter:n=>{o&&o(n),s||e?.()},onKeyDown:n=>{"Enter"===n.key&&(n.preventDefault(),t()),a&&a(n)}}));function gn(n){const{to:t,transition:e,replace:r,activeClassName:o="active",onNavigate:a,partialMatch:s,params:i,query:h,hash:u,className:d,disablePrefetch:p,...f}=n,{ref:m,fullPath:y,isActive:g,go:w}=mn({to:t,transition:e,replace:r,params:i,query:h,hash:u,partialMatch:s,disablePrefetch:p});return c(yn,{ref:m,fullPath:y,onNavigate:()=>{w(),a?.(y)},onPrefetch:()=>tn(y),className:l(d,g&&o),disablePrefetch:p,...f})}function wn(n){const{to:t,transition:e,replace:r,params:o,query:a,hash:s,partialMatch:i,className:h,style:u,disablePrefetch:d,...p}=n,{ref:f,fullPath:m,isActive:y,go:g}=mn({to:t,transition:e,replace:r,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:d}),w="function"==typeof h?h({isActive:y}):h,v="function"==typeof u?u({isActive:y}):u;return c(yn,{ref:f,fullPath:m,onNavigate:g,onPrefetch:()=>tn(m),className:l(w),style:v,disablePrefetch:d,...p})}yn.displayName="LinkCore";export{gn as Link,g as LoaderDataContext,wn as NavLink,v as ParamsContext,w as RouteContext,rn as RouterProvider,P as RouterStateContext,q as TransitionContext,cn as addAfterNavigationHook,sn as addBeforeNavigationHook,G as buildPath,X as cleanupCache,p as clearRoutes,Z as createResource,L as getGlobalError,U as getGlobalFallback,nn as getGlobalResolve,Y as getResource,f as getRoutes,D as getUseGlobalError,B as getUseGlobalFallback,Q as loadRouteData,j as matchRouteChain,un as navigate,J as normalizeQuery,$ as parseQueryHash,tn as prefetch,ln as redirect,y as route,M as setGlobalError,K as setGlobalFallback,mn as useLinkCore,b as useLoaderData,fn as useNavigate,S as useNavigation,k as useParams,pn as usePrefetch,N as useQuery,E as useRouteContext,dn as useRouteMatch,R as useRouterData,x as useTransition};
|
|
1
|
+
import n,{createContext as t,useContext as e,Suspense as r,useState as a,useEffect as o,forwardRef as s,useRef as i}from"react";import{jsx as c,jsxs as h,Fragment as u}from"react/jsx-runtime";import l from"joinclass";let p=[];function d(){p=[]}function f(){return p}function m(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=e,t),route(t,e,r){const a={path:t,component:e,children:[],options:{},guardFns:[],middlewares:[],parent:n};"function"==typeof r?m(a)(r):r&&(a.options=r),n.children.push(a);const o={guard:n=>(a.guardFns?.push(n),o),middleware:n=>(a.middlewares?.push(n),o),meta:n=>(a.meta=n,o),route(n,t,e){const r={path:n,component:t,children:[],options:{},guardFns:[],middlewares:[],parent:a};return"function"==typeof e?m(r)(e):e&&(r.options=e),a.children.push(r),o}};return o}};return n=>{n(t)}}function y(n,t,e){const r={path:n,component:t,children:[],options:{}};"function"==typeof e?m(r)(e):e&&(r.options=e),p.push(r)}const g=t(null),w=t(null),v=t(null),q=t({loading:!1,path:"",pendingPath:""}),P=t({name:"",stage:"idle"});function b(){const n=e(g);try{return n?.read()}catch{return}}function E(){const n=e(w);if(!n)throw new Error("RouteContext not found");return n}function k(){return e(v)??{}}function N(){return E().query??{}}function S(){return e(q)}function x(){return e(P)}function R(){const{meta:n,params:t,path:e,query:r}=E();let a;try{a=b()}catch{}return{data:a,params:t??{},query:r??{},path:e,meta:n}}let A=!1,C=!1,F=c("div",{children:"Loading..."}),I=({error:n})=>h("div",{children:["Global Error: ",String(n)]});function K(n,t){A=n,t&&(F=t)}function M(n,t){C=n,t&&(I=({error:n})=>t(n))}function B(){return A}function D(){return C}function U(){return F}function L(){return I}function O(n){return n.startsWith("/")||(n="/"+n),"/"!==n&&n.endsWith("/")&&(n=n.slice(0,-1)),n}function j(n){return O(n).split("/").filter(Boolean)}function W(n){const[t,e]=n.split("#"),[r,a]=t.split("?"),o={};return a&&a.split("&").forEach(n=>{const[t,e]=n.split("=");t&&(o[decodeURIComponent(t)]=decodeURIComponent(e??""))}),{path:O(r),query:o,hash:e??""}}function $(n,t){const{path:e,query:r,hash:a}=W(t),o=j(e),s=n.map(n=>({node:n,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:n,segIndex:t,params:e,chain:i}=s.pop(),c=j(n.path);let h=0;const u={...e};for(;h<c.length;h++){const n=c[h],e=o[t+h];if(!e)break;if(n.startsWith(":"))u[n.slice(1)]=decodeURIComponent(e);else if(n!==e)break}if(h!==c.length)continue;const l=t+h,p=[...i,{node:n,params:u,query:r,hash:a}];if(l===o.length)return p;for(const t of n.children)s.push({node:t,segIndex:l,params:u,chain:p})}return null}function J(n){const t={};if(!n)return t;if("string"==typeof n){const e=new URLSearchParams(n);e.forEach((n,r)=>{const a=e.getAll(r);t[r]=a.length>1?a:a[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 G(n,t,e,r){const{path:a,query:o,hash:s}=W(n);let i=O(a);if(t)for(const n in t)i=i.replace(`:${n}`,encodeURIComponent(t[n]));const c={...o,...e??{}};if(Object.keys(c).length){const n=new URLSearchParams;for(const t in c){const e=c[t];Array.isArray(e)?e.forEach(e=>n.append(t,String(e))):n.append(t,String(e))}const t=n.toString();t&&(i+=`?${t}`)}return r?i+=`#${r}`:s&&(i+=`#${s}`),i}class T 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??(D()?L():void 0),t=this.props.fallback??h("div",{children:["Error: ",String(this.state.error)]});return n?c(n,{error:this.state.error}):t}return c(n.Fragment,{children:this.props.children},this.state.resetKey)}}function z({resource:n,errorBoundary:t,children:e}){try{return n?.read(),c(u,{children:e})}catch(n){if(n instanceof Promise)throw n;const e=t??(D()?L():void 0);if(e)return c(e,{error:n});throw n}}function H({chain:n,data:t}){return c(u,{children:function e(a=0){const o=n[a];if(!o)return null;const s=o.node.component,i=t[o.node.path];if(!s)return e(a+1);let h=o.node.options?.fallback,u=o.node.parent;for(;!h&&u;)h=u.options?.fallback,u=u.parent;!h&&B()&&(h=U());const l=Object.assign({},...n.slice(0,a+1).map(n=>n.node.meta));return c(T,{fallback:h,errorBoundary:o.node.options?.errorBoundary,children:c(r,{fallback:h,children:c(w.Provider,{value:{path:o.node.path,params:o.params,query:J(o.node.query??window.location.search),hash:o.node.hash??"",meta:l},children:c(g.Provider,{value:i,children:c(v.Provider,{value:o.params,children:c(z,{resource:i,errorBoundary:o.node.options?.errorBoundary,children:c(s,{children:e(a+1)})})})})})})})}()})}async function Q(n){const t=$(f(),n);if(!t)return{};const e={};t.forEach(n=>{const t=n.node.options?.loader;t&&(e[n.node.path]=Y(JSON.stringify({path:n.node.path,params:n.params,query:n.query,hash:n.hash}),()=>t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}),n.node.options?.ttl))});for(const n in e)e[n].read();return e}const V=new Map;function X(n=1/0){const t=Date.now();let e=0;for(const[r,a]of V.entries())if(a.expiry<=t&&(V.delete(r),e++,e>=n))break}function Y(n,t,e,r){const a=Date.now(),o=V.get(n);if(o&&!r&&(!e||o.expiry>a))return o.resource;const s=Z(t());return V.set(n,{resource:s,expiry:a+(e??0)}),s}function Z(n,t){let e=t?"success":"pending",r=t??null;const a=n.then(n=>{e="success",r=n},n=>{e="error",r=n});return{read(){if("pending"===e)throw a;if("error"===e)throw r;return r},update(n){r=n,e="success"},get:()=>r}}let _=null;function nn(){return _}function tn(n,t){const e=f(),[r,a]=n.split("?"),o={...J(a),...t?.query??{}},s=$(e,G(r,t?.params,o,t?.hash));s&&en(s,{forceReload:!1})}function en(n,t){const e={};return n.forEach(n=>{const r=n.node.options?.loader;if(!r)return;const a=JSON.stringify({path:n.node.path,params:n.params,query:n.query,hash:n.hash}),o=n.node.options?.ttl,s=Y(a,()=>r({params:n.params,path:n.node.path,query:n.query,hash:n.hash}),o,t?.forceReload);e[n.node.path]=s}),e}function rn(){const[n,t]=a({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"",stage:"idle"}});async function e(n,e){const r=f();let a=$(r,n)??$(r,"*")??[];for(const t of a)for(const e of t.node.guardFns||[]){if(!await e({params:t.params,path:t.node.path,query:t.query,hash:t.hash}))return console.warn("Navigation blocked by guard",n)}for(const n of a)for(const t of n.node.middlewares||[])await t({params:n.params,path:n.node.path,query:n.query,hash:n.hash}).catch(console.error);t(t=>({...t,loading:!0,pendingPath:n,transition:{name:e?.transition||"fade",stage:"exiting"}}));const o=en(a,e);t(t=>({...t,chain:a,data:{...t.data,...o},loading:!1,path:n,pendingPath:"",transition:{name:e?.transition||"fade",stage:"entering"}})),window.scrollTo(0,0)}return o(()=>{_=e,e(window.location.pathname),window.addEventListener("popstate",()=>e(window.location.pathname))},[]),c(q.Provider,{value:{loading:n.loading,path:n.path,pendingPath:n.pendingPath},children:c(P.Provider,{value:n.transition,children:c(H,{chain:n.chain,data:n.data})})})}let an=[],on=[];function sn(n){an.push(n)}function cn(n){on.push(n)}async function hn(n,t,e=!1){const r={...t,forceReload:t?.forceReload??!0},a=G(n,r.params,r.query,r.hash),o=await async function(n){return Promise.all(an.map(t=>t(n))).then(n=>n.every(n=>!1!==n))}(a);if(!o)return console.warn("Navigation blocked by beforeNavigation hook",a);e?window.history.replaceState({},"",a):window.history.pushState({},"",a),nn()?.(a,r),async function(n){for(const t of on)await t(n)}(a)}function un(n,t){return hn(n,t,!1)}function ln(n,t){return un(n,t)}function pn(n){return S().path===n}function dn(n,t){return()=>tn(n,t)}function fn(){const n=(n,t)=>hn(G(n,t?.params,t?.query,t?.hash),t);return n.replace=(n,t)=>hn(G(n,t?.params,t?.query,t?.hash),t,!0),n}function mn({to:n,transition:t,replace:e,params:r,query:a,hash:s,partialMatch:c,disablePrefetch:h}){const u=fn(),l=S(),p=i(null),d=G(n,r,a,s),f=c?l.path?.startsWith(d):l.path===d;return o(()=>{if(h)return;const n=p.current;if(!n)return;const t=new IntersectionObserver(n=>{n.some(n=>n.isIntersecting)&&(tn(d),t.disconnect())},{rootMargin:"200px"});return t.observe(n),()=>t.disconnect()},[d,h]),{ref:p,fullPath:d,isActive:f,go:()=>{e?u.replace(n,{transition:t,params:r,query:a,hash:s}):u(n,{transition:t,params:r,query:a,hash:s})}}}un.replace=(n,t)=>hn(n,t,!0);const yn=s(({fullPath:n,onNavigate:t,onPrefetch:e,onClick:r,onMouseEnter:a,onKeyDown:o,disablePrefetch:s,...i},h)=>c("a",{ref:h,href:n,...i,onClick:n=>{r&&r(n),n.metaKey||n.ctrlKey||n.shiftKey||(n.preventDefault(),t())},onMouseEnter:n=>{a&&a(n),s||e?.()},onKeyDown:n=>{"Enter"===n.key&&(n.preventDefault(),t()),o&&o(n)}}));function gn(n){const{to:t,transition:e,replace:r,activeClassName:a="active",onNavigate:o,partialMatch:s,params:i,query:h,hash:u,className:p,disablePrefetch:d,...f}=n,{ref:m,fullPath:y,isActive:g,go:w}=mn({to:t,transition:e,replace:r,params:i,query:h,hash:u,partialMatch:s,disablePrefetch:d});return c(yn,{ref:m,fullPath:y,onNavigate:()=>{w(),o?.(y)},onPrefetch:()=>tn(y),className:l(p,g&&a),disablePrefetch:d,...f})}function wn(n){const{to:t,transition:e,replace:r,params:a,query:o,hash:s,partialMatch:i,className:h,style:u,disablePrefetch:p,...d}=n,{ref:f,fullPath:m,isActive:y,go:g}=mn({to:t,transition:e,replace:r,params:a,query:o,hash:s,partialMatch:i,disablePrefetch:p}),w="function"==typeof h?h({isActive:y}):h,v="function"==typeof u?u({isActive:y}):u;return c(yn,{ref:f,fullPath:m,onNavigate:g,onPrefetch:()=>tn(m),className:l(w),style:v,disablePrefetch:p,...d})}yn.displayName="LinkCore";export{gn as Link,g as LoaderDataContext,wn as NavLink,v as ParamsContext,w as RouteContext,rn as RouterProvider,q as RouterStateContext,P as TransitionContext,cn as addAfterNavigationHook,sn as addBeforeNavigationHook,G as buildPath,X as cleanupCache,d as clearRoutes,Z as createResource,L as getGlobalError,U as getGlobalFallback,nn as getGlobalResolve,Y as getResource,f as getRoutes,D as getUseGlobalError,B as getUseGlobalFallback,Q as loadRouteData,$ as matchRouteChain,un as navigate,J as normalizeQuery,W as parseQueryHash,tn as prefetch,ln as redirect,y as route,M as setGlobalError,K as setGlobalFallback,mn as useLinkCore,b as useLoaderData,fn as useNavigate,S as useNavigation,k as useParams,dn as usePrefetch,N as useQuery,E as useRouteContext,pn as useRouteMatch,R as useRouterData,x as useTransition};
|
package/build/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");let n=[];function o(){return n}function a(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=r,t),route(t,r,n){const o={path:t,component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};"function"==typeof n?a(o)(n):n&&(o.options=n),e.children.push(o);const s={guard:e=>(o.guardFns?.push(e),s),middleware:e=>(o.middlewares?.push(e),s),meta:e=>(o.meta=e,s),route(e,t,r){const n={path:e,component:t,children:[],options:{},guardFns:[],middlewares:[],parent:o};return"function"==typeof r?a(n)(r):r&&(n.options=r),o.children.push(n),s}};return s}};return e=>{e(t)}}const s=e.createContext(null),i=e.createContext(null),c=e.createContext(null),u=e.createContext({loading:!1,path:"",pendingPath:""}),h=e.createContext({name:"",stage:"idle"});function p(){const t=e.useContext(s);return t?.read()}function l(){const t=e.useContext(i);if(!t)throw new Error("RouteContext not found");return t}function d(){return e.useContext(c)??{}}function f(){return l().query??{}}function x(){return e.useContext(u)}let m=!1,g=!1,y=t.jsx("div",{children:"Loading..."}),v=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});function w(){return m}function P(){return g}function b(){return y}function q(){return v}function C(e){return e.startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function R(e){return C(e).split("/").filter(Boolean)}function j(e){const[t,r]=e.split("#"),[n,o]=t.split("?"),a={};return o&&o.split("&").forEach(e=>{const[t,r]=e.split("=");t&&(a[decodeURIComponent(t)]=decodeURIComponent(r??""))}),{path:C(n),query:a,hash:r??""}}function k(e,t){const{path:r,query:n,hash:o}=j(t),a=R(r),s=e.map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:i}=s.pop(),c=R(e.path);let u=0;const h={...r};for(;u<c.length;u++){const e=c[u],r=a[t+u];if(!r)break;if(e.startsWith(":"))h[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(u!==c.length)continue;const p=t+u,l=[...i,{node:e,params:h,query:n,hash:o}];if(p===a.length)return l;for(const t of e.children)s.push({node:t,segIndex:p,params:h,chain:l})}return null}function E(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 N(e,t,r,n){const{path:o,query:a,hash:s}=j(e);let i=C(o);if(t)for(const e in t)i=i.replace(`:${e}`,encodeURIComponent(t[e]));const c={...a,...r??{}};if(Object.keys(c).length){const e=new URLSearchParams;for(const t in c){const r=c[t];Array.isArray(r)?r.forEach(r=>e.append(t,String(r))):e.append(t,String(r))}const t=e.toString();t&&(i+=`?${t}`)}return n?i+=`#${n}`:s&&(i+=`#${s}`),i}class S 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??(P()?q():void 0),r=this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]});return e?t.jsx(e,{error:this.state.error}):r}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function F({resource:e,errorBoundary:r,children:n}){try{return e?.read(),t.jsx(t.Fragment,{children:n})}catch(e){if(e instanceof Promise)throw e;const n=r??(P()?q():void 0);if(n)return t.jsx(n,{error:e});throw e}}function A({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const u=r[a];if(!u)return null;const h=u.node.component,p=n[u.node.path];if(!h)return o(a+1);let l=u.node.options?.fallback,d=u.node.parent;for(;!l&&d;)l=d.options?.fallback,d=d.parent;return!l&&w()&&(l=b()),t.jsx(S,{fallback:l,errorBoundary:u.node.options?.errorBoundary,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(i.Provider,{value:{path:u.node.path,params:u.params,query:E(u.node.query??window.location.search),hash:u.node.hash??""},children:t.jsx(s.Provider,{value:p,children:t.jsx(c.Provider,{value:u.params,children:t.jsx(F,{resource:p,errorBoundary:u.node.options?.errorBoundary,children:t.jsx(h,{children:o(a+1)})})})})})})})}()})}const D=new Map;function L(e,t,r,n){const o=Date.now(),a=D.get(e);if(a&&!n&&(!r||a.expiry>o))return a.resource;const s=M(t());return D.set(e,{resource:s,expiry:o+(r??0)}),s}function M(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}}let I=null;function K(){return I}function B(e,t){const r=o(),[n,a]=e.split("?"),s={...E(a),...t?.query??{}},i=k(r,N(n,t?.params,s,t?.hash));i&&G(i,{forceReload:!1})}function G(e,t){const r={};return e.forEach(e=>{const n=e.node.options?.loader;if(!n)return;const o=JSON.stringify({path:e.node.path,params:e.params,query:e.query,hash:e.hash}),a=e.node.options?.ttl,s=L(o,()=>n({params:e.params,path:e.node.path,query:e.query,hash:e.hash}),a,t?.forceReload);r[e.node.path]=s}),r}let U=[],O=[];async function W(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=N(e,n.params,n.query,n.hash),a=await async function(e){return Promise.all(U.map(t=>t(e))).then(e=>e.every(e=>!1!==e))}(o);if(!a)return console.warn("Navigation blocked by beforeNavigation hook",o);r?window.history.replaceState({},"",o):window.history.pushState({},"",o),K()?.(o,n),async function(e){for(const t of O)await t(e)}(o)}function $(e,t){return W(e,t,!1)}function H(){const e=(e,t)=>W(N(e,t?.params,t?.query,t?.hash),t);return e.replace=(e,t)=>W(N(e,t?.params,t?.query,t?.hash),t,!0),e}function Q({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c}){const u=H(),h=x(),p=e.useRef(null),l=N(t,o,a,s),d=i?h.path?.startsWith(l):h.path===l;return e.useEffect(()=>{if(c)return;const e=p.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(B(l),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[l,c]),{ref:p,fullPath:l,isActive:d,go:()=>{n?u.replace(t,{transition:r,params:o,query:a,hash:s}):u(t,{transition:r,params:o,query:a,hash:s})}}}$.replace=(e,t)=>W(e,t,!0);const T=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:e=>{o&&o(e),e.metaKey||e.ctrlKey||e.shiftKey||(e.preventDefault(),r())},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),r()),s&&s(e)}}));T.displayName="LinkCore",exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:h,hash:p,className:l,disablePrefetch:d,...f}=e,{ref:x,fullPath:m,isActive:g,go:y}=Q({to:n,transition:o,replace:a,params:u,query:h,hash:p,partialMatch:c,disablePrefetch:d});return t.jsx(T,{ref:x,fullPath:m,onNavigate:()=>{y(),i?.(m)},onPrefetch:()=>B(m),className:r(l,g&&s),disablePrefetch:d,...f})},exports.LoaderDataContext=s,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:h,style:p,disablePrefetch:l,...d}=e,{ref:f,fullPath:x,isActive:m,go:g}=Q({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:l}),y="function"==typeof h?h({isActive:m}):h,v="function"==typeof p?p({isActive:m}):p;return t.jsx(T,{ref:f,fullPath:x,onNavigate:g,onPrefetch:()=>B(x),className:r(y),style:v,disablePrefetch:l,...d})},exports.ParamsContext=c,exports.RouteContext=i,exports.RouterProvider=function(){const[r,n]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"",stage:"idle"}});async function a(e,t){const r=o();let a=k(r,e)??k(r,"*")??[];for(const t of a)for(const r of t.node.guardFns||[]){if(!await r({params:t.params,path:t.node.path,query:t.query,hash:t.hash}))return console.warn("Navigation blocked by guard",e)}for(const e of a)for(const t of e.node.middlewares||[])await t({params:e.params,path:e.node.path,query:e.query,hash:e.hash}).catch(console.error);n(r=>({...r,loading:!0,pendingPath:e,transition:{name:t?.transition||"fade",stage:"exiting"}}));const s=G(a,t);n(r=>({...r,chain:a,data:{...r.data,...s},loading:!1,path:e,pendingPath:"",transition:{name:t?.transition||"fade",stage:"entering"}})),window.scrollTo(0,0)}return e.useEffect(()=>{I=a,a(window.location.pathname),window.addEventListener("popstate",()=>a(window.location.pathname))},[]),t.jsx(u.Provider,{value:{loading:r.loading,path:r.path,pendingPath:r.pendingPath},children:t.jsx(h.Provider,{value:r.transition,children:t.jsx(A,{chain:r.chain,data:r.data})})})},exports.RouterStateContext=u,exports.TransitionContext=h,exports.addAfterNavigationHook=function(e){O.push(e)},exports.addBeforeNavigationHook=function(e){U.push(e)},exports.buildPath=N,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[n,o]of D.entries())if(o.expiry<=t&&(D.delete(n),r++,r>=e))break},exports.clearRoutes=function(){n=[]},exports.createResource=M,exports.getGlobalError=q,exports.getGlobalFallback=b,exports.getGlobalResolve=K,exports.getResource=L,exports.getRoutes=o,exports.getUseGlobalError=P,exports.getUseGlobalFallback=w,exports.loadRouteData=async function(e){const t=k(o(),e);if(!t)return{};const r={};t.forEach(e=>{const t=e.node.options?.loader;t&&(r[e.node.path]=L(JSON.stringify({path:e.node.path,params:e.params,query:e.query,hash:e.hash}),()=>t({params:e.params,path:e.node.path,query:e.query,hash:e.hash}),e.node.options?.ttl))});for(const e in r)r[e].read();return r},exports.matchRouteChain=k,exports.navigate=$,exports.normalizeQuery=E,exports.parseQueryHash=j,exports.prefetch=B,exports.redirect=function(e,t){return $(e,t)},exports.route=function(e,t,r){const o={path:e,component:t,children:[],options:{}};"function"==typeof r?a(o)(r):r&&(o.options=r),n.push(o)},exports.setGlobalError=function(e,t){g=e,t&&(v=({error:e})=>t(e))},exports.setGlobalFallback=function(e,t){m=e,t&&(y=t)},exports.useLinkCore=Q,exports.useLoaderData=p,exports.useNavigate=H,exports.useNavigation=x,exports.useParams=d,exports.usePrefetch=function(e,t){return()=>B(e,t)},exports.useQuery=f,exports.useRouteContext=l,exports.useRouteMatch=function(e){return x().path===e},exports.useRouterData=function(){const e=l(),t=d(),r=f();let n;try{n=p()}catch{}return{data:n,params:t,query:r,path:e.path}},exports.useTransition=function(){return e.useContext(h)};
|
|
1
|
+
"use strict";var e=require("react"),t=require("react/jsx-runtime"),r=require("joinclass");let n=[];function o(){return n}function a(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=r,t),route(t,r,n){const o={path:t,component:r,children:[],options:{},guardFns:[],middlewares:[],parent:e};"function"==typeof n?a(o)(n):n&&(o.options=n),e.children.push(o);const s={guard:e=>(o.guardFns?.push(e),s),middleware:e=>(o.middlewares?.push(e),s),meta:e=>(o.meta=e,s),route(e,t,r){const n={path:e,component:t,children:[],options:{},guardFns:[],middlewares:[],parent:o};return"function"==typeof r?a(n)(r):r&&(n.options=r),o.children.push(n),s}};return s}};return e=>{e(t)}}const s=e.createContext(null),i=e.createContext(null),c=e.createContext(null),u=e.createContext({loading:!1,path:"",pendingPath:""}),h=e.createContext({name:"",stage:"idle"});function p(){const t=e.useContext(s);try{return t?.read()}catch{return}}function l(){const t=e.useContext(i);if(!t)throw new Error("RouteContext not found");return t}function d(){return e.useContext(u)}let f=!1,x=!1,m=t.jsx("div",{children:"Loading..."}),y=({error:e})=>t.jsxs("div",{children:["Global Error: ",String(e)]});function g(){return f}function v(){return x}function w(){return m}function b(){return y}function P(e){return e.startsWith("/")||(e="/"+e),"/"!==e&&e.endsWith("/")&&(e=e.slice(0,-1)),e}function q(e){return P(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("=");t&&(a[decodeURIComponent(t)]=decodeURIComponent(r??""))}),{path:P(n),query:a,hash:r??""}}function R(e,t){const{path:r,query:n,hash:o}=C(t),a=q(r),s=e.map(e=>({node:e,segIndex:0,params:{},chain:[]}));for(;s.length;){const{node:e,segIndex:t,params:r,chain:i}=s.pop(),c=q(e.path);let u=0;const h={...r};for(;u<c.length;u++){const e=c[u],r=a[t+u];if(!r)break;if(e.startsWith(":"))h[e.slice(1)]=decodeURIComponent(r);else if(e!==r)break}if(u!==c.length)continue;const p=t+u,l=[...i,{node:e,params:h,query:n,hash:o}];if(p===a.length)return l;for(const t of e.children)s.push({node:t,segIndex:p,params:h,chain:l})}return null}function j(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 k(e,t,r,n){const{path:o,query:a,hash:s}=C(e);let i=P(o);if(t)for(const e in t)i=i.replace(`:${e}`,encodeURIComponent(t[e]));const c={...a,...r??{}};if(Object.keys(c).length){const e=new URLSearchParams;for(const t in c){const r=c[t];Array.isArray(r)?r.forEach(r=>e.append(t,String(r))):e.append(t,String(r))}const t=e.toString();t&&(i+=`?${t}`)}return n?i+=`#${n}`:s&&(i+=`#${s}`),i}class E 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??(v()?b():void 0),r=this.props.fallback??t.jsxs("div",{children:["Error: ",String(this.state.error)]});return e?t.jsx(e,{error:this.state.error}):r}return t.jsx(e.Fragment,{children:this.props.children},this.state.resetKey)}}function N({resource:e,errorBoundary:r,children:n}){try{return e?.read(),t.jsx(t.Fragment,{children:n})}catch(e){if(e instanceof Promise)throw e;const n=r??(v()?b():void 0);if(n)return t.jsx(n,{error:e});throw e}}function S({chain:r,data:n}){return t.jsx(t.Fragment,{children:function o(a=0){const u=r[a];if(!u)return null;const h=u.node.component,p=n[u.node.path];if(!h)return o(a+1);let l=u.node.options?.fallback,d=u.node.parent;for(;!l&&d;)l=d.options?.fallback,d=d.parent;!l&&g()&&(l=w());const f=Object.assign({},...r.slice(0,a+1).map(e=>e.node.meta));return t.jsx(E,{fallback:l,errorBoundary:u.node.options?.errorBoundary,children:t.jsx(e.Suspense,{fallback:l,children:t.jsx(i.Provider,{value:{path:u.node.path,params:u.params,query:j(u.node.query??window.location.search),hash:u.node.hash??"",meta:f},children:t.jsx(s.Provider,{value:p,children:t.jsx(c.Provider,{value:u.params,children:t.jsx(N,{resource:p,errorBoundary:u.node.options?.errorBoundary,children:t.jsx(h,{children:o(a+1)})})})})})})})}()})}const F=new Map;function A(e,t,r,n){const o=Date.now(),a=F.get(e);if(a&&!n&&(!r||a.expiry>o))return a.resource;const s=D(t());return F.set(e,{resource:s,expiry:o+(r??0)}),s}function D(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}}let L=null;function M(){return L}function I(e,t){const r=o(),[n,a]=e.split("?"),s={...j(a),...t?.query??{}},i=R(r,k(n,t?.params,s,t?.hash));i&&K(i,{forceReload:!1})}function K(e,t){const r={};return e.forEach(e=>{const n=e.node.options?.loader;if(!n)return;const o=JSON.stringify({path:e.node.path,params:e.params,query:e.query,hash:e.hash}),a=e.node.options?.ttl,s=A(o,()=>n({params:e.params,path:e.node.path,query:e.query,hash:e.hash}),a,t?.forceReload);r[e.node.path]=s}),r}let B=[],G=[];async function U(e,t,r=!1){const n={...t,forceReload:t?.forceReload??!0},o=k(e,n.params,n.query,n.hash),a=await async function(e){return Promise.all(B.map(t=>t(e))).then(e=>e.every(e=>!1!==e))}(o);if(!a)return console.warn("Navigation blocked by beforeNavigation hook",o);r?window.history.replaceState({},"",o):window.history.pushState({},"",o),M()?.(o,n),async function(e){for(const t of G)await t(e)}(o)}function O(e,t){return U(e,t,!1)}function W(){const e=(e,t)=>U(k(e,t?.params,t?.query,t?.hash),t);return e.replace=(e,t)=>U(k(e,t?.params,t?.query,t?.hash),t,!0),e}function $({to:t,transition:r,replace:n,params:o,query:a,hash:s,partialMatch:i,disablePrefetch:c}){const u=W(),h=d(),p=e.useRef(null),l=k(t,o,a,s),f=i?h.path?.startsWith(l):h.path===l;return e.useEffect(()=>{if(c)return;const e=p.current;if(!e)return;const t=new IntersectionObserver(e=>{e.some(e=>e.isIntersecting)&&(I(l),t.disconnect())},{rootMargin:"200px"});return t.observe(e),()=>t.disconnect()},[l,c]),{ref:p,fullPath:l,isActive:f,go:()=>{n?u.replace(t,{transition:r,params:o,query:a,hash:s}):u(t,{transition:r,params:o,query:a,hash:s})}}}O.replace=(e,t)=>U(e,t,!0);const H=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:e=>{o&&o(e),e.metaKey||e.ctrlKey||e.shiftKey||(e.preventDefault(),r())},onMouseEnter:e=>{a&&a(e),i||n?.()},onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),r()),s&&s(e)}}));H.displayName="LinkCore",exports.Link=function(e){const{to:n,transition:o,replace:a,activeClassName:s="active",onNavigate:i,partialMatch:c,params:u,query:h,hash:p,className:l,disablePrefetch:d,...f}=e,{ref:x,fullPath:m,isActive:y,go:g}=$({to:n,transition:o,replace:a,params:u,query:h,hash:p,partialMatch:c,disablePrefetch:d});return t.jsx(H,{ref:x,fullPath:m,onNavigate:()=>{g(),i?.(m)},onPrefetch:()=>I(m),className:r(l,y&&s),disablePrefetch:d,...f})},exports.LoaderDataContext=s,exports.NavLink=function(e){const{to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,className:h,style:p,disablePrefetch:l,...d}=e,{ref:f,fullPath:x,isActive:m,go:y}=$({to:n,transition:o,replace:a,params:s,query:i,hash:c,partialMatch:u,disablePrefetch:l}),g="function"==typeof h?h({isActive:m}):h,v="function"==typeof p?p({isActive:m}):p;return t.jsx(H,{ref:f,fullPath:x,onNavigate:y,onPrefetch:()=>I(x),className:r(g),style:v,disablePrefetch:l,...d})},exports.ParamsContext=c,exports.RouteContext=i,exports.RouterProvider=function(){const[r,n]=e.useState({chain:[],data:{},loading:!1,path:"",pendingPath:"",transition:{name:"",stage:"idle"}});async function a(e,t){const r=o();let a=R(r,e)??R(r,"*")??[];for(const t of a)for(const r of t.node.guardFns||[]){if(!await r({params:t.params,path:t.node.path,query:t.query,hash:t.hash}))return console.warn("Navigation blocked by guard",e)}for(const e of a)for(const t of e.node.middlewares||[])await t({params:e.params,path:e.node.path,query:e.query,hash:e.hash}).catch(console.error);n(r=>({...r,loading:!0,pendingPath:e,transition:{name:t?.transition||"fade",stage:"exiting"}}));const s=K(a,t);n(r=>({...r,chain:a,data:{...r.data,...s},loading:!1,path:e,pendingPath:"",transition:{name:t?.transition||"fade",stage:"entering"}})),window.scrollTo(0,0)}return e.useEffect(()=>{L=a,a(window.location.pathname),window.addEventListener("popstate",()=>a(window.location.pathname))},[]),t.jsx(u.Provider,{value:{loading:r.loading,path:r.path,pendingPath:r.pendingPath},children:t.jsx(h.Provider,{value:r.transition,children:t.jsx(S,{chain:r.chain,data:r.data})})})},exports.RouterStateContext=u,exports.TransitionContext=h,exports.addAfterNavigationHook=function(e){G.push(e)},exports.addBeforeNavigationHook=function(e){B.push(e)},exports.buildPath=k,exports.cleanupCache=function(e=1/0){const t=Date.now();let r=0;for(const[n,o]of F.entries())if(o.expiry<=t&&(F.delete(n),r++,r>=e))break},exports.clearRoutes=function(){n=[]},exports.createResource=D,exports.getGlobalError=b,exports.getGlobalFallback=w,exports.getGlobalResolve=M,exports.getResource=A,exports.getRoutes=o,exports.getUseGlobalError=v,exports.getUseGlobalFallback=g,exports.loadRouteData=async function(e){const t=R(o(),e);if(!t)return{};const r={};t.forEach(e=>{const t=e.node.options?.loader;t&&(r[e.node.path]=A(JSON.stringify({path:e.node.path,params:e.params,query:e.query,hash:e.hash}),()=>t({params:e.params,path:e.node.path,query:e.query,hash:e.hash}),e.node.options?.ttl))});for(const e in r)r[e].read();return r},exports.matchRouteChain=R,exports.navigate=O,exports.normalizeQuery=j,exports.parseQueryHash=C,exports.prefetch=I,exports.redirect=function(e,t){return O(e,t)},exports.route=function(e,t,r){const o={path:e,component:t,children:[],options:{}};"function"==typeof r?a(o)(r):r&&(o.options=r),n.push(o)},exports.setGlobalError=function(e,t){x=e,t&&(y=({error:e})=>t(e))},exports.setGlobalFallback=function(e,t){f=e,t&&(m=t)},exports.useLinkCore=$,exports.useLoaderData=p,exports.useNavigate=W,exports.useNavigation=d,exports.useParams=function(){return e.useContext(c)??{}},exports.usePrefetch=function(e,t){return()=>I(e,t)},exports.useQuery=function(){return l().query??{}},exports.useRouteContext=l,exports.useRouteMatch=function(e){return d().path===e},exports.useRouterData=function(){const{meta:e,params:t,path:r,query:n}=l();let o;try{o=p()}catch{}return{data:o,params:t??{},query:n??{},path:r,meta:e}},exports.useTransition=function(){return e.useContext(h)};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
export type TransitionStage = "idle" | "entering" | "exiting";
|
|
3
|
-
export type RouteContextType = {
|
|
3
|
+
export type RouteContextType<M = any> = {
|
|
4
4
|
path: string;
|
|
5
5
|
params: Record<string, any>;
|
|
6
6
|
query: Record<string, string | string[]>;
|
|
7
7
|
hash?: string;
|
|
8
|
+
meta?: M;
|
|
8
9
|
};
|
|
9
10
|
export type RouterState = {
|
|
10
11
|
loading: boolean;
|
|
@@ -16,12 +17,12 @@ export type TransitionState = {
|
|
|
16
17
|
stage: TransitionStage;
|
|
17
18
|
};
|
|
18
19
|
export declare const LoaderDataContext: React.Context<any>;
|
|
19
|
-
export declare const RouteContext: React.Context<RouteContextType | null>;
|
|
20
|
+
export declare const RouteContext: React.Context<RouteContextType<any> | null>;
|
|
20
21
|
export declare const ParamsContext: React.Context<any>;
|
|
21
22
|
export declare const RouterStateContext: React.Context<RouterState>;
|
|
22
23
|
export declare const TransitionContext: React.Context<TransitionState>;
|
|
23
|
-
export declare function useLoaderData<T = unknown>(): T;
|
|
24
|
-
export declare function useRouteContext(): RouteContextType
|
|
24
|
+
export declare function useLoaderData<T = unknown>(): T | undefined;
|
|
25
|
+
export declare function useRouteContext<M = any>(): RouteContextType<M>;
|
|
25
26
|
export declare function useParams<P extends Record<string, any> = Record<string, any>>(): P;
|
|
26
27
|
export declare function useQuery<Q extends Record<string, any> = Record<string, any>>(): Q;
|
|
27
28
|
export declare function useNavigation(): RouterState;
|
|
@@ -31,5 +32,6 @@ export type RouterData<T = unknown, P extends Record<string, any> = Record<strin
|
|
|
31
32
|
params: P;
|
|
32
33
|
query: Q;
|
|
33
34
|
path: string;
|
|
35
|
+
meta?: any;
|
|
34
36
|
};
|
|
35
37
|
export declare function useRouterData<T = unknown, P extends Record<string, any> = Record<string, any>, Q extends Record<string, any> = Record<string, string>>(): RouterData<T, P, Q>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "routexiz",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0-z2",
|
|
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",
|
|
7
7
|
"sideEffects": false,
|