UNPKG

react-concurrent-router

Version:

Performant routing embracing React concurrent UI patterns

2 lines (1 loc) 16 kB
"use strict";!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("react")):"function"==typeof define&&define.amd?define(["exports","react"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).ReactConcurrentRouter={},e.React)}(this,function(e,t){function n(){return n=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n,r=arguments[t];for(n in r)({}).hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},n.apply(null,arguments)}function r(e,t){if(null==e)return{};var n,r={};for(n in e)({}).hasOwnProperty.call(e,n)&&-1===t.indexOf(n)&&(r[n]=e[n]);return r}function a(){return a=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n,r=arguments[t];for(n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},a.apply(this,arguments)}function o(e,t){if(!e){"undefined"!=typeof console&&console.warn(t);try{throw Error(t)}catch(e){}}}function i(e){function t(){var e=c.location,t=y.state||{};return[t.idx,v({pathname:e.pathname,search:e.search,hash:e.hash,state:t.usr||null,key:t.key||"default"})]}function n(e){return"string"==typeof e?e:p(e)}function r(e,t){return void 0===t&&(t=null),v(a({pathname:b.pathname,hash:"",search:""},"string"==typeof e?d(e):e,{state:t,key:h()}))}function i(e){g=e,e=t(),w=e[0],b=e[1],k.call({action:g,location:b})}function s(e){y.go(e)}void 0===e&&(e={});var c=void 0===(e=e.window)?document.defaultView:e,y=c.history,m=null;c.addEventListener("popstate",function(){if(m)E.call(m),m=null;else{var e=f.Pop,n=t(),r=n[0];if(n=n[1],E.length)if(null!=r){var a=w-r;a&&(m={action:e,location:n,retry:function(){s(-1*a)}},s(a))}else"production"!==process.env.NODE_ENV&&o(!1,"You are trying to block a POP navigation to a location that was not created by the history library. The block will fail silently in production, but in general you should do all navigation with the history library (instead of using window.history.pushState directly) to avoid this situation.");else i(e)}});var g=f.Pop,w=(e=t())[0],b=e[1],k=l(),E=l();return null==w&&(w=0,y.replaceState(a({},y.state,{idx:w}),"")),{get action(){return g},get location(){return b},createHref:n,push:function e(t,a){var o=f.Push,s=r(t,a);if(!E.length||(E.call({action:o,location:s,retry:function(){e(t,a)}}),0)){var u=[{usr:s.state,key:s.key,idx:w+1},n(s)];s=u[0],u=u[1];try{y.pushState(s,"",u)}catch(e){c.location.assign(u)}i(o)}},replace:function e(t,a){var o=f.Replace,s=r(t,a);E.length&&(E.call({action:o,location:s,retry:function(){e(t,a)}}),1)||(s=[{usr:s.state,key:s.key,idx:w},n(s)],y.replaceState(s[0],"",s[1]),i(o))},go:s,back:function(){s(-1)},forward:function(){s(1)},listen:function(e){return k.push(e)},block:function(e){var t=E.push(e);return 1===E.length&&c.addEventListener("beforeunload",u),function(){t(),E.length||c.removeEventListener("beforeunload",u)}}}}function s(e){function t(){var e=d(y.location.hash.substr(1)),t=e.pathname,n=e.search;e=e.hash;var r=m.state||{};return[r.idx,v({pathname:void 0===t?"/":t,search:void 0===n?"":n,hash:void 0===e?"":e,state:r.usr||null,key:r.key||"default"})]}function n(){if(g)P.call(g),g=null;else{var e=f.Pop,n=t(),r=n[0];if(n=n[1],P.length)if(null!=r){var a=b-r;a&&(g={action:e,location:n,retry:function(){c(-1*a)}},c(a))}else"production"!==process.env.NODE_ENV&&o(!1,"You are trying to block a POP navigation to a location that was not created by the history library. The block will fail silently in production, but in general you should do all navigation with the history library (instead of using window.history.pushState directly) to avoid this situation.");else s(e)}}function r(e){var t=document.querySelector("base"),n="";return t&&t.getAttribute("href")&&(n=-1===(n=(t=y.location.href).indexOf("#"))?t:t.slice(0,n)),n+"#"+("string"==typeof e?e:p(e))}function i(e,t){return void 0===t&&(t=null),v(a({pathname:k.pathname,hash:"",search:""},"string"==typeof e?d(e):e,{state:t,key:h()}))}function s(e){w=e,e=t(),b=e[0],k=e[1],E.call({action:w,location:k})}function c(e){m.go(e)}void 0===e&&(e={});var y=void 0===(e=e.window)?document.defaultView:e,m=y.history,g=null;y.addEventListener("popstate",n),y.addEventListener("hashchange",function(){p(t()[1])!==p(k)&&n()});var w=f.Pop,b=(e=t())[0],k=e[1],E=l(),P=l();return null==b&&(b=0,m.replaceState(a({},m.state,{idx:b}),"")),{get action(){return w},get location(){return k},createHref:r,push:function e(t,n){var a=f.Push,c=i(t,n);if("production"!==process.env.NODE_ENV&&o("/"===c.pathname.charAt(0),"Relative pathnames are not supported in hash history.push("+JSON.stringify(t)+")"),!P.length||(P.call({action:a,location:c,retry:function(){e(t,n)}}),0)){var u=[{usr:c.state,key:c.key,idx:b+1},r(c)];c=u[0],u=u[1];try{m.pushState(c,"",u)}catch(e){y.location.assign(u)}s(a)}},replace:function e(t,n){var a=f.Replace,c=i(t,n);"production"!==process.env.NODE_ENV&&o("/"===c.pathname.charAt(0),"Relative pathnames are not supported in hash history.replace("+JSON.stringify(t)+")"),P.length&&(P.call({action:a,location:c,retry:function(){e(t,n)}}),1)||(c=[{usr:c.state,key:c.key,idx:b},r(c)],m.replaceState(c[0],"",c[1]),s(a))},go:c,back:function(){c(-1)},forward:function(){c(1)},listen:function(e){return E.push(e)},block:function(e){var t=P.push(e);return 1===P.length&&y.addEventListener("beforeunload",u),function(){t(),P.length||y.removeEventListener("beforeunload",u)}}}}function c(e){function t(e,t){return void 0===t&&(t=null),v(a({pathname:m.pathname,search:"",hash:""},"string"==typeof e?d(e):e,{state:t,key:h()}))}function n(e,t,n){return!w.length||(w.call({action:e,location:t,retry:n}),!1)}function r(e,t){y=e,m=t,g.call({action:y,location:m})}function i(e){var t=Math.min(Math.max(u+e,0),c.length-1),a=f.Pop,o=c[t];n(a,o,function(){i(e)})&&(u=t,r(a,o))}void 0===e&&(e={});var s=e;e=s.initialEntries,s=s.initialIndex;var c=(void 0===e?["/"]:e).map(function(e){var t=v(a({pathname:"/",search:"",hash:"",state:null,key:h()},"string"==typeof e?d(e):e));return"production"!==process.env.NODE_ENV&&o("/"===t.pathname.charAt(0),"Relative pathnames are not supported in createMemoryHistory({ initialEntries }) (invalid entry: "+JSON.stringify(e)+")"),t}),u=Math.min(Math.max(null==s?c.length-1:s,0),c.length-1),y=f.Pop,m=c[u],g=l(),w=l();return{get index(){return u},get action(){return y},get location(){return m},createHref:function(e){return"string"==typeof e?e:p(e)},push:function e(a,i){var s=f.Push,l=t(a,i);"production"!==process.env.NODE_ENV&&o("/"===m.pathname.charAt(0),"Relative pathnames are not supported in memory history.push("+JSON.stringify(a)+")"),n(s,l,function(){e(a,i)})&&(u+=1,c.splice(u,c.length,l),r(s,l))},replace:function e(a,i){var s=f.Replace,l=t(a,i);"production"!==process.env.NODE_ENV&&o("/"===m.pathname.charAt(0),"Relative pathnames are not supported in memory history.replace("+JSON.stringify(a)+")"),n(s,l,function(){e(a,i)})&&(c[u]=l,r(s,l))},go:i,back:function(){i(-1)},forward:function(){i(1)},listen:function(e){return g.push(e)},block:function(e){return w.push(e)}}}function u(e){e.preventDefault(),e.returnValue=""}function l(){var e=[];return{get length(){return e.length},push:function(t){return e.push(t),function(){e=e.filter(function(e){return e!==t})}},call:function(t){e.forEach(function(e){return e&&e(t)})}}}function h(){return Math.random().toString(36).substr(2,8)}function p(e){var t=e.pathname;t=void 0===t?"/":t;var n=e.search;return n=void 0===n?"":n,e=void 0===(e=e.hash)?"":e,n&&"?"!==n&&(t+="?"===n.charAt(0)?n:"?"+n),e&&"#"!==e&&(t+="#"===e.charAt(0)?e:"#"+e),t}function d(e){var t={};if(e){var n=e.indexOf("#");0<=n&&(t.hash=e.substr(n),e=e.substr(0,n)),0<=(n=e.indexOf("?"))&&(t.search=e.substr(n),e=e.substr(0,n)),e&&(t.pathname=e)}return t}var f,y;(y=f||(f={})).Pop="POP",y.Push="PUSH",y.Replace="REPLACE";var v="production"!==process.env.NODE_ENV?function(e){return Object.freeze(e)}:function(e){return e};class m{constructor(e,t=!1){this.load=()=>this._result?this._result:this._promise?this._promise:this._promise=this._loader().then(e=>this._result=this._isModule&&e.default||e).catch(e=>{this._error=e}),this.isLoaded=()=>!!this._result,this.read=()=>{if(this._result)return this._result;if(this._error)throw this._error;if(this._promise)throw this._promise;throw this.load()},this._isModule=t,this._loader=e,this._error=this._result=this._promise=null}}let g=["path","children"],w=["path"];var b="",k="",E=null;let P=e=>"/"===e.charAt(0)?e:`/${e}`,x=e=>{const t=[];for(const n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.push({index:t.length,value:n});return t.sort(({value:e},{value:t})=>e>t?1:-1).reduce((t,n)=>{var r=e[n.value];return r=Array.isArray(r)?r.reduce((e,t,r)=>(t=encodeURIComponent(t),e.concat(1<=r?`&${n.value}=${t}`:t)),""):encodeURIComponent(r),`${t}${t?"&":"?"}${n.value}=${r}`},"")},R=(e,t,n="")=>(n=decodeURIComponent(n),e[t]?Array.isArray(e[t])?e[t].concat(n):[e[t],n]:n),C=e=>e?e.slice(1).split("&").reduce((e,t)=>{const[n,r]=t.split("=");return t=R(e,n,r),e[n]=t,e},{}):{},O=e=>"string"==typeof e?d(e):e,N=(e,t,n=!1)=>(e=O(e),t=O(t),e.pathname===t.pathname&&(!n||e.search===t.search&&e.hash===t.hash)),_=(e,t)=>{t=P(t);const n=[];e="^("+e.replace(/[.*+\-?^$/{}()|[\]\\]/g,"\\$&").replace(/\\\*$/,".*").replace(/:(\w+)|(.\*)/g,(e,t="rest")=>(n.push(t),`([^${"rest"===t?":(w+)|(.*)":"\\/"}]+)`))+")\\/?$";const r=t.match(new RegExp(e));return r?{params:n.reduce((e,t,n)=>(n=R(e,t,r[n+2]),e[t]=n,e),{})}:null},S=(e,t,r=!1)=>{t=O(t);const{pathname:a,search:o}=t,i=n({},C(o));let s=e.has(a)&&e.get(a);if(!s)for(const[t,r]of e.entries()){if("/*"!==t){const e=_(t,a);if(!e)continue;n(i,e.params)}s=r;break}return s?(r=!r&&s.redirectRules&&s.redirectRules(i))?S(e,r):{route:s,params:i,location:t}:null},A=(e,t,n)=>{const{route:r,params:a,location:o}=e;return e=(t=t&&r.prefetch||r.assistedPrefetch)?(({params:e,location:t},n,r)=>{var a=t.pathname===b;const o=a&&x(e);if(a&&o===k)return E;a=new Map,n=n(e);for(const e in n){if(!Object.prototype.hasOwnProperty.call(n,e))continue;const t="function"==typeof n[e],o=new m(t?n[e]:n[e].data);o.load(),a.set(e,{defer:t||void 0===n[e].defer?!r:n[e].defer,data:o})}return b=t.pathname,k=o||x(e),a})(e,t,n):null==r.prefetch?void 0:r.prefetch(a),(n=!(!t||!e))&&e===E?E:(r.component.load(),e={location:o,component:r.component,params:a,prefetched:e,assistedPrefetch:n},n&&(E=e),e)},D=({assistPrefetch:e=!1,awaitComponent:t=!1,awaitPrefetch:a=!1,history:o,routes:i})=>{const s=(e=>{const t=new Map,a=(e,o={},i=!1)=>e.forEach(e=>{const{path:s,children:c}=e;e=r(e,g);var{path:u=""}=o,l=r(o,w);u="/"===u?"":u;var h=s?P(s):"";u+=h,l=n({},l,e,!(h=!e.component)&&{component:new m(e.component,!0)}),h||t.set(u,l),c&&Array.isArray(c)&&a(c,n({},h&&e||i&&o,{path:u}),h||i)});return a(e),"production"===process.env.NODE_ENV||t.has("/*")||console.warn("You didn't set a wildcard (*) route to catch any unmatched path.\n This is required to make sure you push users to a Not Found page\n when they request a route that doesn't exist; e.g. 404."),t})(i);i=S(s,o.location);let c=A(i,e,a);N(i.location,o.location,!0)||o.replace(i.location);let u=0;const l=new Map;return o.listen(({location:t,action:r})=>{if(!N(c.location,t,!0)){r=t.state&&t.state.skipRender&&"POP"!==r;var i=S(s,t),u=r?n({},c,{location:i.location,params:i.params,skipRender:r}):A(i,e,a);if(!N(i.location,t,!0))return o.replace(i.location);c=u,l.forEach(e=>e(u))}}),{assistPrefetch:e,awaitComponent:t,history:o,isActive:(e,{exact:t}={})=>N(o.location,e,t),get:()=>c,preloadCode:(e,{ignoreRedirectRules:t}={})=>{({route:e}=S(s,e,t)),e.component.load()},warmRoute:(t,{ignoreRedirectRules:n}={})=>{t=S(s,t,n),A(t,e,a)},subscribe:e=>{const t=u++;return l.set(t,e),()=>{l.delete(t)}}}},L=["window"],M=["window"],$=["initialEntries","initialIndex"],V=t.createContext(null),T=["activeClassName","exact","target","to","onClick"],j=t.forwardRef((e,a)=>{let{activeClassName:o,exact:i,target:s,to:c,onClick:u}=e;e=r(e,T);const{isActive:l,preloadCode:h,warmRoute:p,history:d}=t.useContext(V),f=l(c,{exact:i}),y=t.useCallback(e=>{u&&u(e),e.defaultPrevented||0!==e.button||e.target.target&&"_self"!==e.target.target||e.metaKey||e.altKey||e.ctrlKey||e.shiftKey||(e.preventDefault(),e=l(c,{exact:!0})?"replace":"push",d[e](c))},[u,l,c,d]),v=t.useCallback(()=>{h(c)},[h,c]),m=t.useCallback(({type:e,key:t,code:n,keyCode:r})=>{"mousedown"!==e&&("keydown"!==e||"Enter"!==t&&"Enter"!==n&&"NumpadEnter"!==n&&13!==r)||p(c)},[p,c]);return a=n({},e,{target:s,ref:a,href:c,onClick:y,onMouseOver:v,onFocus:v,onMouseDown:m,onKeyDown:m},f&&{className:e.className?`${e.className} ${o}`:o,"aria-current":"page"}),t.createElement("a",a)});j.defaultProps={activeClassName:"active",exact:!1},j.displayName="Link",e.Link=j,e.RouteRenderer=({pendingIndicator:e})=>{const{assistPrefetch:r,awaitComponent:a,get:o,subscribe:i}=t.useContext(V);var s=t.useCallback(e=>{if(!r||!e.prefetched)return e;const t={};for(const[n,r]of e.prefetched.entries())t[n]=r.data;return n({},e,{prefetched:t})},[]);const[c,u]=t.useState(!1),[l,h]=t.useState(s(o()));s=t.useMemo(()=>l.component.read(),[l]);const p=t.useCallback(e=>{if(!e.assistedPrefetch)return{prefetched:e.prefetched,toBePrefetched:[]};const t={},n=[];for(const[r,a]of e.prefetched.entries())!1===a.defer&&!1===a.data.isLoaded()?n.push({key:r,data:a.data}):t[r]=a.data;return{prefetched:t,toBePrefetched:n}},[]);return t.useEffect(()=>{const t=i(async t=>{if(!t.skipRender){var{prefetched:r,toBePrefetched:o}=p(t),i=!(!e||!(a&&!t.component.isLoaded()||t.assistedPrefetch&&o.length));i&&u(!0),a&&await t.component.load();var s=o.length?await o.reduce(async(e,{key:t,data:r})=>(await r.load(),n({},e,{[t]:r})),{}):{};t=n({},t,{prefetched:n({},r,s)}),h(t),i&&u(!1)}});return()=>t()},[r,a,p,e,i]),t.createElement(t.Fragment,{key:l.location?l.location.pathname+l.location.search+l.location.hash:"default"},c&&e?e:null,t.createElement(s,{key:window.location.href,params:l.params,prefetched:l.prefetched}))},e.RouterProvider=({children:e,router:n})=>t.createElement(V.Provider,{value:n,children:e}),e.SuspendableResource=m,e.createBrowserRouter=e=>{let{window:t}=e;return e=r(e,L),D(n({},e,{history:i({window:t})}))},e.createHashRouter=e=>{let{window:t}=e;return e=r(e,M),D(n({},e,{history:s({window:t})}))},e.createMemoryRouter=e=>{let{initialEntries:t,initialIndex:a}=e;return e=r(e,$),D(n({},e,{history:c({initialEntries:t,initialIndex:a})}))},e.useBeforeRouteLeave=({toggle:e=!0,unload:n=!0,message:r=""})=>{const{history:{block:a}}=t.useContext(V);let o;const i=t.useCallback(e=>{e.preventDefault(),e.returnValue=""},[]),s=t.useCallback(()=>{o=a(r),n&&window.addEventListener("beforeunload",i)},[a]),c=t.useCallback(()=>{o(),n&&window.removeEventListener("beforeunload",i)},[o]);t.useEffect(()=>(e&&s(),()=>{e&&c()}),[e,s,c])},e.useHistory=()=>{const{history:{length:e,location:n,action:r,index:a,entries:o},subscribe:i}=t.useContext(V),[,s]=t.useState((new Date).getTime());return t.useEffect(()=>{const e=i(async()=>{setTimeout(()=>s((new Date).getTime()),1)});return()=>e()},[]),{length:e,location:n,action:r,index:a,entries:o}},e.useNavigation=()=>{const{history:{push:e,replace:n,go:r,back:a,forward:o}}=t.useContext(V);return{push:e,replace:n,go:r,goBack:a,goForward:o}},e.useParams=()=>{const{get:e,subscribe:n}=t.useContext(V),[r,a]=t.useState(e().params);return t.useEffect(()=>{const e=n(async e=>{setTimeout(()=>a(e.params),1)});return()=>e()},[]),r},e.useRouter=()=>{const{isActive:e,preloadCode:n,warmRoute:r}=t.useContext(V);return{isActive:e,preloadCode:n,warmRoute:r}},e.useSearchParams=()=>{const{get:e,history:r,subscribe:a}=t.useContext(V),[o,i]=t.useState(C(e().location.search)),s=t.useCallback((t,{replace:a=!1}={})=>{const{location:o}=e(),i="function"==typeof t&&C(o.search);t="function"==typeof t?t(i):t,r[a?"replace":"push"]({pathname:o.pathname,search:x(t)},n({},o.state,a&&{skipRender:!0}))},[]);return t.useEffect(()=>{const e=a(async e=>{const t=C(e.location.search);setTimeout(()=>i(t),1)});return()=>e()},[]),[o,s]}});