experimental new frontend() style
This commit is contained in:
parent
7b9cade936
commit
b9b4e205c9
|
|
@ -1,11 +1,11 @@
|
|||
import { frontend as fe } from 'hype'
|
||||
import { fe } from 'hype'
|
||||
|
||||
fe(function test() {
|
||||
const test = fe(() => {
|
||||
alert('ding dong')
|
||||
})
|
||||
|
||||
export default () => (
|
||||
<section>
|
||||
<a href="#" onclick="test()">test</a>
|
||||
<a href="#" onclick={test}>test</a>
|
||||
</section>
|
||||
)
|
||||
|
|
@ -1,14 +1,48 @@
|
|||
import { AsyncLocalStorage } from 'async_hooks'
|
||||
|
||||
export const fnStorage = new AsyncLocalStorage<{ fns: string[] }>()
|
||||
export const fnStorage = new AsyncLocalStorage<{
|
||||
fns: Map<string, string>
|
||||
counter: number
|
||||
}>()
|
||||
|
||||
// Designate a function in a .tsx file as frontend
|
||||
export function frontend(code: Function) {
|
||||
export function fe<T extends Record<string, unknown>>(
|
||||
fn: (args?: T) => void,
|
||||
args?: T
|
||||
): string {
|
||||
const store = fnStorage.getStore()
|
||||
store?.fns.push(code.toString())
|
||||
if (!store) {
|
||||
// Fallback to IIFE if outside request context
|
||||
return args
|
||||
? `(${fn.toString()})(${JSON.stringify(args)})`
|
||||
: `(${fn.toString()})()`
|
||||
}
|
||||
|
||||
const fnStr = fn.toString()
|
||||
|
||||
// Dedupe by function body
|
||||
for (const [name, body] of store.fns)
|
||||
if (body === fnStr)
|
||||
return args ? `${name}(${JSON.stringify(args)})` : `${name}()`
|
||||
|
||||
const name = `frontendFn${store.counter++}`
|
||||
store.fns.set(name, fnStr)
|
||||
|
||||
return args ? `${name}(${JSON.stringify(args)})` : `${name}()`
|
||||
}
|
||||
|
||||
export function feFunctions(): string[] {
|
||||
const store = fnStorage.getStore()
|
||||
return store?.fns ?? []
|
||||
if (!store?.fns.size) return []
|
||||
|
||||
return [...store.fns.entries()].map(([name, body]) => {
|
||||
// Handle arrow functions vs regular functions
|
||||
if (body.startsWith('(') || body.startsWith('async ('))
|
||||
return `const ${name} = ${body};`
|
||||
|
||||
// Named function - rename it
|
||||
return body.replace(/^(async\s+)?function\s*\w*/, `$1function ${name}`)
|
||||
})
|
||||
}
|
||||
|
||||
// Keep for backwards compat
|
||||
export const frontend = fe
|
||||
|
|
@ -13,7 +13,7 @@ const CSS_RESET = await Bun.file(join(import.meta.dir, '/css/reset.css')).text()
|
|||
const PICO_CSS = await Bun.file(join(import.meta.dir, '/css/pico.css')).text()
|
||||
|
||||
export * from './utils'
|
||||
export { frontend } from './frontend'
|
||||
export { frontend, frontend as fe } from './frontend'
|
||||
export type { Context } from 'hono'
|
||||
|
||||
const pageCache = new Map()
|
||||
|
|
@ -94,7 +94,7 @@ export class Hype<
|
|||
|
||||
// serve frontend js
|
||||
this.use('*', async (c, next) => {
|
||||
await fnStorage.run({ fns: [] }, async () => {
|
||||
await fnStorage.run({ fns: new Map(), counter: 0 }, async () => {
|
||||
await next()
|
||||
|
||||
const contentType = c.res.headers.get('content-type')
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user