waymark 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/index.d.ts +8 -8
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1177,7 +1177,7 @@ The `History` interface defines how Waymark interacts with navigation. All histo
|
|
|
1177
1177
|
```tsx
|
|
1178
1178
|
interface HistoryLike {
|
|
1179
1179
|
getPath: () => string;
|
|
1180
|
-
getSearch: () => string
|
|
1180
|
+
getSearch: () => Record<string, unknown>;
|
|
1181
1181
|
getState: () => any;
|
|
1182
1182
|
go: (delta: number) => void;
|
|
1183
1183
|
push: (options: HistoryPushOptions) => void;
|
|
@@ -1194,11 +1194,11 @@ const path = history.getPath();
|
|
|
1194
1194
|
// Returns "/users/42"
|
|
1195
1195
|
```
|
|
1196
1196
|
|
|
1197
|
-
`history.getSearch()` returns the current search
|
|
1197
|
+
`history.getSearch()` returns the current search parameters as a parsed object:
|
|
1198
1198
|
|
|
1199
1199
|
```tsx
|
|
1200
1200
|
const search = history.getSearch();
|
|
1201
|
-
// Returns "
|
|
1201
|
+
// Returns { tab: "posts", page: 2 }
|
|
1202
1202
|
```
|
|
1203
1203
|
|
|
1204
1204
|
`history.getState()` returns the current history state:
|
package/dist/index.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ interface HistoryPushOptions {
|
|
|
46
46
|
}
|
|
47
47
|
interface HistoryLike {
|
|
48
48
|
getPath: () => string;
|
|
49
|
-
getSearch: () => string
|
|
49
|
+
getSearch: () => Record<string, unknown>;
|
|
50
50
|
getState: () => any;
|
|
51
51
|
go: (delta: number) => void;
|
|
52
52
|
push: (options: HistoryPushOptions) => void;
|
|
@@ -142,9 +142,7 @@ declare class Router {
|
|
|
142
142
|
readonly routes: RouteList;
|
|
143
143
|
readonly history: HistoryLike;
|
|
144
144
|
readonly defaultLinkOptions?: LinkOptions;
|
|
145
|
-
readonly _
|
|
146
|
-
routeMap: Map<string, Route>;
|
|
147
|
-
};
|
|
145
|
+
private readonly _;
|
|
148
146
|
constructor(options: RouterOptions);
|
|
149
147
|
getRoute<P extends Pattern>(pattern: P | GetRoute<P>): GetRoute<P>;
|
|
150
148
|
match<P extends Pattern>(path: string, options: MatchOptions<P>): Match<P> | null;
|
|
@@ -156,9 +154,11 @@ declare class Router {
|
|
|
156
154
|
//#region src/router/browser-history.d.ts
|
|
157
155
|
declare class BrowserHistory implements HistoryLike {
|
|
158
156
|
private static patchKey;
|
|
157
|
+
private memo?;
|
|
159
158
|
constructor();
|
|
159
|
+
protected getSearchMemo: (search: string) => Record<string, unknown>;
|
|
160
160
|
getPath: () => string;
|
|
161
|
-
getSearch: () => string
|
|
161
|
+
getSearch: () => Record<string, unknown>;
|
|
162
162
|
getState: () => any;
|
|
163
163
|
go: (delta: number) => void;
|
|
164
164
|
push: (options: HistoryPushOptions) => void;
|
|
@@ -168,7 +168,7 @@ declare class BrowserHistory implements HistoryLike {
|
|
|
168
168
|
//#region src/router/memory-history.d.ts
|
|
169
169
|
interface MemoryLocation {
|
|
170
170
|
path: string;
|
|
171
|
-
search: string
|
|
171
|
+
search: Record<string, unknown>;
|
|
172
172
|
state: any;
|
|
173
173
|
}
|
|
174
174
|
declare class MemoryHistory implements HistoryLike {
|
|
@@ -178,7 +178,7 @@ declare class MemoryHistory implements HistoryLike {
|
|
|
178
178
|
constructor(url?: string);
|
|
179
179
|
private getCurrent;
|
|
180
180
|
getPath: () => string;
|
|
181
|
-
getSearch: () => string
|
|
181
|
+
getSearch: () => Record<string, unknown>;
|
|
182
182
|
getState: () => any;
|
|
183
183
|
go: (delta: number) => void;
|
|
184
184
|
push: (options: HistoryPushOptions) => void;
|
|
@@ -189,7 +189,7 @@ declare class MemoryHistory implements HistoryLike {
|
|
|
189
189
|
declare class HashHistory extends BrowserHistory {
|
|
190
190
|
private getHashUrl;
|
|
191
191
|
getPath: () => string;
|
|
192
|
-
getSearch: () => string
|
|
192
|
+
getSearch: () => Record<string, unknown>;
|
|
193
193
|
push: (options: HistoryPushOptions) => void;
|
|
194
194
|
}
|
|
195
195
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Component as e,Suspense as t,cloneElement as n,createContext as r,isValidElement as i,lazy as a,memo as o,useCallback as s,useContext as c,useEffect as
|
|
1
|
+
import{Component as e,Suspense as t,cloneElement as n,createContext as r,isValidElement as i,lazy as a,memo as o,useCallback as s,useContext as c,useEffect as ee,useLayoutEffect as l,useMemo as u,useRef as d,useState as f,useSyncExternalStore as p}from"react";import{inject as m,parse as h}from"regexparam";import{jsx as g}from"react/jsx-runtime";function _(e){return`/${e}`.replaceAll(/\/+/g,`/`).replace(/(.+)\/$/,`$1`)}function v(e){return e.split(`/`).slice(1).map(e=>e.includes(`*`)?0:e.includes(`:`)?1:2)}function y(e){return typeof e==`function`?e:t=>{let n=e[`~standard`].validate(t);if(n instanceof Promise)throw Error(`[Waymark] Validation must be synchronous`);if(n.issues)throw Error(`[Waymark] Validation failed`,{cause:n.issues});return n.value}}function b(e){return Object.entries(e).filter(([e,t])=>t!==void 0).map(([e,t])=>`${e}=${encodeURIComponent(S(t))}`).join(`&`)}function x(e){let t=new URLSearchParams(e);return Object.fromEntries([...t.entries()].map(([e,t])=>(t=decodeURIComponent(t),[e,C(t)?JSON.parse(t):t])))}function S(e){return typeof e==`string`&&!C(e)?e:JSON.stringify(e)}function C(e){try{return JSON.parse(e),!0}catch{return!1}}function w(e,t){return _(`${t}/${e}`)}function T(e,t){return(e===t||e.startsWith(`${t}/`))&&(e=e.slice(t.length)||`/`),e}function E(e,t){return[e,b(t)].filter(Boolean).join(`?`)}function D(e){let{pathname:t,search:n}=new URL(e,`http://w`);return{path:t,search:x(n)}}function O(e,t,n,r){let i=e.exec(T(n,r));if(!i)return null;let a={};return t.forEach((e,t)=>{let n=i[t+1];n&&(a[e]=n)}),a}function k(e){return[...e].sort((e,t)=>{let n=e.route._.weights,r=t.route._.weights,i=Math.max(n.length,r.length);for(let e=0;e<i;e++){let t=n[e]??-1,i=r[e]??-1;if(t!==i)return i-t}return 0})}const A=r(null),j=r(null),M=r(null);function N(){let e=c(A);if(!e)throw Error(`[Waymark] useRouter must be used within a router context`);return e}function P(){let e=c(j);return u(()=>e?.route._.handles??[],[e])}function F(){return c(M)}function I(e,t){return p(e.history.subscribe,t,t)}function L(){let e=N();return u(()=>e.navigate.bind(e),[e])}function te(){let e=N(),t=I(e,e.history.getPath),n=I(e,e.history.getSearch),r=I(e,e.history.getState);return u(()=>({path:t,search:n,state:r}),[t,n,r])}function R(e){let t=N(),n=I(t,t.history.getPath);return u(()=>t.match(n,e),[t,n,e])}function z(e){let t=R({from:e});if(!t)throw Error(`[Waymark] Can't read params for non-matching route: ${e}`);return t.params}function B(e){let t=N(),n=t.getRoute(e),r=s(e=>n._.mapSearch(e),[n]),i=I(t,t.history.getSearch);return[u(()=>r(i),[r,i]),s((e,n)=>{let i=r(t.history.getSearch());e=typeof e==`function`?e(i):e;let a=E(t.history.getPath(),{...i,...e});t.navigate({url:a,replace:n})},[t,r])]}var V=class e{static patchKey=Symbol.for(`waymark_history_patch_v01`);memo;constructor(){if(typeof history<`u`&&!(e.patchKey in window)){for(let e of[H,U]){let t=history[e];history[e]=function(...n){let r=t.apply(this,n),i=new Event(e);return i.arguments=n,dispatchEvent(i),r}}window[e.patchKey]=!0}}getSearchMemo=e=>this.memo?.search===e?this.memo.parsed:(this.memo={search:e,parsed:x(e)}).parsed;getPath=()=>location.pathname;getSearch=()=>this.getSearchMemo(location.search);getState=()=>history.state;go=e=>history.go(e);push=e=>{let{url:t,replace:n,state:r}=e;history[n?U:H](r,``,t)};subscribe=e=>(W.forEach(t=>window.addEventListener(t,e)),()=>{W.forEach(t=>window.removeEventListener(t,e))})};const H=`pushState`,U=`replaceState`,W=[`popstate`,H,U,`hashchange`];var G=class{basePath;routes;history;defaultLinkOptions;_;constructor(e){let{basePath:t=`/`,routes:n,history:r,defaultLinkOptions:i}=e;this.basePath=_(t),this.routes=n,this.history=r??new V,this.defaultLinkOptions=i,this._={routeMap:new Map(n.map(e=>[e.pattern,e]))}}getRoute(e){if(typeof e!=`string`)return e;let t=this._.routeMap.get(e);if(!t)throw Error(`[Waymark] Route not found for pattern: ${e}`);return t}match(e,t){let{from:n,strict:r,params:i}=t,a=this.getRoute(n),o=O(r?a._.regex:a._.looseRegex,a._.keys,e,this.basePath);return!o||i&&Object.keys(i).some(e=>i[e]!==o[e])?null:{route:a,params:o}}matchAll(e){return k(this.routes.map(t=>this.match(e,{from:t,strict:!0})).filter(e=>!!e))[0]??null}createUrl(e){let{to:t,params:n={},search:r={}}=e,{pattern:i}=this.getRoute(t);return E(w(m(i,n),this.basePath),r)}navigate(e){if(typeof e==`number`)this.history.go(e);else if(`url`in e)this.history.push(e);else{let{replace:t,state:n}=e;this.history.push({url:this.createUrl(e),replace:t,state:n})}}},K=class{stack=[];index=0;listeners=new Set;constructor(e=`/`){this.stack.push({...D(e),state:void 0})}getCurrent=()=>this.stack[this.index];getPath=()=>this.getCurrent().path;getSearch=()=>this.getCurrent().search;getState=()=>this.getCurrent().state;go=e=>{let t=this.index+e;this.stack[t]&&(this.index=t,this.listeners.forEach(e=>e()))};push=e=>{let{url:t,replace:n,state:r}=e,i={...D(t),state:r};this.stack=this.stack.slice(0,this.index+1),n?this.stack[this.index]=i:this.index=this.stack.push(i)-1,this.listeners.forEach(e=>e())};subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)})},q=class extends V{getHashUrl=()=>new URL(location.hash.slice(1),`http://w`);getPath=()=>this.getHashUrl().pathname;getSearch=()=>this.getSearchMemo(this.getHashUrl().search);push=e=>{let{url:t,replace:n,state:r}=e;history[n?`replaceState`:`pushState`](r,``,`#${t}`)}};function J(e){let[t]=f(()=>`router`in e?e.router:new G(e)),n=I(t,t.history.getPath),r=u(()=>t.matchAll(n),[t,n]);return r||console.error(`[Waymark] No matching route found for path:`,n),u(()=>g(A.Provider,{value:t,children:g(j.Provider,{value:r,children:r?.route._.components.reduceRight((e,t)=>g(M.Provider,{value:e,children:g(t,{})}),null)})}),[t,r])}function Y(){return F()}function X(e){let t=L();return l(()=>t(e),[]),null}function Z(e){let t=N(),{to:r,replace:a,state:o,params:s,search:c,strict:l,preload:f,style:p,className:m,activeStyle:h,activeClassName:_,asChild:v,children:y,...b}={...t.defaultLinkOptions,...e},x=d(null),S=t.createUrl(e),C=u(()=>t.getRoute(e.to),[t,e.to]),w=!!R({from:C,strict:l,params:s}),T=u(()=>({"data-active":w,style:{...p,...w&&h},className:[m,w&&_].filter(Boolean).join(` `)||void 0}),[w,p,m,h,_]);ee(()=>{if(f===`render`)C.preload();else if(f===`viewport`&&x.current){let e=new IntersectionObserver(t=>{t.forEach(t=>{t.isIntersecting&&(C.preload(),e.disconnect())})});return e.observe(x.current),()=>e.disconnect()}},[f,C]);let E=e=>{b.onClick?.(e),!(e.ctrlKey||e.metaKey||e.shiftKey||e.altKey||e.button!==0||e.defaultPrevented)&&(e.preventDefault(),t.navigate({url:S,replace:a,state:o}))},D=e=>{b.onFocus?.(e),f===`intent`&&!e.defaultPrevented&&C.preload()},O=e=>{b.onPointerEnter?.(e),f===`intent`&&!e.defaultPrevented&&C.preload()},k={...b,...T,ref:ne(b.ref,x),href:S,onClick:E,onFocus:D,onPointerEnter:O};return v&&i(y)?n(y,k):g(`a`,{...k,children:y})}function ne(...e){let t=e.filter(e=>!!e);return t.length<=1?t[0]??null:e=>{let n=[];for(let r of t){let t=Q(r,e);n.push(t??(()=>Q(r,null)))}return()=>n.forEach(e=>e())}}function Q(e,t){if(typeof e==`function`)return e(t);e&&(e.current=t)}function re(e){return()=>g(t,{fallback:g(e,{}),children:F()})}function ie(t){class n extends e{constructor(e){super(e),this.state={children:e.children,error:null}}static getDerivedStateFromError(e){return{error:[e]}}static getDerivedStateFromProps(e,t){return e.children===t.children?t:{children:e.children,error:null}}render(){return this.state.error?g(t,{error:this.state.error[0]}):this.props.children}}return()=>g(n,{children:F()})}function ae(e){return new $(_(e),e=>e,[],[],[])}var $=class e{pattern;_;constructor(e,t,n,r,i){let{keys:a,pattern:o}=h(e),s=h(e,!0).pattern,c=v(e);this.pattern=e,this._={keys:a,regex:o,looseRegex:s,weights:c,mapSearch:t,handles:n,components:r,preloaded:!1,preloaders:i}}route(t){let{mapSearch:n,handles:r,components:i,preloaders:a}=this._;return new e(_(`${this.pattern}/${t}`),n,r,i,a)}search(t){let{mapSearch:n,handles:r,components:i,preloaders:a}=this._;return t=y(t),new e(this.pattern,e=>{let r=n(e);return{...r,...t(r)}},r,i,a)}handle(t){let{mapSearch:n,handles:r,components:i,preloaders:a}=this._;return new e(this.pattern,n,[...r,t],i,a)}preloader(t){let{mapSearch:n,handles:r,components:i,preloaders:a}=this._;return new e(this.pattern,n,r,i,[...a,t])}component(t){let{mapSearch:n,handles:r,components:i,preloaders:a}=this._;return new e(this.pattern,n,r,[...i,o(t)],a)}lazy(e){let t=a(async()=>{let t=await e();return{default:o(`default`in t?t.default:t)}});return this.preloader(e).component(t)}suspense(e){return this.component(re(e))}error(e){return this.component(ie(e))}async preload(){let{preloaded:e,preloaders:t}=this._;e||(this._.preloaded=!0,await Promise.all(t.map(e=>e())))}toString(){return this.pattern}};export{V as BrowserHistory,q as HashHistory,Z as Link,j as MatchContext,K as MemoryHistory,X as Navigate,Y as Outlet,M as OutletContext,$ as Route,G as Router,A as RouterContext,J as RouterRoot,ae as route,P as useHandles,te as useLocation,R as useMatch,L as useNavigate,F as useOutlet,z as useParams,N as useRouter,B as useSearch,I as useSubscribe};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "waymark",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "strblr",
|
|
6
6
|
"description": "Lightweight type-safe router for React",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"minimal"
|
|
40
40
|
],
|
|
41
41
|
"scripts": {
|
|
42
|
-
"build": "tsdown --minify --platform browser",
|
|
42
|
+
"build": "tsc --noEmit && tsdown --minify --platform browser",
|
|
43
43
|
"copy-readme": "cp ../../README.md README.md",
|
|
44
44
|
"prepublishOnly": "bun run build && bun run copy-readme"
|
|
45
45
|
},
|