find-my-way
Advanced tools
Comparing version 7.6.2 to 7.7.0
@@ -119,2 +119,7 @@ import { IncomingMessage, ServerResponse } from 'http'; | ||
interface FindRouteResult<V extends HTTPVersion> { | ||
handler: Handler<V>; | ||
store: any; | ||
} | ||
interface Instance<V extends HTTPVersion> { | ||
@@ -163,2 +168,14 @@ on( | ||
findRoute( | ||
method: HTTPMethod, | ||
path: string, | ||
constraints?: { [key: string]: any } | ||
): FindRouteResult<V> | null; | ||
hasRoute( | ||
method: HTTPMethod, | ||
path: string, | ||
constraints?: { [key: string]: any } | ||
): boolean; | ||
reset(): void; | ||
@@ -165,0 +182,0 @@ prettyPrint(): string; |
154
index.js
@@ -295,2 +295,152 @@ 'use strict' | ||
Router.prototype.hasRoute = function hasRoute (method, path, constraints) { | ||
const route = this.findRoute(method, path, constraints) | ||
return route !== null | ||
} | ||
Router.prototype.findRoute = function findNode (method, path, constraints = {}) { | ||
if (this.trees[method] === undefined) { | ||
return null | ||
} | ||
let pattern = path | ||
let currentNode = this.trees[method] | ||
let parentNodePathIndex = currentNode.prefix.length | ||
const params = [] | ||
for (let i = 0; i <= pattern.length; i++) { | ||
if (pattern.charCodeAt(i) === 58 && pattern.charCodeAt(i + 1) === 58) { | ||
// It's a double colon | ||
i++ | ||
continue | ||
} | ||
const isParametricNode = pattern.charCodeAt(i) === 58 && pattern.charCodeAt(i + 1) !== 58 | ||
const isWildcardNode = pattern.charCodeAt(i) === 42 | ||
if (isParametricNode || isWildcardNode || (i === pattern.length && i !== parentNodePathIndex)) { | ||
let staticNodePath = pattern.slice(parentNodePathIndex, i) | ||
if (!this.caseSensitive) { | ||
staticNodePath = staticNodePath.toLowerCase() | ||
} | ||
staticNodePath = staticNodePath.split('::').join(':') | ||
staticNodePath = staticNodePath.split('%').join('%25') | ||
// add the static part of the route to the tree | ||
currentNode = currentNode.getStaticChild(staticNodePath) | ||
if (currentNode === null) { | ||
return null | ||
} | ||
} | ||
if (isParametricNode) { | ||
let isRegexNode = false | ||
const regexps = [] | ||
let lastParamStartIndex = i + 1 | ||
for (let j = lastParamStartIndex; ; j++) { | ||
const charCode = pattern.charCodeAt(j) | ||
const isRegexParam = charCode === 40 | ||
const isStaticPart = charCode === 45 || charCode === 46 | ||
const isEndOfNode = charCode === 47 || j === pattern.length | ||
if (isRegexParam || isStaticPart || isEndOfNode) { | ||
const paramName = pattern.slice(lastParamStartIndex, j) | ||
params.push(paramName) | ||
isRegexNode = isRegexNode || isRegexParam || isStaticPart | ||
if (isRegexParam) { | ||
const endOfRegexIndex = getClosingParenthensePosition(pattern, j) | ||
const regexString = pattern.slice(j, endOfRegexIndex + 1) | ||
if (!this.allowUnsafeRegex) { | ||
assert(isRegexSafe(new RegExp(regexString)), `The regex '${regexString}' is not safe!`) | ||
} | ||
regexps.push(trimRegExpStartAndEnd(regexString)) | ||
j = endOfRegexIndex + 1 | ||
} else { | ||
regexps.push('(.*?)') | ||
} | ||
const staticPartStartIndex = j | ||
for (; j < pattern.length; j++) { | ||
const charCode = pattern.charCodeAt(j) | ||
if (charCode === 47) break | ||
if (charCode === 58) { | ||
const nextCharCode = pattern.charCodeAt(j + 1) | ||
if (nextCharCode === 58) j++ | ||
else break | ||
} | ||
} | ||
let staticPart = pattern.slice(staticPartStartIndex, j) | ||
if (staticPart) { | ||
staticPart = staticPart.split('::').join(':') | ||
staticPart = staticPart.split('%').join('%25') | ||
regexps.push(escapeRegExp(staticPart)) | ||
} | ||
lastParamStartIndex = j + 1 | ||
if (isEndOfNode || pattern.charCodeAt(j) === 47 || j === pattern.length) { | ||
const nodePattern = isRegexNode ? '()' + staticPart : staticPart | ||
const nodePath = pattern.slice(i, j) | ||
pattern = pattern.slice(0, i + 1) + nodePattern + pattern.slice(j) | ||
i += nodePattern.length | ||
const regex = isRegexNode ? new RegExp('^' + regexps.join('') + '$') : null | ||
currentNode = currentNode.getParametricChild(regex, staticPart || null, nodePath) | ||
if (currentNode === null) { | ||
return null | ||
} | ||
parentNodePathIndex = i + 1 | ||
break | ||
} | ||
} | ||
} | ||
} else if (isWildcardNode) { | ||
// add the wildcard parameter | ||
params.push('*') | ||
currentNode = currentNode.getWildcardChild() | ||
if (currentNode === null) { | ||
return null | ||
} | ||
parentNodePathIndex = i + 1 | ||
if (i !== pattern.length - 1) { | ||
throw new Error('Wildcard must be the last character in the route') | ||
} | ||
} | ||
} | ||
if (!this.caseSensitive) { | ||
pattern = pattern.toLowerCase() | ||
} | ||
if (pattern === '*') { | ||
pattern = '/*' | ||
} | ||
for (const existRoute of this.routes) { | ||
const routeConstraints = existRoute.opts.constraints || {} | ||
if ( | ||
existRoute.method === method && | ||
existRoute.pattern === pattern && | ||
deepEqual(routeConstraints, constraints) | ||
) { | ||
return { | ||
handler: existRoute.handler, | ||
store: existRoute.store | ||
} | ||
} | ||
} | ||
return null | ||
} | ||
Router.prototype.hasConstraintStrategy = function (strategyName) { | ||
@@ -588,3 +738,3 @@ return this.constrainer.hasConstraintStrategy(strategyName) | ||
for (var i in httpMethods) { | ||
for (const i in httpMethods) { | ||
/* eslint no-prototype-builtins: "off" */ | ||
@@ -641,3 +791,3 @@ if (!httpMethods.hasOwnProperty(i)) continue | ||
var parentheses = 1 | ||
let parentheses = 1 | ||
@@ -644,0 +794,0 @@ while (idx < path.length) { |
@@ -45,2 +45,15 @@ 'use strict' | ||
getStaticChild (path, pathIndex = 0) { | ||
if (path.length === pathIndex) { | ||
return this | ||
} | ||
const staticChild = this.findStaticMatchingChild(path, pathIndex) | ||
if (staticChild) { | ||
return staticChild.getStaticChild(path, pathIndex + staticChild.prefix.length) | ||
} | ||
return null | ||
} | ||
createStaticChild (path) { | ||
@@ -79,6 +92,6 @@ if (path.length === 0) { | ||
createParametricChild (regex, staticSuffix, nodePath) { | ||
getParametricChild (regex) { | ||
const regexpSource = regex && regex.source | ||
let parametricChild = this.parametricChildren.find(child => { | ||
const parametricChild = this.parametricChildren.find(child => { | ||
const childRegexSource = child.regex && child.regex.source | ||
@@ -89,2 +102,11 @@ return childRegexSource === regexpSource | ||
if (parametricChild) { | ||
return parametricChild | ||
} | ||
return null | ||
} | ||
createParametricChild (regex, staticSuffix, nodePath) { | ||
let parametricChild = this.getParametricChild(regex) | ||
if (parametricChild) { | ||
parametricChild.nodePaths.add(nodePath) | ||
@@ -112,8 +134,11 @@ return parametricChild | ||
createWildcardChild () { | ||
getWildcardChild () { | ||
if (this.wildcardChild) { | ||
return this.wildcardChild | ||
} | ||
return null | ||
} | ||
this.wildcardChild = new WildcardNode() | ||
createWildcardChild () { | ||
this.wildcardChild = this.getWildcardChild() || new WildcardNode() | ||
return this.wildcardChild | ||
@@ -120,0 +145,0 @@ } |
{ | ||
"name": "find-my-way", | ||
"version": "7.6.2", | ||
"version": "7.7.0", | ||
"description": "Crazy fast http radix based router", | ||
@@ -43,2 +43,3 @@ "main": "index.js", | ||
"pre-commit": "^1.2.2", | ||
"rfdc": "^1.3.0", | ||
"simple-git": "^3.7.1", | ||
@@ -45,0 +46,0 @@ "standard": "^14.3.4", |
@@ -22,6 +22,10 @@ # find-my-way | ||
- [Supported methods](#supported-methods) | ||
- [off(method, path)](#offmethod-path) | ||
- [off(methods[], path)](#offmethods-path) | ||
- [off(methods, path, [constraints])](#offmethods-path-constraints) | ||
- [lookup(request, response, [context], [done])](#lookuprequest-response-context) | ||
- [off(methods[], path, [constraints])](#offmethods-path-constraints) | ||
- [off(methods, path)](#offmethods-path) | ||
- [off(methods, path, constraints)](#offmethods-path-constraints-1) | ||
- [off(methods[], path)](#offmethods-path-1) | ||
- [off(methods[], path, constraints)](#offmethods-path-constraints-2) | ||
- [findRoute (method, path, [constraints])](#findroute-method-path-constraints) | ||
- [hasRoute (method, path, [constraints])](#hasroute-method-path-constraints) | ||
- [lookup(request, response, [context], [done])](#lookuprequest-response-context-done) | ||
- [find(method, path, [constraints])](#findmethod-path-constraints) | ||
@@ -391,2 +395,47 @@ - [prettyPrint([{ method: 'GET', commonPrefix: false, includeMeta: true || [] }])](#prettyprint-commonprefix-false-includemeta-true---) | ||
#### findRoute (method, path, [constraints]) | ||
Finds a route by server route's path (not like `find` which finds a route by the url). Returns the route object if found, otherwise returns `null`. `findRoute` does not compare routes paths directly, instead it compares only paths patters. This means that `findRoute` will return a route even if the path passed to it does not match the route's path exactly. For example, if a route is registered with the path `/example/:param1`, `findRoute` will return the route if the path passed to it is `/example/:param2`. | ||
```js | ||
const handler = (req, res, params) => { | ||
res.end('Hello World!') | ||
} | ||
router.on('GET', '/:file(^\\S+).png', handler) | ||
router.findRoute('GET', '/:file(^\\S+).png') | ||
// => { handler: Function, store: Object } | ||
router.findRoute('GET', '/:file(^\\D+).jpg') | ||
// => null | ||
``` | ||
```js | ||
const handler = (req, res, params) => { | ||
res.end('Hello World!') | ||
} | ||
router.on('GET', '/:param1', handler) | ||
router.findRoute('GET', '/:param1') | ||
// => { handler: Function, store: Object } | ||
router.findRoute('GET', '/:param2') | ||
// => { handler: Function, store: Object } | ||
``` | ||
#### hasRoute (method, path, [constraints]) | ||
Checks if a route exists by server route's path (see `findRoute` for more details). Returns `true` if found, otherwise returns `false`. | ||
```js | ||
router.on('GET', '/:file(^\\S+).png', handler) | ||
router.hasRoute('GET', '/:file(^\\S+).png') | ||
// => true | ||
router.hasRoute('GET', '/:file(^\\D+).jpg') | ||
// => false | ||
``` | ||
```js | ||
#### lookup(request, response, [context], [done]) | ||
@@ -393,0 +442,0 @@ Start a new search, `request` and `response` are the server req/res objects.<br> |
@@ -129,3 +129,3 @@ 'use strict' | ||
var route1 = findMyWay.find('GET', '/foo3/first/second') | ||
const route1 = findMyWay.find('GET', '/foo3/first/second') | ||
t.equal(route1.params['*'], 'first/second') | ||
@@ -135,3 +135,3 @@ | ||
var route2 = findMyWay.find('GET', '/foo3/first/second') | ||
const route2 = findMyWay.find('GET', '/foo3/first/second') | ||
t.equal(route2.params['*'], '/foo3/first/second') | ||
@@ -677,3 +677,3 @@ | ||
const findMyWay = FindMyWay() | ||
var first = true | ||
let first = true | ||
@@ -751,4 +751,4 @@ findMyWay.on('GET', '/test/:id', (req, res, params) => { | ||
const handlers = {} | ||
for (var i in httpMethods) { | ||
var m = httpMethods[i] | ||
for (const i in httpMethods) { | ||
const m = httpMethods[i] | ||
handlers[m] = function myHandler () {} | ||
@@ -755,0 +755,0 @@ findMyWay.on(m, '/test', handlers[m]) |
@@ -23,3 +23,3 @@ 'use strict' | ||
t.equal(findMyWay.routes.length, quantity) | ||
findMyWay.routes.map((route, idx) => { | ||
findMyWay.routes.forEach((route, idx) => { | ||
t.match(route, { | ||
@@ -26,0 +26,0 @@ method: 'GET', |
@@ -11,3 +11,3 @@ 'use strict' | ||
for (var i in httpMethods) { | ||
for (const i in httpMethods) { | ||
const m = httpMethods[i] | ||
@@ -14,0 +14,0 @@ const methodName = m.toLowerCase() |
@@ -36,3 +36,3 @@ 'use strict' | ||
const findMyWay = FindMyWay() | ||
var bool = false | ||
let bool = false | ||
@@ -39,0 +39,0 @@ findMyWay.on('GET', '/test', (req, res, params, store) => { |
Sorry, the diff of this file is not supported yet
372740
90
9327
816
11