diff --git a/packages/repl/src/lib/Output/ReplProxy.ts b/packages/repl/src/lib/Output/ReplProxy.ts index a42f28cd7..11a6adfc0 100644 --- a/packages/repl/src/lib/Output/ReplProxy.ts +++ b/packages/repl/src/lib/Output/ReplProxy.ts @@ -23,6 +23,8 @@ export default class ReplProxy { return this.handlers.on_error(event.data); case 'unhandledrejection': return this.handlers.on_unhandled_rejection(event.data); + case 'iframe_reload': + return this.handlers.on_iframe_reload(event.data); case 'console': if (event.data.command === 'info' && event.data.args[0]?.type === '__error') { const data = event.data.args[0]; diff --git a/packages/repl/src/lib/Output/Viewer.svelte b/packages/repl/src/lib/Output/Viewer.svelte index 53685b8f6..310024a01 100644 --- a/packages/repl/src/lib/Output/Viewer.svelte +++ b/packages/repl/src/lib/Output/Viewer.svelte @@ -77,6 +77,9 @@ error.message = 'Uncaught (in promise): ' + error.message; push_logs({ command: 'error', args: [error] }); }, + on_iframe_reload: () => { + ready = false; + }, on_console: (log) => { switch (log.command) { case 'clear': @@ -169,11 +172,65 @@ console.error(err); } } + window.__reset_custom_elements?.(); document.body.innerHTML = ''; window._svelteTransitionManager = null; } + if (!window.__reset_custom_elements) { + const registered = new Map(); + const define = CustomElementRegistry.prototype.define; + CustomElementRegistry.prototype.define = function(name, el, options) { + let ce = registered.get(name); + if (ce) { + if (ce.registered) { + // trigger error of re-registering + define.call(this, name, ce.el, options); + } + if (ce.options?.extends != options?.extends) { + parent.postMessage({ action: 'iframe_reload' }, '*'); + location.reload(); + } + ce.el = el; + ce.registered = true; + } else { + ce = { el, options, registered: true }; + registered.set(name, ce); + const Wrapper = class extends el { + connectedCallback() { + ce.el.prototype.connectedCallback?.apply(this, arguments); + } + disconnectedCallback() { + ce.el.prototype.disconnectedCallback?.apply(this, arguments); + } + adoptedCallback() { + ce.el.prototype.adoptedCallback?.apply(this, arguments); + } + }; + const DynamicWrapper = new Proxy(Wrapper, { + construct: function (_, args, newTarget) { + return Reflect.construct(ce.el, args, newTarget); + } + }); + try { + define.call(this, name, DynamicWrapper, options); + } catch (error) { + console.error(error); + throw new Error('Failed to define a custom element '+name); + } + } + }; + window.__reset_custom_elements = () => { + for (const ce of registered.values()) { + if (ce.registered) { + ce.el = HTMLElement; + ce.registered = false; + } + } + } + } + const __repl_exports = ${bundle.client?.code}; { const { mount, unmount, App, untrack } = __repl_exports; diff --git a/packages/repl/src/lib/Output/proxy.d.ts b/packages/repl/src/lib/Output/proxy.d.ts index b3f9fa8d1..87ea1ae97 100644 --- a/packages/repl/src/lib/Output/proxy.d.ts +++ b/packages/repl/src/lib/Output/proxy.d.ts @@ -1,4 +1,4 @@ export type Handlers = Record< - 'on_fetch_progress' | 'on_error' | 'on_unhandled_rejection' | 'on_console', + 'on_fetch_progress' | 'on_error' | 'on_unhandled_rejection' | 'on_iframe_reload' | 'on_console', (data: any) => void >;