diff options
Diffstat (limited to 'cli/bench/testdata/npm/hono/dist/router/reg-exp-router/router.js')
-rw-r--r-- | cli/bench/testdata/npm/hono/dist/router/reg-exp-router/router.js | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/router.js b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/router.js new file mode 100644 index 000000000..782bd8ba4 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/router.js @@ -0,0 +1,360 @@ +"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; |