Compare commits
7 Commits
56362b0ba7
...
0556efb41f
| Author | SHA1 | Date | |
|---|---|---|---|
| 0556efb41f | |||
| 68cd61227b | |||
| d48098b006 | |||
| f063621369 | |||
| d5b7bc16e0 | |||
| 6045144e9e | |||
| 77328f7441 |
|
|
@ -17,9 +17,9 @@
|
|||
- [x] Access to query params
|
||||
- [x] Serve static files in pub/
|
||||
- [x] Working CLI
|
||||
- [ ] Nice error messages
|
||||
- [x] Nice error messages
|
||||
- [ ] dev mode / prod mode (caching, errors)
|
||||
- [ ] Form body parsing for POST
|
||||
- [ ] auto-serve index.sh for subdirectories (`/users` -> `/users/index.sh` if dir)
|
||||
- [ ] Layouts
|
||||
- [x] Layouts
|
||||
- [ ] Caching
|
||||
4
bun.lock
4
bun.lock
|
|
@ -61,9 +61,9 @@
|
|||
|
||||
"hono": ["hono@4.10.4", "", {}, "sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg=="],
|
||||
|
||||
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#0f39e9401eb7a0a7c906e150127f9829458a79b6", { "peerDependencies": { "typescript": "^5" } }, "0f39e9401eb7a0a7c906e150127f9829458a79b6"],
|
||||
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#33ea94a2476f87f22408dce8b7beb3587070e01b", { "peerDependencies": { "typescript": "^5" } }, "33ea94a2476f87f22408dce8b7beb3587070e01b"],
|
||||
|
||||
"shrimp": ["shrimp@git+https://git.nose.space/probablycorey/shrimp#e0e5e828692713fcaf5d62f88d3ad2c3a43802d4", { "dependencies": { "@codemirror/view": "^6.38.3", "@lezer/generator": "^1.8.0", "bun-plugin-tailwind": "^0.0.15", "codemirror": "^6.0.2", "hono": "^4.9.8", "reefvm": "git+https://git.nose.space/defunkt/reefvm", "tailwindcss": "^4.1.11" } }, "e0e5e828692713fcaf5d62f88d3ad2c3a43802d4"],
|
||||
"shrimp": ["shrimp@git+https://git.nose.space/probablycorey/shrimp#1a3e041001358ce6b10078c46a6585e76633310f", { "dependencies": { "@codemirror/view": "^6.38.3", "@lezer/generator": "^1.8.0", "bun-plugin-tailwind": "^0.0.15", "codemirror": "^6.0.2", "hono": "^4.9.8", "reefvm": "git+https://git.nose.space/defunkt/reefvm", "tailwindcss": "^4.1.11" } }, "1a3e041001358ce6b10078c46a6585e76633310f"],
|
||||
|
||||
"style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="],
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
"dev": "bun run --hot src/server.tsx",
|
||||
"start": "bun run src/server.tsx",
|
||||
"cli:install": "ln -s \"$(pwd)/bin/foam\" ~/.bun/bin/foam",
|
||||
"cli:remove": "rm ~/.bun/bin/foam"
|
||||
"cli:remove": "rm ~/.bun/bin/foam",
|
||||
"update-shrimp": "rm -rf ~/.bun/install/cache/ && rm bun.lock && bun update shrimp"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
|
|
@ -21,4 +22,4 @@
|
|||
"hono": "^4.10.4",
|
||||
"shrimp": "git+https://git.nose.space/probablycorey/shrimp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { runCode } from 'shrimp'
|
||||
import { globals } from 'shrimp'
|
||||
import { AnsiUp } from 'ansi_up'
|
||||
|
||||
const buffer: string[] = []
|
||||
const NOSPACE_TOKEN = '!!ribbit-nospace!!'
|
||||
|
|
@ -20,11 +21,22 @@ const HTML5_TAGS = [
|
|||
"time", "title", "tr", "track", "u", "ul", "var", "video", "wbr"
|
||||
]
|
||||
|
||||
export async function wrapAndRunCode(code: string, globals?: Record<string, any>): Promise<any> {
|
||||
return await runCode("ribbit do:\n " + code + "\nend", Object.assign({}, ribbitGlobals, globals))
|
||||
export function wrapCode(code: string): string {
|
||||
return "ribbit do:\n " + code + "\nend"
|
||||
}
|
||||
|
||||
const ansiUp = new AnsiUp()
|
||||
|
||||
export const ribbitGlobals = {
|
||||
// special variables
|
||||
'page-title': '🦐 shrimp',
|
||||
|
||||
// html-friendly info functions
|
||||
describe: (v: any) => ansiUp.ansi_to_html(globals.describe(v)),
|
||||
inspect: (v: any) => ansiUp.ansi_to_html(globals.inspect(v)),
|
||||
echo: (...args: any[]) => console.log(...args),
|
||||
|
||||
// 🐸
|
||||
ribbit: async (cb: Function) => {
|
||||
await cb()
|
||||
const output = buffer.join("\n")
|
||||
|
|
@ -34,7 +46,6 @@ export const ribbitGlobals = {
|
|||
tag: async (tagFn: Function, atDefaults = {}) =>
|
||||
(atNamed = {}, ...args: any[]) => tagFn(Object.assign({}, atDefaults, atNamed), ...args),
|
||||
nospace: () => NOSPACE_TOKEN,
|
||||
echo: (...args: any[]) => console.log(...args)
|
||||
}
|
||||
|
||||
for (const name of HTML5_TAGS) {
|
||||
|
|
@ -42,6 +53,15 @@ for (const name of HTML5_TAGS) {
|
|||
; (ribbitGlobals as any)[name].tagName = name
|
||||
}
|
||||
|
||||
const tag = async (tagName: string, atNamed = {}, ...args: any[]) => {
|
||||
if (typeof args[0] === 'function')
|
||||
await tagBlock(tagName, atNamed, args[0])
|
||||
else
|
||||
tagCall(tagName, atNamed, ...args)
|
||||
|
||||
return TAG_TOKEN
|
||||
}
|
||||
|
||||
const tagBlock = async (tagName: string, props = {}, fn: Function) => {
|
||||
const attrs = Object.entries(props).map(([key, value]) => `${key}="${value}"`)
|
||||
const space = attrs.length ? ' ' : ''
|
||||
|
|
@ -66,13 +86,4 @@ const tagCall = (tagName: string, atNamed = {}, ...args: any[]) => {
|
|||
buffer.push(`<${tagName}${space}${attrs.join(' ')} />`)
|
||||
else
|
||||
buffer.push(`<${tagName}${space}${attrs.join(' ')}>${children}</${tagName}>`)
|
||||
}
|
||||
|
||||
const tag = async (tagName: string, atNamed = {}, ...args: any[]) => {
|
||||
if (typeof args[0] === 'function')
|
||||
await tagBlock(tagName, atNamed, args[0])
|
||||
else
|
||||
tagCall(tagName, atNamed, ...args)
|
||||
|
||||
return TAG_TOKEN
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
import { AnsiUp } from 'ansi_up'
|
||||
import { Hono } from 'hono'
|
||||
import { serveStatic } from 'hono/bun'
|
||||
import { join, resolve } from 'path'
|
||||
import { wrapAndRunCode } from './ribbit'
|
||||
import { AnsiUp } from 'ansi_up'
|
||||
import { wrapCode, ribbitGlobals } from './ribbit'
|
||||
import { Shrimp } from 'shrimp'
|
||||
|
||||
export function startWeb(rootPath: string) {
|
||||
const root = resolve(rootPath)
|
||||
|
|
@ -21,19 +22,42 @@ export function startWeb(rootPath: string) {
|
|||
|
||||
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 {
|
||||
return c.html(await wrapAndRunCode(await file.text(), { params }))
|
||||
} catch (e) {
|
||||
let error = e instanceof Error ? e.message : String(e)
|
||||
const ansiUp = new AnsiUp()
|
||||
const errorHtml = ansiUp.ansi_to_html(error)
|
||||
const blue = '#42A5F5'
|
||||
return c.html(`<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>`)
|
||||
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)
|
||||
|
|
@ -46,3 +70,10 @@ export function startWeb(rootPath: string) {
|
|||
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>`
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user