97 lines
2.6 KiB
TypeScript
97 lines
2.6 KiB
TypeScript
// ReplProxy and srcdoc implementation from Svelte REPL
|
|
// MIT License https://github.com/sveltejs/svelte-repl/blob/master/LICENSE
|
|
|
|
let uid = 1
|
|
|
|
export class PreviewProxy {
|
|
iframe: HTMLIFrameElement
|
|
handlers: Record<string, Function>
|
|
pending_cmds: Map<
|
|
number,
|
|
{ resolve: (value: unknown) => void; reject: (reason?: any) => void }
|
|
>
|
|
handle_event: (e: any) => void
|
|
|
|
constructor(iframe: HTMLIFrameElement, handlers: Record<string, Function>) {
|
|
this.iframe = iframe
|
|
this.handlers = handlers
|
|
|
|
this.pending_cmds = new Map()
|
|
|
|
this.handle_event = e => this.handle_repl_message(e)
|
|
window.addEventListener('message', this.handle_event, false)
|
|
}
|
|
|
|
destroy() {
|
|
window.removeEventListener('message', this.handle_event)
|
|
}
|
|
|
|
iframe_command(action: string, args: any) {
|
|
return new Promise((resolve, reject) => {
|
|
const cmd_id = uid++
|
|
|
|
this.pending_cmds.set(cmd_id, { resolve, reject })
|
|
|
|
this.iframe.contentWindow!.postMessage({ action, cmd_id, args }, '*')
|
|
})
|
|
}
|
|
|
|
handle_command_message(cmd_data: any) {
|
|
let action = cmd_data.action
|
|
let id = cmd_data.cmd_id
|
|
let handler = this.pending_cmds.get(id)
|
|
|
|
if (handler) {
|
|
this.pending_cmds.delete(id)
|
|
if (action === 'cmd_error') {
|
|
let { message, stack } = cmd_data
|
|
let e = new Error(message)
|
|
e.stack = stack
|
|
handler.reject(e)
|
|
}
|
|
|
|
if (action === 'cmd_ok') {
|
|
handler.resolve(cmd_data.args)
|
|
}
|
|
} else {
|
|
console.error('command not found', id, cmd_data, [
|
|
...this.pending_cmds.keys()
|
|
])
|
|
}
|
|
}
|
|
|
|
handle_repl_message(event: any) {
|
|
if (event.source !== this.iframe.contentWindow) return
|
|
|
|
const { action, args } = event.data
|
|
|
|
switch (action) {
|
|
case 'cmd_error':
|
|
case 'cmd_ok':
|
|
return this.handle_command_message(event.data)
|
|
case 'fetch_progress':
|
|
return this.handlers.on_fetch_progress(args.remaining)
|
|
case 'error':
|
|
return this.handlers.on_error(event.data)
|
|
case 'unhandledrejection':
|
|
return this.handlers.on_unhandled_rejection(event.data)
|
|
case 'console':
|
|
return this.handlers.on_console(event.data)
|
|
case 'console_group':
|
|
return this.handlers.on_console_group(event.data)
|
|
case 'console_group_collapsed':
|
|
return this.handlers.on_console_group_collapsed(event.data)
|
|
case 'console_group_end':
|
|
return this.handlers.on_console_group_end(event.data)
|
|
}
|
|
}
|
|
|
|
eval(script: string | string[]) {
|
|
return this.iframe_command('eval', { script })
|
|
}
|
|
|
|
handle_links() {
|
|
return this.iframe_command('catch_clicks', {})
|
|
}
|
|
}
|