import { join } from 'path' import { type Context, Hono } from 'hono' import { serveStatic } from 'hono/bun' import color from 'kleur' import { transpile } from './utils' import defaultLayout from './layout' const SHOW_HTTP_LOG = true const CSS_RESET = await Bun.file(join(import.meta.dir, '/reset.css')).text() export class Hype extends Hono { routesRegistered = false constructor() { super() } registerRoutes() { if (this.routesRegistered) return this.routesRegistered = true // static assets in pub/ this.use('/*', serveStatic({ root: './pub' })) // css lives in src/, close to real code this.use('/css/*', serveStatic({ root: './src' })) // console logging this.use('*', async (c, next) => { if (!SHOW_HTTP_LOG) return await next() const start = Date.now() await next() const end = Date.now() const fn = c.res.status < 400 ? color.green : c.res.status < 500 ? color.yellow : color.red const method = c.req.method === 'GET' ? color.cyan(c.req.method) : color.magenta(c.req.method) console.log(fn(`${c.res.status}`), `${color.bold(method)} ${c.req.url} (${end - start}ms)`) }) // css reset this.get('/css/reset.css', async c => new Response(CSS_RESET, { headers: { 'Content-Type': 'text/css' } })) // serve transpiled js this.on('GET', ['/js/:path{.+}', '/shared/:path{.+}'], async c => { let path = './src/' + c.req.path.replace('..', '.') // path must end in .js or .ts if (!path.endsWith('.js') && !path.endsWith('.ts')) path += '.ts' const ts = path.replace('.js', '.ts') if (await Bun.file(ts).exists()) { return new Response(await transpile(ts), { headers: { 'Content-Type': 'text/javascript' } }) } else if (await Bun.file(path).exists()) { return new Response(Bun.file(path), { headers: { 'Content-Type': 'text/javascript' } }) } else { return render404(c) } }) // file based routing this.on('GET', ['/', '/:page'], async c => { const pageName = (c.req.param('page') ?? 'index').replace('.', '') if (pageName.startsWith('_')) return render404(c) const path = join(process.env.PWD ?? '.', `./src/pages/${pageName}.tsx`) if (!(await Bun.file(path).exists())) return render404(c) const layoutPath = join(process.env.PWD ?? '.', `./src/pages/_layout.tsx`) let Layout = defaultLayout if (await Bun.file(layoutPath).exists()) Layout = (await import(layoutPath + `?t=${Date.now()}`)).default const page = await import(path + `?t=${Date.now()}`) const innerHTML = typeof page.default === 'function' ? : page.default const withLayout = Layout ? {innerHTML} : innerHTML return c.html(withLayout) }) } get defaults() { this.registerRoutes() return { fetch: this.fetch, idleTimeout: 255 } } } function render404(c: Context) { return c.text('File not found', 404) }