find-my-way
Advanced tools
Comparing version 7.4.0 to 7.5.0
@@ -64,3 +64,3 @@ 'use strict' | ||
createParametricChild (regex) { | ||
createParametricChild (regex, staticSuffix) { | ||
const regexpSource = regex && regex.source | ||
@@ -77,8 +77,17 @@ | ||
parametricChild = new ParametricNode(regex) | ||
if (regex) { | ||
this.parametricChildren.unshift(parametricChild) | ||
} else { | ||
this.parametricChildren.push(parametricChild) | ||
} | ||
parametricChild = new ParametricNode(regex, staticSuffix) | ||
this.parametricChildren.push(parametricChild) | ||
this.parametricChildren.sort((child1, child2) => { | ||
if (!child1.isRegex) return 1 | ||
if (!child2.isRegex) return -1 | ||
if (child1.staticSuffix === null) return 1 | ||
if (child2.staticSuffix === null) return -1 | ||
if (child2.staticSuffix.endsWith(child1.staticSuffix)) return 1 | ||
if (child1.staticSuffix.endsWith(child2.staticSuffix)) return -1 | ||
return 0 | ||
}) | ||
return parametricChild | ||
@@ -158,6 +167,7 @@ } | ||
class ParametricNode extends ParentNode { | ||
constructor (regex) { | ||
constructor (regex, staticSuffix) { | ||
super() | ||
this.isRegex = !!regex | ||
this.regex = regex || null | ||
this.isRegex = !!regex | ||
this.staticSuffix = staticSuffix || null | ||
this.kind = NODE_TYPES.PARAMETRIC | ||
@@ -164,0 +174,0 @@ } |
89
index.js
@@ -92,3 +92,3 @@ 'use strict' | ||
this._routesPatterns = [] | ||
this._routesPatterns = {} | ||
} | ||
@@ -160,2 +160,3 @@ | ||
this.trees[method] = new StaticNode('/') | ||
this._routesPatterns[method] = [] | ||
} | ||
@@ -198,3 +199,2 @@ | ||
let staticEndingLength = 0 | ||
let lastParamStartIndex = i + 1 | ||
@@ -204,9 +204,13 @@ for (let j = lastParamStartIndex; ; j++) { | ||
if (charCode === 40 || charCode === 45 || charCode === 46) { | ||
isRegexNode = true | ||
const isRegexParam = charCode === 40 | ||
const isStaticPart = charCode === 45 || charCode === 46 | ||
const isEndOfNode = charCode === 47 || j === path.length | ||
if (isRegexParam || isStaticPart || isEndOfNode) { | ||
const paramName = path.slice(lastParamStartIndex, j) | ||
params.push(paramName) | ||
if (charCode === 40) { | ||
isRegexNode = isRegexNode || isRegexParam || isStaticPart | ||
if (isRegexParam) { | ||
const endOfRegexIndex = getClosingParenthensePosition(path, j) | ||
@@ -226,14 +230,14 @@ const regexString = path.slice(j, endOfRegexIndex + 1) | ||
let lastParamEndIndex = j | ||
for (; lastParamEndIndex < path.length; lastParamEndIndex++) { | ||
const charCode = path.charCodeAt(lastParamEndIndex) | ||
const nextCharCode = path.charCodeAt(lastParamEndIndex + 1) | ||
if (charCode === 58 && nextCharCode === 58) { | ||
lastParamEndIndex++ | ||
continue | ||
const staticPartStartIndex = j | ||
for (; j < path.length; j++) { | ||
const charCode = path.charCodeAt(j) | ||
if (charCode === 47) break | ||
if (charCode === 58) { | ||
const nextCharCode = path.charCodeAt(j + 1) | ||
if (nextCharCode === 58) j++ | ||
else break | ||
} | ||
if (charCode === 58 || charCode === 47) break | ||
} | ||
let staticPart = path.slice(j, lastParamEndIndex) | ||
let staticPart = path.slice(staticPartStartIndex, j) | ||
if (staticPart) { | ||
@@ -245,31 +249,17 @@ staticPart = staticPart.split('::').join(':') | ||
lastParamStartIndex = lastParamEndIndex + 1 | ||
j = lastParamEndIndex | ||
lastParamStartIndex = j + 1 | ||
if (path.charCodeAt(j) === 47 || j === path.length) { | ||
staticEndingLength = staticPart.length | ||
} | ||
} else if (charCode === 47 || j === path.length) { | ||
const paramName = path.slice(lastParamStartIndex, j) | ||
params.push(paramName) | ||
if (isEndOfNode || path.charCodeAt(j) === 47 || j === path.length) { | ||
const nodePattern = isRegexNode ? '()' + staticPart : staticPart | ||
if (regexps.length !== 0) { | ||
regexps.push('(.*?)') | ||
path = path.slice(0, i + 1) + nodePattern + path.slice(j) | ||
i += nodePattern.length | ||
const regex = isRegexNode ? new RegExp('^' + regexps.join('') + '$') : null | ||
currentNode = currentNode.createParametricChild(regex, staticPart || null) | ||
parentNodePathIndex = i + 1 | ||
break | ||
} | ||
} | ||
if (path.charCodeAt(j) === 47 || j === path.length) { | ||
path = path.slice(0, i + 1) + path.slice(j - staticEndingLength) | ||
i += staticEndingLength | ||
break | ||
} | ||
} | ||
let regex = null | ||
if (isRegexNode) { | ||
regex = new RegExp('^' + regexps.join('') + '$') | ||
} | ||
currentNode = currentNode.createParametricChild(regex) | ||
parentNodePathIndex = i + 1 | ||
} else if (isWildcardNode) { | ||
@@ -291,21 +281,12 @@ // add the wildcard parameter | ||
const isRootWildcard = path === '*' || path === '/*' | ||
for (const existRoute of this._routesPatterns) { | ||
let samePath = false | ||
if (path === '*') { | ||
path = '/*' | ||
} | ||
if (existRoute.path === path) { | ||
samePath = true | ||
} else if (isRootWildcard && (existRoute.path === '/*' || existRoute.path === '*')) { | ||
samePath = true | ||
} | ||
if ( | ||
samePath && | ||
existRoute.method === method && | ||
deepEqual(existRoute.constraints, constraints) | ||
) { | ||
for (const existRoute of this._routesPatterns[method]) { | ||
if (existRoute.path === path && deepEqual(existRoute.constraints, constraints)) { | ||
throw new Error(`Method '${method}' already declared for route '${path}' with constraints '${JSON.stringify(constraints)}'`) | ||
} | ||
} | ||
this._routesPatterns.push({ method, path, constraints }) | ||
this._routesPatterns[method].push({ path, params, constraints }) | ||
@@ -327,3 +308,3 @@ currentNode.handlerStorage.addHandler(handler, params, store, this.constrainer, constraints) | ||
this.routes = [] | ||
this._routesPatterns = [] | ||
this._routesPatterns = {} | ||
} | ||
@@ -330,0 +311,0 @@ |
{ | ||
"name": "find-my-way", | ||
"version": "7.4.0", | ||
"version": "7.5.0", | ||
"description": "Crazy fast http radix based router", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -286,21 +286,30 @@ # find-my-way | ||
The routing algorithm matches one chunk at a time (where the chunk is a string between two slashes), | ||
The routing algorithm matches one node at a time (where the node is a string between two slashes), | ||
this means that it cannot know if a route is static or dynamic until it finishes to match the URL. | ||
The chunks are matched in the following order: | ||
The nodes are matched in the following order: | ||
1. static | ||
1. parametric | ||
1. wildcards | ||
1. parametric(regex) | ||
1. multi parametric(regex) | ||
2. parametric node with static ending | ||
3. parametric(regex)/multi-parametric | ||
4. parametric | ||
5. wildcard | ||
So if you declare the following routes | ||
- `/:userId/foo/bar` | ||
- `/33/:a(^.*$)/:b` | ||
- `/foo/filename.png` - static route | ||
- `/foo/:filename.png` - route with param `filename` and static ending `.png` | ||
- `/foo/:filename.:ext` - route with two params `filename` and `ext` | ||
- `/foo/:filename` - route with one param `filename` | ||
- `/*` - wildcard route | ||
and the URL of the incoming request is /33/foo/bar, | ||
the second route will be matched because the first chunk (33) matches the static chunk. | ||
If the URL would have been /32/foo/bar, the first route would have been matched. | ||
You will have next matching rules: | ||
- the static node would have the highest priority. It will be matched only if incoming URL equals `/foo/filename.png` | ||
- the parametric node with a static ending would have the higher priority than other parametric nodes without it. This node would match any filenames with `.png` extension. If one node static ending ends with another node static ending, the node with a longer static ending would have higher priority. | ||
- `/foo/:filename.png.png` - higher priority, more specific route | ||
- `/foo/:filename.png` - lower priority | ||
- the multi-parametric node (or any regexp node) without static ending would have lower priority than parametric node with static ending and higher priority than generic parametric node. You can declare only one node like that for the same route (see [caveats](#caveats)). It would match any filenames with any extensions. | ||
- the parametric node has lower priority than any other parametric node. It would match any filenames, even if they don't have an extension. | ||
- the wildcard node has the lowest priority of all nodes. | ||
Once a url has been matched, `find-my-way` will figure out which handler registered for that path matches the request if there are any constraints. | ||
@@ -307,0 +316,0 @@ `find-my-way` will check the most constrained handlers first, which means the handlers with the most keys in the `constraints` object. |
@@ -257,3 +257,3 @@ 'use strict' | ||
} catch (e) { | ||
t.equal(e.message, 'Method \'GET\' already declared for route \'*\' with constraints \'{}\'') | ||
t.equal(e.message, 'Method \'GET\' already declared for route \'/*\' with constraints \'{}\'') | ||
} | ||
@@ -260,0 +260,0 @@ }) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
331245
86
8225
740