/** * Router */ class Branch { constructor() { this.children = new Map() this.paramName = null this.fullparamName = null this.handler = null this.middlewares = [] } } const __paramMapName = '__param' const __fullParamMapName = '__fullparam' export class FlaskaRouter { constructor() { this.root = new Branch() } addRoute(route, orgMiddlewares, orgHandler) { if (route[0] !== '/') throw new Error(`route "${route}" must start with forward slash`) let middlewares = orgMiddlewares let handler = orgHandler if (!orgHandler) { handler = orgMiddlewares middlewares = [] } if (middlewares && typeof(middlewares) === 'function') { middlewares = [middlewares] } if (typeof(handler) !== 'function') { throw new Error(`route "${route}" was missing a handler`) } let start = 1 let end = 1 let name = '' let param = '' let isParam = false let isFullParam = false let branch = this.root if (route.indexOf(':') < 0) { let name = route if (name.length > 1 && name[name.length - 1] === '/') { name = name.slice(0, -1) } let child = new Branch() branch.children.set(name, child) child.handler = handler child.middlewares = middlewares } for (let i = 1; i <= route.length; i++) { if ((i === route.length || route[i] === '/') && end > start) { if (branch.fullparamName) { throw new Error(`route "${route}" conflicts with a sub-branch that has a full param child`) } let child name = route.substring(start, end) if (isFullParam) { param = name name = __fullParamMapName } else if (isParam) { param = name name = __paramMapName } if (branch.children.has(name)) { child = branch.children.get(name) } else if (isParam && !isFullParam && branch.children.has(__fullParamMapName)) { throw new Error(`route "${route}" conflicts with a sub-branch that has a full param child`) } else if (isFullParam && branch.children.has(__paramMapName)) { throw new Error(`route "${route}" conflicts with a sub-branch that has a partial param child`) } else { child = new Branch() branch.children.set(name, child) } branch = child end = i start = i if (isParam) { if (branch.paramName && branch.paramName !== param) { throw new Error(`route "${route}" conflicts with pre-existing param name of ${branch.paramName} instead of ${param}`) } if (isFullParam) { branch.fullparamName = param } else { branch.paramName = param } isParam = false } } else if (route[i] === '/' && end === start) { throw new Error(`route "${route}" has missing path name inbetween slashes`) } if (i === route.length) { branch.handler = handler branch.middlewares = middlewares continue } if (route[i] === ':') { if (isParam) { isFullParam = true } isParam = true end = start = i + 1 } else if (route[i] === '/') { end = start = i + 1 } else { end++ } } } match(orgUrl) { let url = orgUrl if (url.length > 1 && url[url.length - 1] === '/') { url = url.slice(0, -1) } let branch = this.root let start = 1 let end = 1 let output let name let char let params = {} if (output = branch.children.get(url)) { return { handler: output.handler, middlewares: output.middlewares, params: params, } } for (let i = 1; i <= url.length; i++) { char = url[i] if ((i === url.length || char === '/') && end > start) { name = url.slice(start, end) if (output = branch.children.get(name)) { branch = output } else if (output = branch.children.get(__paramMapName)) { branch = output params[branch.paramName] = name } else { return null } i++ end = start = i char = url[i] } if (i >= url.length) { return { handler: branch.handler, middlewares: branch.middlewares, params: params, } } if (char === '/') { end = start = i + 1 } else { end++ } } return null } } export function FlaskaRouter() { this.root = new Branch() } FlaskaRouter.prototype.addRoute = function(route, orgMiddlewares, orgHandler) { if (route[0] !== '/') throw new Error(`route "${route}" must start with forward slash`) let middlewares = orgMiddlewares let handler = orgHandler if (!orgHandler) { handler = orgMiddlewares middlewares = [] } if (middlewares && typeof(middlewares) === 'function') { middlewares = [middlewares] } if (typeof(handler) !== 'function') { throw new Error(`route "${route}" was missing a handler`) } let start = 1 let end = 1 let name = '' let param = '' let isParam = false let isFullParam = false let branch = this.root if (route.indexOf(':') < 0) { let name = route if (name.length > 1 && name[name.length - 1] === '/') { name = name.slice(0, -1) } let child = new Branch() branch.children.set(name, child) child.handler = handler child.middlewares = middlewares } for (let i = 1; i <= route.length; i++) { if ((i === route.length || route[i] === '/') && end > start) { if (branch.fullparamName) { throw new Error(`route "${route}" conflicts with a sub-branch that has a full param child`) } let child name = route.substring(start, end) if (isFullParam) { param = name name = __fullParamMapName } else if (isParam) { param = name name = __paramMapName } if (branch.children.has(name)) { child = branch.children.get(name) } else if (isParam && !isFullParam && branch.children.has(__fullParamMapName)) { throw new Error(`route "${route}" conflicts with a sub-branch that has a full param child`) } else if (isFullParam && branch.children.has(__paramMapName)) { throw new Error(`route "${route}" conflicts with a sub-branch that has a partial param child`) } else { child = new Branch() branch.children.set(name, child) } branch = child end = i start = i if (isParam) { if (branch.paramName && branch.paramName !== param) { throw new Error(`route "${route}" conflicts with pre-existing param name of ${branch.paramName} instead of ${param}`) } if (isFullParam) { branch.fullparamName = param } else { branch.paramName = param } isParam = false } } else if (route[i] === '/' && end === start) { throw new Error(`route "${route}" has missing path name inbetween slashes`) } if (i === route.length) { branch.handler = handler branch.middlewares = middlewares continue } if (route[i] === ':') { if (isParam) { isFullParam = true } isParam = true end = start = i + 1 } else if (route[i] === '/') { end = start = i + 1 } else { end++ } } } FlaskaRouter.prototype.match = function(orgUrl) { let url = orgUrl if (url.length > 1 && url[url.length - 1] === '/') { url = url.slice(0, -1) } let branch = this.root let start = 1 let end = 1 let output let name let char let params = {} if (output = branch.children.get(url)) { return { handler: output.handler, middlewares: output.middlewares, params: params, } } for (let i = 1; i <= url.length; i++) { char = url[i] if ((i === url.length || char === '/') && end > start) { name = url.slice(start, end) if (output = branch.children.get(name)) { branch = output } else if (output = branch.children.get(__paramMapName)) { branch = output params[branch.paramName] = name } else { return null } i++ end = start = i char = url[i] } if (i >= url.length) { return { handler: branch.handler, middlewares: branch.middlewares, params: params, } } if (char === '/') { end = start = i + 1 } else { end++ } } return null } export function Flaska() { }