Repository: honojs/hono (TypeScript)

Issue:
optional route parameter does not work
The route `/:optional?` is not used by Hono.

When i go to [`/test`](https://honojs.deno.dev/test), the optional route is correctly used by Hono.

However, when i go to [`/`](https://honojs.deno.dev/), the optional route is skipped.

## Reproduction
https://dash.deno.com/playground/honojs

## Raw Code
```ts
import { serve } from 'https://deno.land/std@0.179.0/http/server.ts';
import { logger } from 'https://deno.land/x/hono@v3.0.4/middleware.ts';
import { Hono } from 'https://deno.land/x/hono@v3.0.4/mod.ts';

// json helper
const formatJSON = (input: string) => JSON.stringify(input, null, 4);

// create a hono instance
const app = new Hono();

// middleware
app.use(logger());

// optional route
app.get('/:optional?', c => c.text(formatJSON({ route: '/:optional?', params: c.req.param() })));

// fallback route
app.get('*', c => c.text(formatJSON({ route: '*', params: c.req.param() })));

// list routes
app.showRoutes();

// start server
await serve(app.fetch);
```



Discussion:
Hi @navtoj !

I've released v3.0.5, including the change to resolve this issue. Try it.
> I've released v3.0.5, including the change to resolve this issue. Try it.

Wow, that was fast. Thanks for the patch, the optional parameter works now.



Patch:
diff --git a/deno_dist/utils/url.ts b/deno_dist/utils/url.ts
index b63f27b88..6d8959ef2 100644
--- a/deno_dist/utils/url.ts
+++ b/deno_dist/utils/url.ts
@@ -125,12 +125,12 @@ export const checkOptionalParameter = (path: string): string[] | null => {
    [`/api/animals`, `/api/animals/:type`]
    in other cases it will return null
    */
-  const match = path.match(/(^.+)(\/\:[^\/]+)\?$/)
+  const match = path.match(/^(.+|)(\/\:[^\/]+)\?$/)
   if (!match) return null
 
   const base = match[1]
   const optional = base + match[2]
-  return [base, optional]
+  return [base === '' ? '/' : base.replace(/\/$/, ''), optional]
 }
 
 const removeFragment = (queryString: string): string => {
diff --git a/src/utils/url.ts b/src/utils/url.ts
index b63f27b88..6d8959ef2 100644
--- a/src/utils/url.ts
+++ b/src/utils/url.ts
@@ -125,12 +125,12 @@ export const checkOptionalParameter = (path: string): string[] | null => {
    [`/api/animals`, `/api/animals/:type`]
    in other cases it will return null
    */
-  const match = path.match(/(^.+)(\/\:[^\/]+)\?$/)
+  const match = path.match(/^(.+|)(\/\:[^\/]+)\?$/)
   if (!match) return null
 
   const base = match[1]
   const optional = base + match[2]
-  return [base, optional]
+  return [base === '' ? '/' : base.replace(/\/$/, ''), optional]
 }
 
 const removeFragment = (queryString: string): string => {


Test patch:
diff --git a/src/utils/url.test.ts b/src/utils/url.test.ts
index ff5f4d7f2..c98161e6f 100644
--- a/src/utils/url.test.ts
+++ b/src/utils/url.test.ts
@@ -148,6 +148,7 @@ describe('url', () => {
       expect(checkOptionalParameter('/api/animals')).toBeNull()
       expect(checkOptionalParameter('/api/:animals?/type')).toBeNull()
       expect(checkOptionalParameter('/api/animals/:type?/')).toBeNull()
+      expect(checkOptionalParameter('/:optional?')).toEqual(['/', '/:optional'])
     })
   })