hype/src/frontend.ts

48 lines
1.3 KiB
TypeScript

import { AsyncLocalStorage } from 'async_hooks'
export const fnStorage = new AsyncLocalStorage<{
fns: Map<string, string>
counter: number
}>()
export function fe<T extends Record<string, unknown>>(
fn: (args?: T) => void,
args?: T
): string {
const store = fnStorage.getStore()
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()
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