Repository: honojs/honox (ts)

Issue:
Nested _middleware.ts is not being applied to nested folders
### What version of HonoX are you using?

0.1.23

### What steps can reproduce the bug?

Have a folder structure as below
/user
  _middleware.ts
    /todos
 
the _middleware in  inside `/user` folder, is not being applied to route `/user/todos` . 

### What is the expected behavior?

`_middleware.ts` should apply to all nested child routes

### What do you see instead?

`_middleware` is being applied to current folder level route only.
When I move `_middleware.ts` to inside `/user/todos` it get apply to routes `/user/todos` correctly.

### Additional information

_No response_
_middleware.ts is not loaded for deeply nested param routes
### What version of HonoX are you using?

0.1.25

### What steps can reproduce the bug?

1. Add `_middleware.ts` with `console.log()` to `app/routes/organizations/[organizationId]/products/[productId]`
2. Add `index.tsx` to `app/routes/organizations/[organizationId]/products/[productId]`
3. Navigate to `/organizations/:organizationId/products/:productId` and see that the log is not shown

### What is the expected behavior?

Deeply nested param routes load the `_middleware.ts` file.

### What do you see instead?

`_middleware.ts` file is not loaded for deeply nested param routes.

### Additional information

This bug is related to https://github.com/honojs/honox/issues/184

Using `replaceAll` instead of `replace` fixes the issue.

https://github.com/honojs/honox/blob/main/src/server/server.ts#L138

Pull request description:
fix: support `_middleware.ts` for deeply nested param routes
Fixes #228
Maybe fixes #227

Patch:
diff --git a/mocks/app/routes/about/[name]/_middleware.ts b/mocks/app/routes/about/[name]/_middleware.ts
index fea11d5..6cee1ae 100644
--- a/mocks/app/routes/about/[name]/_middleware.ts
+++ b/mocks/app/routes/about/[name]/_middleware.ts
@@ -3,7 +3,6 @@ import { createRoute } from '../../../../../src/factory'
 
 const addHeader = createMiddleware(async (c, next) => {
   await next()
-  console.log('fooo')
   c.res.headers.append('x-message', 'from middleware')
 })
 
diff --git a/mocks/app/routes/about/[name]/_renderer.tsx b/mocks/app/routes/about/[name]/_renderer.tsx
index 50a9e22..2b1f667 100644
--- a/mocks/app/routes/about/[name]/_renderer.tsx
+++ b/mocks/app/routes/about/[name]/_renderer.tsx
@@ -8,7 +8,7 @@ export default jsxRenderer(({ children, title }) => {
       </head>
       <body>
         <h1>About</h1>
-        <address>{children}</address>
+        <div>{children}</div>
       </body>
     </html>
   )
diff --git a/mocks/app/routes/about/[name]/hobbies/[hobby_name]/_middleware.ts b/mocks/app/routes/about/[name]/hobbies/[hobby_name]/_middleware.ts
new file mode 100644
index 0000000..319036d
--- /dev/null
+++ b/mocks/app/routes/about/[name]/hobbies/[hobby_name]/_middleware.ts
@@ -0,0 +1,9 @@
+import { createMiddleware } from 'hono/factory'
+import { createRoute } from '../../../../../../../src/factory'
+
+const addHeader = createMiddleware(async (c, next) => {
+  await next()
+  c.res.headers.append('x-message-nested', 'from nested middleware')
+})
+
+export default createRoute(addHeader)
diff --git a/mocks/app/routes/about/[name]/hobbies/[hobby_name]/index.tsx b/mocks/app/routes/about/[name]/hobbies/[hobby_name]/index.tsx
new file mode 100644
index 0000000..3f445f5
--- /dev/null
+++ b/mocks/app/routes/about/[name]/hobbies/[hobby_name]/index.tsx
@@ -0,0 +1,10 @@
+import { createRoute } from '../../../../../../../src/factory'
+
+export default createRoute((c) => {
+  const { name, hobby_name } = c.req.param()
+  return c.render(
+    <p>
+      {name}'s hobby is {hobby_name}
+    </p>
+  )
+})
diff --git a/src/server/server.ts b/src/server/server.ts
index 7121836..465592e 100644
--- a/src/server/server.ts
+++ b/src/server/server.ts
@@ -135,7 +135,7 @@ export const createApp = <E extends Env>(options: BaseServerOptions<E>): Hono<E>
       })
 
       const middlewareFile = Object.keys(MIDDLEWARE_FILE).find((x) => {
-        const replacedDir = dir.replace('[', '\\[').replace(']', '\\]')
+        const replacedDir = dir.replaceAll('[', '\\[').replace(']', '\\]')
         return new RegExp(replacedDir + '/_middleware.tsx?').test(x)
       })
 


Test patch:
diff --git a/test-integration/apps.test.ts b/test-integration/apps.test.ts
index 6e117f2..1aaaed0 100644
--- a/test-integration/apps.test.ts
+++ b/test-integration/apps.test.ts
@@ -97,7 +97,7 @@ describe('Basic', () => {
         method: 'GET',
       },
     ]
-    expect(app.routes).toHaveLength(routes.length * 2)
+    expect(app.routes).toHaveLength(46)
     expect(app.routes).toEqual(
       expect.arrayContaining(
         routes.map(({ path, method }) => {
@@ -224,7 +224,18 @@ describe('With preserved', () => {
     expect(res.headers.get('x-message')).toBe('from middleware')
     // hono/jsx escape a single quote to &#39;
     expect(await res.text()).toBe(
-      '<!DOCTYPE html><html><head><title>me&#39;s address</title></head><body><h1>About</h1><address><b>me&#39;s address</b></address></body></html>'
+      '<!DOCTYPE html><html><head><title>me&#39;s address</title></head><body><h1>About</h1><div><b>me&#39;s address</b></div></body></html>'
+    )
+  })
+
+  it('Should return 200 response - /about/me/hobbies/baseball', async () => {
+    const res = await app.request('/about/me/hobbies/baseball')
+    expect(res.status).toBe(200)
+    expect(res.headers.get('x-message')).toBe('from middleware')
+    expect(res.headers.get('x-message-nested')).toBe('from nested middleware')
+    // hono/jsx escape a single quote to &#39;
+    expect(await res.text()).toBe(
+      '<!DOCTYPE html><html><head><title></title></head><body><h1>About</h1><div><p>me&#39;s hobby is baseball</p></div></body></html>'
     )
   })