79 lines
2.4 KiB
TypeScript
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>`
|
|
} |