UNPKG

trek-router

Version:
266 lines (224 loc) 5.95 kB
/*! * router * Copyright(c) 2015-2017 Fangdun Cai * MIT Licensed */ 'use strict' // Static Param Any `*` `/` `:` const [SKIND, PKIND, AKIND, STAR, SLASH, COLON] = [0, 1, 2, 42, 47, 58] class Node { constructor(prefix = '/', children = [], kind = SKIND, map = Object.create(null)) { this.label = prefix.charCodeAt(0) this.prefix = prefix this.children = children this.kind = kind this.map = map } addChild(n) { this.children.push(n) } findChild(c, t, l, e, i = 0) { for (l = this.children.length; i < l; i++) { e = this.children[i] if (c === e.label && t === e.kind) { return e } } } findChildWithLabel(c, l, e, i = 0) { for (l = this.children.length; i < l; i++) { e = this.children[i] if (c === e.label) { return e } } } findChildByKind(t, l, e, i = 0) { for (l = this.children.length; i < l; i++) { e = this.children[i] if (t === e.kind) { return e } } } addHandler(method, handler, pnames) { this.map[method] = {handler, pnames} } findHandler(method) { return this.map[method] } } class Router { constructor() { this.tree = new Node() } add(method, path, handler) { // Pnames: Param names let [i, l, pnames, ch, j] = [0, path.length, []] for (; i < l; ++i) { ch = path.charCodeAt(i) if (ch === COLON) { j = i + 1 this.insert(method, path.substring(0, i), SKIND) while (i < l && path.charCodeAt(i) !== SLASH) { i++ } pnames.push(path.substring(j, i)) path = path.substring(0, j) + path.substring(i) i = j l = path.length if (i === l) { this.insert(method, path.substring(0, i), PKIND, pnames, handler) return } this.insert(method, path.substring(0, i), PKIND, pnames) } else if (ch === STAR) { this.insert(method, path.substring(0, i), SKIND) pnames.push('*') this.insert(method, path.substring(0, l), AKIND, pnames, handler) return } } this.insert(method, path, SKIND, pnames, handler) } insert(method, path, t, pnames, handler) { // Current node as root let [cn, prefix, sl, pl, l, max, n, c] = [this.tree] while (true) { prefix = cn.prefix sl = path.length pl = prefix.length l = 0 // LCP max = sl < pl ? sl : pl while (l < max && path.charCodeAt(l) === prefix.charCodeAt(l)) { l++ } /* If (l === 0) { // At root node cn.label = search.charCodeAt(0) cn.prefix = search if (handler !== undefined) { cn.addHandler(method, { pnames, handler }) } } else if (l < pl) { */ if (l < pl) { // Split node n = new Node(prefix.substring(l), cn.children, cn.kind, cn.map) cn.children = [n] // Add to parent // Reset parent node cn.label = prefix.charCodeAt(0) cn.prefix = prefix.substring(0, l) cn.map = Object.create(null) cn.kind = SKIND if (l === sl) { // At parent node cn.addHandler(method, handler, pnames) cn.kind = t } else { // Create child node n = new Node(path.substring(l), [], t) n.addHandler(method, handler, pnames) cn.addChild(n) } } else if (l < sl) { path = path.substring(l) c = cn.findChildWithLabel(path.charCodeAt(0)) if (c !== undefined) { // Go deeper cn = c continue } // Create child node n = new Node(path, [], t) n.addHandler(method, handler, pnames) cn.addChild(n) } else if (handler !== undefined) { // Node already exists cn.addHandler(method, handler, pnames) } return } } find(method, path, cn, n = 0, result = [undefined, []]) { cn = cn || this.tree // Current node as root const sl = path.length const prefix = cn.prefix const pvalues = result[1] // Params let i, pl, l, max, c let preSearch // Pre search // Search order static > param > match-any if (sl === 0 || path === prefix) { // Found const r = cn.findHandler(method) if ((result[0] = r && r.handler) !== undefined) { const pnames = r.pnames if (pnames !== undefined) { for (i = 0, l = pnames.length; i < l; ++i) { pvalues[i] = { name: pnames[i], value: pvalues[i] } } } } return result } pl = prefix.length l = 0 // LCP max = sl < pl ? sl : pl while (l < max && path.charCodeAt(l) === prefix.charCodeAt(l)) { l++ } if (l === pl) { path = path.substring(l) } preSearch = path // Static node c = cn.findChild(path.charCodeAt(0), SKIND) if (c !== undefined) { this.find(method, path, c, n, result) if (result[0] !== undefined) { return result } path = preSearch } // Not found node if (l !== pl) { return result } // Param node c = cn.findChildByKind(PKIND) if (c !== undefined) { l = path.length i = 0 while (i < l && path.charCodeAt(i) !== SLASH) { i++ } pvalues[n] = path.substring(0, i) n++ preSearch = path path = path.substring(i) this.find(method, path, c, n, result) if (result[0] !== undefined) { return result } n-- pvalues.pop() path = preSearch } // Any node c = cn.findChildByKind(AKIND) if (c !== undefined) { pvalues[n] = path path = '' // End search this.find(method, path, c, n, result) } return result } } Router.Node = Node module.exports = Router