diff --git a/.changeset/smart-fans-crash.md b/.changeset/smart-fans-crash.md new file mode 100644 index 0000000000..ca38575acb --- /dev/null +++ b/.changeset/smart-fans-crash.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly compile $effect.root in svelte modules diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index b9d6a5bc4d..581d4dcd03 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -411,6 +411,14 @@ const global_visitors = { return b.literal(false); } + if (rune === '$effect.root') { + const args = /** @type {import('estree').Expression[]} */ ( + node.arguments.map((arg) => context.visit(arg)) + ); + // Just call the function directly + return b.call(args[0]); + } + if (rune === '$state.snapshot') { return /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])); } @@ -571,7 +579,7 @@ const javascript_visitors_runes = { for (const declarator of node.declarations) { const init = declarator.init; const rune = get_rune(init, state.scope); - if (!rune || rune === '$effect.tracking' || rune === '$inspect') { + if (!rune || rune === '$effect.tracking' || rune === '$inspect' || rune === '$effect.root') { declarations.push(/** @type {import('estree').VariableDeclarator} */ (visit(declarator))); continue; } diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-3/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-root-3/_config.js new file mode 100644 index 0000000000..fa69197903 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-3/_config.js @@ -0,0 +1,30 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: '', + + async test({ assert, target, logs }) { + const [b1, b2, b3] = target.querySelectorAll('button'); + + flushSync(() => { + b1.click(); + b2.click(); + }); + + assert.deepEqual(logs, [0, 1]); + + flushSync(() => { + b3.click(); + }); + + assert.deepEqual(logs, [0, 1, 'cleanup 1', 'cleanup 2']); + + flushSync(() => { + b1.click(); + b2.click(); + }); + + assert.deepEqual(logs, [0, 1, 'cleanup 1', 'cleanup 2']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-root-3/main.svelte new file mode 100644 index 0000000000..fb37dfe47d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-3/main.svelte @@ -0,0 +1,11 @@ + + + + + diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-3/root.svelte.js b/packages/svelte/tests/runtime-runes/samples/effect-root-3/root.svelte.js new file mode 100644 index 0000000000..4e05cdf8ef --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-3/root.svelte.js @@ -0,0 +1,20 @@ +export function with_root(get_x) { + const cleanup = $effect.root(() => { + $effect(() => { + console.log(get_x()); + }); + + const nested_cleanup = $effect.root(() => { + return () => { + console.log('cleanup 2'); + }; + }); + + return () => { + console.log('cleanup 1'); + nested_cleanup(); + }; + }); + + return cleanup; +}