raviger 2.5.4 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { useRoutes } from './router';
2
- export type { RouteParams, RouteOptionParams } from './router';
1
+ export { useRoutes, useMatch, usePathParams } from './router';
2
+ export type { RouteParams, RouteOptionParams, PathParamOptions } from './router';
3
3
  export { useRedirect, Redirect } from './redirect';
4
4
  export type { RedirectProps, UseRedirectProps } from './redirect';
5
5
  export { Link, ActiveLink } from './Link';
package/dist/main.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react");const t=e.createContext(""),n=e.createContext(null);let r=!0;try{r=void 0===window}catch(e){}const a=new Set;let o=!1,s=!1,u=[0,0];function shouldCancelNavigation(){return u=[window.scrollX,window.scrollY],o?s:Array.from(a).some((e=>{const t=e();return!!t&&(s=!window.confirm(t),o=!0,setTimeout((()=>{o=!1,s=!1}),0),s)}))}function usePath(t){const r=e.useContext(n),a=useBasePath();t=t||a;const[o,s]=e.useState(getFormattedPath(t));return useLocationChange((e=>s(e)),{basePath:t,inheritBasePath:!t}),r||o}function useBasePath(){return e.useContext(t)}function useFullPath(){const[t,n]=e.useState(getCurrentPath());return useLocationChange(n,{inheritBasePath:!1}),t||"/"}function getCurrentPath(){return r?"/":window.location.pathname||"/"}function getCurrentHash(){if(r){const e="/",t=e.indexOf("#");return e.substring(t)}return window.location.hash}function useLocationChange(t,{inheritBasePath:n=!0,basePath:a="",isActive:o,onInitial:s=!1}={}){if(r)return;const u=useBasePath();n&&u&&(a=u);const i=e.useRef(t);e.useLayoutEffect((()=>{i.current=t}));const c=e.useCallback((()=>{(void 0===o||isPredicateActive(o))&&(shouldCancelNavigation()||i.current(getFormattedPath(a)))}),[o,a]);e.useLayoutEffect((()=>(window.addEventListener("popstate",c),()=>window.removeEventListener("popstate",c))),[c]),function useMountedLayout(t,n,{onInitial:r=!1}={}){const a=e.useRef(r);e.useLayoutEffect((()=>{a.current?t():a.current=!0}),n)}((()=>{(void 0===o||isPredicateActive(o))&&i.current(getFormattedPath(a))}),[a,o],{onInitial:s})}function getFormattedPath(e){const t=getCurrentPath(),n=e&&!function isPathInBase(e,t){return!!(e&&t&&t.toLowerCase().startsWith(e.toLowerCase()))}(e,t);return null===t||n?null:e?t.replace(function basePathMatcher(e){return new RegExp("^"+e,"i")}(e),"")||"/":t}function isPredicateActive(e){return function isFunction(e){return e&&"function"==typeof e}(e)?e():e}function createRouteMatcher(e){var t;return{routePath:e,regex:new RegExp(`${"*"===e.substr(0,1)?"":"^"}${e.replace(/:[a-zA-Z]+/g,"([^/]+)").replace(/\*/g,"")}${"*"===e.substr(-1)?"":"$"}`,"i"),props:(null!==(t=e.match(/:[a-zA-Z]+/g))&&void 0!==t?t:[]).map((e=>e.substr(1)))}}function hashRoutes(e){return Object.keys(e).sort().join(":")}let i="";function navigate(e,t,n,r=null){if("string"!=typeof e)throw new Error('"url" must be a string, was provided a(n) '+typeof e);if(Array.isArray(t))throw new Error('"replaceOrQuery" must be boolean, object, or URLSearchParams');shouldCancelNavigation()||(null!==t&&"object"==typeof t?e+="?"+new URLSearchParams(t).toString():void 0===n&&void 0!==t?n=null!=t?t:void 0:void 0===n&&void 0===t&&(n=!1),i=e,!function isAbsolute(e){return/^(?:[a-z]+:)?\/\//i.test(e)}(e)||function isCurrentOrigin(e){return window.location.origin===new URL(e).origin}(e)?(n?window.history.replaceState(r,"",e):window.history.pushState(r,"",e),dispatchEvent(new PopStateEvent("popstate"))):window.location.assign(e))}function useQueryParams(t=parseQuery,n=serializeQuery){const[r,a]=e.useState(getQueryString()),o=e.useCallback(((e,{replace:a=!0}={})=>{let o=getCurrentPath();e=a?e:{...t(r),...e};const s=n(e).toString();s&&(o+="?"+s),a||(o+=getCurrentHash()),navigate(o)}),[r,t,n]);return useLocationChange(e.useCallback((()=>a(getQueryString())),[])),[t(r),o]}function parseQuery(e){const t=new URLSearchParams(e);return Object.fromEntries(t.entries())}function serializeQuery(e){return new URLSearchParams(Object.entries(e).filter((([,e])=>null!==e))).toString()}function getQueryString(){if(r){const e="/",t=e.indexOf("?");return-1===t?"":e.substring(t+1)}return window.location.search}function useRedirect(t,n,{query:r,replace:a=!0,merge:o=!0}={}){const s=usePath(),[u]=useQueryParams(),i=getCurrentHash();let c=n;const l=new URLSearchParams({...o?u:{},...r}).toString();l&&(c+="?"+l),o&&i&&i.length&&(c+=i),e.useLayoutEffect((()=>{s===t&&navigate(c,void 0,a)}),[t,c,a,s])}const c=e.forwardRef((function Link({href:t,basePath:n,...r},a){const o=useBasePath();t=getLinkHref(t,n=n||o);const{onClick:s,target:u}=r,i=e.useCallback((e=>{try{s&&s(e)}catch(t){throw e.preventDefault(),t}(function shouldTrap(e,t){return!e.defaultPrevented&&0===e.button&&!(t||"_self"===t)&&!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)})(e,u)&&(e.preventDefault(),navigate(e.currentTarget.href))}),[s,u]);return e.createElement("a",{...r,href:t,onClick:i,ref:a})}));const l=e.forwardRef((function ActiveLink({basePath:t,className:n,exactActiveClass:r,activeClass:a,...o},s){const u=useBasePath();t=t||u;const i=useFullPath();let{href:l}=o;return l=function absolutePathName(e){return e.startsWith("/")?e:new URL(e,document.baseURI).pathname}(getLinkHref(l,t)),r&&i===l&&(n+=` ${r}`),a&&i.startsWith(l)&&(n+=` ${a}`),e.createElement(c,{...o,basePath:t,className:n,ref:s})}));function getLinkHref(e,t=""){return e.startsWith("/")?t+e:e}exports.ActiveLink=l,exports.Link=c,exports.Redirect=function Redirect({to:e,query:t,replace:n=!0,merge:r=!0}){return useRedirect(usePath(),e,{query:t,replace:n,merge:r}),null},exports.navigate=navigate,exports.useBasePath=useBasePath,exports.useFullPath=useFullPath,exports.useHash=function useHash({stripHash:t=!0}={}){const[n,r]=e.useState(window.location.hash),a=e.useCallback((()=>{window.location.hash!==n&&r(window.location.hash)}),[r,n]);return e.useLayoutEffect((()=>(window.addEventListener("hashchange",a,!1),()=>window.removeEventListener("hashchange",a))),[a]),useLocationChange(a),t?n.substring(1):n},exports.useLocationChange=useLocationChange,exports.useNavigate=function useNavigate(t=""){const n=useBasePath();return e.useCallback(((e,r,a)=>{const o=t||n;navigate(e.startsWith("/")?o+e:e,r,a)}),[n,t])},exports.useNavigationPrompt=function useNavigationPrompt(t=!0,n="Are you sure you want to leave this page?"){r||(e.useLayoutEffect((()=>{const onPopStateNavigation=()=>{shouldCancelNavigation()&&function undoNavigation(e){window.history.pushState(null,null,e),setTimeout((()=>{window.scrollTo(...u)}),0)}(i)};return window.addEventListener("popstate",onPopStateNavigation),()=>window.removeEventListener("popstate",onPopStateNavigation)}),[]),e.useLayoutEffect((()=>{const handler=e=>{if(t)return e?function cancelNavigation(e,t){return e.preventDefault(),e.returnValue=t,t}(e,n):n};return function addInterceptor(e){window.addEventListener("beforeunload",e),a.add(e)}(handler),()=>function removeInterceptor(e){window.removeEventListener("beforeunload",e),a.delete(e)}(handler)}),[t,n]))},exports.usePath=usePath,exports.useQueryParams=useQueryParams,exports.useRedirect=useRedirect,exports.useRoutes=function useRoutes(r,{basePath:a="",routeProps:o={},overridePathParams:s=!0,matchTrailingSlash:u=!0}={}){const i=usePath(a)&&getFormattedPath(a);!function useRedirectDetection(t,n){const[,r]=e.useState(!1);e.useLayoutEffect((()=>{n!==getFormattedPath(t)&&r((e=>!e))}),[t,n])}(a,i);const c=function useMatchRoute(t,n,{routeProps:r,overridePathParams:a,matchTrailingSlash:o}){o&&n&&"/"===n[n.length-1]&&n.length>1&&(n=n.substring(0,n.length-1));const s=e.useMemo((()=>Object.keys(t).map(createRouteMatcher)),[hashRoutes(t)]);if(null===n)return null;let u=null;const i=s.find((({regex:e})=>(u=(null!=n?n:"").match(e),!!u)));if(!i||null===u)return null;const c=i.props.reduce(((e,t,n)=>(e[t]=u[n+1],e)),{});return t[i.routePath](a?{...c,...r}:{...r,...c})}(r,i,{routeProps:o,overridePathParams:s,matchTrailingSlash:u});return c&&null!==i?e.createElement(t.Provider,{value:a},e.createElement(n.Provider,{value:i},c)):null};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react");const t=e.createContext(""),n=e.createContext(null);let r=!0;try{r=void 0===window}catch(e){}const a=new Set;let s=!1,o=!1,u=[0,0];function shouldCancelNavigation(){return u=[window.scrollX,window.scrollY],s?o:Array.from(a).some((e=>{const t=e();return!!t&&(o=!window.confirm(t),s=!0,setTimeout((()=>{s=!1,o=!1}),0),o)}))}function usePath(t){const r=e.useContext(n),a=useBasePath();t=t||a;const[s,o]=e.useState(getFormattedPath(t));return useLocationChange((e=>o(e)),{basePath:t,inheritBasePath:!t}),r||s}function useBasePath(){return e.useContext(t)}function useFullPath(){const[t,n]=e.useState(getCurrentPath());return useLocationChange(n,{inheritBasePath:!1}),t||"/"}function getCurrentPath(){return r?"/":window.location.pathname||"/"}function getCurrentHash(){if(r){const e="/",t=e.indexOf("#");return e.substring(t)}return window.location.hash}function useLocationChange(t,{inheritBasePath:n=!0,basePath:a="",isActive:s,onInitial:o=!1}={}){if(r)return;const u=useBasePath();n&&u&&(a=u);const i=e.useRef(t);e.useLayoutEffect((()=>{i.current=t}));const c=e.useCallback((()=>{(void 0===s||isPredicateActive(s))&&(shouldCancelNavigation()||i.current(getFormattedPath(a)))}),[s,a]);e.useLayoutEffect((()=>(window.addEventListener("popstate",c),()=>window.removeEventListener("popstate",c))),[c]),function useMountedLayout(t,n,{onInitial:r=!1}={}){const a=e.useRef(r);e.useLayoutEffect((()=>{a.current?t():a.current=!0}),n)}((()=>{(void 0===s||isPredicateActive(s))&&i.current(getFormattedPath(a))}),[a,s],{onInitial:o})}function getFormattedPath(e){const t=getCurrentPath(),n=e&&!function isPathInBase(e,t){return!!(e&&t&&t.toLowerCase().startsWith(e.toLowerCase()))}(e,t);return null===t||n?null:e?t.replace(function basePathMatcher(e){return new RegExp("^"+e,"i")}(e),"")||"/":t}function isPredicateActive(e){return function isFunction(e){return e&&"function"==typeof e}(e)?e():e}const i=[null,null];function usePathOptions(e,{basePath:t,matchTrailingSlash:n=!0}){const r=useMatchers(Array.isArray(e)?e:[e]);return[trailingMatch(usePath(t),n),r]}function useMatchers(t){return e.useMemo((()=>t.map(createRouteMatcher)),[(n=t,[...n].sort().join(":"))]);var n}function getMatchParams(e,t){let n=null;const r=t.find((({regex:t})=>(n=e.match(t),!!n)));if(!r||null===n)return i;const a=r.props.reduce(((e,t,r)=>(e[t]=n[r+1],e)),{});return[r,a]}function createRouteMatcher(e){var t;return{path:e,regex:new RegExp(`${"*"===e.substr(0,1)?"":"^"}${e.replace(/:[a-zA-Z]+/g,"([^/]+)").replace(/\*/g,"")}${"*"===e.substr(-1)?"":"$"}`,"i"),props:(null!==(t=e.match(/:[a-zA-Z]+/g))&&void 0!==t?t:[]).map((e=>e.substr(1)))}}function trailingMatch(e,t){return null===e||t&&e&&"/"===e[e.length-1]&&e.length>1&&(e=e.substring(0,e.length-1)),e}let c="";function navigate(e,t,n,r=null){if("string"!=typeof e)throw new Error('"url" must be a string, was provided a(n) '+typeof e);if(Array.isArray(t))throw new Error('"replaceOrQuery" must be boolean, object, or URLSearchParams');shouldCancelNavigation()||(null!==t&&"object"==typeof t?e+="?"+new URLSearchParams(t).toString():void 0===n&&void 0!==t?n=null!=t?t:void 0:void 0===n&&void 0===t&&(n=!1),c=e,!function isAbsolute(e){return/^(?:[a-z]+:)?\/\//i.test(e)}(e)||function isCurrentOrigin(e){return window.location.origin===new URL(e).origin}(e)?(n?window.history.replaceState(r,"",e):window.history.pushState(r,"",e),dispatchEvent(new PopStateEvent("popstate"))):window.location.assign(e))}function useQueryParams(t=parseQuery,n=serializeQuery){const[r,a]=e.useState(getQueryString()),s=e.useCallback(((e,{replace:a=!0}={})=>{let s=getCurrentPath();e=a?e:{...t(r),...e};const o=n(e).toString();o&&(s+="?"+o),a||(s+=getCurrentHash()),navigate(s)}),[r,t,n]);return useLocationChange(e.useCallback((()=>a(getQueryString())),[])),[t(r),s]}function parseQuery(e){const t=new URLSearchParams(e);return Object.fromEntries(t.entries())}function serializeQuery(e){return new URLSearchParams(Object.entries(e).filter((([,e])=>null!==e))).toString()}function getQueryString(){if(r){const e="/",t=e.indexOf("?");return-1===t?"":e.substring(t+1)}return window.location.search}function useRedirect(t,n,{query:r,replace:a=!0,merge:s=!0}={}){const o=usePath(),[u]=useQueryParams(),i=getCurrentHash();let c=n;const l=new URLSearchParams({...s?u:{},...r}).toString();l&&(c+="?"+l),s&&i&&i.length&&(c+=i),e.useLayoutEffect((()=>{o===t&&navigate(c,void 0,a)}),[t,c,a,o])}const l=e.forwardRef((function Link({href:t,basePath:n,...r},a){t=getLinkHref(t,n=useLinkBasePath(n));const{onClick:s,target:o}=r,u=e.useCallback((e=>{try{s&&s(e)}catch(t){throw e.preventDefault(),t}(function shouldTrap(e,t){return!e.defaultPrevented&&0===e.button&&!(t||"_self"===t)&&!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)})(e,o)&&(e.preventDefault(),navigate(e.currentTarget.href))}),[s,o]);return e.createElement("a",{...r,href:t,onClick:u,ref:a})}));const h=e.forwardRef((function ActiveLink({basePath:t,className:n,exactActiveClass:r,activeClass:a,...s},o){t=useLinkBasePath(t);const u=useFullPath();let{href:i}=s;return i=function absolutePathName(e){return e.startsWith("/")?e:new URL(e,document.baseURI).pathname}(getLinkHref(i,t)),r&&u===i&&(n+=` ${r}`),a&&u.startsWith(i)&&(n+=` ${a}`),e.createElement(l,{...s,basePath:t,className:n,ref:o})}));function useLinkBasePath(e){const t=useBasePath();return"/"===e?"":e||t}function getLinkHref(e,t=""){return e.startsWith("/")?t+e:e}exports.ActiveLink=h,exports.Link=l,exports.Redirect=function Redirect({to:e,query:t,replace:n=!0,merge:r=!0}){return useRedirect(usePath(),e,{query:t,replace:n,merge:r}),null},exports.navigate=navigate,exports.useBasePath=useBasePath,exports.useFullPath=useFullPath,exports.useHash=function useHash({stripHash:t=!0}={}){const[n,r]=e.useState(window.location.hash),a=e.useCallback((()=>{window.location.hash!==n&&r(window.location.hash)}),[r,n]);return e.useLayoutEffect((()=>(window.addEventListener("hashchange",a,!1),()=>window.removeEventListener("hashchange",a))),[a]),useLocationChange(a),t?n.substring(1):n},exports.useLocationChange=useLocationChange,exports.useMatch=function useMatch(e,t={}){var n;const[r,a]=usePathOptions(e,t),s=a.find((({regex:e})=>null==r?void 0:r.match(e)));return null!==(n=null==s?void 0:s.path)&&void 0!==n?n:null},exports.useNavigate=function useNavigate(t=""){const n=useBasePath();return e.useCallback(((e,r,a)=>{const s=t||n;navigate(e.startsWith("/")?s+e:e,r,a)}),[n,t])},exports.useNavigationPrompt=function useNavigationPrompt(t=!0,n="Are you sure you want to leave this page?"){r||(e.useLayoutEffect((()=>{const onPopStateNavigation=()=>{shouldCancelNavigation()&&function undoNavigation(e){window.history.pushState(null,null,e),setTimeout((()=>{window.scrollTo(...u)}),0)}(c)};return window.addEventListener("popstate",onPopStateNavigation),()=>window.removeEventListener("popstate",onPopStateNavigation)}),[]),e.useLayoutEffect((()=>{const handler=e=>{if(t)return e?function cancelNavigation(e,t){return e.preventDefault(),e.returnValue=t,t}(e,n):n};return function addInterceptor(e){window.addEventListener("beforeunload",e),a.add(e)}(handler),()=>function removeInterceptor(e){window.removeEventListener("beforeunload",e),a.delete(e)}(handler)}),[t,n]))},exports.usePath=usePath,exports.usePathParams=function usePathParams(e,t={}){const[n,r]=usePathOptions(e,t);if(null===n)return i;const[a,s]=getMatchParams(n,r);return a?[a.path,s]:i},exports.useQueryParams=useQueryParams,exports.useRedirect=useRedirect,exports.useRoutes=function useRoutes(r,{basePath:a="",routeProps:s={},overridePathParams:o=!0,matchTrailingSlash:u=!0}={}){const i=usePath(a)&&getFormattedPath(a);!function useRedirectDetection(t,n){const[,r]=e.useState(!1);e.useLayoutEffect((()=>{n!==getFormattedPath(t)&&r((e=>!e))}),[t,n])}(a,i);const c=function useMatchRoute(e,t,{routeProps:n,overridePathParams:r,matchTrailingSlash:a}){t=trailingMatch(t,a);const s=useMatchers(Object.keys(e));if(null===t)return null;const[o,u]=getMatchParams(t,s);return o?e[o.path](r?{...u,...n}:{...n,...u}):null}(r,i,{routeProps:s,overridePathParams:o,matchTrailingSlash:u});return c&&null!==i?e.createElement(t.Provider,{value:a},e.createElement(n.Provider,{value:i},c)):null};
2
2
  //# sourceMappingURL=main.js.map
package/dist/main.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sources":["../src/context.ts","../src/node.ts","../src/intercept.ts","../src/path.ts","../src/hooks.ts","../src/typeChecks.ts","../src/router.tsx","../src/navigate.ts","../src/querystring.ts","../src/redirect.ts","../src/Link.tsx"],"sourcesContent":["import { createContext, useContext, useMemo } from 'react'\n\nconst BasePathContext = createContext('')\nconst PathContext = createContext<string | null>(null)\n\nexport { BasePathContext }\nexport { PathContext }\n\nexport function useRouter(): { basePath: string; path: string | null } {\n const [basePath, path] = [useContext(BasePathContext), useContext(PathContext)]\n return useMemo(() => ({ basePath, path }), [basePath, path])\n}\n","let ssrPath = '/'\nlet isNode = true // eslint-disable-line import/no-mutable-exports\ntry {\n isNode = window === undefined\n} catch (e) {} // eslint-disable-line no-empty\n\nexport { isNode }\nexport function getSsrPath(): string {\n return ssrPath\n}\nexport function setSsrPath(path: string): void {\n ssrPath = path\n}\n","const interceptors = new Set<() => string | void>()\n\nexport const defaultPrompt = 'Are you sure you want to leave this page?'\n\nlet hasIntercepted = false\nlet hasUserCancelled = false\nlet lastScroll = [0, 0] as [number, number]\n\nexport function shouldCancelNavigation(): boolean {\n lastScroll = [window.scrollX, window.scrollY]\n if (hasIntercepted) return hasUserCancelled\n\n // confirm if any interceptors return true\n return Array.from(interceptors).some((interceptor) => {\n const prompt = interceptor()\n if (!prompt) return false\n\n // cancel navigation if user declines\n hasUserCancelled = !window.confirm(prompt) // eslint-disable-line no-alert\n\n // track user response so that multiple interceptors don't prompt\n hasIntercepted = true\n\n // reset so that future navigation attempts are prompted\n setTimeout(() => {\n hasIntercepted = false\n hasUserCancelled = false\n }, 0)\n\n return hasUserCancelled\n })\n}\n\nexport function addInterceptor(handler: () => string | void): void {\n window.addEventListener('beforeunload', handler)\n interceptors.add(handler)\n}\n\nexport function removeInterceptor(handler: () => string | void): void {\n window.removeEventListener('beforeunload', handler)\n interceptors.delete(handler)\n}\n\nexport function undoNavigation(lastPath: string): void {\n window.history.pushState(null, null as unknown as string, lastPath)\n setTimeout(() => {\n window.scrollTo(...lastScroll)\n }, 0)\n}\n","import { useState, useCallback, useRef, useContext, useLayoutEffect } from 'react'\n\nimport { BasePathContext, PathContext } from './context'\nimport { useMountedLayout } from './hooks'\nimport { getSsrPath, isNode } from './node'\nimport { shouldCancelNavigation } from './intercept'\nimport { isFunction } from './typeChecks'\n\nexport interface LocationChangeSetFn {\n (path: string | null): void\n}\nexport interface LocationChangeOptionParams {\n inheritBasePath?: boolean\n basePath?: string\n isActive?: boolean | (() => boolean)\n onInitial?: boolean\n}\n\nexport function usePath(basePath?: string): string | null {\n const contextPath = useContext(PathContext)\n const contextBasePath = useBasePath() // hooks can't be called conditionally\n basePath = basePath || contextBasePath\n\n const [path, setPath] = useState(getFormattedPath(basePath))\n useLocationChange((newPath) => setPath(newPath), {\n basePath,\n inheritBasePath: !basePath,\n })\n\n return contextPath || path\n}\n\nexport function useBasePath(): string {\n return useContext(BasePathContext)\n}\n\nexport function useFullPath(): string {\n const [path, setPath] = useState<string | null>(getCurrentPath())\n useLocationChange(setPath, { inheritBasePath: false })\n\n return path || '/'\n}\n\nexport function useHash({ stripHash = true } = {}): string {\n const [hash, setHash] = useState(window.location.hash)\n const handleHash = useCallback(() => {\n if (window.location.hash === hash) return\n setHash(window.location.hash)\n }, [setHash, hash])\n\n useLayoutEffect(() => {\n window.addEventListener('hashchange', handleHash, false)\n return () => window.removeEventListener('hashchange', handleHash)\n }, [handleHash])\n\n useLocationChange(handleHash)\n return stripHash ? hash.substring(1) : hash\n}\n\nexport function getCurrentPath(): string {\n return isNode ? getSsrPath() : window.location.pathname || '/'\n}\n\nexport function getCurrentHash(): string {\n if (isNode) {\n const path = getSsrPath()\n const hashIndex = path.indexOf('#')\n return path.substring(hashIndex)\n }\n return window.location.hash\n}\n\nexport function useLocationChange(\n setFn: LocationChangeSetFn,\n {\n inheritBasePath = true,\n basePath = '',\n isActive,\n onInitial = false,\n }: LocationChangeOptionParams = {}\n): void {\n if (isNode) return\n\n // All hooks after this are conditional, but the runtime can't actually change\n /* eslint-disable react-hooks/rules-of-hooks */\n\n const routerBasePath = useBasePath()\n if (inheritBasePath && routerBasePath) basePath = routerBasePath\n\n const setRef = useRef<LocationChangeSetFn>(setFn)\n useLayoutEffect(() => {\n // setFn could be an in-render declared callback, making it unstable\n // This is a method of using an often-changing callback from React Hooks\n // https://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback\n // While not recommended, it is the best current (16.9) available method\n // For reducing the useEffect cleanup from setFn changing every render\n setRef.current = setFn\n })\n\n const onPopState = useCallback(() => {\n // No predicate defaults true\n if (isActive !== undefined && !isPredicateActive(isActive)) return\n if (shouldCancelNavigation()) return\n setRef.current(getFormattedPath(basePath))\n }, [isActive, basePath])\n\n useLayoutEffect(() => {\n window.addEventListener('popstate', onPopState)\n return () => window.removeEventListener('popstate', onPopState)\n }, [onPopState])\n\n // When the basePath changes re-check the path after the render completes\n // This allows nested contexts to get an up-to-date formatted path\n useMountedLayout(\n () => {\n if (isActive !== undefined && !isPredicateActive(isActive)) return\n setRef.current(getFormattedPath(basePath))\n },\n [basePath, isActive],\n { onInitial }\n )\n\n /* eslint-enable react-hooks/rules-of-hooks */\n}\n\n/**\n * Returns the current path. If basePath is provided it will be removed from the front of the path.\n * If basePath is provided and the path does not begin with it will return null\n * @param {string} basePath basePath, if any\n * @return {string | null} returns path with basePath prefix removed, or null if basePath is provided and missing\n */\nexport function getFormattedPath(basePath: string): string | null {\n const path = getCurrentPath()\n const baseMissing = basePath && !isPathInBase(basePath, path)\n if (path === null || baseMissing) return null\n return !basePath ? path : path.replace(basePathMatcher(basePath), '') || '/'\n}\n\nfunction isPredicateActive(predicate: boolean | (() => boolean)): boolean {\n return isFunction(predicate) ? predicate() : predicate\n}\n\nfunction basePathMatcher(basePath: string): RegExp {\n return new RegExp('^' + basePath, 'i')\n}\n\nfunction isPathInBase(basePath: string, path: string): boolean {\n return !!(basePath && path && path.toLowerCase().startsWith(basePath.toLowerCase()))\n}\n","import { useLayoutEffect, useRef } from 'react'\n\nexport function useMountedLayout(\n fn: () => any,\n deps: React.DependencyList | undefined,\n { onInitial = false } = {}\n): void {\n const hasMounted = useRef(onInitial)\n useLayoutEffect(() => {\n if (!hasMounted.current) hasMounted.current = true\n else fn()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps)\n}\n","// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-module-boundary-types\nexport function isFunction(obj: any): obj is Function {\n return obj && typeof obj === 'function'\n}\n","import React, { useMemo, useState, useLayoutEffect } from 'react'\n\nimport { BasePathContext, PathContext } from './context'\nimport { isNode, setSsrPath, getSsrPath } from './node'\nimport { getFormattedPath, usePath } from './path'\n\nexport interface RouteParams {\n [key: string]: (...props: any) => JSX.Element\n}\n\nexport interface RouteOptionParams {\n basePath?: string\n routeProps?: { [k: string]: any }\n overridePathParams?: boolean\n matchTrailingSlash?: boolean\n}\n\ninterface RouteMatcher {\n routePath: string\n regex: RegExp\n props: string[]\n}\n\nexport function useRoutes(\n routes: RouteParams,\n {\n basePath = '',\n routeProps = {},\n overridePathParams = true,\n matchTrailingSlash = true,\n }: RouteOptionParams = {}\n): JSX.Element | null {\n /*\n This is a hack to setup a listener for the path while always using this latest path\n The issue with usePath is that, in order to not re-render nested components when\n their parent router changes the path, it uses the context's path\n But since that path has to get _set_ here in useRoutes something has to give\n If usePath returns latest it causes render thrashing\n If useRoutes hacks itself into the latest path nothing bad happens (...afaik)\n */\n\n const path = usePath(basePath) && getFormattedPath(basePath)\n\n // Handle potential <Redirect /> use in routes\n useRedirectDetection(basePath, path)\n\n // Get the current route\n const route = useMatchRoute(routes, path, {\n routeProps,\n overridePathParams,\n matchTrailingSlash,\n })\n // No match should not return an empty Provider, just null\n if (!route || path === null) return null\n return (\n <BasePathContext.Provider value={basePath}>\n <PathContext.Provider value={path}>{route}</PathContext.Provider>\n </BasePathContext.Provider>\n )\n}\n\nexport function setPath(path: string): void {\n if (!isNode) {\n throw new Error('This method should only be used in NodeJS environments')\n }\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const url = require('url')\n setSsrPath(url.resolve(getSsrPath(), path))\n}\n\nfunction useMatchRoute(\n routes: RouteParams,\n path: string | null,\n { routeProps, overridePathParams, matchTrailingSlash }: Omit<RouteOptionParams, 'basePath'>\n) {\n // path.length > 1 ensure we still match on the root route \"/\" when matchTrailingSlash is set\n if (matchTrailingSlash && path && path[path.length - 1] === '/' && path.length > 1) {\n path = path.substring(0, path.length - 1)\n }\n\n const routeMatchers = useMemo(\n () => Object.keys(routes).map(createRouteMatcher),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [hashRoutes(routes)]\n )\n\n if (path === null) return null\n\n // Hacky method for find + map\n let pathParams: RegExpMatchArray | null = null\n const routeMatch = routeMatchers.find(({ regex }) => {\n pathParams = (path ?? '').match(regex)\n return !!pathParams\n })\n\n if (!routeMatch || pathParams === null) return null\n\n const props = routeMatch.props.reduce((props: any, prop, i) => {\n // The following `match` can't be null because the above return asserts it\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n props[prop] = pathParams![i + 1]\n return props\n }, {})\n\n return routes[routeMatch.routePath](\n overridePathParams ? { ...props, ...routeProps } : { ...routeProps, ...props }\n )\n}\n\nfunction createRouteMatcher(routePath: string): RouteMatcher {\n return {\n routePath,\n regex: new RegExp(\n `${routePath.substr(0, 1) === '*' ? '' : '^'}${routePath\n .replace(/:[a-zA-Z]+/g, '([^/]+)')\n .replace(/\\*/g, '')}${routePath.substr(-1) === '*' ? '' : '$'}`,\n 'i'\n ),\n props: (routePath.match(/:[a-zA-Z]+/g) ?? []).map((paramName) => paramName.substr(1)),\n }\n}\n\n// React doesn't like when the hook dependency array changes size\n// >> Warning: The final argument passed to useMemo changed size between renders. The order and size of this array must remain constant.\n// It is recommended to use a hashing function to produce a single, stable value\n// https://github.com/facebook/react/issues/14324#issuecomment-441489421\nfunction hashRoutes(routes: RouteParams): string {\n return Object.keys(routes).sort().join(':')\n}\n\n// React appears to suppress parent's re-rendering when a child's\n// useLayoutEffect updates internal state\n// the `navigate` call in useRedirect *does* cause usePath/useLocationChange\n// to fire, but without this hack useRoutes suppresses the update\n// TODO: find a better way to cause a synchronous update from useRoutes\nfunction useRedirectDetection(basePath: string, path: string | null) {\n const [, forceRender] = useState(false)\n useLayoutEffect(() => {\n if (path !== getFormattedPath(basePath)) {\n forceRender((s) => !s)\n }\n }, [basePath, path])\n}\n","import { useCallback, useLayoutEffect } from 'react'\n\nimport { useBasePath } from './path'\nimport { isNode } from './node'\nimport type { QueryParam } from './querystring'\nimport {\n shouldCancelNavigation,\n addInterceptor,\n removeInterceptor,\n defaultPrompt,\n undoNavigation,\n} from './intercept'\n\nexport interface NavigateWithReplace {\n (url: string, replace?: boolean): void\n}\nexport interface NavigateWithQuery {\n (url: string, query?: QueryParam | URLSearchParams, replace?: boolean): void\n}\n\nlet lastPath = ''\n\nexport function navigate(url: string): void\nexport function navigate(url: string, replace: boolean): void\nexport function navigate(url: string, query: QueryParam | URLSearchParams): void\nexport function navigate(url: string, query: QueryParam | URLSearchParams, replace: boolean): void\nexport function navigate(\n url: string,\n queryOrReplace?: QueryParam | URLSearchParams | boolean | null,\n replace?: boolean\n): void\nexport function navigate(\n url: string,\n query: QueryParam | URLSearchParams,\n replace: boolean,\n state: unknown\n): void\nexport function navigate(\n url: string,\n replaceOrQuery?: QueryParam | URLSearchParams | boolean | null,\n replace?: boolean,\n state: unknown = null\n): void {\n if (typeof url !== 'string') {\n throw new Error(`\"url\" must be a string, was provided a(n) ${typeof url}`)\n }\n\n if (Array.isArray(replaceOrQuery)) {\n throw new Error('\"replaceOrQuery\" must be boolean, object, or URLSearchParams')\n }\n\n if (shouldCancelNavigation()) return\n if (replaceOrQuery !== null && typeof replaceOrQuery === 'object') {\n url += '?' + new URLSearchParams(replaceOrQuery).toString()\n } else if (replace === undefined && replaceOrQuery !== undefined) {\n replace = replaceOrQuery ?? undefined\n } else if (replace === undefined && replaceOrQuery === undefined) {\n replace = false\n }\n\n lastPath = url\n // if the origin does not match history navigation will fail with\n // \"cannot be created in a document with origin\"\n // When navigating to another domain we must use location instead of history\n if (isAbsolute(url) && !isCurrentOrigin(url)) {\n window.location.assign(url)\n return\n }\n\n if (replace) window.history.replaceState(state, '', url)\n else window.history.pushState(state, '', url)\n\n dispatchEvent(new PopStateEvent('popstate'))\n}\n\nexport function useNavigationPrompt(predicate = true, prompt: string = defaultPrompt): void {\n if (isNode) return\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useLayoutEffect(() => {\n const onPopStateNavigation = () => {\n if (shouldCancelNavigation()) {\n undoNavigation(lastPath)\n }\n }\n window.addEventListener('popstate', onPopStateNavigation)\n return () => window.removeEventListener('popstate', onPopStateNavigation)\n }, [])\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useLayoutEffect(() => {\n const handler = (e?: BeforeUnloadEvent): string | void => {\n if (predicate) {\n return e ? cancelNavigation(e, prompt) : prompt\n }\n }\n addInterceptor(handler)\n return () => removeInterceptor(handler)\n }, [predicate, prompt])\n}\n\nfunction cancelNavigation(event: BeforeUnloadEvent, prompt: string) {\n // Cancel the event as stated by the standard.\n event.preventDefault()\n // Chrome requires returnValue to be set.\n event.returnValue = prompt\n // Return value for prompt per spec\n return prompt\n}\n\nexport function useNavigate(optBasePath = ''): NavigateWithReplace & NavigateWithQuery {\n const basePath = useBasePath()\n const navigateWithBasePath = useCallback<NavigateWithReplace & NavigateWithQuery>(\n (url: string, replaceOrQuery?: boolean | QueryParam | URLSearchParams, replace?: boolean) => {\n const base = optBasePath || basePath\n const href = url.startsWith('/') ? base + url : url\n navigate(href, replaceOrQuery, replace)\n },\n [basePath, optBasePath]\n )\n return navigateWithBasePath\n}\n\nfunction isAbsolute(url: string) {\n return /^(?:[a-z]+:)?\\/\\//i.test(url)\n}\n\nfunction isCurrentOrigin(url: string) {\n return window.location.origin === new URL(url).origin\n}\n","import { useState, useCallback } from 'react'\n\nimport { navigate } from './navigate'\nimport { isNode, getSsrPath } from './node'\nimport { getCurrentPath, getCurrentHash, useLocationChange } from './path'\n\nexport interface QueryParam {\n [key: string]: any\n}\n\nexport interface setQueryParamsOptions {\n replace?: boolean\n}\n\nexport function useQueryParams<T extends QueryParam>(\n parseFn: (query: string) => T = parseQuery,\n serializeFn: (query: T) => string = serializeQuery\n): [T, (query: T, options?: setQueryParamsOptions) => void] {\n const [querystring, setQuerystring] = useState(getQueryString())\n const setQueryParams = useCallback(\n (params, { replace = true } = {}) => {\n let path = getCurrentPath()\n params = replace ? params : { ...parseFn(querystring), ...params }\n const serialized = serializeFn(params).toString()\n\n if (serialized) path += '?' + serialized\n if (!replace) path += getCurrentHash()\n\n navigate(path)\n },\n [querystring, parseFn, serializeFn]\n )\n\n // Update state when route changes\n const updateQuery = useCallback(() => setQuerystring(getQueryString()), [])\n\n useLocationChange(updateQuery)\n return [parseFn(querystring), setQueryParams]\n}\n\nfunction parseQuery<T extends QueryParam>(querystring: string): T {\n const q = new URLSearchParams(querystring)\n return Object.fromEntries(q.entries()) as T\n}\n\nfunction serializeQuery<T extends QueryParam>(queryParams: T): string {\n return new URLSearchParams(Object.entries(queryParams).filter(([, v]) => v !== null)).toString()\n}\n\nexport function getQueryString(): string {\n if (isNode) {\n const ssrPath = getSsrPath()\n const queryIndex = ssrPath.indexOf('?')\n return queryIndex === -1 ? '' : ssrPath.substring(queryIndex + 1)\n }\n return window.location.search\n}\n","import { useLayoutEffect } from 'react'\n\nimport { getCurrentHash, usePath } from './path'\nimport { navigate } from './navigate'\nimport { QueryParam, useQueryParams } from './querystring'\n\nexport interface RedirectProps {\n to: string\n query?: QueryParam | URLSearchParams\n replace?: boolean\n merge?: boolean\n}\n\nexport interface UseRedirectProps {\n predicateUrl: string\n targetUrl: string\n queryParams?: QueryParam | URLSearchParams\n replace?: boolean\n}\n\nexport function Redirect({\n to,\n query,\n replace = true,\n merge = true,\n}: RedirectProps): JSX.Element | null {\n useRedirect(usePath(), to, { query, replace, merge })\n return null\n}\n\nexport function useRedirect(\n predicateUrl: string | null,\n targetUrl: string,\n {\n query,\n replace = true,\n merge = true,\n }: { query?: QueryParam; replace?: boolean; merge?: boolean } = {}\n): void {\n const currentPath = usePath()\n const [currentQuery] = useQueryParams()\n const hash = getCurrentHash()\n\n let url = targetUrl\n const targetQuery = new URLSearchParams({\n ...(merge ? currentQuery : {}),\n ...query,\n }).toString()\n if (targetQuery) {\n url += '?' + targetQuery\n }\n if (merge && hash && hash.length) {\n url += hash\n }\n\n useLayoutEffect(() => {\n if (currentPath === predicateUrl) {\n navigate(url, undefined, replace)\n }\n }, [predicateUrl, url, replace, currentPath])\n}\n","import React, { useCallback, forwardRef, Ref } from 'react'\n\nimport { navigate } from './navigate'\nimport { useBasePath, useFullPath } from './path'\n\nexport interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string\n basePath?: string\n children?: React.ReactNode\n}\nexport type LinkRef = HTMLAnchorElement | null\n\nexport interface ActiveLinkProps extends LinkProps {\n activeClass?: string\n exactActiveClass?: string\n}\n\nfunction Link({ href, basePath, ...props }: LinkProps, ref?: Ref<HTMLAnchorElement>) {\n const contextBasePath = useBasePath()\n basePath = basePath || contextBasePath\n href = getLinkHref(href, basePath)\n\n const { onClick, target } = props\n\n const handleClick = useCallback<React.MouseEventHandler<HTMLAnchorElement>>(\n (e) => {\n try {\n if (onClick) onClick(e)\n } catch (ex) {\n e.preventDefault()\n throw ex\n }\n if (shouldTrap(e, target)) {\n e.preventDefault() // prevent the link from actually navigating\n navigate(e.currentTarget.href)\n }\n },\n [onClick, target]\n )\n\n return <a {...props} href={href} onClick={handleClick} ref={ref} />\n}\n\nconst RefLink = forwardRef<LinkRef, LinkProps>(Link) as (\n props: LinkProps & { ref?: React.ForwardedRef<HTMLAnchorElement> }\n) => ReturnType<typeof Link>\n\nexport default RefLink\nexport { RefLink as Link }\n\nfunction ActiveLink(\n { basePath, className, exactActiveClass, activeClass, ...props }: ActiveLinkProps,\n ref?: Ref<HTMLAnchorElement>\n) {\n const contextBasePath = useBasePath()\n basePath = basePath || contextBasePath\n const fullPath = useFullPath()\n\n let { href } = props\n href = absolutePathName(getLinkHref(href, basePath))\n\n if (exactActiveClass && fullPath === href) className += ` ${exactActiveClass}`\n if (activeClass && fullPath.startsWith(href)) className += ` ${activeClass}`\n\n return <RefLink {...props} basePath={basePath} className={className} ref={ref} />\n}\n\nconst ActiveLinkRef = forwardRef<LinkRef, ActiveLinkProps>(ActiveLink) as (\n props: ActiveLinkProps & { ref?: React.ForwardedRef<HTMLAnchorElement> }\n) => ReturnType<typeof ActiveLink>\n\nexport { ActiveLinkRef as ActiveLink }\n\nfunction getLinkHref(href: string, basePath = '') {\n return href.startsWith('/') ? basePath + href : href\n}\n\nfunction absolutePathName(href: string): string {\n if (href.startsWith('/')) return href\n return new URL(href, document.baseURI).pathname\n}\n\nfunction shouldTrap(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, target?: string) {\n return (\n !e.defaultPrevented && // onClick prevented default\n e.button === 0 && // ignore everything but left clicks\n !(target || target === '_self') && // don't trap target === blank\n !(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)\n )\n}\n"],"names":["BasePathContext","createContext","PathContext","isNode","undefined","window","e","interceptors","Set","hasIntercepted","hasUserCancelled","lastScroll","shouldCancelNavigation","scrollX","scrollY","Array","from","some","interceptor","prompt","confirm","setTimeout","usePath","basePath","contextPath","useContext","contextBasePath","useBasePath","path","setPath","useState","getFormattedPath","useLocationChange","newPath","inheritBasePath","useFullPath","getCurrentPath","location","pathname","getCurrentHash","hashIndex","indexOf","substring","hash","setFn","isActive","onInitial","routerBasePath","setRef","useRef","useLayoutEffect","current","onPopState","useCallback","isPredicateActive","addEventListener","removeEventListener","useMountedLayout","fn","deps","hasMounted","baseMissing","isPathInBase","toLowerCase","startsWith","replace","basePathMatcher","RegExp","predicate","isFunction","obj","createRouteMatcher","routePath","regex","substr","props","match","map","paramName","hashRoutes","routes","Object","keys","sort","join","lastPath","navigate","url","replaceOrQuery","state","Error","isArray","URLSearchParams","toString","isAbsolute","test","isCurrentOrigin","origin","URL","history","replaceState","pushState","dispatchEvent","PopStateEvent","assign","useQueryParams","parseFn","parseQuery","serializeFn","serializeQuery","querystring","setQuerystring","getQueryString","setQueryParams","params","serialized","q","fromEntries","entries","queryParams","filter","v","ssrPath","queryIndex","search","useRedirect","predicateUrl","targetUrl","query","merge","currentPath","currentQuery","targetQuery","length","RefLink","forwardRef","Link","href","ref","getLinkHref","onClick","target","handleClick","ex","preventDefault","shouldTrap","defaultPrevented","button","metaKey","altKey","ctrlKey","shiftKey","currentTarget","React","ActiveLinkRef","ActiveLink","className","exactActiveClass","activeClass","fullPath","absolutePathName","document","baseURI","Redirect","to","useHash","stripHash","setHash","handleHash","useNavigate","optBasePath","base","useNavigationPrompt","onPopStateNavigation","undoNavigation","scrollTo","handler","cancelNavigation","event","returnValue","addInterceptor","add","removeInterceptor","delete","useRoutes","routeProps","overridePathParams","matchTrailingSlash","useRedirectDetection","forceRender","s","route","useMatchRoute","routeMatchers","useMemo","pathParams","routeMatch","find","reduce","prop","i","Provider","value"],"mappings":"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"}
1
+ {"version":3,"file":"main.js","sources":["../src/context.ts","../src/node.ts","../src/intercept.ts","../src/path.ts","../src/hooks.ts","../src/typeChecks.ts","../src/router.tsx","../src/navigate.ts","../src/querystring.ts","../src/redirect.ts","../src/Link.tsx"],"sourcesContent":["import { createContext, useContext, useMemo } from 'react'\n\nconst BasePathContext = createContext('')\nconst PathContext = createContext<string | null>(null)\n\nexport { BasePathContext }\nexport { PathContext }\n\nexport function useRouter(): { basePath: string; path: string | null } {\n const [basePath, path] = [useContext(BasePathContext), useContext(PathContext)]\n return useMemo(() => ({ basePath, path }), [basePath, path])\n}\n","let ssrPath = '/'\nlet isNode = true // eslint-disable-line import/no-mutable-exports\ntry {\n isNode = window === undefined\n} catch (e) {} // eslint-disable-line no-empty\n\nexport { isNode }\nexport function getSsrPath(): string {\n return ssrPath\n}\nexport function setSsrPath(path: string): void {\n ssrPath = path\n}\n","const interceptors = new Set<() => string | void>()\n\nexport const defaultPrompt = 'Are you sure you want to leave this page?'\n\nlet hasIntercepted = false\nlet hasUserCancelled = false\nlet lastScroll = [0, 0] as [number, number]\n\nexport function shouldCancelNavigation(): boolean {\n lastScroll = [window.scrollX, window.scrollY]\n if (hasIntercepted) return hasUserCancelled\n\n // confirm if any interceptors return true\n return Array.from(interceptors).some((interceptor) => {\n const prompt = interceptor()\n if (!prompt) return false\n\n // cancel navigation if user declines\n hasUserCancelled = !window.confirm(prompt) // eslint-disable-line no-alert\n\n // track user response so that multiple interceptors don't prompt\n hasIntercepted = true\n\n // reset so that future navigation attempts are prompted\n setTimeout(() => {\n hasIntercepted = false\n hasUserCancelled = false\n }, 0)\n\n return hasUserCancelled\n })\n}\n\nexport function addInterceptor(handler: () => string | void): void {\n window.addEventListener('beforeunload', handler)\n interceptors.add(handler)\n}\n\nexport function removeInterceptor(handler: () => string | void): void {\n window.removeEventListener('beforeunload', handler)\n interceptors.delete(handler)\n}\n\nexport function undoNavigation(lastPath: string): void {\n window.history.pushState(null, null as unknown as string, lastPath)\n setTimeout(() => {\n window.scrollTo(...lastScroll)\n }, 0)\n}\n","import { useState, useCallback, useRef, useContext, useLayoutEffect } from 'react'\n\nimport { BasePathContext, PathContext } from './context'\nimport { useMountedLayout } from './hooks'\nimport { getSsrPath, isNode } from './node'\nimport { shouldCancelNavigation } from './intercept'\nimport { isFunction } from './typeChecks'\n\nexport interface LocationChangeSetFn {\n (path: string | null): void\n}\nexport interface LocationChangeOptionParams {\n inheritBasePath?: boolean\n basePath?: string\n isActive?: boolean | (() => boolean)\n onInitial?: boolean\n}\n\nexport function usePath(basePath?: string): string | null {\n const contextPath = useContext(PathContext)\n const contextBasePath = useBasePath() // hooks can't be called conditionally\n basePath = basePath || contextBasePath\n\n const [path, setPath] = useState(getFormattedPath(basePath))\n useLocationChange((newPath) => setPath(newPath), {\n basePath,\n inheritBasePath: !basePath,\n })\n\n return contextPath || path\n}\n\nexport function useBasePath(): string {\n return useContext(BasePathContext)\n}\n\nexport function useFullPath(): string {\n const [path, setPath] = useState<string | null>(getCurrentPath())\n useLocationChange(setPath, { inheritBasePath: false })\n\n return path || '/'\n}\n\nexport function useHash({ stripHash = true } = {}): string {\n const [hash, setHash] = useState(window.location.hash)\n const handleHash = useCallback(() => {\n if (window.location.hash === hash) return\n setHash(window.location.hash)\n }, [setHash, hash])\n\n useLayoutEffect(() => {\n window.addEventListener('hashchange', handleHash, false)\n return () => window.removeEventListener('hashchange', handleHash)\n }, [handleHash])\n\n useLocationChange(handleHash)\n return stripHash ? hash.substring(1) : hash\n}\n\nexport function getCurrentPath(): string {\n return isNode ? getSsrPath() : window.location.pathname || '/'\n}\n\nexport function getCurrentHash(): string {\n if (isNode) {\n const path = getSsrPath()\n const hashIndex = path.indexOf('#')\n return path.substring(hashIndex)\n }\n return window.location.hash\n}\n\nexport function useLocationChange(\n setFn: LocationChangeSetFn,\n {\n inheritBasePath = true,\n basePath = '',\n isActive,\n onInitial = false,\n }: LocationChangeOptionParams = {}\n): void {\n if (isNode) return\n\n // All hooks after this are conditional, but the runtime can't actually change\n /* eslint-disable react-hooks/rules-of-hooks */\n\n const routerBasePath = useBasePath()\n if (inheritBasePath && routerBasePath) basePath = routerBasePath\n\n const setRef = useRef<LocationChangeSetFn>(setFn)\n useLayoutEffect(() => {\n // setFn could be an in-render declared callback, making it unstable\n // This is a method of using an often-changing callback from React Hooks\n // https://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback\n // While not recommended, it is the best current (16.9) available method\n // For reducing the useEffect cleanup from setFn changing every render\n setRef.current = setFn\n })\n\n const onPopState = useCallback(() => {\n // No predicate defaults true\n if (isActive !== undefined && !isPredicateActive(isActive)) return\n if (shouldCancelNavigation()) return\n setRef.current(getFormattedPath(basePath))\n }, [isActive, basePath])\n\n useLayoutEffect(() => {\n window.addEventListener('popstate', onPopState)\n return () => window.removeEventListener('popstate', onPopState)\n }, [onPopState])\n\n // When the basePath changes re-check the path after the render completes\n // This allows nested contexts to get an up-to-date formatted path\n useMountedLayout(\n () => {\n if (isActive !== undefined && !isPredicateActive(isActive)) return\n setRef.current(getFormattedPath(basePath))\n },\n [basePath, isActive],\n { onInitial }\n )\n\n /* eslint-enable react-hooks/rules-of-hooks */\n}\n\n/**\n * Returns the current path. If basePath is provided it will be removed from the front of the path.\n * If basePath is provided and the path does not begin with it will return null\n * @param {string} basePath basePath, if any\n * @return {string | null} returns path with basePath prefix removed, or null if basePath is provided and missing\n */\nexport function getFormattedPath(basePath: string): string | null {\n const path = getCurrentPath()\n const baseMissing = basePath && !isPathInBase(basePath, path)\n if (path === null || baseMissing) return null\n return !basePath ? path : path.replace(basePathMatcher(basePath), '') || '/'\n}\n\nfunction isPredicateActive(predicate: boolean | (() => boolean)): boolean {\n return isFunction(predicate) ? predicate() : predicate\n}\n\nfunction basePathMatcher(basePath: string): RegExp {\n return new RegExp('^' + basePath, 'i')\n}\n\nfunction isPathInBase(basePath: string, path: string): boolean {\n return !!(basePath && path && path.toLowerCase().startsWith(basePath.toLowerCase()))\n}\n","import { useLayoutEffect, useRef } from 'react'\n\nexport function useMountedLayout(\n fn: () => any,\n deps: React.DependencyList | undefined,\n { onInitial = false } = {}\n): void {\n const hasMounted = useRef(onInitial)\n useLayoutEffect(() => {\n if (!hasMounted.current) hasMounted.current = true\n else fn()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps)\n}\n","// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-module-boundary-types\nexport function isFunction(obj: any): obj is Function {\n return obj && typeof obj === 'function'\n}\n","import React, { useMemo, useState, useLayoutEffect } from 'react'\n\nimport { BasePathContext, PathContext } from './context'\nimport { isNode, setSsrPath, getSsrPath } from './node'\nimport { getFormattedPath, usePath } from './path'\n\nconst emptyPathResult: [null, null] = [null, null]\n\nexport interface RouteParams {\n [key: string]: (...props: any) => JSX.Element\n}\nexport interface PathParamOptions {\n basePath?: string\n matchTrailingSlash?: boolean\n}\nexport interface RouteOptionParams extends PathParamOptions {\n routeProps?: { [k: string]: any }\n overridePathParams?: boolean\n}\ninterface RouteMatcher {\n path: string\n regex: RegExp\n props: string[]\n}\n\nexport function useRoutes(\n routes: RouteParams,\n {\n basePath = '',\n routeProps = {},\n overridePathParams = true,\n matchTrailingSlash = true,\n }: RouteOptionParams = {}\n): JSX.Element | null {\n /*\n This is a hack to setup a listener for the path while always using this latest path\n The issue with usePath is that, in order to not re-render nested components when\n their parent router changes the path, it uses the context's path\n But since that path has to get _set_ here in useRoutes something has to give\n If usePath returns latest it causes render thrashing\n If useRoutes hacks itself into the latest path nothing bad happens (...afaik)\n */\n\n const path = usePath(basePath) && getFormattedPath(basePath)\n\n // Handle potential <Redirect /> use in routes\n useRedirectDetection(basePath, path)\n\n // Get the current route\n const route = useMatchRoute(routes, path, {\n routeProps,\n overridePathParams,\n matchTrailingSlash,\n })\n // No match should not return an empty Provider, just null\n if (!route || path === null) return null\n return (\n <BasePathContext.Provider value={basePath}>\n <PathContext.Provider value={path}>{route}</PathContext.Provider>\n </BasePathContext.Provider>\n )\n}\n\nfunction useMatchRoute(\n routes: RouteParams,\n path: string | null,\n {\n routeProps,\n overridePathParams,\n matchTrailingSlash,\n }: Omit<RouteOptionParams, 'basePath' | 'matchTrailingSlash'> & { matchTrailingSlash: boolean }\n) {\n path = trailingMatch(path, matchTrailingSlash)\n const matchers = useMatchers(Object.keys(routes))\n\n if (path === null) return null\n const [routeMatch, props] = getMatchParams(path, matchers)\n\n if (!routeMatch) return null\n\n return routes[routeMatch.path](\n overridePathParams ? { ...props, ...routeProps } : { ...routeProps, ...props }\n )\n}\n\nexport function usePathParams<T extends Record<string, string>>(\n routes: string | string[],\n options: PathParamOptions = {}\n): [string, T] | [null, null] {\n const [path, matchers] = usePathOptions(routes, options)\n if (path === null) return emptyPathResult\n\n const [routeMatch, props] = getMatchParams(path, matchers)\n\n if (!routeMatch) return emptyPathResult\n return [routeMatch.path, props] as [string, T]\n}\n\nexport function useMatch(routes: string | string[], options: PathParamOptions = {}): string | null {\n const [path, matchers] = usePathOptions(routes, options)\n const match = matchers.find(({ regex }) => path?.match(regex))\n\n return match?.path ?? null\n}\n\nfunction usePathOptions(\n routeOrRoutes: string | string[],\n { basePath, matchTrailingSlash = true }: PathParamOptions\n): [string | null, RouteMatcher[]] {\n const routes = (!Array.isArray(routeOrRoutes) ? [routeOrRoutes] : routeOrRoutes) as string[]\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const matchers = useMatchers(routes)\n\n return [trailingMatch(usePath(basePath), matchTrailingSlash), matchers]\n}\n\nfunction useMatchers(routes: string[]): RouteMatcher[] {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return useMemo(() => routes.map(createRouteMatcher), [hashParams(routes)])\n}\n\nfunction getMatchParams(\n path: string,\n routeMatchers: RouteMatcher[]\n): [RouteMatcher, Record<string, unknown>] | [null, null] {\n let pathParams: RegExpMatchArray | null = null\n\n // Hacky method for find + map\n const routeMatch = routeMatchers.find(({ regex }) => {\n pathParams = path.match(regex)\n return !!pathParams\n })\n\n if (!routeMatch || pathParams === null) return emptyPathResult\n const props = routeMatch.props.reduce((props: any, prop, i) => {\n // The following `match` can't be null because the above return asserts it\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n props[prop] = pathParams![i + 1]\n return props\n }, {})\n\n return [routeMatch, props]\n}\n\nfunction createRouteMatcher(path: string): RouteMatcher {\n return {\n path,\n regex: new RegExp(\n `${path.substr(0, 1) === '*' ? '' : '^'}${path\n .replace(/:[a-zA-Z]+/g, '([^/]+)')\n .replace(/\\*/g, '')}${path.substr(-1) === '*' ? '' : '$'}`,\n 'i'\n ),\n props: (path.match(/:[a-zA-Z]+/g) ?? []).map((paramName) => paramName.substr(1)),\n }\n}\n\nexport function setPath(path: string): void {\n if (!isNode) {\n throw new Error('This method should only be used in NodeJS environments')\n }\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const url = require('url')\n setSsrPath(url.resolve(getSsrPath(), path))\n}\n\n// React doesn't like when the hook dependency array changes size\n// >> Warning: The final argument passed to useMemo changed size between renders. The order and size of this array must remain constant.\n// It is recommended to use a hashing function to produce a single, stable value\n// https://github.com/facebook/react/issues/14324#issuecomment-441489421\nfunction hashParams(params: string[]): string {\n return [...params].sort().join(':')\n}\n\n// React appears to suppress parent's re-rendering when a child's\n// useLayoutEffect updates internal state\n// the `navigate` call in useRedirect *does* cause usePath/useLocationChange\n// to fire, but without this hack useRoutes suppresses the update\n// TODO: find a better way to cause a synchronous update from useRoutes\nfunction useRedirectDetection(basePath: string, path: string | null) {\n const [, forceRender] = useState(false)\n useLayoutEffect(() => {\n if (path !== getFormattedPath(basePath)) {\n forceRender((s) => !s)\n }\n }, [basePath, path])\n}\n\nfunction trailingMatch(path: string | null, matchTrailingSlash: boolean): string | null {\n if (path === null) return path\n // path.length > 1 ensure we still match on the root route \"/\" when matchTrailingSlash is set\n if (matchTrailingSlash && path && path[path.length - 1] === '/' && path.length > 1) {\n path = path.substring(0, path.length - 1)\n }\n return path\n}\n","import { useCallback, useLayoutEffect } from 'react'\n\nimport { useBasePath } from './path'\nimport { isNode } from './node'\nimport type { QueryParam } from './querystring'\nimport {\n shouldCancelNavigation,\n addInterceptor,\n removeInterceptor,\n defaultPrompt,\n undoNavigation,\n} from './intercept'\n\nexport interface NavigateWithReplace {\n (url: string, replace?: boolean): void\n}\nexport interface NavigateWithQuery {\n (url: string, query?: QueryParam | URLSearchParams, replace?: boolean): void\n}\n\nlet lastPath = ''\n\nexport function navigate(url: string): void\nexport function navigate(url: string, replace: boolean): void\nexport function navigate(url: string, query: QueryParam | URLSearchParams): void\nexport function navigate(url: string, query: QueryParam | URLSearchParams, replace: boolean): void\nexport function navigate(\n url: string,\n queryOrReplace?: QueryParam | URLSearchParams | boolean | null,\n replace?: boolean\n): void\nexport function navigate(\n url: string,\n query: QueryParam | URLSearchParams,\n replace: boolean,\n state: unknown\n): void\nexport function navigate(\n url: string,\n replaceOrQuery?: QueryParam | URLSearchParams | boolean | null,\n replace?: boolean,\n state: unknown = null\n): void {\n if (typeof url !== 'string') {\n throw new Error(`\"url\" must be a string, was provided a(n) ${typeof url}`)\n }\n\n if (Array.isArray(replaceOrQuery)) {\n throw new Error('\"replaceOrQuery\" must be boolean, object, or URLSearchParams')\n }\n\n if (shouldCancelNavigation()) return\n if (replaceOrQuery !== null && typeof replaceOrQuery === 'object') {\n url += '?' + new URLSearchParams(replaceOrQuery).toString()\n } else if (replace === undefined && replaceOrQuery !== undefined) {\n replace = replaceOrQuery ?? undefined\n } else if (replace === undefined && replaceOrQuery === undefined) {\n replace = false\n }\n\n lastPath = url\n // if the origin does not match history navigation will fail with\n // \"cannot be created in a document with origin\"\n // When navigating to another domain we must use location instead of history\n if (isAbsolute(url) && !isCurrentOrigin(url)) {\n window.location.assign(url)\n return\n }\n\n if (replace) window.history.replaceState(state, '', url)\n else window.history.pushState(state, '', url)\n\n dispatchEvent(new PopStateEvent('popstate'))\n}\n\nexport function useNavigationPrompt(predicate = true, prompt: string = defaultPrompt): void {\n if (isNode) return\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useLayoutEffect(() => {\n const onPopStateNavigation = () => {\n if (shouldCancelNavigation()) {\n undoNavigation(lastPath)\n }\n }\n window.addEventListener('popstate', onPopStateNavigation)\n return () => window.removeEventListener('popstate', onPopStateNavigation)\n }, [])\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useLayoutEffect(() => {\n const handler = (e?: BeforeUnloadEvent): string | void => {\n if (predicate) {\n return e ? cancelNavigation(e, prompt) : prompt\n }\n }\n addInterceptor(handler)\n return () => removeInterceptor(handler)\n }, [predicate, prompt])\n}\n\nfunction cancelNavigation(event: BeforeUnloadEvent, prompt: string) {\n // Cancel the event as stated by the standard.\n event.preventDefault()\n // Chrome requires returnValue to be set.\n event.returnValue = prompt\n // Return value for prompt per spec\n return prompt\n}\n\nexport function useNavigate(optBasePath = ''): NavigateWithReplace & NavigateWithQuery {\n const basePath = useBasePath()\n const navigateWithBasePath = useCallback<NavigateWithReplace & NavigateWithQuery>(\n (url: string, replaceOrQuery?: boolean | QueryParam | URLSearchParams, replace?: boolean) => {\n const base = optBasePath || basePath\n const href = url.startsWith('/') ? base + url : url\n navigate(href, replaceOrQuery, replace)\n },\n [basePath, optBasePath]\n )\n return navigateWithBasePath\n}\n\nfunction isAbsolute(url: string) {\n return /^(?:[a-z]+:)?\\/\\//i.test(url)\n}\n\nfunction isCurrentOrigin(url: string) {\n return window.location.origin === new URL(url).origin\n}\n","import { useState, useCallback } from 'react'\n\nimport { navigate } from './navigate'\nimport { isNode, getSsrPath } from './node'\nimport { getCurrentPath, getCurrentHash, useLocationChange } from './path'\n\nexport interface QueryParam {\n [key: string]: any\n}\n\nexport interface setQueryParamsOptions {\n replace?: boolean\n}\n\nexport function useQueryParams<T extends QueryParam>(\n parseFn: (query: string) => T = parseQuery,\n serializeFn: (query: Partial<T>) => string = serializeQuery\n): [T, (query: T, options?: setQueryParamsOptions) => void] {\n const [querystring, setQuerystring] = useState(getQueryString())\n const setQueryParams = useCallback(\n (params, { replace = true } = {}) => {\n let path = getCurrentPath()\n params = replace ? params : { ...parseFn(querystring), ...params }\n const serialized = serializeFn(params).toString()\n\n if (serialized) path += '?' + serialized\n if (!replace) path += getCurrentHash()\n\n navigate(path)\n },\n [querystring, parseFn, serializeFn]\n )\n\n // Update state when route changes\n const updateQuery = useCallback(() => setQuerystring(getQueryString()), [])\n\n useLocationChange(updateQuery)\n return [parseFn(querystring), setQueryParams]\n}\n\nfunction parseQuery<T extends QueryParam>(querystring: string): T {\n const q = new URLSearchParams(querystring)\n return Object.fromEntries(q.entries()) as T\n}\n\nfunction serializeQuery<T extends QueryParam>(queryParams: T): string {\n return new URLSearchParams(Object.entries(queryParams).filter(([, v]) => v !== null)).toString()\n}\n\nexport function getQueryString(): string {\n if (isNode) {\n const ssrPath = getSsrPath()\n const queryIndex = ssrPath.indexOf('?')\n return queryIndex === -1 ? '' : ssrPath.substring(queryIndex + 1)\n }\n return window.location.search\n}\n","import { useLayoutEffect } from 'react'\n\nimport { getCurrentHash, usePath } from './path'\nimport { navigate } from './navigate'\nimport { QueryParam, useQueryParams } from './querystring'\n\nexport interface RedirectProps {\n to: string\n query?: QueryParam | URLSearchParams\n replace?: boolean\n merge?: boolean\n}\n\nexport interface UseRedirectProps {\n predicateUrl: string\n targetUrl: string\n queryParams?: QueryParam | URLSearchParams\n replace?: boolean\n}\n\nexport function Redirect({\n to,\n query,\n replace = true,\n merge = true,\n}: RedirectProps): JSX.Element | null {\n useRedirect(usePath(), to, { query, replace, merge })\n return null\n}\n\nexport function useRedirect(\n predicateUrl: string | null,\n targetUrl: string,\n {\n query,\n replace = true,\n merge = true,\n }: { query?: QueryParam; replace?: boolean; merge?: boolean } = {}\n): void {\n const currentPath = usePath()\n const [currentQuery] = useQueryParams()\n const hash = getCurrentHash()\n\n let url = targetUrl\n const targetQuery = new URLSearchParams({\n ...(merge ? currentQuery : {}),\n ...query,\n }).toString()\n if (targetQuery) {\n url += '?' + targetQuery\n }\n if (merge && hash && hash.length) {\n url += hash\n }\n\n useLayoutEffect(() => {\n if (currentPath === predicateUrl) {\n navigate(url, undefined, replace)\n }\n }, [predicateUrl, url, replace, currentPath])\n}\n","import React, { useCallback, forwardRef, Ref } from 'react'\n\nimport { navigate } from './navigate'\nimport { useBasePath, useFullPath } from './path'\n\nexport interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string\n basePath?: string\n children?: React.ReactNode\n}\nexport type LinkRef = HTMLAnchorElement | null\n\nexport interface ActiveLinkProps extends LinkProps {\n activeClass?: string\n exactActiveClass?: string\n}\n\nfunction Link({ href, basePath, ...props }: LinkProps, ref?: Ref<HTMLAnchorElement>) {\n basePath = useLinkBasePath(basePath)\n href = getLinkHref(href, basePath)\n\n const { onClick, target } = props\n\n const handleClick = useCallback<React.MouseEventHandler<HTMLAnchorElement>>(\n (e) => {\n try {\n if (onClick) onClick(e)\n } catch (ex) {\n e.preventDefault()\n throw ex\n }\n if (shouldTrap(e, target)) {\n e.preventDefault() // prevent the link from actually navigating\n navigate(e.currentTarget.href)\n }\n },\n [onClick, target]\n )\n\n return <a {...props} href={href} onClick={handleClick} ref={ref} />\n}\n\nconst RefLink = forwardRef<LinkRef, LinkProps>(Link) as (\n props: LinkProps & { ref?: React.ForwardedRef<HTMLAnchorElement> }\n) => ReturnType<typeof Link>\n\nexport default RefLink\nexport { RefLink as Link }\n\nfunction ActiveLink(\n { basePath, className, exactActiveClass, activeClass, ...props }: ActiveLinkProps,\n ref?: Ref<HTMLAnchorElement>\n) {\n basePath = useLinkBasePath(basePath)\n const fullPath = useFullPath()\n\n let { href } = props\n href = absolutePathName(getLinkHref(href, basePath))\n\n if (exactActiveClass && fullPath === href) className += ` ${exactActiveClass}`\n if (activeClass && fullPath.startsWith(href)) className += ` ${activeClass}`\n\n return <RefLink {...props} basePath={basePath} className={className} ref={ref} />\n}\n\nconst ActiveLinkRef = forwardRef<LinkRef, ActiveLinkProps>(ActiveLink) as (\n props: ActiveLinkProps & { ref?: React.ForwardedRef<HTMLAnchorElement> }\n) => ReturnType<typeof ActiveLink>\n\nexport { ActiveLinkRef as ActiveLink }\n\nfunction useLinkBasePath(basePath?: string): string {\n const contextBasePath = useBasePath()\n if (basePath === '/') return ''\n return basePath || contextBasePath\n}\n\nfunction getLinkHref(href: string, basePath = '') {\n return href.startsWith('/') ? basePath + href : href\n}\n\nfunction absolutePathName(href: string): string {\n if (href.startsWith('/')) return href\n return new URL(href, document.baseURI).pathname\n}\n\nfunction shouldTrap(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, target?: string) {\n return (\n !e.defaultPrevented && // onClick prevented default\n e.button === 0 && // ignore everything but left clicks\n !(target || target === '_self') && // don't trap target === blank\n !(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)\n )\n}\n"],"names":["BasePathContext","createContext","PathContext","isNode","undefined","window","e","interceptors","Set","hasIntercepted","hasUserCancelled","lastScroll","shouldCancelNavigation","scrollX","scrollY","Array","from","some","interceptor","prompt","confirm","setTimeout","usePath","basePath","contextPath","useContext","contextBasePath","useBasePath","path","setPath","useState","getFormattedPath","useLocationChange","newPath","inheritBasePath","useFullPath","getCurrentPath","location","pathname","getCurrentHash","hashIndex","indexOf","substring","hash","setFn","isActive","onInitial","routerBasePath","setRef","useRef","useLayoutEffect","current","onPopState","useCallback","isPredicateActive","addEventListener","removeEventListener","useMountedLayout","fn","deps","hasMounted","baseMissing","isPathInBase","toLowerCase","startsWith","replace","basePathMatcher","RegExp","predicate","isFunction","obj","emptyPathResult","usePathOptions","routeOrRoutes","matchTrailingSlash","matchers","useMatchers","isArray","trailingMatch","routes","useMemo","map","createRouteMatcher","params","sort","join","getMatchParams","routeMatchers","pathParams","routeMatch","find","regex","match","props","reduce","prop","i","substr","paramName","length","lastPath","navigate","url","replaceOrQuery","state","Error","URLSearchParams","toString","isAbsolute","test","isCurrentOrigin","origin","URL","history","replaceState","pushState","dispatchEvent","PopStateEvent","assign","useQueryParams","parseFn","parseQuery","serializeFn","serializeQuery","querystring","setQuerystring","getQueryString","setQueryParams","serialized","q","Object","fromEntries","entries","queryParams","filter","v","ssrPath","queryIndex","search","useRedirect","predicateUrl","targetUrl","query","merge","currentPath","currentQuery","targetQuery","RefLink","forwardRef","Link","href","ref","getLinkHref","useLinkBasePath","onClick","target","handleClick","ex","preventDefault","shouldTrap","defaultPrevented","button","metaKey","altKey","ctrlKey","shiftKey","currentTarget","React","ActiveLinkRef","ActiveLink","className","exactActiveClass","activeClass","fullPath","absolutePathName","document","baseURI","Redirect","to","useHash","stripHash","setHash","handleHash","useMatch","options","useNavigate","optBasePath","base","useNavigationPrompt","onPopStateNavigation","undoNavigation","scrollTo","handler","cancelNavigation","event","returnValue","addInterceptor","add","removeInterceptor","delete","usePathParams","useRoutes","routeProps","overridePathParams","useRedirectDetection","forceRender","s","route","useMatchRoute","keys","Provider","value"],"mappings":"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"}
package/dist/module.js CHANGED
@@ -1,2 +1,2 @@
1
- import e,{createContext as t,useRef as n,useLayoutEffect as r,useContext as a,useState as o,useCallback as i,useMemo as s,forwardRef as u}from"react";const c=t(""),l=t(null);let h=!0;try{h=void 0===window}catch(e){}const d=new Set;let g=!1,f=!1,w=[0,0];function shouldCancelNavigation(){return w=[window.scrollX,window.scrollY],g?f:Array.from(d).some((e=>{const t=e();return!!t&&(f=!window.confirm(t),g=!0,setTimeout((()=>{g=!1,f=!1}),0),f)}))}function usePath(e){const t=a(l),n=useBasePath();e=e||n;const[r,i]=o(getFormattedPath(e));return useLocationChange((e=>i(e)),{basePath:e,inheritBasePath:!e}),t||r}function useBasePath(){return a(c)}function useFullPath(){const[e,t]=o(getCurrentPath());return useLocationChange(t,{inheritBasePath:!1}),e||"/"}function useHash({stripHash:e=!0}={}){const[t,n]=o(window.location.hash),a=i((()=>{window.location.hash!==t&&n(window.location.hash)}),[n,t]);return r((()=>(window.addEventListener("hashchange",a,!1),()=>window.removeEventListener("hashchange",a))),[a]),useLocationChange(a),e?t.substring(1):t}function getCurrentPath(){return h?"/":window.location.pathname||"/"}function getCurrentHash(){if(h){const e="/",t=e.indexOf("#");return e.substring(t)}return window.location.hash}function useLocationChange(e,{inheritBasePath:t=!0,basePath:a="",isActive:o,onInitial:s=!1}={}){if(h)return;const u=useBasePath();t&&u&&(a=u);const c=n(e);r((()=>{c.current=e}));const l=i((()=>{(void 0===o||isPredicateActive(o))&&(shouldCancelNavigation()||c.current(getFormattedPath(a)))}),[o,a]);r((()=>(window.addEventListener("popstate",l),()=>window.removeEventListener("popstate",l))),[l]),function useMountedLayout(e,t,{onInitial:a=!1}={}){const o=n(a);r((()=>{o.current?e():o.current=!0}),t)}((()=>{(void 0===o||isPredicateActive(o))&&c.current(getFormattedPath(a))}),[a,o],{onInitial:s})}function getFormattedPath(e){const t=getCurrentPath(),n=e&&!function isPathInBase(e,t){return!!(e&&t&&t.toLowerCase().startsWith(e.toLowerCase()))}(e,t);return null===t||n?null:e?t.replace(function basePathMatcher(e){return new RegExp("^"+e,"i")}(e),"")||"/":t}function isPredicateActive(e){return function isFunction(e){return e&&"function"==typeof e}(e)?e():e}function useRoutes(t,{basePath:n="",routeProps:a={},overridePathParams:i=!0,matchTrailingSlash:u=!0}={}){const h=usePath(n)&&getFormattedPath(n);!function useRedirectDetection(e,t){const[,n]=o(!1);r((()=>{t!==getFormattedPath(e)&&n((e=>!e))}),[e,t])}(n,h);const d=function useMatchRoute(e,t,{routeProps:n,overridePathParams:r,matchTrailingSlash:a}){a&&t&&"/"===t[t.length-1]&&t.length>1&&(t=t.substring(0,t.length-1));const o=s((()=>Object.keys(e).map(createRouteMatcher)),[hashRoutes(e)]);if(null===t)return null;let i=null;const u=o.find((({regex:e})=>(i=(null!=t?t:"").match(e),!!i)));if(!u||null===i)return null;const c=u.props.reduce(((e,t,n)=>(e[t]=i[n+1],e)),{});return e[u.routePath](r?{...c,...n}:{...n,...c})}(t,h,{routeProps:a,overridePathParams:i,matchTrailingSlash:u});return d&&null!==h?e.createElement(c.Provider,{value:n},e.createElement(l.Provider,{value:h},d)):null}function createRouteMatcher(e){var t;return{routePath:e,regex:new RegExp(`${"*"===e.substr(0,1)?"":"^"}${e.replace(/:[a-zA-Z]+/g,"([^/]+)").replace(/\*/g,"")}${"*"===e.substr(-1)?"":"$"}`,"i"),props:(null!==(t=e.match(/:[a-zA-Z]+/g))&&void 0!==t?t:[]).map((e=>e.substr(1)))}}function hashRoutes(e){return Object.keys(e).sort().join(":")}let P="";function navigate(e,t,n,r=null){if("string"!=typeof e)throw new Error('"url" must be a string, was provided a(n) '+typeof e);if(Array.isArray(t))throw new Error('"replaceOrQuery" must be boolean, object, or URLSearchParams');shouldCancelNavigation()||(null!==t&&"object"==typeof t?e+="?"+new URLSearchParams(t).toString():void 0===n&&void 0!==t?n=null!=t?t:void 0:void 0===n&&void 0===t&&(n=!1),P=e,!function isAbsolute(e){return/^(?:[a-z]+:)?\/\//i.test(e)}(e)||function isCurrentOrigin(e){return window.location.origin===new URL(e).origin}(e)?(n?window.history.replaceState(r,"",e):window.history.pushState(r,"",e),dispatchEvent(new PopStateEvent("popstate"))):window.location.assign(e))}function useNavigationPrompt(e=!0,t="Are you sure you want to leave this page?"){h||(r((()=>{const onPopStateNavigation=()=>{shouldCancelNavigation()&&function undoNavigation(e){window.history.pushState(null,null,e),setTimeout((()=>{window.scrollTo(...w)}),0)}(P)};return window.addEventListener("popstate",onPopStateNavigation),()=>window.removeEventListener("popstate",onPopStateNavigation)}),[]),r((()=>{const handler=n=>{if(e)return n?function cancelNavigation(e,t){return e.preventDefault(),e.returnValue=t,t}(n,t):t};return function addInterceptor(e){window.addEventListener("beforeunload",e),d.add(e)}(handler),()=>function removeInterceptor(e){window.removeEventListener("beforeunload",e),d.delete(e)}(handler)}),[e,t]))}function useNavigate(e=""){const t=useBasePath();return i(((n,r,a)=>{const o=e||t;navigate(n.startsWith("/")?o+n:n,r,a)}),[t,e])}function useQueryParams(e=parseQuery,t=serializeQuery){const[n,r]=o(getQueryString()),a=i(((r,{replace:a=!0}={})=>{let o=getCurrentPath();r=a?r:{...e(n),...r};const i=t(r).toString();i&&(o+="?"+i),a||(o+=getCurrentHash()),navigate(o)}),[n,e,t]);return useLocationChange(i((()=>r(getQueryString())),[])),[e(n),a]}function parseQuery(e){const t=new URLSearchParams(e);return Object.fromEntries(t.entries())}function serializeQuery(e){return new URLSearchParams(Object.entries(e).filter((([,e])=>null!==e))).toString()}function getQueryString(){if(h){const e="/",t=e.indexOf("?");return-1===t?"":e.substring(t+1)}return window.location.search}function Redirect({to:e,query:t,replace:n=!0,merge:r=!0}){return useRedirect(usePath(),e,{query:t,replace:n,merge:r}),null}function useRedirect(e,t,{query:n,replace:a=!0,merge:o=!0}={}){const i=usePath(),[s]=useQueryParams(),u=getCurrentHash();let c=t;const l=new URLSearchParams({...o?s:{},...n}).toString();l&&(c+="?"+l),o&&u&&u.length&&(c+=u),r((()=>{i===e&&navigate(c,void 0,a)}),[e,c,a,i])}const v=u((function Link({href:t,basePath:n,...r},a){const o=useBasePath();t=getLinkHref(t,n=n||o);const{onClick:s,target:u}=r,c=i((e=>{try{s&&s(e)}catch(t){throw e.preventDefault(),t}(function shouldTrap(e,t){return!e.defaultPrevented&&0===e.button&&!(t||"_self"===t)&&!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)})(e,u)&&(e.preventDefault(),navigate(e.currentTarget.href))}),[s,u]);return e.createElement("a",{...r,href:t,onClick:c,ref:a})}));const m=u((function ActiveLink({basePath:t,className:n,exactActiveClass:r,activeClass:a,...o},i){const s=useBasePath();t=t||s;const u=useFullPath();let{href:c}=o;return c=function absolutePathName(e){return e.startsWith("/")?e:new URL(e,document.baseURI).pathname}(getLinkHref(c,t)),r&&u===c&&(n+=` ${r}`),a&&u.startsWith(c)&&(n+=` ${a}`),e.createElement(v,{...o,basePath:t,className:n,ref:i})}));function getLinkHref(e,t=""){return e.startsWith("/")?t+e:e}export{m as ActiveLink,v as Link,Redirect,navigate,useBasePath,useFullPath,useHash,useLocationChange,useNavigate,useNavigationPrompt,usePath,useQueryParams,useRedirect,useRoutes};
1
+ import t,{createContext as e,useRef as n,useLayoutEffect as r,useContext as a,useState as o,useCallback as i,useMemo as s,forwardRef as u}from"react";const c=e(""),h=e(null);let l=!0;try{l=void 0===window}catch(t){}const d=new Set;let g=!1,f=!1,P=[0,0];function shouldCancelNavigation(){return P=[window.scrollX,window.scrollY],g?f:Array.from(d).some((t=>{const e=t();return!!e&&(f=!window.confirm(e),g=!0,setTimeout((()=>{g=!1,f=!1}),0),f)}))}function usePath(t){const e=a(h),n=useBasePath();t=t||n;const[r,i]=o(getFormattedPath(t));return useLocationChange((t=>i(t)),{basePath:t,inheritBasePath:!t}),e||r}function useBasePath(){return a(c)}function useFullPath(){const[t,e]=o(getCurrentPath());return useLocationChange(e,{inheritBasePath:!1}),t||"/"}function useHash({stripHash:t=!0}={}){const[e,n]=o(window.location.hash),a=i((()=>{window.location.hash!==e&&n(window.location.hash)}),[n,e]);return r((()=>(window.addEventListener("hashchange",a,!1),()=>window.removeEventListener("hashchange",a))),[a]),useLocationChange(a),t?e.substring(1):e}function getCurrentPath(){return l?"/":window.location.pathname||"/"}function getCurrentHash(){if(l){const t="/",e=t.indexOf("#");return t.substring(e)}return window.location.hash}function useLocationChange(t,{inheritBasePath:e=!0,basePath:a="",isActive:o,onInitial:s=!1}={}){if(l)return;const u=useBasePath();e&&u&&(a=u);const c=n(t);r((()=>{c.current=t}));const h=i((()=>{(void 0===o||isPredicateActive(o))&&(shouldCancelNavigation()||c.current(getFormattedPath(a)))}),[o,a]);r((()=>(window.addEventListener("popstate",h),()=>window.removeEventListener("popstate",h))),[h]),function useMountedLayout(t,e,{onInitial:a=!1}={}){const o=n(a);r((()=>{o.current?t():o.current=!0}),e)}((()=>{(void 0===o||isPredicateActive(o))&&c.current(getFormattedPath(a))}),[a,o],{onInitial:s})}function getFormattedPath(t){const e=getCurrentPath(),n=t&&!function isPathInBase(t,e){return!!(t&&e&&e.toLowerCase().startsWith(t.toLowerCase()))}(t,e);return null===e||n?null:t?e.replace(function basePathMatcher(t){return new RegExp("^"+t,"i")}(t),"")||"/":e}function isPredicateActive(t){return function isFunction(t){return t&&"function"==typeof t}(t)?t():t}const v=[null,null];function useRoutes(e,{basePath:n="",routeProps:a={},overridePathParams:i=!0,matchTrailingSlash:s=!0}={}){const u=usePath(n)&&getFormattedPath(n);!function useRedirectDetection(t,e){const[,n]=o(!1);r((()=>{e!==getFormattedPath(t)&&n((t=>!t))}),[t,e])}(n,u);const l=function useMatchRoute(t,e,{routeProps:n,overridePathParams:r,matchTrailingSlash:a}){e=trailingMatch(e,a);const o=useMatchers(Object.keys(t));if(null===e)return null;const[i,s]=getMatchParams(e,o);return i?t[i.path](r?{...s,...n}:{...n,...s}):null}(e,u,{routeProps:a,overridePathParams:i,matchTrailingSlash:s});return l&&null!==u?t.createElement(c.Provider,{value:n},t.createElement(h.Provider,{value:u},l)):null}function usePathParams(t,e={}){const[n,r]=usePathOptions(t,e);if(null===n)return v;const[a,o]=getMatchParams(n,r);return a?[a.path,o]:v}function useMatch(t,e={}){var n;const[r,a]=usePathOptions(t,e),o=a.find((({regex:t})=>null==r?void 0:r.match(t)));return null!==(n=null==o?void 0:o.path)&&void 0!==n?n:null}function usePathOptions(t,{basePath:e,matchTrailingSlash:n=!0}){const r=useMatchers(Array.isArray(t)?t:[t]);return[trailingMatch(usePath(e),n),r]}function useMatchers(t){return s((()=>t.map(createRouteMatcher)),[(e=t,[...e].sort().join(":"))]);var e}function getMatchParams(t,e){let n=null;const r=e.find((({regex:e})=>(n=t.match(e),!!n)));if(!r||null===n)return v;const a=r.props.reduce(((t,e,r)=>(t[e]=n[r+1],t)),{});return[r,a]}function createRouteMatcher(t){var e;return{path:t,regex:new RegExp(`${"*"===t.substr(0,1)?"":"^"}${t.replace(/:[a-zA-Z]+/g,"([^/]+)").replace(/\*/g,"")}${"*"===t.substr(-1)?"":"$"}`,"i"),props:(null!==(e=t.match(/:[a-zA-Z]+/g))&&void 0!==e?e:[]).map((t=>t.substr(1)))}}function trailingMatch(t,e){return null===t||e&&t&&"/"===t[t.length-1]&&t.length>1&&(t=t.substring(0,t.length-1)),t}let w="";function navigate(t,e,n,r=null){if("string"!=typeof t)throw new Error('"url" must be a string, was provided a(n) '+typeof t);if(Array.isArray(e))throw new Error('"replaceOrQuery" must be boolean, object, or URLSearchParams');shouldCancelNavigation()||(null!==e&&"object"==typeof e?t+="?"+new URLSearchParams(e).toString():void 0===n&&void 0!==e?n=null!=e?e:void 0:void 0===n&&void 0===e&&(n=!1),w=t,!function isAbsolute(t){return/^(?:[a-z]+:)?\/\//i.test(t)}(t)||function isCurrentOrigin(t){return window.location.origin===new URL(t).origin}(t)?(n?window.history.replaceState(r,"",t):window.history.pushState(r,"",t),dispatchEvent(new PopStateEvent("popstate"))):window.location.assign(t))}function useNavigationPrompt(t=!0,e="Are you sure you want to leave this page?"){l||(r((()=>{const onPopStateNavigation=()=>{shouldCancelNavigation()&&function undoNavigation(t){window.history.pushState(null,null,t),setTimeout((()=>{window.scrollTo(...P)}),0)}(w)};return window.addEventListener("popstate",onPopStateNavigation),()=>window.removeEventListener("popstate",onPopStateNavigation)}),[]),r((()=>{const handler=n=>{if(t)return n?function cancelNavigation(t,e){return t.preventDefault(),t.returnValue=e,e}(n,e):e};return function addInterceptor(t){window.addEventListener("beforeunload",t),d.add(t)}(handler),()=>function removeInterceptor(t){window.removeEventListener("beforeunload",t),d.delete(t)}(handler)}),[t,e]))}function useNavigate(t=""){const e=useBasePath();return i(((n,r,a)=>{const o=t||e;navigate(n.startsWith("/")?o+n:n,r,a)}),[e,t])}function useQueryParams(t=parseQuery,e=serializeQuery){const[n,r]=o(getQueryString()),a=i(((r,{replace:a=!0}={})=>{let o=getCurrentPath();r=a?r:{...t(n),...r};const i=e(r).toString();i&&(o+="?"+i),a||(o+=getCurrentHash()),navigate(o)}),[n,t,e]);return useLocationChange(i((()=>r(getQueryString())),[])),[t(n),a]}function parseQuery(t){const e=new URLSearchParams(t);return Object.fromEntries(e.entries())}function serializeQuery(t){return new URLSearchParams(Object.entries(t).filter((([,t])=>null!==t))).toString()}function getQueryString(){if(l){const t="/",e=t.indexOf("?");return-1===e?"":t.substring(e+1)}return window.location.search}function Redirect({to:t,query:e,replace:n=!0,merge:r=!0}){return useRedirect(usePath(),t,{query:e,replace:n,merge:r}),null}function useRedirect(t,e,{query:n,replace:a=!0,merge:o=!0}={}){const i=usePath(),[s]=useQueryParams(),u=getCurrentHash();let c=e;const h=new URLSearchParams({...o?s:{},...n}).toString();h&&(c+="?"+h),o&&u&&u.length&&(c+=u),r((()=>{i===t&&navigate(c,void 0,a)}),[t,c,a,i])}const m=u((function Link({href:e,basePath:n,...r},a){e=getLinkHref(e,n=useLinkBasePath(n));const{onClick:o,target:s}=r,u=i((t=>{try{o&&o(t)}catch(e){throw t.preventDefault(),e}(function shouldTrap(t,e){return!t.defaultPrevented&&0===t.button&&!(e||"_self"===e)&&!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)})(t,s)&&(t.preventDefault(),navigate(t.currentTarget.href))}),[o,s]);return t.createElement("a",{...r,href:e,onClick:u,ref:a})}));const p=u((function ActiveLink({basePath:e,className:n,exactActiveClass:r,activeClass:a,...o},i){e=useLinkBasePath(e);const s=useFullPath();let{href:u}=o;return u=function absolutePathName(t){return t.startsWith("/")?t:new URL(t,document.baseURI).pathname}(getLinkHref(u,e)),r&&s===u&&(n+=` ${r}`),a&&s.startsWith(u)&&(n+=` ${a}`),t.createElement(m,{...o,basePath:e,className:n,ref:i})}));function useLinkBasePath(t){const e=useBasePath();return"/"===t?"":t||e}function getLinkHref(t,e=""){return t.startsWith("/")?e+t:t}export{p as ActiveLink,m as Link,Redirect,navigate,useBasePath,useFullPath,useHash,useLocationChange,useMatch,useNavigate,useNavigationPrompt,usePath,usePathParams,useQueryParams,useRedirect,useRoutes};
2
2
  //# sourceMappingURL=module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.js","sources":["../src/context.ts","../src/node.ts","../src/intercept.ts","../src/path.ts","../src/hooks.ts","../src/typeChecks.ts","../src/router.tsx","../src/navigate.ts","../src/querystring.ts","../src/redirect.ts","../src/Link.tsx"],"sourcesContent":["import { createContext, useContext, useMemo } from 'react'\n\nconst BasePathContext = createContext('')\nconst PathContext = createContext<string | null>(null)\n\nexport { BasePathContext }\nexport { PathContext }\n\nexport function useRouter(): { basePath: string; path: string | null } {\n const [basePath, path] = [useContext(BasePathContext), useContext(PathContext)]\n return useMemo(() => ({ basePath, path }), [basePath, path])\n}\n","let ssrPath = '/'\nlet isNode = true // eslint-disable-line import/no-mutable-exports\ntry {\n isNode = window === undefined\n} catch (e) {} // eslint-disable-line no-empty\n\nexport { isNode }\nexport function getSsrPath(): string {\n return ssrPath\n}\nexport function setSsrPath(path: string): void {\n ssrPath = path\n}\n","const interceptors = new Set<() => string | void>()\n\nexport const defaultPrompt = 'Are you sure you want to leave this page?'\n\nlet hasIntercepted = false\nlet hasUserCancelled = false\nlet lastScroll = [0, 0] as [number, number]\n\nexport function shouldCancelNavigation(): boolean {\n lastScroll = [window.scrollX, window.scrollY]\n if (hasIntercepted) return hasUserCancelled\n\n // confirm if any interceptors return true\n return Array.from(interceptors).some((interceptor) => {\n const prompt = interceptor()\n if (!prompt) return false\n\n // cancel navigation if user declines\n hasUserCancelled = !window.confirm(prompt) // eslint-disable-line no-alert\n\n // track user response so that multiple interceptors don't prompt\n hasIntercepted = true\n\n // reset so that future navigation attempts are prompted\n setTimeout(() => {\n hasIntercepted = false\n hasUserCancelled = false\n }, 0)\n\n return hasUserCancelled\n })\n}\n\nexport function addInterceptor(handler: () => string | void): void {\n window.addEventListener('beforeunload', handler)\n interceptors.add(handler)\n}\n\nexport function removeInterceptor(handler: () => string | void): void {\n window.removeEventListener('beforeunload', handler)\n interceptors.delete(handler)\n}\n\nexport function undoNavigation(lastPath: string): void {\n window.history.pushState(null, null as unknown as string, lastPath)\n setTimeout(() => {\n window.scrollTo(...lastScroll)\n }, 0)\n}\n","import { useState, useCallback, useRef, useContext, useLayoutEffect } from 'react'\n\nimport { BasePathContext, PathContext } from './context'\nimport { useMountedLayout } from './hooks'\nimport { getSsrPath, isNode } from './node'\nimport { shouldCancelNavigation } from './intercept'\nimport { isFunction } from './typeChecks'\n\nexport interface LocationChangeSetFn {\n (path: string | null): void\n}\nexport interface LocationChangeOptionParams {\n inheritBasePath?: boolean\n basePath?: string\n isActive?: boolean | (() => boolean)\n onInitial?: boolean\n}\n\nexport function usePath(basePath?: string): string | null {\n const contextPath = useContext(PathContext)\n const contextBasePath = useBasePath() // hooks can't be called conditionally\n basePath = basePath || contextBasePath\n\n const [path, setPath] = useState(getFormattedPath(basePath))\n useLocationChange((newPath) => setPath(newPath), {\n basePath,\n inheritBasePath: !basePath,\n })\n\n return contextPath || path\n}\n\nexport function useBasePath(): string {\n return useContext(BasePathContext)\n}\n\nexport function useFullPath(): string {\n const [path, setPath] = useState<string | null>(getCurrentPath())\n useLocationChange(setPath, { inheritBasePath: false })\n\n return path || '/'\n}\n\nexport function useHash({ stripHash = true } = {}): string {\n const [hash, setHash] = useState(window.location.hash)\n const handleHash = useCallback(() => {\n if (window.location.hash === hash) return\n setHash(window.location.hash)\n }, [setHash, hash])\n\n useLayoutEffect(() => {\n window.addEventListener('hashchange', handleHash, false)\n return () => window.removeEventListener('hashchange', handleHash)\n }, [handleHash])\n\n useLocationChange(handleHash)\n return stripHash ? hash.substring(1) : hash\n}\n\nexport function getCurrentPath(): string {\n return isNode ? getSsrPath() : window.location.pathname || '/'\n}\n\nexport function getCurrentHash(): string {\n if (isNode) {\n const path = getSsrPath()\n const hashIndex = path.indexOf('#')\n return path.substring(hashIndex)\n }\n return window.location.hash\n}\n\nexport function useLocationChange(\n setFn: LocationChangeSetFn,\n {\n inheritBasePath = true,\n basePath = '',\n isActive,\n onInitial = false,\n }: LocationChangeOptionParams = {}\n): void {\n if (isNode) return\n\n // All hooks after this are conditional, but the runtime can't actually change\n /* eslint-disable react-hooks/rules-of-hooks */\n\n const routerBasePath = useBasePath()\n if (inheritBasePath && routerBasePath) basePath = routerBasePath\n\n const setRef = useRef<LocationChangeSetFn>(setFn)\n useLayoutEffect(() => {\n // setFn could be an in-render declared callback, making it unstable\n // This is a method of using an often-changing callback from React Hooks\n // https://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback\n // While not recommended, it is the best current (16.9) available method\n // For reducing the useEffect cleanup from setFn changing every render\n setRef.current = setFn\n })\n\n const onPopState = useCallback(() => {\n // No predicate defaults true\n if (isActive !== undefined && !isPredicateActive(isActive)) return\n if (shouldCancelNavigation()) return\n setRef.current(getFormattedPath(basePath))\n }, [isActive, basePath])\n\n useLayoutEffect(() => {\n window.addEventListener('popstate', onPopState)\n return () => window.removeEventListener('popstate', onPopState)\n }, [onPopState])\n\n // When the basePath changes re-check the path after the render completes\n // This allows nested contexts to get an up-to-date formatted path\n useMountedLayout(\n () => {\n if (isActive !== undefined && !isPredicateActive(isActive)) return\n setRef.current(getFormattedPath(basePath))\n },\n [basePath, isActive],\n { onInitial }\n )\n\n /* eslint-enable react-hooks/rules-of-hooks */\n}\n\n/**\n * Returns the current path. If basePath is provided it will be removed from the front of the path.\n * If basePath is provided and the path does not begin with it will return null\n * @param {string} basePath basePath, if any\n * @return {string | null} returns path with basePath prefix removed, or null if basePath is provided and missing\n */\nexport function getFormattedPath(basePath: string): string | null {\n const path = getCurrentPath()\n const baseMissing = basePath && !isPathInBase(basePath, path)\n if (path === null || baseMissing) return null\n return !basePath ? path : path.replace(basePathMatcher(basePath), '') || '/'\n}\n\nfunction isPredicateActive(predicate: boolean | (() => boolean)): boolean {\n return isFunction(predicate) ? predicate() : predicate\n}\n\nfunction basePathMatcher(basePath: string): RegExp {\n return new RegExp('^' + basePath, 'i')\n}\n\nfunction isPathInBase(basePath: string, path: string): boolean {\n return !!(basePath && path && path.toLowerCase().startsWith(basePath.toLowerCase()))\n}\n","import { useLayoutEffect, useRef } from 'react'\n\nexport function useMountedLayout(\n fn: () => any,\n deps: React.DependencyList | undefined,\n { onInitial = false } = {}\n): void {\n const hasMounted = useRef(onInitial)\n useLayoutEffect(() => {\n if (!hasMounted.current) hasMounted.current = true\n else fn()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps)\n}\n","// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-module-boundary-types\nexport function isFunction(obj: any): obj is Function {\n return obj && typeof obj === 'function'\n}\n","import React, { useMemo, useState, useLayoutEffect } from 'react'\n\nimport { BasePathContext, PathContext } from './context'\nimport { isNode, setSsrPath, getSsrPath } from './node'\nimport { getFormattedPath, usePath } from './path'\n\nexport interface RouteParams {\n [key: string]: (...props: any) => JSX.Element\n}\n\nexport interface RouteOptionParams {\n basePath?: string\n routeProps?: { [k: string]: any }\n overridePathParams?: boolean\n matchTrailingSlash?: boolean\n}\n\ninterface RouteMatcher {\n routePath: string\n regex: RegExp\n props: string[]\n}\n\nexport function useRoutes(\n routes: RouteParams,\n {\n basePath = '',\n routeProps = {},\n overridePathParams = true,\n matchTrailingSlash = true,\n }: RouteOptionParams = {}\n): JSX.Element | null {\n /*\n This is a hack to setup a listener for the path while always using this latest path\n The issue with usePath is that, in order to not re-render nested components when\n their parent router changes the path, it uses the context's path\n But since that path has to get _set_ here in useRoutes something has to give\n If usePath returns latest it causes render thrashing\n If useRoutes hacks itself into the latest path nothing bad happens (...afaik)\n */\n\n const path = usePath(basePath) && getFormattedPath(basePath)\n\n // Handle potential <Redirect /> use in routes\n useRedirectDetection(basePath, path)\n\n // Get the current route\n const route = useMatchRoute(routes, path, {\n routeProps,\n overridePathParams,\n matchTrailingSlash,\n })\n // No match should not return an empty Provider, just null\n if (!route || path === null) return null\n return (\n <BasePathContext.Provider value={basePath}>\n <PathContext.Provider value={path}>{route}</PathContext.Provider>\n </BasePathContext.Provider>\n )\n}\n\nexport function setPath(path: string): void {\n if (!isNode) {\n throw new Error('This method should only be used in NodeJS environments')\n }\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const url = require('url')\n setSsrPath(url.resolve(getSsrPath(), path))\n}\n\nfunction useMatchRoute(\n routes: RouteParams,\n path: string | null,\n { routeProps, overridePathParams, matchTrailingSlash }: Omit<RouteOptionParams, 'basePath'>\n) {\n // path.length > 1 ensure we still match on the root route \"/\" when matchTrailingSlash is set\n if (matchTrailingSlash && path && path[path.length - 1] === '/' && path.length > 1) {\n path = path.substring(0, path.length - 1)\n }\n\n const routeMatchers = useMemo(\n () => Object.keys(routes).map(createRouteMatcher),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [hashRoutes(routes)]\n )\n\n if (path === null) return null\n\n // Hacky method for find + map\n let pathParams: RegExpMatchArray | null = null\n const routeMatch = routeMatchers.find(({ regex }) => {\n pathParams = (path ?? '').match(regex)\n return !!pathParams\n })\n\n if (!routeMatch || pathParams === null) return null\n\n const props = routeMatch.props.reduce((props: any, prop, i) => {\n // The following `match` can't be null because the above return asserts it\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n props[prop] = pathParams![i + 1]\n return props\n }, {})\n\n return routes[routeMatch.routePath](\n overridePathParams ? { ...props, ...routeProps } : { ...routeProps, ...props }\n )\n}\n\nfunction createRouteMatcher(routePath: string): RouteMatcher {\n return {\n routePath,\n regex: new RegExp(\n `${routePath.substr(0, 1) === '*' ? '' : '^'}${routePath\n .replace(/:[a-zA-Z]+/g, '([^/]+)')\n .replace(/\\*/g, '')}${routePath.substr(-1) === '*' ? '' : '$'}`,\n 'i'\n ),\n props: (routePath.match(/:[a-zA-Z]+/g) ?? []).map((paramName) => paramName.substr(1)),\n }\n}\n\n// React doesn't like when the hook dependency array changes size\n// >> Warning: The final argument passed to useMemo changed size between renders. The order and size of this array must remain constant.\n// It is recommended to use a hashing function to produce a single, stable value\n// https://github.com/facebook/react/issues/14324#issuecomment-441489421\nfunction hashRoutes(routes: RouteParams): string {\n return Object.keys(routes).sort().join(':')\n}\n\n// React appears to suppress parent's re-rendering when a child's\n// useLayoutEffect updates internal state\n// the `navigate` call in useRedirect *does* cause usePath/useLocationChange\n// to fire, but without this hack useRoutes suppresses the update\n// TODO: find a better way to cause a synchronous update from useRoutes\nfunction useRedirectDetection(basePath: string, path: string | null) {\n const [, forceRender] = useState(false)\n useLayoutEffect(() => {\n if (path !== getFormattedPath(basePath)) {\n forceRender((s) => !s)\n }\n }, [basePath, path])\n}\n","import { useCallback, useLayoutEffect } from 'react'\n\nimport { useBasePath } from './path'\nimport { isNode } from './node'\nimport type { QueryParam } from './querystring'\nimport {\n shouldCancelNavigation,\n addInterceptor,\n removeInterceptor,\n defaultPrompt,\n undoNavigation,\n} from './intercept'\n\nexport interface NavigateWithReplace {\n (url: string, replace?: boolean): void\n}\nexport interface NavigateWithQuery {\n (url: string, query?: QueryParam | URLSearchParams, replace?: boolean): void\n}\n\nlet lastPath = ''\n\nexport function navigate(url: string): void\nexport function navigate(url: string, replace: boolean): void\nexport function navigate(url: string, query: QueryParam | URLSearchParams): void\nexport function navigate(url: string, query: QueryParam | URLSearchParams, replace: boolean): void\nexport function navigate(\n url: string,\n queryOrReplace?: QueryParam | URLSearchParams | boolean | null,\n replace?: boolean\n): void\nexport function navigate(\n url: string,\n query: QueryParam | URLSearchParams,\n replace: boolean,\n state: unknown\n): void\nexport function navigate(\n url: string,\n replaceOrQuery?: QueryParam | URLSearchParams | boolean | null,\n replace?: boolean,\n state: unknown = null\n): void {\n if (typeof url !== 'string') {\n throw new Error(`\"url\" must be a string, was provided a(n) ${typeof url}`)\n }\n\n if (Array.isArray(replaceOrQuery)) {\n throw new Error('\"replaceOrQuery\" must be boolean, object, or URLSearchParams')\n }\n\n if (shouldCancelNavigation()) return\n if (replaceOrQuery !== null && typeof replaceOrQuery === 'object') {\n url += '?' + new URLSearchParams(replaceOrQuery).toString()\n } else if (replace === undefined && replaceOrQuery !== undefined) {\n replace = replaceOrQuery ?? undefined\n } else if (replace === undefined && replaceOrQuery === undefined) {\n replace = false\n }\n\n lastPath = url\n // if the origin does not match history navigation will fail with\n // \"cannot be created in a document with origin\"\n // When navigating to another domain we must use location instead of history\n if (isAbsolute(url) && !isCurrentOrigin(url)) {\n window.location.assign(url)\n return\n }\n\n if (replace) window.history.replaceState(state, '', url)\n else window.history.pushState(state, '', url)\n\n dispatchEvent(new PopStateEvent('popstate'))\n}\n\nexport function useNavigationPrompt(predicate = true, prompt: string = defaultPrompt): void {\n if (isNode) return\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useLayoutEffect(() => {\n const onPopStateNavigation = () => {\n if (shouldCancelNavigation()) {\n undoNavigation(lastPath)\n }\n }\n window.addEventListener('popstate', onPopStateNavigation)\n return () => window.removeEventListener('popstate', onPopStateNavigation)\n }, [])\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useLayoutEffect(() => {\n const handler = (e?: BeforeUnloadEvent): string | void => {\n if (predicate) {\n return e ? cancelNavigation(e, prompt) : prompt\n }\n }\n addInterceptor(handler)\n return () => removeInterceptor(handler)\n }, [predicate, prompt])\n}\n\nfunction cancelNavigation(event: BeforeUnloadEvent, prompt: string) {\n // Cancel the event as stated by the standard.\n event.preventDefault()\n // Chrome requires returnValue to be set.\n event.returnValue = prompt\n // Return value for prompt per spec\n return prompt\n}\n\nexport function useNavigate(optBasePath = ''): NavigateWithReplace & NavigateWithQuery {\n const basePath = useBasePath()\n const navigateWithBasePath = useCallback<NavigateWithReplace & NavigateWithQuery>(\n (url: string, replaceOrQuery?: boolean | QueryParam | URLSearchParams, replace?: boolean) => {\n const base = optBasePath || basePath\n const href = url.startsWith('/') ? base + url : url\n navigate(href, replaceOrQuery, replace)\n },\n [basePath, optBasePath]\n )\n return navigateWithBasePath\n}\n\nfunction isAbsolute(url: string) {\n return /^(?:[a-z]+:)?\\/\\//i.test(url)\n}\n\nfunction isCurrentOrigin(url: string) {\n return window.location.origin === new URL(url).origin\n}\n","import { useState, useCallback } from 'react'\n\nimport { navigate } from './navigate'\nimport { isNode, getSsrPath } from './node'\nimport { getCurrentPath, getCurrentHash, useLocationChange } from './path'\n\nexport interface QueryParam {\n [key: string]: any\n}\n\nexport interface setQueryParamsOptions {\n replace?: boolean\n}\n\nexport function useQueryParams<T extends QueryParam>(\n parseFn: (query: string) => T = parseQuery,\n serializeFn: (query: T) => string = serializeQuery\n): [T, (query: T, options?: setQueryParamsOptions) => void] {\n const [querystring, setQuerystring] = useState(getQueryString())\n const setQueryParams = useCallback(\n (params, { replace = true } = {}) => {\n let path = getCurrentPath()\n params = replace ? params : { ...parseFn(querystring), ...params }\n const serialized = serializeFn(params).toString()\n\n if (serialized) path += '?' + serialized\n if (!replace) path += getCurrentHash()\n\n navigate(path)\n },\n [querystring, parseFn, serializeFn]\n )\n\n // Update state when route changes\n const updateQuery = useCallback(() => setQuerystring(getQueryString()), [])\n\n useLocationChange(updateQuery)\n return [parseFn(querystring), setQueryParams]\n}\n\nfunction parseQuery<T extends QueryParam>(querystring: string): T {\n const q = new URLSearchParams(querystring)\n return Object.fromEntries(q.entries()) as T\n}\n\nfunction serializeQuery<T extends QueryParam>(queryParams: T): string {\n return new URLSearchParams(Object.entries(queryParams).filter(([, v]) => v !== null)).toString()\n}\n\nexport function getQueryString(): string {\n if (isNode) {\n const ssrPath = getSsrPath()\n const queryIndex = ssrPath.indexOf('?')\n return queryIndex === -1 ? '' : ssrPath.substring(queryIndex + 1)\n }\n return window.location.search\n}\n","import { useLayoutEffect } from 'react'\n\nimport { getCurrentHash, usePath } from './path'\nimport { navigate } from './navigate'\nimport { QueryParam, useQueryParams } from './querystring'\n\nexport interface RedirectProps {\n to: string\n query?: QueryParam | URLSearchParams\n replace?: boolean\n merge?: boolean\n}\n\nexport interface UseRedirectProps {\n predicateUrl: string\n targetUrl: string\n queryParams?: QueryParam | URLSearchParams\n replace?: boolean\n}\n\nexport function Redirect({\n to,\n query,\n replace = true,\n merge = true,\n}: RedirectProps): JSX.Element | null {\n useRedirect(usePath(), to, { query, replace, merge })\n return null\n}\n\nexport function useRedirect(\n predicateUrl: string | null,\n targetUrl: string,\n {\n query,\n replace = true,\n merge = true,\n }: { query?: QueryParam; replace?: boolean; merge?: boolean } = {}\n): void {\n const currentPath = usePath()\n const [currentQuery] = useQueryParams()\n const hash = getCurrentHash()\n\n let url = targetUrl\n const targetQuery = new URLSearchParams({\n ...(merge ? currentQuery : {}),\n ...query,\n }).toString()\n if (targetQuery) {\n url += '?' + targetQuery\n }\n if (merge && hash && hash.length) {\n url += hash\n }\n\n useLayoutEffect(() => {\n if (currentPath === predicateUrl) {\n navigate(url, undefined, replace)\n }\n }, [predicateUrl, url, replace, currentPath])\n}\n","import React, { useCallback, forwardRef, Ref } from 'react'\n\nimport { navigate } from './navigate'\nimport { useBasePath, useFullPath } from './path'\n\nexport interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string\n basePath?: string\n children?: React.ReactNode\n}\nexport type LinkRef = HTMLAnchorElement | null\n\nexport interface ActiveLinkProps extends LinkProps {\n activeClass?: string\n exactActiveClass?: string\n}\n\nfunction Link({ href, basePath, ...props }: LinkProps, ref?: Ref<HTMLAnchorElement>) {\n const contextBasePath = useBasePath()\n basePath = basePath || contextBasePath\n href = getLinkHref(href, basePath)\n\n const { onClick, target } = props\n\n const handleClick = useCallback<React.MouseEventHandler<HTMLAnchorElement>>(\n (e) => {\n try {\n if (onClick) onClick(e)\n } catch (ex) {\n e.preventDefault()\n throw ex\n }\n if (shouldTrap(e, target)) {\n e.preventDefault() // prevent the link from actually navigating\n navigate(e.currentTarget.href)\n }\n },\n [onClick, target]\n )\n\n return <a {...props} href={href} onClick={handleClick} ref={ref} />\n}\n\nconst RefLink = forwardRef<LinkRef, LinkProps>(Link) as (\n props: LinkProps & { ref?: React.ForwardedRef<HTMLAnchorElement> }\n) => ReturnType<typeof Link>\n\nexport default RefLink\nexport { RefLink as Link }\n\nfunction ActiveLink(\n { basePath, className, exactActiveClass, activeClass, ...props }: ActiveLinkProps,\n ref?: Ref<HTMLAnchorElement>\n) {\n const contextBasePath = useBasePath()\n basePath = basePath || contextBasePath\n const fullPath = useFullPath()\n\n let { href } = props\n href = absolutePathName(getLinkHref(href, basePath))\n\n if (exactActiveClass && fullPath === href) className += ` ${exactActiveClass}`\n if (activeClass && fullPath.startsWith(href)) className += ` ${activeClass}`\n\n return <RefLink {...props} basePath={basePath} className={className} ref={ref} />\n}\n\nconst ActiveLinkRef = forwardRef<LinkRef, ActiveLinkProps>(ActiveLink) as (\n props: ActiveLinkProps & { ref?: React.ForwardedRef<HTMLAnchorElement> }\n) => ReturnType<typeof ActiveLink>\n\nexport { ActiveLinkRef as ActiveLink }\n\nfunction getLinkHref(href: string, basePath = '') {\n return href.startsWith('/') ? basePath + href : href\n}\n\nfunction absolutePathName(href: string): string {\n if (href.startsWith('/')) return href\n return new URL(href, document.baseURI).pathname\n}\n\nfunction shouldTrap(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, target?: string) {\n return (\n !e.defaultPrevented && // onClick prevented default\n e.button === 0 && // ignore everything but left clicks\n !(target || target === '_self') && // don't trap target === blank\n !(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)\n )\n}\n"],"names":["BasePathContext","createContext","PathContext","isNode","undefined","window","e","interceptors","Set","hasIntercepted","hasUserCancelled","lastScroll","shouldCancelNavigation","scrollX","scrollY","Array","from","some","interceptor","prompt","confirm","setTimeout","usePath","basePath","contextPath","useContext","contextBasePath","useBasePath","path","setPath","useState","getFormattedPath","useLocationChange","newPath","inheritBasePath","useFullPath","getCurrentPath","useHash","stripHash","hash","setHash","location","handleHash","useCallback","useLayoutEffect","addEventListener","removeEventListener","substring","pathname","getCurrentHash","hashIndex","indexOf","setFn","isActive","onInitial","routerBasePath","setRef","useRef","current","onPopState","isPredicateActive","useMountedLayout","fn","deps","hasMounted","baseMissing","isPathInBase","toLowerCase","startsWith","replace","basePathMatcher","RegExp","predicate","isFunction","obj","useRoutes","routes","routeProps","overridePathParams","matchTrailingSlash","useRedirectDetection","forceRender","s","route","useMatchRoute","length","routeMatchers","useMemo","Object","keys","map","createRouteMatcher","hashRoutes","pathParams","routeMatch","find","regex","match","props","reduce","prop","i","routePath","React","Provider","value","substr","paramName","sort","join","lastPath","navigate","url","replaceOrQuery","state","Error","isArray","URLSearchParams","toString","isAbsolute","test","isCurrentOrigin","origin","URL","history","replaceState","pushState","dispatchEvent","PopStateEvent","assign","useNavigationPrompt","onPopStateNavigation","undoNavigation","scrollTo","handler","cancelNavigation","event","preventDefault","returnValue","addInterceptor","add","removeInterceptor","delete","useNavigate","optBasePath","base","useQueryParams","parseFn","parseQuery","serializeFn","serializeQuery","querystring","setQuerystring","getQueryString","setQueryParams","params","serialized","q","fromEntries","entries","queryParams","filter","v","ssrPath","queryIndex","search","Redirect","to","query","merge","useRedirect","predicateUrl","targetUrl","currentPath","currentQuery","targetQuery","RefLink","forwardRef","Link","href","ref","getLinkHref","onClick","target","handleClick","ex","shouldTrap","defaultPrevented","button","metaKey","altKey","ctrlKey","shiftKey","currentTarget","ActiveLinkRef","ActiveLink","className","exactActiveClass","activeClass","fullPath","absolutePathName","document","baseURI"],"mappings":"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"}
1
+ {"version":3,"file":"module.js","sources":["../src/context.ts","../src/node.ts","../src/intercept.ts","../src/path.ts","../src/hooks.ts","../src/typeChecks.ts","../src/router.tsx","../src/navigate.ts","../src/querystring.ts","../src/redirect.ts","../src/Link.tsx"],"sourcesContent":["import { createContext, useContext, useMemo } from 'react'\n\nconst BasePathContext = createContext('')\nconst PathContext = createContext<string | null>(null)\n\nexport { BasePathContext }\nexport { PathContext }\n\nexport function useRouter(): { basePath: string; path: string | null } {\n const [basePath, path] = [useContext(BasePathContext), useContext(PathContext)]\n return useMemo(() => ({ basePath, path }), [basePath, path])\n}\n","let ssrPath = '/'\nlet isNode = true // eslint-disable-line import/no-mutable-exports\ntry {\n isNode = window === undefined\n} catch (e) {} // eslint-disable-line no-empty\n\nexport { isNode }\nexport function getSsrPath(): string {\n return ssrPath\n}\nexport function setSsrPath(path: string): void {\n ssrPath = path\n}\n","const interceptors = new Set<() => string | void>()\n\nexport const defaultPrompt = 'Are you sure you want to leave this page?'\n\nlet hasIntercepted = false\nlet hasUserCancelled = false\nlet lastScroll = [0, 0] as [number, number]\n\nexport function shouldCancelNavigation(): boolean {\n lastScroll = [window.scrollX, window.scrollY]\n if (hasIntercepted) return hasUserCancelled\n\n // confirm if any interceptors return true\n return Array.from(interceptors).some((interceptor) => {\n const prompt = interceptor()\n if (!prompt) return false\n\n // cancel navigation if user declines\n hasUserCancelled = !window.confirm(prompt) // eslint-disable-line no-alert\n\n // track user response so that multiple interceptors don't prompt\n hasIntercepted = true\n\n // reset so that future navigation attempts are prompted\n setTimeout(() => {\n hasIntercepted = false\n hasUserCancelled = false\n }, 0)\n\n return hasUserCancelled\n })\n}\n\nexport function addInterceptor(handler: () => string | void): void {\n window.addEventListener('beforeunload', handler)\n interceptors.add(handler)\n}\n\nexport function removeInterceptor(handler: () => string | void): void {\n window.removeEventListener('beforeunload', handler)\n interceptors.delete(handler)\n}\n\nexport function undoNavigation(lastPath: string): void {\n window.history.pushState(null, null as unknown as string, lastPath)\n setTimeout(() => {\n window.scrollTo(...lastScroll)\n }, 0)\n}\n","import { useState, useCallback, useRef, useContext, useLayoutEffect } from 'react'\n\nimport { BasePathContext, PathContext } from './context'\nimport { useMountedLayout } from './hooks'\nimport { getSsrPath, isNode } from './node'\nimport { shouldCancelNavigation } from './intercept'\nimport { isFunction } from './typeChecks'\n\nexport interface LocationChangeSetFn {\n (path: string | null): void\n}\nexport interface LocationChangeOptionParams {\n inheritBasePath?: boolean\n basePath?: string\n isActive?: boolean | (() => boolean)\n onInitial?: boolean\n}\n\nexport function usePath(basePath?: string): string | null {\n const contextPath = useContext(PathContext)\n const contextBasePath = useBasePath() // hooks can't be called conditionally\n basePath = basePath || contextBasePath\n\n const [path, setPath] = useState(getFormattedPath(basePath))\n useLocationChange((newPath) => setPath(newPath), {\n basePath,\n inheritBasePath: !basePath,\n })\n\n return contextPath || path\n}\n\nexport function useBasePath(): string {\n return useContext(BasePathContext)\n}\n\nexport function useFullPath(): string {\n const [path, setPath] = useState<string | null>(getCurrentPath())\n useLocationChange(setPath, { inheritBasePath: false })\n\n return path || '/'\n}\n\nexport function useHash({ stripHash = true } = {}): string {\n const [hash, setHash] = useState(window.location.hash)\n const handleHash = useCallback(() => {\n if (window.location.hash === hash) return\n setHash(window.location.hash)\n }, [setHash, hash])\n\n useLayoutEffect(() => {\n window.addEventListener('hashchange', handleHash, false)\n return () => window.removeEventListener('hashchange', handleHash)\n }, [handleHash])\n\n useLocationChange(handleHash)\n return stripHash ? hash.substring(1) : hash\n}\n\nexport function getCurrentPath(): string {\n return isNode ? getSsrPath() : window.location.pathname || '/'\n}\n\nexport function getCurrentHash(): string {\n if (isNode) {\n const path = getSsrPath()\n const hashIndex = path.indexOf('#')\n return path.substring(hashIndex)\n }\n return window.location.hash\n}\n\nexport function useLocationChange(\n setFn: LocationChangeSetFn,\n {\n inheritBasePath = true,\n basePath = '',\n isActive,\n onInitial = false,\n }: LocationChangeOptionParams = {}\n): void {\n if (isNode) return\n\n // All hooks after this are conditional, but the runtime can't actually change\n /* eslint-disable react-hooks/rules-of-hooks */\n\n const routerBasePath = useBasePath()\n if (inheritBasePath && routerBasePath) basePath = routerBasePath\n\n const setRef = useRef<LocationChangeSetFn>(setFn)\n useLayoutEffect(() => {\n // setFn could be an in-render declared callback, making it unstable\n // This is a method of using an often-changing callback from React Hooks\n // https://reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback\n // While not recommended, it is the best current (16.9) available method\n // For reducing the useEffect cleanup from setFn changing every render\n setRef.current = setFn\n })\n\n const onPopState = useCallback(() => {\n // No predicate defaults true\n if (isActive !== undefined && !isPredicateActive(isActive)) return\n if (shouldCancelNavigation()) return\n setRef.current(getFormattedPath(basePath))\n }, [isActive, basePath])\n\n useLayoutEffect(() => {\n window.addEventListener('popstate', onPopState)\n return () => window.removeEventListener('popstate', onPopState)\n }, [onPopState])\n\n // When the basePath changes re-check the path after the render completes\n // This allows nested contexts to get an up-to-date formatted path\n useMountedLayout(\n () => {\n if (isActive !== undefined && !isPredicateActive(isActive)) return\n setRef.current(getFormattedPath(basePath))\n },\n [basePath, isActive],\n { onInitial }\n )\n\n /* eslint-enable react-hooks/rules-of-hooks */\n}\n\n/**\n * Returns the current path. If basePath is provided it will be removed from the front of the path.\n * If basePath is provided and the path does not begin with it will return null\n * @param {string} basePath basePath, if any\n * @return {string | null} returns path with basePath prefix removed, or null if basePath is provided and missing\n */\nexport function getFormattedPath(basePath: string): string | null {\n const path = getCurrentPath()\n const baseMissing = basePath && !isPathInBase(basePath, path)\n if (path === null || baseMissing) return null\n return !basePath ? path : path.replace(basePathMatcher(basePath), '') || '/'\n}\n\nfunction isPredicateActive(predicate: boolean | (() => boolean)): boolean {\n return isFunction(predicate) ? predicate() : predicate\n}\n\nfunction basePathMatcher(basePath: string): RegExp {\n return new RegExp('^' + basePath, 'i')\n}\n\nfunction isPathInBase(basePath: string, path: string): boolean {\n return !!(basePath && path && path.toLowerCase().startsWith(basePath.toLowerCase()))\n}\n","import { useLayoutEffect, useRef } from 'react'\n\nexport function useMountedLayout(\n fn: () => any,\n deps: React.DependencyList | undefined,\n { onInitial = false } = {}\n): void {\n const hasMounted = useRef(onInitial)\n useLayoutEffect(() => {\n if (!hasMounted.current) hasMounted.current = true\n else fn()\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps)\n}\n","// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/explicit-module-boundary-types\nexport function isFunction(obj: any): obj is Function {\n return obj && typeof obj === 'function'\n}\n","import React, { useMemo, useState, useLayoutEffect } from 'react'\n\nimport { BasePathContext, PathContext } from './context'\nimport { isNode, setSsrPath, getSsrPath } from './node'\nimport { getFormattedPath, usePath } from './path'\n\nconst emptyPathResult: [null, null] = [null, null]\n\nexport interface RouteParams {\n [key: string]: (...props: any) => JSX.Element\n}\nexport interface PathParamOptions {\n basePath?: string\n matchTrailingSlash?: boolean\n}\nexport interface RouteOptionParams extends PathParamOptions {\n routeProps?: { [k: string]: any }\n overridePathParams?: boolean\n}\ninterface RouteMatcher {\n path: string\n regex: RegExp\n props: string[]\n}\n\nexport function useRoutes(\n routes: RouteParams,\n {\n basePath = '',\n routeProps = {},\n overridePathParams = true,\n matchTrailingSlash = true,\n }: RouteOptionParams = {}\n): JSX.Element | null {\n /*\n This is a hack to setup a listener for the path while always using this latest path\n The issue with usePath is that, in order to not re-render nested components when\n their parent router changes the path, it uses the context's path\n But since that path has to get _set_ here in useRoutes something has to give\n If usePath returns latest it causes render thrashing\n If useRoutes hacks itself into the latest path nothing bad happens (...afaik)\n */\n\n const path = usePath(basePath) && getFormattedPath(basePath)\n\n // Handle potential <Redirect /> use in routes\n useRedirectDetection(basePath, path)\n\n // Get the current route\n const route = useMatchRoute(routes, path, {\n routeProps,\n overridePathParams,\n matchTrailingSlash,\n })\n // No match should not return an empty Provider, just null\n if (!route || path === null) return null\n return (\n <BasePathContext.Provider value={basePath}>\n <PathContext.Provider value={path}>{route}</PathContext.Provider>\n </BasePathContext.Provider>\n )\n}\n\nfunction useMatchRoute(\n routes: RouteParams,\n path: string | null,\n {\n routeProps,\n overridePathParams,\n matchTrailingSlash,\n }: Omit<RouteOptionParams, 'basePath' | 'matchTrailingSlash'> & { matchTrailingSlash: boolean }\n) {\n path = trailingMatch(path, matchTrailingSlash)\n const matchers = useMatchers(Object.keys(routes))\n\n if (path === null) return null\n const [routeMatch, props] = getMatchParams(path, matchers)\n\n if (!routeMatch) return null\n\n return routes[routeMatch.path](\n overridePathParams ? { ...props, ...routeProps } : { ...routeProps, ...props }\n )\n}\n\nexport function usePathParams<T extends Record<string, string>>(\n routes: string | string[],\n options: PathParamOptions = {}\n): [string, T] | [null, null] {\n const [path, matchers] = usePathOptions(routes, options)\n if (path === null) return emptyPathResult\n\n const [routeMatch, props] = getMatchParams(path, matchers)\n\n if (!routeMatch) return emptyPathResult\n return [routeMatch.path, props] as [string, T]\n}\n\nexport function useMatch(routes: string | string[], options: PathParamOptions = {}): string | null {\n const [path, matchers] = usePathOptions(routes, options)\n const match = matchers.find(({ regex }) => path?.match(regex))\n\n return match?.path ?? null\n}\n\nfunction usePathOptions(\n routeOrRoutes: string | string[],\n { basePath, matchTrailingSlash = true }: PathParamOptions\n): [string | null, RouteMatcher[]] {\n const routes = (!Array.isArray(routeOrRoutes) ? [routeOrRoutes] : routeOrRoutes) as string[]\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const matchers = useMatchers(routes)\n\n return [trailingMatch(usePath(basePath), matchTrailingSlash), matchers]\n}\n\nfunction useMatchers(routes: string[]): RouteMatcher[] {\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return useMemo(() => routes.map(createRouteMatcher), [hashParams(routes)])\n}\n\nfunction getMatchParams(\n path: string,\n routeMatchers: RouteMatcher[]\n): [RouteMatcher, Record<string, unknown>] | [null, null] {\n let pathParams: RegExpMatchArray | null = null\n\n // Hacky method for find + map\n const routeMatch = routeMatchers.find(({ regex }) => {\n pathParams = path.match(regex)\n return !!pathParams\n })\n\n if (!routeMatch || pathParams === null) return emptyPathResult\n const props = routeMatch.props.reduce((props: any, prop, i) => {\n // The following `match` can't be null because the above return asserts it\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n props[prop] = pathParams![i + 1]\n return props\n }, {})\n\n return [routeMatch, props]\n}\n\nfunction createRouteMatcher(path: string): RouteMatcher {\n return {\n path,\n regex: new RegExp(\n `${path.substr(0, 1) === '*' ? '' : '^'}${path\n .replace(/:[a-zA-Z]+/g, '([^/]+)')\n .replace(/\\*/g, '')}${path.substr(-1) === '*' ? '' : '$'}`,\n 'i'\n ),\n props: (path.match(/:[a-zA-Z]+/g) ?? []).map((paramName) => paramName.substr(1)),\n }\n}\n\nexport function setPath(path: string): void {\n if (!isNode) {\n throw new Error('This method should only be used in NodeJS environments')\n }\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const url = require('url')\n setSsrPath(url.resolve(getSsrPath(), path))\n}\n\n// React doesn't like when the hook dependency array changes size\n// >> Warning: The final argument passed to useMemo changed size between renders. The order and size of this array must remain constant.\n// It is recommended to use a hashing function to produce a single, stable value\n// https://github.com/facebook/react/issues/14324#issuecomment-441489421\nfunction hashParams(params: string[]): string {\n return [...params].sort().join(':')\n}\n\n// React appears to suppress parent's re-rendering when a child's\n// useLayoutEffect updates internal state\n// the `navigate` call in useRedirect *does* cause usePath/useLocationChange\n// to fire, but without this hack useRoutes suppresses the update\n// TODO: find a better way to cause a synchronous update from useRoutes\nfunction useRedirectDetection(basePath: string, path: string | null) {\n const [, forceRender] = useState(false)\n useLayoutEffect(() => {\n if (path !== getFormattedPath(basePath)) {\n forceRender((s) => !s)\n }\n }, [basePath, path])\n}\n\nfunction trailingMatch(path: string | null, matchTrailingSlash: boolean): string | null {\n if (path === null) return path\n // path.length > 1 ensure we still match on the root route \"/\" when matchTrailingSlash is set\n if (matchTrailingSlash && path && path[path.length - 1] === '/' && path.length > 1) {\n path = path.substring(0, path.length - 1)\n }\n return path\n}\n","import { useCallback, useLayoutEffect } from 'react'\n\nimport { useBasePath } from './path'\nimport { isNode } from './node'\nimport type { QueryParam } from './querystring'\nimport {\n shouldCancelNavigation,\n addInterceptor,\n removeInterceptor,\n defaultPrompt,\n undoNavigation,\n} from './intercept'\n\nexport interface NavigateWithReplace {\n (url: string, replace?: boolean): void\n}\nexport interface NavigateWithQuery {\n (url: string, query?: QueryParam | URLSearchParams, replace?: boolean): void\n}\n\nlet lastPath = ''\n\nexport function navigate(url: string): void\nexport function navigate(url: string, replace: boolean): void\nexport function navigate(url: string, query: QueryParam | URLSearchParams): void\nexport function navigate(url: string, query: QueryParam | URLSearchParams, replace: boolean): void\nexport function navigate(\n url: string,\n queryOrReplace?: QueryParam | URLSearchParams | boolean | null,\n replace?: boolean\n): void\nexport function navigate(\n url: string,\n query: QueryParam | URLSearchParams,\n replace: boolean,\n state: unknown\n): void\nexport function navigate(\n url: string,\n replaceOrQuery?: QueryParam | URLSearchParams | boolean | null,\n replace?: boolean,\n state: unknown = null\n): void {\n if (typeof url !== 'string') {\n throw new Error(`\"url\" must be a string, was provided a(n) ${typeof url}`)\n }\n\n if (Array.isArray(replaceOrQuery)) {\n throw new Error('\"replaceOrQuery\" must be boolean, object, or URLSearchParams')\n }\n\n if (shouldCancelNavigation()) return\n if (replaceOrQuery !== null && typeof replaceOrQuery === 'object') {\n url += '?' + new URLSearchParams(replaceOrQuery).toString()\n } else if (replace === undefined && replaceOrQuery !== undefined) {\n replace = replaceOrQuery ?? undefined\n } else if (replace === undefined && replaceOrQuery === undefined) {\n replace = false\n }\n\n lastPath = url\n // if the origin does not match history navigation will fail with\n // \"cannot be created in a document with origin\"\n // When navigating to another domain we must use location instead of history\n if (isAbsolute(url) && !isCurrentOrigin(url)) {\n window.location.assign(url)\n return\n }\n\n if (replace) window.history.replaceState(state, '', url)\n else window.history.pushState(state, '', url)\n\n dispatchEvent(new PopStateEvent('popstate'))\n}\n\nexport function useNavigationPrompt(predicate = true, prompt: string = defaultPrompt): void {\n if (isNode) return\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useLayoutEffect(() => {\n const onPopStateNavigation = () => {\n if (shouldCancelNavigation()) {\n undoNavigation(lastPath)\n }\n }\n window.addEventListener('popstate', onPopStateNavigation)\n return () => window.removeEventListener('popstate', onPopStateNavigation)\n }, [])\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useLayoutEffect(() => {\n const handler = (e?: BeforeUnloadEvent): string | void => {\n if (predicate) {\n return e ? cancelNavigation(e, prompt) : prompt\n }\n }\n addInterceptor(handler)\n return () => removeInterceptor(handler)\n }, [predicate, prompt])\n}\n\nfunction cancelNavigation(event: BeforeUnloadEvent, prompt: string) {\n // Cancel the event as stated by the standard.\n event.preventDefault()\n // Chrome requires returnValue to be set.\n event.returnValue = prompt\n // Return value for prompt per spec\n return prompt\n}\n\nexport function useNavigate(optBasePath = ''): NavigateWithReplace & NavigateWithQuery {\n const basePath = useBasePath()\n const navigateWithBasePath = useCallback<NavigateWithReplace & NavigateWithQuery>(\n (url: string, replaceOrQuery?: boolean | QueryParam | URLSearchParams, replace?: boolean) => {\n const base = optBasePath || basePath\n const href = url.startsWith('/') ? base + url : url\n navigate(href, replaceOrQuery, replace)\n },\n [basePath, optBasePath]\n )\n return navigateWithBasePath\n}\n\nfunction isAbsolute(url: string) {\n return /^(?:[a-z]+:)?\\/\\//i.test(url)\n}\n\nfunction isCurrentOrigin(url: string) {\n return window.location.origin === new URL(url).origin\n}\n","import { useState, useCallback } from 'react'\n\nimport { navigate } from './navigate'\nimport { isNode, getSsrPath } from './node'\nimport { getCurrentPath, getCurrentHash, useLocationChange } from './path'\n\nexport interface QueryParam {\n [key: string]: any\n}\n\nexport interface setQueryParamsOptions {\n replace?: boolean\n}\n\nexport function useQueryParams<T extends QueryParam>(\n parseFn: (query: string) => T = parseQuery,\n serializeFn: (query: Partial<T>) => string = serializeQuery\n): [T, (query: T, options?: setQueryParamsOptions) => void] {\n const [querystring, setQuerystring] = useState(getQueryString())\n const setQueryParams = useCallback(\n (params, { replace = true } = {}) => {\n let path = getCurrentPath()\n params = replace ? params : { ...parseFn(querystring), ...params }\n const serialized = serializeFn(params).toString()\n\n if (serialized) path += '?' + serialized\n if (!replace) path += getCurrentHash()\n\n navigate(path)\n },\n [querystring, parseFn, serializeFn]\n )\n\n // Update state when route changes\n const updateQuery = useCallback(() => setQuerystring(getQueryString()), [])\n\n useLocationChange(updateQuery)\n return [parseFn(querystring), setQueryParams]\n}\n\nfunction parseQuery<T extends QueryParam>(querystring: string): T {\n const q = new URLSearchParams(querystring)\n return Object.fromEntries(q.entries()) as T\n}\n\nfunction serializeQuery<T extends QueryParam>(queryParams: T): string {\n return new URLSearchParams(Object.entries(queryParams).filter(([, v]) => v !== null)).toString()\n}\n\nexport function getQueryString(): string {\n if (isNode) {\n const ssrPath = getSsrPath()\n const queryIndex = ssrPath.indexOf('?')\n return queryIndex === -1 ? '' : ssrPath.substring(queryIndex + 1)\n }\n return window.location.search\n}\n","import { useLayoutEffect } from 'react'\n\nimport { getCurrentHash, usePath } from './path'\nimport { navigate } from './navigate'\nimport { QueryParam, useQueryParams } from './querystring'\n\nexport interface RedirectProps {\n to: string\n query?: QueryParam | URLSearchParams\n replace?: boolean\n merge?: boolean\n}\n\nexport interface UseRedirectProps {\n predicateUrl: string\n targetUrl: string\n queryParams?: QueryParam | URLSearchParams\n replace?: boolean\n}\n\nexport function Redirect({\n to,\n query,\n replace = true,\n merge = true,\n}: RedirectProps): JSX.Element | null {\n useRedirect(usePath(), to, { query, replace, merge })\n return null\n}\n\nexport function useRedirect(\n predicateUrl: string | null,\n targetUrl: string,\n {\n query,\n replace = true,\n merge = true,\n }: { query?: QueryParam; replace?: boolean; merge?: boolean } = {}\n): void {\n const currentPath = usePath()\n const [currentQuery] = useQueryParams()\n const hash = getCurrentHash()\n\n let url = targetUrl\n const targetQuery = new URLSearchParams({\n ...(merge ? currentQuery : {}),\n ...query,\n }).toString()\n if (targetQuery) {\n url += '?' + targetQuery\n }\n if (merge && hash && hash.length) {\n url += hash\n }\n\n useLayoutEffect(() => {\n if (currentPath === predicateUrl) {\n navigate(url, undefined, replace)\n }\n }, [predicateUrl, url, replace, currentPath])\n}\n","import React, { useCallback, forwardRef, Ref } from 'react'\n\nimport { navigate } from './navigate'\nimport { useBasePath, useFullPath } from './path'\n\nexport interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string\n basePath?: string\n children?: React.ReactNode\n}\nexport type LinkRef = HTMLAnchorElement | null\n\nexport interface ActiveLinkProps extends LinkProps {\n activeClass?: string\n exactActiveClass?: string\n}\n\nfunction Link({ href, basePath, ...props }: LinkProps, ref?: Ref<HTMLAnchorElement>) {\n basePath = useLinkBasePath(basePath)\n href = getLinkHref(href, basePath)\n\n const { onClick, target } = props\n\n const handleClick = useCallback<React.MouseEventHandler<HTMLAnchorElement>>(\n (e) => {\n try {\n if (onClick) onClick(e)\n } catch (ex) {\n e.preventDefault()\n throw ex\n }\n if (shouldTrap(e, target)) {\n e.preventDefault() // prevent the link from actually navigating\n navigate(e.currentTarget.href)\n }\n },\n [onClick, target]\n )\n\n return <a {...props} href={href} onClick={handleClick} ref={ref} />\n}\n\nconst RefLink = forwardRef<LinkRef, LinkProps>(Link) as (\n props: LinkProps & { ref?: React.ForwardedRef<HTMLAnchorElement> }\n) => ReturnType<typeof Link>\n\nexport default RefLink\nexport { RefLink as Link }\n\nfunction ActiveLink(\n { basePath, className, exactActiveClass, activeClass, ...props }: ActiveLinkProps,\n ref?: Ref<HTMLAnchorElement>\n) {\n basePath = useLinkBasePath(basePath)\n const fullPath = useFullPath()\n\n let { href } = props\n href = absolutePathName(getLinkHref(href, basePath))\n\n if (exactActiveClass && fullPath === href) className += ` ${exactActiveClass}`\n if (activeClass && fullPath.startsWith(href)) className += ` ${activeClass}`\n\n return <RefLink {...props} basePath={basePath} className={className} ref={ref} />\n}\n\nconst ActiveLinkRef = forwardRef<LinkRef, ActiveLinkProps>(ActiveLink) as (\n props: ActiveLinkProps & { ref?: React.ForwardedRef<HTMLAnchorElement> }\n) => ReturnType<typeof ActiveLink>\n\nexport { ActiveLinkRef as ActiveLink }\n\nfunction useLinkBasePath(basePath?: string): string {\n const contextBasePath = useBasePath()\n if (basePath === '/') return ''\n return basePath || contextBasePath\n}\n\nfunction getLinkHref(href: string, basePath = '') {\n return href.startsWith('/') ? basePath + href : href\n}\n\nfunction absolutePathName(href: string): string {\n if (href.startsWith('/')) return href\n return new URL(href, document.baseURI).pathname\n}\n\nfunction shouldTrap(e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, target?: string) {\n return (\n !e.defaultPrevented && // onClick prevented default\n e.button === 0 && // ignore everything but left clicks\n !(target || target === '_self') && // don't trap target === blank\n !(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)\n )\n}\n"],"names":["BasePathContext","createContext","PathContext","isNode","undefined","window","e","interceptors","Set","hasIntercepted","hasUserCancelled","lastScroll","shouldCancelNavigation","scrollX","scrollY","Array","from","some","interceptor","prompt","confirm","setTimeout","usePath","basePath","contextPath","useContext","contextBasePath","useBasePath","path","setPath","useState","getFormattedPath","useLocationChange","newPath","inheritBasePath","useFullPath","getCurrentPath","useHash","stripHash","hash","setHash","location","handleHash","useCallback","useLayoutEffect","addEventListener","removeEventListener","substring","pathname","getCurrentHash","hashIndex","indexOf","setFn","isActive","onInitial","routerBasePath","setRef","useRef","current","onPopState","isPredicateActive","useMountedLayout","fn","deps","hasMounted","baseMissing","isPathInBase","toLowerCase","startsWith","replace","basePathMatcher","RegExp","predicate","isFunction","obj","emptyPathResult","useRoutes","routes","routeProps","overridePathParams","matchTrailingSlash","useRedirectDetection","forceRender","s","route","useMatchRoute","trailingMatch","matchers","useMatchers","Object","keys","routeMatch","props","getMatchParams","React","Provider","value","usePathParams","options","usePathOptions","useMatch","match","find","regex","routeOrRoutes","isArray","useMemo","map","createRouteMatcher","params","sort","join","routeMatchers","pathParams","reduce","prop","i","substr","paramName","length","lastPath","navigate","url","replaceOrQuery","state","Error","URLSearchParams","toString","isAbsolute","test","isCurrentOrigin","origin","URL","history","replaceState","pushState","dispatchEvent","PopStateEvent","assign","useNavigationPrompt","onPopStateNavigation","undoNavigation","scrollTo","handler","cancelNavigation","event","preventDefault","returnValue","addInterceptor","add","removeInterceptor","delete","useNavigate","optBasePath","base","useQueryParams","parseFn","parseQuery","serializeFn","serializeQuery","querystring","setQuerystring","getQueryString","setQueryParams","serialized","q","fromEntries","entries","queryParams","filter","v","ssrPath","queryIndex","search","Redirect","to","query","merge","useRedirect","predicateUrl","targetUrl","currentPath","currentQuery","targetQuery","RefLink","forwardRef","Link","href","ref","getLinkHref","useLinkBasePath","onClick","target","handleClick","ex","shouldTrap","defaultPrevented","button","metaKey","altKey","ctrlKey","shiftKey","currentTarget","ActiveLinkRef","ActiveLink","className","exactActiveClass","activeClass","fullPath","absolutePathName","document","baseURI"],"mappings":"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"}
@@ -4,5 +4,5 @@ export interface QueryParam {
4
4
  export interface setQueryParamsOptions {
5
5
  replace?: boolean;
6
6
  }
7
- export declare function useQueryParams<T extends QueryParam>(parseFn?: (query: string) => T, serializeFn?: (query: T) => string): [T, (query: T, options?: setQueryParamsOptions) => void];
7
+ export declare function useQueryParams<T extends QueryParam>(parseFn?: (query: string) => T, serializeFn?: (query: Partial<T>) => string): [T, (query: T, options?: setQueryParamsOptions) => void];
8
8
  export declare function getQueryString(): string;
package/dist/router.d.ts CHANGED
@@ -2,13 +2,17 @@
2
2
  export interface RouteParams {
3
3
  [key: string]: (...props: any) => JSX.Element;
4
4
  }
5
- export interface RouteOptionParams {
5
+ export interface PathParamOptions {
6
6
  basePath?: string;
7
+ matchTrailingSlash?: boolean;
8
+ }
9
+ export interface RouteOptionParams extends PathParamOptions {
7
10
  routeProps?: {
8
11
  [k: string]: any;
9
12
  };
10
13
  overridePathParams?: boolean;
11
- matchTrailingSlash?: boolean;
12
14
  }
13
15
  export declare function useRoutes(routes: RouteParams, { basePath, routeProps, overridePathParams, matchTrailingSlash, }?: RouteOptionParams): JSX.Element | null;
16
+ export declare function usePathParams<T extends Record<string, string>>(routes: string | string[], options?: PathParamOptions): [string, T] | [null, null];
17
+ export declare function useMatch(routes: string | string[], options?: PathParamOptions): string | null;
14
18
  export declare function setPath(path: string): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "raviger",
3
- "version": "2.5.4",
3
+ "version": "2.6.0",
4
4
  "description": "React routing with hooks",
5
5
  "keywords": [
6
6
  "react",
@@ -88,8 +88,9 @@
88
88
  "eslint-plugin-jest": "^24.4.0",
89
89
  "eslint-plugin-react": "^7.24.0",
90
90
  "eslint-plugin-react-hooks": "^4.2.0",
91
- "jest": "^27.0.6",
91
+ "jest": "^27.2.4",
92
92
  "jest-junit": "^12.2.0",
93
+ "jest-watch-typeahead": "^1.0.0",
93
94
  "mkdirp": "^1.0.4",
94
95
  "np": "^7.5.0",
95
96
  "npm-run-all": "^4.1.5",
package/CHANGELOG.md DELETED
@@ -1,205 +0,0 @@
1
- # Changelog
2
- All notable changes to this project will be documented in this file.
3
-
4
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
-
7
- ## [2.5.4] - 2021-08-27
8
- ### Fixed
9
- - terser mangling `location` in querystring methods
10
-
11
- ## [2.5.3] - 2021-08-25
12
- ### Changed
13
- - Updated transpilation to reduce size, remove Babel
14
-
15
- ## [2.5.2] - 2021-08-19
16
- ### Fixed
17
- - `<ActiveLink>` matching in nested contexts
18
- - `<ActiveLink>` matching for relative paths
19
-
20
- ## [2.5.0] - 2021-08-15
21
- ### Changed
22
- - `src/` to typescript
23
- - types are now natively generated from source and provided as declaration files
24
- - Minimum NodeJS version is now 12
25
-
26
- ## [2.4.2] - 2021-07-16
27
- ### Fixed
28
- - `useRoutes` not updating after an internal `<Redirect />` updates the location
29
-
30
- ## [2.4.1] - 2021-06-28
31
- ### Changed
32
- - Rollup build to `keep_fnames` to retain component name checking
33
-
34
- ## [2.4.0] - 2021-06-28
35
- ### Added
36
- - `navigate` support for urls with different origins (external navigation)
37
-
38
- ## [2.3.1] - 2021-06-18
39
- ### Added
40
- - Sourcemaps to published package
41
- ### Changed
42
- - `useNavigationPrompt` now restores scroll position after undoing navigation
43
-
44
- ## [2.3.0] - 2021-06-18
45
- ### Changed
46
- - `useNavigationPrompt` now intercepts browser back/forward button navigation
47
-
48
- ## [2.2.0] - 2021-06-07
49
- ### Changed
50
- - Added support for React@17 in `peerDependencies`
51
-
52
- ## [2.1.0] - 2021-05-02
53
- ### Added
54
- - `options.onInitial` parameter for `useLocationChange` that controls the first render behavior. `default: false`.
55
- ### Fixed
56
- - `useLocationChange` invoking the setter on initial render. This was not intended and was an unannounced change from the v1 behavior, so reverting it is not considered an API change but a bugfix.
57
-
58
- ## [2.0.2] - 2021-03-22
59
- ### Added
60
- - `state` parameter for `navigate`
61
-
62
- ## [2.0.1] - 2021-01-07
63
- ### Removed
64
- - `engine` requirement for 'less than' Node 15
65
-
66
- ## [2.0.0] - 2020-11-17
67
- ### Changed
68
- - **BREAKING**: `useRoutes` and `usePath` will return `null` if `basePath` is provided and missing from path
69
- - **BREAKING**: `useLocationChange` will invoke callback with `null` if `basePath` is provided and missing from path
70
- - **BREAKING**: `useLocationChange` option `inheritBasePath` now accepts any false value (previously required `false` with `===`)
71
- - **BREAKING**: `useRoutes` option `matchTrailingSlash` default to `true` (was `false`)
72
- - **BREAKING**: removed `linkRef` prop from `Link` and `ActiveLink`, replaced with standard React `forwardRef`
73
- - **BREAKING**: `useQueryParams` setter second argument changed from `replace` to options param with `replace` property
74
- - **BREAKING**: `useRedirect` parameters changed to match properties on `Redirect` component
75
- ### Added
76
- - `useFullPath` for getting the full path, ignoring any context-provided `basePath`
77
- - Support for Node 14
78
- - Rollup-plugin-terser for builds
79
- ### Removed
80
- - Support for Node 8
81
-
82
- ## [1.6.0] - 2020-10-22
83
- ### Added
84
- - `useNavigate` hook
85
-
86
- ## [1.5.1] - 2020-10-09
87
- ### Fixed
88
- - `matchTrailingSlash` not matching on the root route `/`
89
-
90
- ## [1.5.0] - 2020-10-09
91
- ### Added
92
- - `query` prop to `<Redirect>`
93
-
94
- ## [1.4.6] - 2020-10-07
95
- ### Fixed
96
- - `useRoutes` path tracking with `usePath` causing improper child invocations
97
-
98
- ## [1.4.5] - 2020-07-31
99
- ### Fixed
100
- - `navigate` handling of `replace` in edge cases for `replaceOrQuery`
101
-
102
- ## [1.4.4] - 2020-06-26
103
- ### Fixed
104
- - `basePath` matches not using case-insensitive like route paths
105
-
106
- ## [1.4.3] - 2020-05-12
107
- ### Fixed
108
- - `useQueryParam` using a `?` when no query is set
109
- - typescript declaration for `useNavigationPrompt`
110
-
111
- ## [1.4.2] - 2020-04-30
112
- ### Fixed
113
- - `useRoutes` error when changing the number of routes
114
-
115
- ## [1.4.1] - 2020-04-28
116
- ### Fixed
117
- - `usePath` sets `inheritBasePath: false` when using provided `basePath`
118
-
119
- ## [1.4.0] - 2020-03-18
120
- ### Added
121
- - `<Redirect>` Component
122
- - `useHash` hook
123
-
124
- ## [1.3.0] - 2020-01-27
125
- ### Added
126
- - `<Link basePath>` prop override.
127
- ### Fixed
128
- - double decoding on `useQueryParams`
129
-
130
- ## [1.2.0] - 2020-01-21
131
- ### Changed
132
- - Internal React-Context setup, reduces wasteful re-renders
133
-
134
- ## [1.1.1] - 2020-01-09
135
- ### Fixed
136
- - `replace: false` on `setQueryParams` replacing `location.hash`
137
-
138
- ## [1.1.0] - 2019-11-21
139
- ### Added
140
- - `linkRef` prop to `<Link>`
141
-
142
- ## [1.0.0] - 2019-11-12
143
- ### Added
144
- - `useLocationChange`: similar to use `usePopState`, but uses different parameters
145
- ### Removed
146
- - `usePopState` hook
147
-
148
- ## [0.5.9] - 2019-10-28
149
- ### Added
150
- - `useNavigationPrompt` for confirming navigation changes
151
-
152
- ## [0.5.8] - 2019-10-14
153
- ### Fixed
154
- - `<Link target="_blank" />` triggering local navigation
155
-
156
- ## [0.5.7] - 2019-09-26
157
- ### Added
158
- - `<ActiveLink />`
159
-
160
- ## [0.5.5] - 2019-09-23
161
- ### Added
162
- - `useRoutes` option `matchTrailingSlash`
163
-
164
- ## [0.5.4] - 2019-09-16
165
- ### Added
166
- - typescript declarations
167
-
168
- ## [0.5.0] - 2019-09-13
169
- ### Changed
170
- - `useRoutes` second parameter from `basePath` to `options`
171
- ### Added
172
- - `useRoutes` option `routeProps`
173
- - `useRoutes` option `overridePathParams`
174
-
175
- ## [0.4.0] - 2019-09-12
176
- ### Added
177
- - `useBasePath` hook to retrieve the basePath
178
-
179
- ## [0.3.11] - 2019-09-09
180
- ### Fixed
181
- - `<Link>` cmd key detection
182
-
183
- ## [0.3.9] - 2019-08-29
184
- ### Added
185
- - `navigate` checks `url` param
186
-
187
- ## [0.3.8] - 2019-08-27
188
- ### Fixed
189
- - `useRedirect` adding null query
190
-
191
- ## [0.3.6] - 2019-08-27
192
- ### Fixed
193
- - Rollup dist output
194
-
195
- ## [0.3.4] - 2019-08-21
196
- ### Added
197
- - `useRedirect` hook
198
-
199
- ## [0.3.3] - 2019-08-21
200
- ### Added
201
- - `navigate(url, queryStringObj)` overload
202
-
203
- ## [0.3.2] - 2019-08-08
204
- ### Added
205
- - rollup output for module and cjs