0
0
mirror of https://github.com/honojs/hono.git synced 2024-12-01 11:51:01 +01:00

feat(reg-exp-router): regexp support path including slashes. (#789)

* feat(reg-exp-router): `regexp` support path including slashes.

* chore: denoify
This commit is contained in:
Taku Amano 2023-01-08 17:22:58 +09:00 committed by Yusuke Wada
parent b723faa81f
commit 9bc52a5ddd
3 changed files with 100 additions and 6 deletions

View File

@ -11,14 +11,37 @@ export class Trie {
insert(path: string, index: number): ParamMap {
const paramMap: ParamMap = []
const groups: [string, string][] = [] // [mark, original string]
for (let i = 0;;) {
let replaced = false
path = path.replace(/\{[^}]+\}/g, (m) => {
const mark = `@\\${i}`
groups[i] = [mark, m]
i++
replaced = true
return mark
})
if (!replaced) {
break
}
}
/**
* - pattern (:label, :label{0-9]+}, ...)
* - /* wildcard
* - character
*/
const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || []
for (let i = groups.length - 1; i >= 0; i--) {
const [mark] = groups[i]
for (let j = tokens.length - 1; j >= 0; j--) {
if (tokens[j].indexOf(mark) !== -1) {
tokens[j] = tokens[j].replace(mark, groups[i][1])
break
}
}
}
this.root.insert(tokens, index, paramMap, this.context)
return paramMap

View File

@ -330,6 +330,54 @@ describe('long prefix, then star', () => {
expect(res?.handlers).toEqual(['long-prefix', 'long', 'star1', 'star2'])
})
})
describe('Including slashes', () => {
const router = new RegExpRouter<string>()
router.add('GET', '/js/:filename{[a-z0-9/]+.js}', 'any file')
// XXX This route can not be added with `:label` to RegExpRouter. This is ambiguous.
// router.add('GET', '/js/main.js', 'main.js')
// it('get /js/main.js', () => {
// const res = router.match('GET', '/js/main.js')
// expect(res).not.toBeNull()
// expect(res?.handlers).toEqual(['any file', 'main.js'])
// expect(res?.params).toEqual({ filename: 'main.js' })
// })
it('get /js/chunk/123.js', () => {
const res = router.match('GET', '/js/chunk/123.js')
expect(res).not.toBeNull()
expect(res?.handlers).toEqual(['any file'])
expect(res?.params).toEqual({ filename: 'chunk/123.js' })
})
it('get /js/chunk/nest/123.js', () => {
const res = router.match('GET', '/js/chunk/nest/123.js')
expect(res).not.toBeNull()
expect(res?.handlers).toEqual(['any file'])
expect(res?.params).toEqual({ filename: 'chunk/nest/123.js' })
})
})
describe('REST API', () => {
const router = new RegExpRouter<string>()
router.add('GET', '/users/:username{[a-z]+}', 'profile')
router.add('GET', '/users/:username{[a-z]+}/posts', 'posts')
it('get /users/hono', () => {
const res = router.match('GET', '/users/hono')
expect(res).not.toBeNull()
expect(res?.handlers).toEqual(['profile'])
})
it('get /users/hono/posts', () => {
const res = router.match('GET', '/users/hono/posts')
expect(res).not.toBeNull()
expect(res?.handlers).toEqual(['posts'])
})
})
})
describe('static routes of ALL and GET', () => {

View File

@ -11,14 +11,37 @@ export class Trie {
insert(path: string, index: number): ParamMap {
const paramMap: ParamMap = []
const groups: [string, string][] = [] // [mark, original string]
for (let i = 0;;) {
let replaced = false
path = path.replace(/\{[^}]+\}/g, (m) => {
const mark = `@\\${i}`
groups[i] = [mark, m]
i++
replaced = true
return mark
})
if (!replaced) {
break
}
}
/**
* - pattern (:label, :label{0-9]+}, ...)
* - /* wildcard
* - character
*/
const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || []
for (let i = groups.length - 1; i >= 0; i--) {
const [mark] = groups[i]
for (let j = tokens.length - 1; j >= 0; j--) {
if (tokens[j].indexOf(mark) !== -1) {
tokens[j] = tokens[j].replace(mark, groups[i][1])
break
}
}
}
this.root.insert(tokens, index, paramMap, this.context)
return paramMap