UNPKG

@neabyte/fast-router

Version:

A fast, versatile router with radix tree structure for JavaScript

2 lines (1 loc) 2.99 kB
"use strict";function getMatchParams(r,s){const t=new nullProtoObj;for(const[a,e]of s){const i=a<0?r.slice(-a).join("/"):r[a]??"";if(typeof e=="string")t[e]=i;else{const n=i.match(e);n?.groups&&Object.assign(t,n.groups)}}return t}const nullProtoObj=(()=>{function r(){}return r.prototype=Object.create(null),Object.freeze(r.prototype),r})();function searchTree(r,s,t,a){if(a===t.length){const o=r.methods?.[s]||r.methods?.[""];if(o)return o;const d=r.param?.methods?.[s]||r.param?.methods?.[""];if(d?.[0]?.paramsMap?.[d[0].paramsMap.length-1]?.[2])return d;const p=r.wildcard?.methods?.[s]||r.wildcard?.methods?.[""];return p?.[0]?.paramsMap?.[p[0].paramsMap.length-1]?.[2]?p:void 0}const e=t[a];if(!e)return;const i=r.static?.[e];if(i){const o=searchTree(i,s,t,a+1);if(o)return o}if(!r.param)return r.wildcard?.methods?.[s]||r.wildcard?.methods?.[""];const n=searchTree(r.param,s,t,a+1);if(!n)return r.wildcard?.methods?.[s]||r.wildcard?.methods?.[""];if(r.param.hasRegexParam){const o=n.find(d=>d.paramsRegexp[a]?.test(e));return o?[o]:void 0}return n}function splitPath(r){const[s,...t]=r.split("/");return t[t.length-1]===""?t.slice(0,-1):t}class FastRouter{context;constructor(){this.context={root:{key:""},static:new nullProtoObj}}add(s,t,a){s=s.toUpperCase(),t.startsWith("/")||(t=`/${t}`);let e=this.context.root,i=0;const n=splitPath(t),o=[],d=[];for(let h=0;h<n.length;h++){const c=n[h];if(c){if(c.startsWith("**")){e.wildcard??={key:"**"},e=e.wildcard,o.push([-h,c.split(":")[1]??"_",c.length===2]);break}if(c==="*"||c.includes(":")){if(e.param??={key:"*"},e=e.param,c==="*")o.push([h,`_${i++}`,!0]);else{const l=c.match(/^:(\w+)\((.+)\)$/);if(l){const m=new RegExp(`^(?<${l[1]}>${l[2]})$`);e.hasRegexParam=!0,d[h]=m,o.push([h,m,!1])}else o.push([h,c.slice(1),!1])}continue}e.static??=new nullProtoObj,e=e.static[c]??={key:c}}}const p=o.length>0;e.methods??=new nullProtoObj,e.methods[s]=[{data:a??null,paramsRegexp:d,paramsMap:p?o:void 0}],p||(this.context.static[t]=e)}find(s,t,a){const e=this.context.static[t];if(e&&e.methods){const d=s.toUpperCase(),p=e.methods[d]||e.methods[""];if(p&&p[0])return p[0]}s=s.toUpperCase(),t.endsWith("/")&&t!=="/"&&(t=t.slice(0,-1)),t||(t="/");const i=splitPath(t),n=searchTree(this.context.root,s,i,0);if(!n?.[0])return;const o=n[0];return a?.params===!1?{data:o.data,params:void 0}:{data:o.data,params:o.paramsMap?getMatchParams(i,o.paramsMap):void 0}}remove(s,t){s=s.toUpperCase(),t.startsWith("/")||(t=`/${t}`);const a=splitPath(t),e=a.some(n=>n==="*"||n.includes(":")||n.startsWith("**")),i=e?this.findNode(a):this.context.static[t];return i?.methods?.[s]?(delete i.methods[s],Object.keys(i.methods).length||delete i.methods,e||delete this.context.static[t],!0):!1}findNode(s){let t=this.context.root;for(const a of s)if(a){if(a.startsWith("**")){if(!t.wildcard)return;t=t.wildcard;break}if(a==="*"||a.includes(":")){if(!t.param)return;t=t.param;continue}if(!t.static?.[a])return;t=t.static[a]}return t}}exports.FastRouter=FastRouter;