route-trie
Advanced tools
Comparing version 2.0.2 to 2.1.0
151
index.js
@@ -7,5 +7,5 @@ // **Github:** https://github.com/zensh/route-trie | ||
const wordReg = /^\w+$/ | ||
const doubleColonReg = /::\w*$/ | ||
const suffixReg = /\+[A-Za-z0-9!$%&'*+,-.:;=@_~]*$/ | ||
const doubleColonReg = /::[A-Za-z0-9!$%&'*+,-.:;=@_~]*$/ | ||
const trimSlashReg = /^\// | ||
const multiSlashReg = /\/{2,}/ | ||
const fixMultiSlashReg = /\/{2,}/g | ||
@@ -39,3 +39,3 @@ | ||
if (typeof pattern !== 'string') throw new TypeError('Pattern must be string.') | ||
if (multiSlashReg.test(pattern)) throw new Error('Multi-slash existhis.') | ||
if (pattern.includes('//')) throw new Error('Multi-slash existhis.') | ||
let _pattern = pattern.replace(trimSlashReg, '') | ||
@@ -62,3 +62,3 @@ let node = defineNode(this.root, _pattern.split('/'), this.ignoreCase) | ||
let end = path.length | ||
let res = new Matched() | ||
let matched = new Matched() | ||
let parent = this.root | ||
@@ -76,9 +76,9 @@ for (let i = 1; i <= end; i++) { | ||
if (this.tsr && frag === '' && i === end && parent.endpoint) { | ||
res.tsr = path.slice(0, end - 1) | ||
matched.tsr = path.slice(0, end - 1) | ||
if (this.fpr && fixedLen > 0) { | ||
res.fpr = res.tsr | ||
res.tsr = '' | ||
matched.fpr = matched.tsr | ||
matched.tsr = '' | ||
} | ||
} | ||
return res | ||
return matched | ||
} | ||
@@ -89,6 +89,9 @@ | ||
if (parent.wildcard) { | ||
res.params[parent.name] = path.slice(start, end) | ||
matched.params[parent.name] = path.slice(start, end) | ||
break | ||
} else { | ||
res.params[parent.name] = frag | ||
if (parent.suffix !== '') { | ||
frag = frag.slice(0, frag.length - parent.suffix.length) | ||
} | ||
matched.params[parent.name] = frag | ||
} | ||
@@ -100,16 +103,16 @@ } | ||
if (parent.endpoint) { | ||
res.node = parent | ||
matched.node = parent | ||
if (this.fpr && fixedLen > 0) { | ||
res.fpr = path | ||
res.node = null | ||
matched.fpr = path | ||
matched.node = null | ||
} | ||
} else if (this.tsr && parent.children[''] != null) { | ||
// TrailingSlashRedirect: /acb/efg -> /acb/efg/ | ||
res.tsr = path + '/' | ||
matched.tsr = path + '/' | ||
if (this.fpr && fixedLen > 0) { | ||
res.fpr = res.tsr | ||
res.tsr = '' | ||
matched.fpr = matched.tsr | ||
matched.tsr = '' | ||
} | ||
} | ||
return res | ||
return matched | ||
} | ||
@@ -137,6 +140,8 @@ } | ||
this.pattern = '' | ||
this.frag = '' | ||
this.suffix = '' | ||
this.regex = null | ||
this.endpoint = false | ||
this.wildcard = false | ||
this.varyChild = null | ||
this.varyChildren = [] | ||
this.parent = parent | ||
@@ -169,2 +174,10 @@ this.children = Object.create(null) | ||
} | ||
getFrags () { | ||
let frags = this.frag | ||
if (this.parent != null) { | ||
frags = this.parent.getFrags() + '/' + frags | ||
} | ||
return frags | ||
} | ||
} | ||
@@ -187,10 +200,19 @@ | ||
function matchNode (parent, frag) { | ||
let child = parent.children[frag] | ||
if (child == null) { | ||
child = parent.varyChild | ||
if (child != null && child.regex != null && !child.regex.test(frag)) { | ||
child = null | ||
if (parent.children[frag] != null) { | ||
return parent.children[frag] | ||
} | ||
for (let child of parent.varyChildren) { | ||
let _frag = frag | ||
if (child.suffix !== '') { | ||
if (frag === child.suffix || !frag.endsWith(child.suffix)) { | ||
continue | ||
} | ||
_frag = frag.slice(0, frag.length - child.suffix.length) | ||
} | ||
if (child.regex != null && !child.regex.test(_frag)) { | ||
continue | ||
} | ||
return child | ||
} | ||
return child | ||
return null | ||
} | ||
@@ -219,39 +241,68 @@ | ||
} else if (frag[0] === ':') { | ||
let regex | ||
let name = frag.slice(1) | ||
let trailing = name[name.length - 1] | ||
if (trailing === ')') { | ||
let index = name.indexOf('(') | ||
if (index > 0) { | ||
regex = name.slice(index + 1, name.length - 1) | ||
if (regex.length > 0) { | ||
name = name.slice(0, index) | ||
node.regex = new RegExp(regex) | ||
} else { | ||
throw new Error(`Invalid pattern: "${frag}"`) | ||
switch (name[name.length - 1]) { | ||
case '*': | ||
name = name.slice(0, name.length - 1) | ||
node.wildcard = true | ||
break | ||
default: | ||
let i = name.search(suffixReg) | ||
if (i >= 0) { | ||
node.suffix = name.slice(i + 1) | ||
name = name.slice(0, i) | ||
if (node.suffix === '') { | ||
throw new Error(`invalid pattern: "${node.getFrags()}"`) | ||
} | ||
} | ||
} | ||
} else if (trailing === '*') { | ||
name = name.slice(0, name.length - 1) | ||
node.wildcard = true | ||
if (name[name.length - 1] === ')') { | ||
let i = name.indexOf('(') | ||
if (i > 0) { | ||
let regex = name.slice(i + 1, name.length - 1) | ||
if (regex.length > 0) { | ||
name = name.slice(0, i) | ||
node.regex = new RegExp(regex) | ||
} else { | ||
throw new Error(`Invalid pattern: "${node.getFrags()}"`) | ||
} | ||
} | ||
} | ||
} | ||
// name must be word characters `[0-9A-Za-z_]` | ||
if (!wordReg.test(name)) { | ||
throw new Error(`Invalid pattern: "${frag}"`) | ||
throw new Error(`Invalid pattern: "${node.getFrags()}"`) | ||
} | ||
node.name = name | ||
let child = parent.varyChild | ||
if (child != null) { | ||
if (child.name !== name || child.wildcard !== node.wildcard) { | ||
throw new Error(`Invalid pattern: "${frag}"`) | ||
for (let child of parent.varyChildren) { | ||
if (child.name !== node.name) { | ||
throw new Error(`invalid pattern: "${node.getFrags()}"`) | ||
} | ||
if (child.regex != null && child.regex.toString() !== node.regex.toString()) { | ||
throw new Error(`Invalid pattern: "${frag}"`) | ||
if (child.wildcard) { | ||
if (!node.wildcard) { | ||
throw new Error(`can't define "${node.getFrags()}" after "${child.getFrags()}"`) | ||
} | ||
return child | ||
} | ||
return child | ||
if (child.suffix === '' && child.regex == null && (node.suffix !== '' || node.regex != null)) { | ||
throw new Error(`can't define "${node.getFrags()}" after "${child.getFrags()}"`) | ||
} | ||
if (child.suffix === node.suffix) { | ||
if (child.regex == null && node.regex == null) { | ||
return child | ||
} | ||
if (child.regex != null && node.regex != null && child.regex.toString() === node.regex.toString()) { | ||
return child | ||
} | ||
if (child.regex == null && node.regex != null) { | ||
throw new Error(`invalid pattern: "${node.getFrags()}"`) | ||
} | ||
} | ||
} | ||
parent.varyChild = node | ||
parent.varyChildren.push(node) | ||
} else if (frag[0] === '*' || frag[0] === '(' || frag[0] === ')') { | ||
throw new Error(`Invalid pattern: "${frag}"`) | ||
throw new Error(`Invalid pattern: "${node.getFrags()}"`) | ||
} else { | ||
@@ -264,5 +315,5 @@ parent.children[_frag] = node | ||
Trie.NAME = 'Trie' | ||
Trie.VERSION = 'v2.0.2' | ||
Trie.VERSION = 'v2.1.0' | ||
Trie.Node = Node | ||
Trie.Matched = Matched | ||
module.exports = Trie.Trie = Trie |
@@ -7,3 +7,3 @@ { | ||
], | ||
"version": "2.0.2", | ||
"version": "2.1.0", | ||
"license": "MIT", | ||
@@ -17,3 +17,3 @@ "main": "index.js", | ||
"engines": { | ||
"node": ">= 4" | ||
"node": ">= 4.0.0" | ||
}, | ||
@@ -32,4 +32,4 @@ "homepage": "https://github.com/zensh/route-trie", | ||
"devDependencies": { | ||
"standard": "^8.6.0", | ||
"tman": "^1.6.4", | ||
"standard": "^9.0.2", | ||
"tman": "^1.6.6", | ||
"istanbul": "^0.4.5" | ||
@@ -36,0 +36,0 @@ }, |
@@ -1,3 +0,3 @@ | ||
route-trie | ||
==== | ||
# route-trie | ||
A minimal and powerful trie based url path router for Node.js. | ||
@@ -30,9 +30,11 @@ | ||
## Features: | ||
## Features | ||
1. Support named parameter | ||
1. Support regexp | ||
2. Fixed path automatic redirection | ||
3. Trailing slash automatic redirection | ||
4. Support `405 Method Not Allowed` | ||
5. Best Performance | ||
1. Support suffix matching | ||
1. Fixed path automatic redirection | ||
1. Trailing slash automatic redirection | ||
1. Support `405 Method Not Allowed` | ||
1. Best Performance | ||
@@ -74,3 +76,3 @@ ## Installation | ||
The defined pattern can contain three types of parameters: | ||
The defined pattern can contain six types of parameters: | ||
@@ -80,4 +82,6 @@ | Syntax | Description | | ||
| `:name` | named parameter | | ||
| `:name(regexp)` | named with regexp parameter | | ||
| `:name+suffix` | named parameter with suffix matching | | ||
| `:name(regexp)+suffix` | named with regexp parameter and suffix matching | | ||
| `:name*` | named with catch-all parameter | | ||
| `:name(regexp)` | named with regexp parameter | | ||
| `::name` | not named parameter, it is literal `:name` | | ||
@@ -94,2 +98,30 @@ | ||
Named with regexp parameters match anything using regexp until the next '/' or the path end: | ||
Defined: `/api/:type/:ID(^\d+$)` | ||
``` | ||
/api/user/123 matched: type="user", ID="123" | ||
/api/user no match | ||
/api/user/abc no match | ||
/api/user/123/comments no match | ||
``` | ||
Named parameters with suffix, such as [Google API Design](https://cloud.google.com/apis/design/custom_methods): | ||
Defined: `/api/:resource/:ID+:undelete` | ||
``` | ||
/api/file/123 no match | ||
/api/file/123:undelete matched: resource="file", ID="123" | ||
/api/file/123:undelete/comments no match | ||
``` | ||
Named with regexp parameters and suffix: | ||
Defined: `/api/:resource/:ID(^\d+$)+:cancel` | ||
``` | ||
/api/task/123 no match | ||
/api/task/123:cancel matched: resource="task", ID="123" | ||
/api/task/abc:cancel no match | ||
``` | ||
Named with catch-all parameters match anything until the path end, including the directory index (the '/' before the catch-all). Since they match anything until the end, catch-all parameters must always be the final path element. | ||
@@ -104,18 +136,8 @@ | ||
Named with regexp parameters match anything using regexp until the next '/' or the path end: | ||
Defined: `/api/:type/:ID(^\\d+$)` | ||
The value of parameters is saved on the `Matched.Params`. Retrieve the value of a parameter by name: | ||
``` | ||
/api/user/123 matched: type="user", ID="123" | ||
/api/user no match | ||
/api/user/abc no match | ||
/api/user/123/comments no match | ||
type := matched.Params("type") | ||
id := matched.Params("ID") | ||
``` | ||
The value of parameters is saved on the `matched.params`. Retrieve the value of a parameter by name: | ||
``` | ||
let type = matched.params.type | ||
let id = matched.Params.ID | ||
``` | ||
**Notice for regex pattern:** | ||
@@ -122,0 +144,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
26120
510
244