export const createDeepProxy = (target: any, handler: ProxyHandler): any => { const keys = Object.keys(target) keys.forEach(key => { if (typeof target[key] === 'object') { target[key] = createDeepProxy(target[key], handler) } }) return new Proxy(target, handler) } export class Reload { listeners: CallableFunction[] = [] fire() { this.listeners.forEach(f => f()) } on(func: () => void) { this.listeners.push(func) } } type TemplateOption = { render: (payload: { s: T, context: V, reload: Reload }) => string, setup: (payload: { s: T, reload: Reload }) => V } type ComponentOption = { render: (payload: { s: T, context: W, props: V, reload: Reload }) => string, setup: (payload: { s: T, props: V, reload: Reload }) => W } const handler: ProxyHandler = { get: (target, key) => { if (key !== "__isProxy") { return target[key]; } return true; }, set(target: any, p: PropertyKey, value: any, receiver: any): boolean { if (value.__isProxy || ['string', 'number', 'boolean'].includes(typeof value)) { return Reflect.set(target, p, value, receiver) } return Reflect.set(target, p, createDeepProxy(value, handler), receiver) } } export const createComponent = (state: V, option: ComponentOption) => (props: T): [() => string, Reload] => { const reload = new Reload() const s = createDeepProxy(state, handler) const context = option.setup({s, props, reload}) return [() => option.render({s, context, props, reload}), reload] } export const createTemplate = (state: T, option: TemplateOption): [() => string, Reload] => { const reload = new Reload() const s = createDeepProxy(state, handler) const context = option.setup({s, reload}) return [() => option.render({s, context, reload}), reload] }