foam/src/server.ts

79 lines
2.4 KiB
TypeScript

import { AnsiUp } from 'ansi_up'
import { Hono } from 'hono'
import { serveStatic } from 'hono/bun'
import { join, resolve } from 'path'
import { wrapCode, ribbitGlobals } from './ribbit'
import { Shrimp } from 'shrimp'
export function startWeb(rootPath: string) {
const root = resolve(rootPath)
const app = new Hono()
// console logging
app.use('*', async (c, next) => {
const start = Date.now()
await next()
const end = Date.now()
console.log(`${c.res.status} ${c.req.method} ${c.req.url} (${end - start}ms)`)
})
// static files get served out of pub/
app.use('/*', serveStatic({ root: join(root, 'pub') }))
app.on(['GET', 'POST'], ['/', '/:page{.+}'], async (c) => {
const page = c.req.param('page') || 'index'
if (page === 'layout') return c.text('404 Not Found', 404)
const params = c.req.query()
const request = { method: c.req.method }
if (c.req.method === 'POST') {
const formData = await c.req.formData()
for (const [key, value] of formData.entries()) {
params[key] = value
}
}
const path = join(root, `${page}.sh`)
const file = Bun.file(path)
const layoutPath = join(root, `layout.sh`)
const layoutFile = Bun.file(layoutPath)
let layoutCode = await layoutFile.exists() ? await layoutFile.text() : ''
const vm = new Shrimp(Object.assign({}, ribbitGlobals, { params, request }))
if (await file.exists()) {
let content = ''
try {
const code = wrapCode(await file.text())
content = await vm.run(code)
} catch (err) {
return c.html(shrimpError(path, err), 500)
}
if (!layoutCode) return c.html(content)
try {
return c.html(await vm.run(wrapCode(layoutCode), { content }))
} catch (err) {
return c.html(shrimpError(layoutPath, err), 500)
}
} else {
return c.text('404 Not Found', 404)
}
})
console.log('🫧 Server started at http://localhost:3000')
return Bun.serve({
fetch: app.fetch,
port: 3000,
})
}
function shrimpError(path: string, error: any): string {
const ansiUp = new AnsiUp()
const errorHtml = ansiUp.ansi_to_html(error?.message ?? String(error))
const blue = '#42A5F5'
return `<h1 style='color:${blue}'>🫧 Error in <a href='vscode://file/${path}' style='text-decoration:none;border-bottom:1px dotted ${blue};color:${blue}'>${path}</a></h1><pre>${errorHtml}</pre>`
}