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 +2 -2
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/module.js +1 -1
- package/dist/module.js.map +1 -1
- package/dist/querystring.d.ts +1 -1
- package/dist/router.d.ts +6 -2
- package/package.json +3 -2
- package/CHANGELOG.md +0 -205
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
|
|
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
|
|
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
|
package/dist/module.js.map
CHANGED
|
@@ -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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"}
|
package/dist/querystring.d.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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.
|
|
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
|