Skip to content

Commit

Permalink
feat: add hasRoute method
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-tymoshenko committed Oct 5, 2023
1 parent 86e7331 commit 1607fcb
Show file tree
Hide file tree
Showing 4 changed files with 400 additions and 4 deletions.
151 changes: 151 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,157 @@ Router.prototype._on = function _on (method, path, opts, handler, store) {
currentNode.addRoute(route, this.constrainer)
}

Router.prototype.hasRoute = function has (opts) {
const route = this._findRoute(opts)
return route !== null
}

Router.prototype._findRoute = function _findNode (opts) {
const method = opts.method
const path = opts.path
const constraints = opts.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 existRoute
}
}

return null
}

Router.prototype.hasConstraintStrategy = function (strategyName) {
return this.constrainer.hasConstraintStrategy(strategyName)
}
Expand Down
33 changes: 29 additions & 4 deletions lib/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ class ParentNode extends Node {
return staticChild
}

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) {
if (path.length === 0) {
return this
Expand Down Expand Up @@ -75,14 +88,23 @@ class StaticNode extends ParentNode {
this._compilePrefixMatch()
}

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
return childRegexSource === regexpSource
})

if (parametricChild) {
return parametricChild
}

return null
}

createParametricChild (regex, staticSuffix, nodePath) {
let parametricChild = this.getParametricChild(regex)
if (parametricChild) {
parametricChild.nodePaths.add(nodePath)
return parametricChild
Expand All @@ -106,12 +128,15 @@ class StaticNode extends ParentNode {
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
}

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"chalk": "^4.1.2",
"inquirer": "^8.2.4",
"pre-commit": "^1.2.2",
"rfdc": "^1.3.0",
"simple-git": "^3.7.1",
"standard": "^14.3.4",
"tap": "^16.0.1",
Expand Down
Loading

0 comments on commit 1607fcb

Please sign in to comment.