From e8c3729fc94ae948983368a50c4745fc04627496 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 8 Jul 2024 18:27:31 +0200 Subject: [PATCH] fix: make `$state` component exports settable (#12345) * fix: make `$state` component exports settable fixes #11983 * failing test * fix --------- Co-authored-by: Rich Harris --- .changeset/tidy-lizards-happen.md | 5 +++ .../src/compiler/phases/2-analyze/index.js | 3 ++ .../3-transform/client/transform-client.js | 32 ++++++++++++++++--- .../samples/exports-3/_config.js | 5 ++- .../samples/exports-3/main.svelte | 2 +- .../samples/exports-4/_config.js | 12 +++++++ .../samples/exports-4/main.svelte | 7 ++++ .../samples/exports-4/sub.svelte | 9 ++++++ 8 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 .changeset/tidy-lizards-happen.md create mode 100644 packages/svelte/tests/runtime-runes/samples/exports-4/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/exports-4/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/exports-4/sub.svelte diff --git a/.changeset/tidy-lizards-happen.md b/.changeset/tidy-lizards-happen.md new file mode 100644 index 0000000000..1b280937a0 --- /dev/null +++ b/.changeset/tidy-lizards-happen.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: make `$state` component exports settable diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 4a15f0e46f..aab31869cd 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -1010,6 +1010,9 @@ const runes_scope_tweaker = { name: node.local.name, alias: node.exported.name }); + + const binding = state.scope.get(node.local.name); + if (binding) binding.reassigned = true; }, ExportNamedDeclaration(node, { next, state }) { if (!node.declaration || state.ast_type !== 'instance') { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index d0dd2315a5..a8438c0aca 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -198,14 +198,38 @@ export function client_component(source, analysis, options) { } /** @type {Array} */ - const component_returned_object = analysis.exports.map(({ name, alias }) => { + const component_returned_object = analysis.exports.flatMap(({ name, alias }) => { + const binding = instance_state.scope.get(name); const expression = serialize_get_binding(b.id(name), instance_state); + const getter = b.get(alias ?? name, [b.return(expression)]); + + if (expression.type === 'Identifier') { + if (binding?.declaration_kind === 'let' || binding?.declaration_kind === 'var') { + return [ + getter, + b.set(alias ?? name, [b.stmt(b.assignment('=', expression, b.id('$$value')))]) + ]; + } else if (!options.dev) { + return b.init(alias ?? name, expression); + } + } - if (expression.type === 'Identifier' && !options.dev) { - return b.init(alias ?? name, expression); + if (binding?.kind === 'state' || binding?.kind === 'frozen_state') { + return [ + getter, + b.set(alias ?? name, [ + b.stmt( + b.call( + '$.set', + b.id(name), + b.call(binding.kind === 'state' ? '$.proxy' : '$.freeze', b.id('$$value')) + ) + ) + ]) + ]; } - return b.get(alias ?? name, [b.return(expression)]); + return getter; }); const properties = [...analysis.instance.scope.declarations].filter( diff --git a/packages/svelte/tests/runtime-runes/samples/exports-3/_config.js b/packages/svelte/tests/runtime-runes/samples/exports-3/_config.js index cf22671d9f..807fed2aa6 100644 --- a/packages/svelte/tests/runtime-runes/samples/exports-3/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/exports-3/_config.js @@ -4,10 +4,9 @@ import { test } from '../../test'; export default test({ test({ assert, target }) { assert.htmlEqual(target.innerHTML, `0 0 `); - const [btn] = target.querySelectorAll('button'); + const btn = target.querySelector('button'); - btn?.click(); - flushSync(); + flushSync(() => btn?.click()); assert.htmlEqual(target.innerHTML, '1 2 '); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/exports-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/exports-3/main.svelte index b1cabb399f..a68a40f300 100644 --- a/packages/svelte/tests/runtime-runes/samples/exports-3/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/exports-3/main.svelte @@ -1,5 +1,5 @@ diff --git a/packages/svelte/tests/runtime-runes/samples/exports-4/_config.js b/packages/svelte/tests/runtime-runes/samples/exports-4/_config.js new file mode 100644 index 0000000000..807fed2aa6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/exports-4/_config.js @@ -0,0 +1,12 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, target }) { + assert.htmlEqual(target.innerHTML, `0 0 `); + const btn = target.querySelector('button'); + + flushSync(() => btn?.click()); + assert.htmlEqual(target.innerHTML, '1 2 '); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/exports-4/main.svelte b/packages/svelte/tests/runtime-runes/samples/exports-4/main.svelte new file mode 100644 index 0000000000..85adcffbe3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/exports-4/main.svelte @@ -0,0 +1,7 @@ + + + + diff --git a/packages/svelte/tests/runtime-runes/samples/exports-4/sub.svelte b/packages/svelte/tests/runtime-runes/samples/exports-4/sub.svelte new file mode 100644 index 0000000000..75b0091235 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/exports-4/sub.svelte @@ -0,0 +1,9 @@ + + +{count} +{doubled}