diff --git a/examples/ssr/src/pages/js-test.tsx b/examples/ssr/src/pages/js-test.tsx index aaf9481..065b8ca 100644 --- a/examples/ssr/src/pages/js-test.tsx +++ b/examples/ssr/src/pages/js-test.tsx @@ -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 () => (
- test + test
) \ No newline at end of file diff --git a/src/frontend.ts b/src/frontend.ts index 0b9b497..4ff25a8 100644 --- a/src/frontend.ts +++ b/src/frontend.ts @@ -1,14 +1,48 @@ import { AsyncLocalStorage } from 'async_hooks' -export const fnStorage = new AsyncLocalStorage<{ fns: string[] }>() +export const fnStorage = new AsyncLocalStorage<{ + fns: Map + counter: number +}>() -// Designate a function in a .tsx file as frontend -export function frontend(code: Function) { +export function fe>( + 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 \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index da5f62c..753d244 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -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')