Skip to content

Commit

Permalink
feat: cascading error pages (#193)
Browse files Browse the repository at this point in the history
  • Loading branch information
yusukebe authored Jun 16, 2024
1 parent c5e8a23 commit ccb5fdf
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 16 deletions.
7 changes: 7 additions & 0 deletions mocks/app/routes/directory/_error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { ErrorHandler } from 'hono'

const handler: ErrorHandler = (error, c) => {
return c.render(<h1>Custom Error in /directory: {error.message}</h1>)
}

export default handler
5 changes: 5 additions & 0 deletions mocks/app/routes/directory/sub/throw_error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createRoute } from '../../../../../src/factory'

export default createRoute(() => {
throw new Error('Foo')
})
5 changes: 5 additions & 0 deletions mocks/app/routes/directory/throw_error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createRoute } from '../../../../src/factory'

export default createRoute(() => {
throw new Error('Foo')
})
42 changes: 27 additions & 15 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ export const createApp = <E extends Env>(options: BaseServerOptions<E>): Hono<E>
return paths
}

const errorHandlerMap: Record<string, ErrorHandler> = {}

for (const map of routesMap) {
for (const [dir, content] of Object.entries(map)) {
const subApp = new Hono<{
Expand Down Expand Up @@ -186,8 +188,19 @@ export const createApp = <E extends Env>(options: BaseServerOptions<E>): Hono<E>
}
}

// Error
applyError(subApp, dir, errorMap)
// Get an error handler
const errorHandler = getErrorHandler(dir, errorMap)
if (errorHandler) {
errorHandlerMap[dir] = errorHandler
}

// Apply an error handler
for (const [path, errorHandler] of Object.entries(errorHandlerMap)) {
const regExp = new RegExp(`^${path}`)
if (regExp.test(dir) && errorHandler) {
subApp.onError(errorHandler)
}
}

let rootPath = getRootPath(dir)
if (trailingSlash) {
Expand Down Expand Up @@ -241,24 +254,23 @@ function applyNotFound(
}
}

function applyError(
app: Hono<{ Variables: Variables }>,
dir: string,
map: Record<string, Record<string, ErrorFile>>
) {
function getErrorHandler(dir: string, map: Record<string, Record<string, ErrorFile>>) {
for (const [mapDir, content] of Object.entries(map)) {
if (dir === mapDir) {
const errorFile = content[ERROR_FILENAME]
if (errorFile) {
const errorHandler = errorFile.default
app.onError(async (error, c) => {
const importingIslands = errorFile[IMPORTING_ISLANDS_ID]
if (importingIslands) {
c.set(IMPORTING_ISLANDS_ID, importingIslands)
const matchedErrorHandler = errorFile.default
if (matchedErrorHandler) {
const errorHandler: ErrorHandler = async (error, c) => {
const importingIslands = errorFile[IMPORTING_ISLANDS_ID]
if (importingIslands) {
c.set(IMPORTING_ISLANDS_ID, importingIslands)
}
c.status(500)
return matchedErrorHandler(error, c)
}
c.status(500)
return errorHandler(error, c)
})
return errorHandler
}
}
}
}
Expand Down
38 changes: 37 additions & 1 deletion test-integration/apps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,26 @@ describe('Basic', () => {
method: 'GET',
handler: expect.any(Function),
},
{
path: '/directory/throw_error',
method: 'GET',
handler: expect.any(Function),
},
{
path: '/directory/throw_error',
method: 'GET',
handler: expect.any(Function),
},
{
path: '/directory/sub/throw_error',
method: 'GET',
handler: expect.any(Function),
},
{
path: '/directory/sub/throw_error',
method: 'GET',
handler: expect.any(Function),
},
{
path: '/fc',
method: 'GET',
Expand Down Expand Up @@ -264,7 +284,7 @@ describe('With preserved', () => {
eager: true,
})

const ERROR = import.meta.glob('../mocks/app/routes/_error.tsx', {
const ERROR = import.meta.glob('../mocks/app/routes/**/_error.tsx', {
eager: true,
})

Expand Down Expand Up @@ -378,6 +398,22 @@ describe('With preserved', () => {
'<!DOCTYPE html><html><head><title>Internal Server Error</title></head><body><h1>Custom Error Message: Foo</h1></body></html>'
)
})

it('Should return 500 response - /directory/throw_error', async () => {
const res = await app.request('/directory/throw_error')
expect(res.status).toBe(500)
expect(await res.text()).toBe(
'<!DOCTYPE html><html><head><title></title></head><body><h1>Custom Error in /directory: Foo</h1></body></html>'
)
})

it('Should return 500 response - /directory/sub/throw_error', async () => {
const res = await app.request('/directory/sub/throw_error')
expect(res.status).toBe(500)
expect(await res.text()).toBe(
'<!DOCTYPE html><html><head><title></title></head><body><h1>Custom Error in /directory: Foo</h1></body></html>'
)
})
})

describe('API', () => {
Expand Down

0 comments on commit ccb5fdf

Please sign in to comment.