hono icon indicating copy to clipboard operation
hono copied to clipboard

Feature Request: Allow Path Capture Groups to trim the Value, or support lookahead/lookbehind

Open JustinGrote opened this issue 2 years ago • 1 comments

What is the feature you are proposing?

router.get('/test/:test{(\\w+)\\.json$}', c => c.text(c.req.param('test')))

Url: /test/something.json Expected: something Actual: something.json

Alternatively, allow matching outside of variable regex

router.get('/test/:test{(\\w+)}.json', c => c.text(c.req.param('test')))

Url: /test/something.json Expected: something Actual: 404 not found

JustinGrote avatar Dec 20 '23 17:12 JustinGrote

Hi @JustinGrote

If there is no performance degradation and the amount of code does not increase too much, we can implement it, but it will be difficult if there is performance degradation.

For TrieRouter, there must be a degration though my implementation might be bad:

diff --git a/src/router/trie-router/node.ts b/src/router/trie-router/node.ts
index 7b120d4..796f6c4 100644
--- a/src/router/trie-router/node.ts
+++ b/src/router/trie-router/node.ts
@@ -163,10 +163,13 @@ export class Node<T> {

           // `/js/:filename{[a-z]+.js}` => match /js/chunk/123.js
           const restPathString = parts.slice(i).join('/')
-          if (matcher instanceof RegExp && matcher.test(restPathString)) {
-            params[name] = restPathString
-            handlerSets.push(...this.gHSets(child, method, { ...params, ...node.params }))
-            continue
+          if (matcher instanceof RegExp) {
+            const matchResult = restPathString.match(matcher)
+            if (matchResult) {
+              params[name] = matchResult[1]
+              handlerSets.push(...this.gHSets(child, method, { ...params, ...node.params }))
+              continue
+            }
           }

           if (matcher === true || (matcher instanceof RegExp && matcher.test(part))) {
diff --git a/src/utils/url.ts b/src/utils/url.ts
index 29a7d58..63bcf61 100644
--- a/src/utils/url.ts
+++ b/src/utils/url.ts
@@ -56,7 +56,11 @@ export const getPattern = (label: string): Pattern | null => {
   if (match) {
     if (!patternCache[label]) {
       if (match[2]) {
-        patternCache[label] = [label, match[1], new RegExp('^' + match[2] + '$')]
+        patternCache[label] = [
+          label,
+          match[1],
+          /\(.+\)/.test(match[2]) ? new RegExp(match[2]) : new RegExp('^(' + match[2] + ')$'),
+        ]
       } else {
         patternCache[label] = [label, match[1], true]
       }

@usualoma

Do you have any thoughts?

yusukebe avatar Dec 22 '23 03:12 yusukebe