rajt
Version:
A serverless bundler layer, fully typed for AWS Lambda (Node.js and LLRT) and Cloudflare Workers.
132 lines (113 loc) • 4.19 kB
text/typescript
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { matchedRoutes } from 'hono/route'
import { describeRoute } from 'hono-openapi'
import { Envir, Datte } from 't0n'
import type {
Env, Context, Next,
HTTPResponseError,
ServerOptions,
} from './types'
import { resolve, resolveMiddleware } from './utils/resolve'
import { getMiddlewares, getHandler } from './register'
import request, { GET_REQUEST } from './request'
import response from './response'
import { isDev } from './utils/environment'
import { gray } from './utils/colors'
const NFHandler = () => response.notFound()
const EHandler = async (e: Error | HTTPResponseError) => {
console.error(e)
switch (true) {
case 'status' in e && e.status == 401:
return response.unauthorized()
case 'status' in e && e.status == 400: // @ts-ignore
return response.badRequest(null, e?.message)
default:
return response.internalError(
// @ts-ignore
isDev()
? e.stack?.split('\n').map(line =>
line.replace(
/at (.+ )?\(?([^)]+)\)?/g,
(match, method, path) => {
if (!path) return match
const nodeModulesIndex = path.indexOf('node_modules')
if (nodeModulesIndex > -1)
return `${method || ''}(node_modules${path.slice(nodeModulesIndex + 'node_modules'.length)})`
const projectRoot = process.cwd()
const relativePath = path.startsWith(projectRoot) ? path.slice(projectRoot.length + 1) : path
return `${method || ''}(${relativePath})`
}
).trim()
)
: undefined,
e.message || 'Internal Error'
)
}
// return json.internalError(
// // @ts-ignore
// isDev() ? e.stack?.split('\n at ').map() : undefined,
// e.message || 'Internal Error'
// )
// error: e.message,
// cause: e.cause || '???',
// stack: isDev (? e.stack : undefined
}
export const createApp = <E extends Env>(options?: ServerOptions<E>) => {
// const root = options?.root ?? '/'
const app = options?.app ?? new Hono<E>()
if (isDev()) {
app.use('*', async function (c: Context, next: Next) {
const method = c.req.method
const route = matchedRoutes(c).find(route => route.method == method)?.path
const logWithRoute = (args: string[]) => {
if (!route || !args.length) return args
return args.map(arg => {
if (!arg) return arg
const split = arg?.split(' ')
if (split.length < 3 || split[2] == route)
return arg
split.splice(Math.min(3, split.length), 0, gray(route))
return split.join(' ')
})
}
const devLogger = logger((...args: any[]) => {
const timestamp = gray(Datte.dateTime())
console.log(timestamp, ...logWithRoute(args))
})
await devLogger(c, next)
})
}
app.use(async (c: Context, next: Next) => {
c.set(GET_REQUEST as unknown as string, new request(c))
if (c.env) Envir.add(c.env)
await next()
})
getMiddlewares().forEach(mw => {
const h = async (c: Context, next: Next) => await resolveMiddleware(mw)(c.get(GET_REQUEST as unknown as string), next)
// @ts-ignore
mw?.path ? app.use(String(mw.path), h) : app.use(h)
})
// @ts-ignore
app.onError(options?.onError || EHandler)
// @ts-ignore
app.notFound(options?.notFound || NFHandler)
if (options?.init) options.init(app)
const routes = options?.routes || []
for (const route of routes) {
if (Array.isArray(route)) { // @ts-ignore
app[route[0]](route[1], ...mw(route[2], route[3]), ...resolve(getHandler(route[3]), route[3]))
} else { // @ts-ignore
app[route.method](route.path, describeRoute(route.desc), ...mw(route.middlewares, route.name), ...resolve(route.handle, route.name))
}
}
return app
}
function mw(...objs: string[]): Function[] {
return objs.flatMap(obj => {
if (typeof obj != 'string') return null
// @ts-ignore
return getHandler(obj)?.mw || null
}).flat().filter(Boolean)
}
export default createApp