import { WebContainer } from '@webcontainer/api'; import { WORK_DIR_NAME } from '~/utils/constants'; import { cleanStackTrace } from '~/utils/stacktrace'; interface WebContainerContext { loaded: boolean; } export const webcontainerContext: WebContainerContext = import.meta.hot?.data.webcontainerContext ?? { loaded: false, }; if (import.meta.hot) { import.meta.hot.data.webcontainerContext = webcontainerContext; } export let webcontainer: Promise = new Promise(() => { // noop for ssr }); if (!import.meta.env.SSR) { webcontainer = import.meta.hot?.data.webcontainer ?? Promise.resolve() .then(() => { return WebContainer.boot({ workdirName: WORK_DIR_NAME, forwardPreviewErrors: true, // Enable error forwarding from iframes }); }) .then(async (webcontainer) => { webcontainerContext.loaded = true; const { workbenchStore } = await import('~/lib/stores/workbench'); // Listen for preview errors webcontainer.on('preview-message', (message) => { console.log('WebContainer preview message:', message); // Handle both uncaught exceptions and unhandled promise rejections if (message.type === 'PREVIEW_UNCAUGHT_EXCEPTION' || message.type === 'PREVIEW_UNHANDLED_REJECTION') { const isPromise = message.type === 'PREVIEW_UNHANDLED_REJECTION'; workbenchStore.actionAlert.set({ type: 'preview', title: isPromise ? 'Unhandled Promise Rejection' : 'Uncaught Exception', description: message.message, content: `Error occurred at ${message.pathname}${message.search}${message.hash}\nPort: ${message.port}\n\nStack trace:\n${cleanStackTrace(message.stack || '')}`, source: 'preview', }); } }); return webcontainer; }); if (import.meta.hot) { import.meta.hot.data.webcontainer = webcontainer; } }