deno.land / x / deno@v1.28.2 / cli / bench / testdata / npm / hono / dist / router / reg-exp-router / router.js
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361"use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.RegExpRouter = void 0;const router_1 = require("../../router");const trie_1 = require("./trie");const emptyParam = {};const nullMatcher = [/^$/, []];function initHint(path) { const components = path.match(/\/(?::\w+{[^}]+}|[^\/]*)/g) || []; let componentsLength = components.length; const paramIndexList = []; const regExpComponents = []; const namedParams = []; for (let i = 0, len = components.length; i < len; i++) { if (i === len - 1 && components[i] === '/*') { componentsLength--; break; } const m = components[i].match(/^\/:(\w+)({[^}]+})?/); if (m) { namedParams.push([i, m[1], m[2] || '[^/]+']); regExpComponents[i] = m[2] || true; } else if (components[i] === '/*') { regExpComponents[i] = true; } else { regExpComponents[i] = components[i]; } if (/\/(?::|\*)/.test(components[i])) { paramIndexList.push(i); } } return { components, regExpComponents, componentsLength, endWithWildcard: path.endsWith('*'), paramIndexList, namedParams, maybeHandler: true, };}function compareRoute(a, b) { if (a.path === '*') { return 1; } let i = 0; const len = a.hint.regExpComponents.length; for (; i < len; i++) { if (a.hint.regExpComponents[i] !== b.hint.regExpComponents[i]) { if (a.hint.regExpComponents[i] === true) { break; } return 0; } } // may be ambiguous for (; i < len; i++) { if (a.hint.regExpComponents[i] !== true && a.hint.regExpComponents[i] !== b.hint.regExpComponents[i]) { return 2; } } return i === b.hint.regExpComponents.length || a.hint.endWithWildcard ? 1 : 0;}function compareHandler(a, b) { return a.index - b.index;}function getSortedHandlers(handlers) { return [...handlers].sort(compareHandler).map((h) => h.handler);}function buildMatcherFromPreprocessedRoutes(routes, hasAmbiguous = false) { const trie = new trie_1.Trie({ reverse: hasAmbiguous }); const handlers = []; if (routes.length === 0) { return nullMatcher; } for (let i = 0, len = routes.length; i < len; i++) { const paramMap = trie.insert(routes[i].path, i); handlers[i] = [ [...routes[i].middleware, ...routes[i].handlers], Object.keys(paramMap).length !== 0 ? paramMap : null, ]; if (!hasAmbiguous) { handlers[i][0] = getSortedHandlers(handlers[i][0]); } } const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp(); for (let i = 0, len = handlers.length; i < len; i++) { const paramMap = handlers[i][1]; if (paramMap) { for (let j = 0, len = paramMap.length; j < len; j++) { paramMap[j][1] = paramReplacementMap[paramMap[j][1]]; const aliasTo = routes[i].paramAliasMap[paramMap[j][0]]; if (aliasTo) { for (let k = 0, len = aliasTo.length; k < len; k++) { paramMap.push([aliasTo[k], paramMap[j][1]]); } } } } } const handlerMap = []; // using `in` because indexReplacementMap is a sparse array for (const i in indexReplacementMap) { handlerMap[i] = handlers[indexReplacementMap[i]]; } return [regexp, handlerMap];}function verifyDuplicateParam(routes) { const nameMap = {}; for (let i = 0, len = routes.length; i < len; i++) { const route = routes[i]; for (let k = 0, len = route.hint.namedParams.length; k < len; k++) { const [index, name] = route.hint.namedParams[k]; if (name in nameMap && index !== nameMap[name]) { return false; } else { nameMap[name] = index; } } const paramAliasMap = route.paramAliasMap; const paramAliasMapKeys = Object.keys(paramAliasMap); for (let k = 0, len = paramAliasMapKeys.length; k < len; k++) { const aliasFrom = paramAliasMapKeys[k]; for (let l = 0, len = paramAliasMap[aliasFrom].length; l < len; l++) { const aliasTo = paramAliasMap[aliasFrom][l]; const index = nameMap[aliasFrom]; if (aliasTo in nameMap && index !== nameMap[aliasTo]) { return false; } else { nameMap[aliasTo] = index; } } } } return true;}class RegExpRouter { constructor() { this.routeData = { index: 0, routes: [], methods: new Set() }; } add(method, path, handler) { if (!this.routeData) { throw new Error('Can not add a route since the matcher is already built.'); } this.routeData.index++; const { index, routes, methods } = this.routeData; if (path === '/*') { path = '*'; } const hint = initHint(path); const handlerWithSortIndex = { index, handler, }; for (let i = 0, len = routes.length; i < len; i++) { if (routes[i].method === method && routes[i].path === path) { routes[i].handlers.push(handlerWithSortIndex); return; } } methods.add(method); routes.push({ method, path, hint, handlers: [handlerWithSortIndex], middleware: [], paramAliasMap: {}, }); } match(method, path) { const [primaryMatchers, secondaryMatchers, hasAmbiguous] = this.buildAllMatchers(); this.match = hasAmbiguous ? (method, path) => { const matcher = (primaryMatchers[method] || primaryMatchers[router_1.METHOD_NAME_ALL]); let match = path.match(matcher[0]); if (!match) { // do not support secondary matchers here. return null; } const params = {}; const handlers = new Set(); let regExpSrc = matcher[0].source; while (match) { let index = match.indexOf('', 1); for (;;) { const [handler, paramMap] = matcher[1][index]; if (paramMap) { for (let i = 0, len = paramMap.length; i < len; i++) { params[paramMap[i][0]] = match[paramMap[i][1]]; } } for (let i = 0, len = handler.length; i < len; i++) { handlers.add(handler[i]); } const newIndex = match.indexOf('', index + 1); if (newIndex === -1) { break; } index = newIndex; } regExpSrc = regExpSrc.replace(new RegExp(`((?:(?:\\(\\?:|.)*?\\([^)]*\\)){${index - 1}}.*?)\\(\\)`), '$1(^)'); match = path.match(new RegExp(regExpSrc)); } return { handlers: getSortedHandlers(handlers.values()), params }; } : (method, path) => { let matcher = (primaryMatchers[method] || primaryMatchers[router_1.METHOD_NAME_ALL]); let match = path.match(matcher[0]); if (!match) { const matchers = secondaryMatchers[method] || secondaryMatchers[router_1.METHOD_NAME_ALL]; for (let i = 0, len = matchers.length; i < len && !match; i++) { matcher = matchers[i]; match = path.match(matcher[0]); } if (!match) { return null; } } const index = match.indexOf('', 1); const [handlers, paramMap] = matcher[1][index]; if (!paramMap) { return { handlers, params: emptyParam }; } const params = {}; for (let i = 0, len = paramMap.length; i < len; i++) { params[paramMap[i][0]] = match[paramMap[i][1]]; } return { handlers, params }; }; return this.match(method, path); } buildAllMatchers() { // @ts-ignore this.routeData.routes.sort(({ hint: a }, { hint: b }) => { if (a.componentsLength !== b.componentsLength) { return a.componentsLength - b.componentsLength; } for (let i = 0, len = Math.min(a.paramIndexList.length, b.paramIndexList.length) + 1; i < len; i++) { if (a.paramIndexList[i] !== b.paramIndexList[i]) { if (a.paramIndexList[i] === undefined) { return -1; } else if (b.paramIndexList[i] === undefined) { return 1; } else { return a.paramIndexList[i] - b.paramIndexList[i]; } } } if (a.endWithWildcard !== b.endWithWildcard) { return a.endWithWildcard ? -1 : 1; } return 0; }); const primaryMatchers = {}; const secondaryMatchers = {}; let hasAmbiguous = false; // @ts-ignore this.routeData.methods.forEach((method) => { let _hasAmbiguous; [primaryMatchers[method], secondaryMatchers[method], _hasAmbiguous] = this.buildMatcher(method); hasAmbiguous = hasAmbiguous || _hasAmbiguous; }); primaryMatchers[router_1.METHOD_NAME_ALL] || (primaryMatchers[router_1.METHOD_NAME_ALL] = nullMatcher); secondaryMatchers[router_1.METHOD_NAME_ALL] || (secondaryMatchers[router_1.METHOD_NAME_ALL] = []); delete this.routeData; // to reduce memory usage return [primaryMatchers, secondaryMatchers, hasAmbiguous]; } buildMatcher(method) { var _a, _b; let hasAmbiguous = false; const targetMethods = new Set([method, router_1.METHOD_NAME_ALL]); // @ts-ignore const routes = this.routeData.routes.filter(({ method }) => targetMethods.has(method)); // Reset temporary data per method for (let i = 0, len = routes.length; i < len; i++) { routes[i].middleware = []; routes[i].paramAliasMap = {}; } // preprocess routes for (let i = 0, len = routes.length; i < len; i++) { for (let j = i + 1; j < len; j++) { const compareResult = compareRoute(routes[i], routes[j]); // i includes j if (compareResult === 1) { const components = routes[j].hint.components; const namedParams = routes[i].hint.namedParams; for (let k = 0, len = namedParams.length; k < len; k++) { const c = components[namedParams[k][0]]; const m = c.match(/^\/:(\w+)({[^}]+})?/); if (m && namedParams[k][1] === m[1]) { continue; } if (m) { (_a = routes[j].paramAliasMap)[_b = m[1]] || (_a[_b] = []); routes[j].paramAliasMap[m[1]].push(namedParams[k][1]); } else { components[namedParams[k][0]] = `/:${namedParams[k][1]}{${c.substring(1)}}`; routes[j].hint.namedParams.push([ namedParams[k][0], namedParams[k][1], c.substring(1), ]); routes[j].path = components.join(''); } } if (routes[j].hint.components.length < routes[i].hint.components.length) { routes[j].middleware.push(...routes[i].handlers.map((h) => ({ index: h.index, handler: h.handler, }))); } else { routes[j].middleware.push(...routes[i].handlers); } routes[i].hint.maybeHandler = false; } else if (compareResult === 2) { // ambiguous hasAmbiguous = true; if (!verifyDuplicateParam([routes[i], routes[j]])) { throw new Error('Duplicate param name'); } } } if (!verifyDuplicateParam([routes[i]])) { throw new Error('Duplicate param name'); } } if (hasAmbiguous) { return [buildMatcherFromPreprocessedRoutes(routes, hasAmbiguous), [], hasAmbiguous]; } const primaryRoutes = []; const secondaryRoutes = []; for (let i = 0, len = routes.length; i < len; i++) { if (routes[i].hint.maybeHandler || !routes[i].hint.endWithWildcard) { primaryRoutes.push(routes[i]); } else { secondaryRoutes.push(routes[i]); } } return [ buildMatcherFromPreprocessedRoutes(primaryRoutes, hasAmbiguous), [buildMatcherFromPreprocessedRoutes(secondaryRoutes, hasAmbiguous)], hasAmbiguous, ]; }}exports.RegExpRouter = RegExpRouter;
Version Info