We are porting our svelte 3 based app to 5. It has a dynamic widget compilation system that allows end-users to create their own svelte widgets (see this post for background). Compilation seems to work fine but when mounting a widget it throws this error:
Svelte error: effect_orphan
$effectcan only be used inside an effect (e.g. during component initialization)
I have tried manually mounting with the mount command and also <svelte:component for initialization. No matter what it always throws the effect_orphan error. Here is the code that wraps the user defined widget and mounts it:
<script> import {onMount} from 'svelte'; export let title = ''; export let name = ''; export let config = {}; export let show_container = true; export let percent_height = 100; export let percent_width = 100; let widget; let dynamicComponent = null; let componentInstance = null; let lastLoadedName = ''; $: if (name && name !== lastLoadedName){ if (typeof window !== 'undefined'){ loadComponent().then(f=>{}).catch(x=> console.warn(x.message)); } } onMount(async function () { console.log('svelte widget mounted:', name); }) export async function loadComponent(){ if (!name) return; let url = `/widgets/${name}.js?${Math.floor(Math.random() * 1000000).toString()}` try{ let comp = await import(url); console.log('loading svelte widget component:', url, config.name); // Set component for svelte:component to render // I have also tried manually mounting with mount() dynamicComponent = comp.default; lastLoadedName = name; }catch(x){ console.warn('error loading svelte component:', url, x) dynamicComponent = null; } }</script>{#if dynamicComponent}<svelte:component bind:this={componentInstance} this={dynamicComponent} {config} />{/if}Here is the server side compile function:
try { let srcFile = path.join(basePath, w.name +'.svelte'); fs.writeFileSync(srcFile, w.config.source); console.log('writing widget source file:', srcFile); const fileName = path.basename(srcFile, '.svelte'); await build({ configFile: false, build: { // {build} from 'vite' lib: { entry: srcFile, name: fileName, fileName: fileName, formats: ['es'] }, outDir: basePath, sourcemap: true, minify: false, emptyOutDir: false, // Don't clean the output directory rollupOptions: { external: [], output: { inlineDynamicImports: true } } }, plugins: [ svelte({ compilerOptions: { dev: false, hydratable: false }, emitCss: false }), nodeResolve({ browser: true, dedupe: ['svelte'], preferBuiltins: false, exportConditions: ['svelte'] }), commonjs() ] }); // Post-process the compiled file to fix import paths const compiledPath = path.join(basePath, fileName +'.js'); if (fs.existsSync(compiledPath)) { let content = fs.readFileSync(compiledPath, 'utf8'); // Replace bare imports with CDN or bundled versions content = content.replace(/import\s+{([^}]+)}\s+from\s+["']svelte["']/g,'import {$1} from "/node_modules/svelte/index.js"'); content = content.replace(/import\s+{([^}]+)}\s+from\s+["']svelte\/([^"']+)["']/g,'import {$1} from "/node_modules/svelte/$2.js"'); fs.writeFileSync(compiledPath, content); } console.log(`Successfully compiled widget: ${fileName}`);} catch (error) { console.warn(`Error compiling widget ${srcFile}:`, error.message); return {error: error, message: error.message, stack: error.stack};}I have had a little more success setting the compatibility mode to svelte 4 during build() but this isn't a good long term solution. Any ideas what is at the root of the effect_orphan error? 🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏