Compare commits

...

3 Commits

Author SHA1 Message Date
ddce0c8589 add frontend() 2025-12-12 11:56:29 -08:00
350ef97df4 more bites 2025-12-12 08:40:12 -08:00
3e3ffe345e akshully 2025-12-12 07:53:11 -08:00
9 changed files with 60 additions and 8 deletions

View File

@ -12,7 +12,7 @@
`hype` wraps `hono` with useful features for fast prototyping: `hype` wraps `hono` with useful features for fast prototyping:
- HTTP logging (disable with `NO_HTTP_LOG` env variable) - HTTP logging to the console (disable with `NO_HTTP_LOG` env variable)
- Static files served from `pub/` - Static files served from `pub/`
- Page-based routing to `.tsx` files that export a `JSX` function in `./src/pages` - Page-based routing to `.tsx` files that export a `JSX` function in `./src/pages`
- Transpile `.ts` files in `src/js/blah.ts` via `website.com/js/blah.ts` - Transpile `.ts` files in `src/js/blah.ts` via `website.com/js/blah.ts`

View File

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
example/pub/img/bite2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -34,3 +34,7 @@ h1 {
background-position: 100% 50%; background-position: 100% 50%;
} }
} }
ul {
list-style-type: none;
}

View File

@ -2,6 +2,8 @@ export function initBurger() {
document.addEventListener('click', ev => { document.addEventListener('click', ev => {
const el = (ev?.target as HTMLElement).closest('.burger') as HTMLImageElement const el = (ev?.target as HTMLElement).closest('.burger') as HTMLImageElement
if (el) el.src = '/img/bite.png' if (!el) return
el.src = el.src.endsWith('/img/burger.png') ? '/img/bite1.png' : '/img/bite2.png'
}) })
} }

View File

@ -4,6 +4,10 @@ export default () => (
<h1>Hello, world!</h1> <h1>Hello, world!</h1>
<p>Welcome to <span class="hype">hype</span>!</p> <p>Welcome to <span class="hype">hype</span>!</p>
<p>If you're seeing this, something definitely went right.</p> <p>If you're seeing this, something definitely went right.</p>
<a href="/about">About This Website</a>
<ul>
<li><a href="/about">About This Website</a></li>
<li><a href="/js-test">JS Test</a></li>
</ul>
</section> </section>
) )

View File

@ -0,0 +1,11 @@
import { frontend as fe } from 'hype'
fe(function test() {
alert('ding dong')
})
export default () => (
<section>
<a href="#" click="test()">test</a>
</section>
)

10
src/frontend.ts Normal file
View File

@ -0,0 +1,10 @@
let funcs: string[] = []
// Designate a function in a .tsx file as frontend
export function frontend(code: Function) {
funcs.push(code.toString())
}
export function feFunctions(): string[] {
return funcs
}

View File

@ -6,12 +6,14 @@ import color from 'kleur'
import { transpile } from './utils' import { transpile } from './utils'
import defaultLayout from './layout' import defaultLayout from './layout'
import { feFunctions } from './frontend'
const SHOW_HTTP_LOG = true const SHOW_HTTP_LOG = true
const CSS_RESET = await Bun.file(join(import.meta.dir, '/reset.css')).text() const CSS_RESET = await Bun.file(join(import.meta.dir, '/reset.css')).text()
const PICO_CSS = await Bun.file(join(import.meta.dir, '/pico.css')).text() const PICO_CSS = await Bun.file(join(import.meta.dir, '/pico.css')).text()
export * from './utils' export * from './utils'
export { frontend } from './frontend'
export type HypeProps = { export type HypeProps = {
pico?: boolean pico?: boolean
@ -76,14 +78,33 @@ export class Hype<
this.use('*', async (c, next) => { this.use('*', async (c, next) => {
await next() await next()
if (c.res.headers.get('content-type')?.includes('text/html')) { const contentType = c.res.headers.get('content-type')
const html = await c.res.text() if (!contentType?.includes('text/html')) return
const formatted = formatHTML(html)
c.res = new Response(formatted, c.res) const res = c.res.clone()
} const html = await res.text()
const formatted = formatHTML(html)
c.res = new Response(formatted, c.res)
}) })
} }
// serve frontend js
this.use('*', async (c, next) => {
await next()
const contentType = c.res.headers.get('content-type')
if (!contentType?.includes('text/html')) return
const fns = feFunctions()
if (!fns.length) return
const res = c.res.clone()
const html = await res.text()
const newHtml = html.replace('</body>', `<script>${fns.join('\n')}</script></body>`)
c.res = new Response(newHtml, c.res)
})
// css reset // css reset
this.get('/css/reset.css', async c => new Response(CSS_RESET, { headers: { 'Content-Type': 'text/css' } })) this.get('/css/reset.css', async c => new Response(CSS_RESET, { headers: { 'Content-Type': 'text/css' } }))