Repository: honojs/honox
Pull request #145 (2024-04-13 06:39:15)

fix: correct routing priority
Correct setting top level-middleware and not found handlers.


Patch:
diff --git a/mocks/app-nested-middleware/routes/_middleware.ts b/mocks/app-nested-middleware/routes/_middleware.ts
new file mode 100644
index 0000000..332f250
--- /dev/null
+++ b/mocks/app-nested-middleware/routes/_middleware.ts
@@ -0,0 +1,4 @@
+import { createRoute } from '../../../src/factory'
+import { headerMiddleware } from '../middleware/appendHeader'
+
+export default createRoute(headerMiddleware('top'))
diff --git a/mocks/app-nested-middleware/routes/index.tsx b/mocks/app-nested-middleware/routes/index.tsx
new file mode 100644
index 0000000..e37d84e
--- /dev/null
+++ b/mocks/app-nested-middleware/routes/index.tsx
@@ -0,0 +1,3 @@
+export default function Top() {
+  return <h1>Top</h1>
+}
diff --git a/mocks/app-nested-middleware/routes/nested/_middleware.ts b/mocks/app-nested-middleware/routes/nested/_middleware.ts
index 161cda3..7915cc4 100644
--- a/mocks/app-nested-middleware/routes/nested/_middleware.ts
+++ b/mocks/app-nested-middleware/routes/nested/_middleware.ts
@@ -1,4 +1,4 @@
 import { createRoute } from '../../../../src/factory'
 import { headerMiddleware } from '../../middleware/appendHeader'
 
-export default createRoute(headerMiddleware('root'), headerMiddleware('root2'))
+export default createRoute(headerMiddleware('sub'))
diff --git a/src/server/server.ts b/src/server/server.ts
index 571ccff..ec79a31 100644
--- a/src/server/server.ts
+++ b/src/server/server.ts
@@ -57,6 +57,8 @@ type Variables = {} & HasIslandFile
 export const createApp = <E extends Env>(options: BaseServerOptions<E>): Hono<E> => {
   const root = options.root
   const rootRegExp = new RegExp(`^${root}`)
+  const getRootPath = (dir: string) => filePathToPath(dir.replace(rootRegExp, ''))
+
   const app = options.app ?? new Hono()
   const trailingSlash = options.trailingSlash ?? false
 
@@ -134,10 +136,6 @@ export const createApp = <E extends Env>(options: BaseServerOptions<E>): Hono<E>
         }
       }
 
-      // Root path
-      let rootPath = dir.replace(rootRegExp, '')
-      rootPath = filePathToPath(rootPath)
-
       for (const [filename, route] of Object.entries(content)) {
         const importingIslands = route[IMPORTING_ISLANDS_ID]
         const setInnerMeta = createMiddleware<{
@@ -179,19 +177,31 @@ export const createApp = <E extends Env>(options: BaseServerOptions<E>): Hono<E>
           })
         }
       }
-      // Not Found
-      applyNotFound(subApp, dir, notFoundMap)
+
       // Error
       applyError(subApp, dir, errorMap)
 
+      let rootPath = getRootPath(dir)
       if (trailingSlash) {
         rootPath = /\/$/.test(rootPath) ? rootPath : rootPath + '/'
       }
-
       app.route(rootPath, subApp)
     }
   }
 
+  /**
+   * Set Not Found handlers
+   */
+  for (const map of routesMap.reverse()) {
+    const dir = Object.entries(map)[0][0]
+    const subApp = new Hono<{
+      Variables: Variables
+    }>()
+    applyNotFound(subApp, dir, notFoundMap)
+    const rootPath = getRootPath(dir)
+    app.route(rootPath, subApp)
+  }
+
   return app
 }
 
diff --git a/src/utils/file.ts b/src/utils/file.ts
index 402ac84..c2a21de 100644
--- a/src/utils/file.ts
+++ b/src/utils/file.ts
@@ -62,12 +62,6 @@ export const sortDirectoriesByDepth = <T>(directories: Record<string, T>) => {
     return depthA - depthB || b.localeCompare(a)
   })
 
-  // if we have root routes, make sure they are registered last
-  if (sortedKeys.find((x) => /.*\/routes$/.test(x))) {
-    sortedKeys.push(sortedKeys[0])
-    sortedKeys.shift()
-  }
-
   return sortedKeys.map((key) => ({
     [key]: directories[key],
   })) as Record<string, T>[]


Test patch:
diff --git a/test-integration/apps.test.ts b/test-integration/apps.test.ts
index 5555ca8..3d1fa50 100644
--- a/test-integration/apps.test.ts
+++ b/test-integration/apps.test.ts
@@ -440,41 +440,46 @@ describe('Nested Middleware', () => {
     MIDDLEWARE: MIDDLEWARE as any,
   })
 
-  it('Should have "root" header - /nested', async () => {
+  it('Should have "top" header - /', async () => {
+    const res = await app.request('/')
+    expect(res.status).toBe(200)
+    expect(res.headers.get('top')).toEqual('top')
+  })
+  it('Should have "sub" header - /nested', async () => {
     const res = await app.request('/nested')
     expect(res.status).toBe(200)
-    expect(res.headers.get('root')).toEqual('root')
-    expect(res.headers.get('root2')).toEqual('root2')
+    expect(res.headers.get('top')).toEqual('top')
+    expect(res.headers.get('sub')).toEqual('sub')
   })
   it('Should have "foo" header and parent headers - /nested/foo', async () => {
     const res = await app.request('/nested/foo')
     expect(res.status).toBe(200)
-    expect(res.headers.get('root')).toEqual('root')
-    expect(res.headers.get('root2')).toEqual('root2')
+    expect(res.headers.get('top')).toEqual('top')
+    expect(res.headers.get('sub')).toEqual('sub')
     expect(res.headers.get('foo')).toEqual('foo')
   })
   it('Should have "bar" header and parent headers - /nested/foo/bar', async () => {
     const res = await app.request('/nested/foo/bar')
     expect(res.status).toBe(200)
-    expect(res.headers.get('root')).toEqual('root')
-    expect(res.headers.get('root2')).toEqual('root2')
+    expect(res.headers.get('top')).toEqual('top')
+    expect(res.headers.get('sub')).toEqual('sub')
     expect(res.headers.get('foo')).toEqual('foo')
     expect(res.headers.get('bar')).toEqual('bar')
   })
   it('Should have "baz" header and parent headers - /nested/foo/bar/baz', async () => {
     const res = await app.request('/nested/foo/bar/baz')
     expect(res.status).toBe(200)
-    expect(res.headers.get('root')).toEqual('root')
-    expect(res.headers.get('root2')).toEqual('root2')
+    expect(res.headers.get('top')).toEqual('top')
+    expect(res.headers.get('sub')).toEqual('sub')
     expect(res.headers.get('foo')).toEqual('foo')
     expect(res.headers.get('bar')).toEqual('bar')
     expect(res.headers.get('baz')).toEqual('baz')
   })
-  it('/nested/foo/bar/123', async () => {
+  it('Should have headers added by parent middleware - /nested/foo/bar/123', async () => {
     const res = await app.request('/nested/foo/bar/123')
     expect(res.status).toBe(200)
-    expect(res.headers.get('root')).toEqual('root')
-    expect(res.headers.get('root2')).toEqual('root2')
+    expect(res.headers.get('top')).toEqual('top')
+    expect(res.headers.get('sub')).toEqual('sub')
     expect(res.headers.get('foo')).toEqual('foo')
     expect(res.headers.get('bar')).toEqual('bar')
   })