UNPKG

@xan105/vanilla-router

Version:

Simple and modern Vanilla JS router based on the Navigation API and URLPattern API

2 lines (1 loc) 4.59 kB
function h(){[...document.head.querySelectorAll('meta[property^="og:"]'),...document.head.querySelectorAll('meta[property^="article:"]'),...document.head.querySelectorAll('meta[property^="book:"]'),...document.head.querySelectorAll('meta[property^="profile:"]'),...document.head.querySelectorAll('meta[property^="video:"]'),...document.head.querySelectorAll('meta[property^="music:"]'),...document.head.querySelectorAll('link[rel^="canonical"]')].forEach(s=>document.head.removeChild(s))}function f(s){if(!Array.isArray(s))return;let e=["og: https://ogp.me/ns/#"];h();for(let{name:t,content:r,details:a}of s){switch(t){case"title":{document.title=r;break}case"description":{let n=document.head.querySelector(`meta[name="${t}"]`)??document.head.appendChild(document.createElement("meta"));n.setAttribute("name",t),n.setAttribute("content",r);break}case"url":{let n=document.head.querySelector('link[rel="canonical"]')??document.head.appendChild(document.createElement("link"));n.setAttribute("rel","canonical"),n.setAttribute("href",r);break}case"type":{let n=`${r}: https://ogp.me/ns/${r}#`;e.push(n);break}}let o=document.createElement("meta");o.setAttribute("property","og:"+t),o.setAttribute("content",r),document.head.appendChild(o);for(let[n,c]of Object.entries(a??{})){let l=Array.isArray(c)?c:[c];for(let u of l){let i=document.createElement("meta");if(t==="type"){let[m]=r.split(".");i.setAttribute("property",m+":"+n)}else i.setAttribute("property","og:"+t+":"+n);i.setAttribute("content",u),document.head.appendChild(i)}}}document.head.setAttribute("prefix",e.join(" "))}var d=class extends EventTarget{#e;#t;constructor(e={}){super(),this.#e=Object.create(null),this.#t=Symbol("404"),this.autoFire=e?.autoFire??!0,this.sensitive=e?.sensitive??!0,this.ignoreAssets=e?.ignoreAssets??!0,this.directoryIndex=Array.isArray(e?.directoryIndex)?e.directoryIndex:["index.html"],this.manualOverride=e?.manualOverride??!0,this.autoFocus=e?.autoFocus??!0,this.autoScroll=e?.autoScroll??!0,this.deferredCommit=e?.deferredCommit??!1}get routes(){return Reflect.ownKeys(this.#e)}get current(){return navigation.currentEntry}get history(){return navigation.entries()}back(){if(navigation.canGoBack)return navigation.back()}forward(){if(navigation.canGoForward)return navigation.forward()}navigate(e,t){return navigation.navigate(e,t)}on(e,t,r={}){return typeof t!="function"?this:(typeof e=="string"&&e.length>0?this.#e[e]={handler:t,options:r}:e===404&&(this.#e[this.#t]={handler:t,options:r}),this)}off(e){return typeof e=="string"&&e.length>0?delete this.#e[e]:e===404&&delete this.#e[this.#t],this}#r(e){for(let t of this.directoryIndex)if(typeof t=="string"&&e.endsWith("/"+t))return e.slice(0,-t.length);return e}#n(e){if(this.sensitive===!0&&Object.hasOwn(this.#e,e))return this.#e[e];for(let[t,{handler:r,options:a}]of Object.entries(this.#e)){let n=new URLPattern({pathname:t},{ignoreCase:this.sensitive===!1}).exec({pathname:e});if(n)return{handler:r,options:a,routeParams:n.pathname.groups}}return this.#e[this.#t]}listen(){return navigation.addEventListener("navigateerror",e=>{let t=new URL(e.currentTarget.currentEntry.url);this.dispatchEvent(new CustomEvent("error",{detail:{error:e.error.message,url:t}}))}),navigation.addEventListener("navigatesuccess",e=>{let t=new URL(e.currentTarget.currentEntry.url);this.dispatchEvent(new CustomEvent("did-navigate",{detail:{url:t}}))}),navigation.addEventListener("navigate",e=>{if(!e.canIntercept||e.hashChange||e.downloadRequest||e.formData||this.manualOverride&&e.sourceElement?.dataset?.navigation==="false")return;let t=new URL(e.destination.url);if(t.pathname=this.#r(t.pathname),this.ignoreAssets===!0&&/\.[^/]+$/.test(t.pathname))return;this.dispatchEvent(new CustomEvent("will-navigate",{detail:{url:t}}));let{handler:r,options:a,routeParams:o}=this.#n(t.pathname)??{},n=(a?.autoFocus??this.autoFocus)===!0,c=(a?.autoScroll??this.autoScroll)===!0,l=e.cancelable&&(a?.deferredCommit??this.deferredCommit)===!0;e.intercept({focusReset:n?"after-transition":"manual",scroll:c?"after-transition":"manual",[l?"precommitHandler":"handler"]:async function(u){if(!r)throw new Error("No route handler found!");await r(Object.freeze({event:e,searchParams:Object.fromEntries(t.searchParams.entries()),routeParams:o??{},redirect:function(i){if(u&&u instanceof NavigationPrecommitController)u.redirect(i,{history:"replace"});else throw navigation.navigate(i,{history:"replace"}),new DOMException("Abort","AbortError")}}))}})}),this.autoFire===!0&&this.navigate(location.pathname,{history:"replace"}),this}};export{d as Router,f as updateMetadata};