From c7bdef595b7673c952ec4c6d3b7bee0916eb477c Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Mon, 29 Apr 2024 09:31:17 +0200 Subject: [PATCH] fix: store from props hoist wrong param (#11367) Fixes #11355 --------- Co-authored-by: Simon Holthausen --- .changeset/tiny-moose-kiss.md | 5 ++++ .../phases/3-transform/client/utils.js | 27 +++++++++---------- .../store-from-props-hoisting/_config.js | 18 +++++++++++++ .../store-from-props-hoisting/child.svelte | 8 ++++++ .../store-from-props-hoisting/main.svelte | 7 +++++ 5 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 .changeset/tiny-moose-kiss.md create mode 100644 packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/main.svelte diff --git a/.changeset/tiny-moose-kiss.md b/.changeset/tiny-moose-kiss.md new file mode 100644 index 0000000000..1c4ec34c60 --- /dev/null +++ b/.changeset/tiny-moose-kiss.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: ensure store from props is hoisted correctly diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index 481caf6561..e6fdb902f0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -456,27 +456,30 @@ function get_hoistable_params(node, context) { /** @type {import('estree').Identifier[]} */ const params = []; - let added_props = false; /** - * we only want to push if it's not already present to avoid name clashing + * We only want to push if it's not already present to avoid name clashing * @param {import('estree').Identifier} id */ - function safe_push(id) { + function push_unique(id) { if (!params.find((param) => param.name === id.name)) { params.push(id); } } for (const [reference] of scope.references) { - const binding = scope.get(reference); + let binding = scope.get(reference); if (binding !== null && !scope.declarations.has(reference) && binding.initial !== node) { if (binding.kind === 'store_sub') { // We need both the subscription for getting the value and the store for updating - safe_push(b.id(binding.node.name.slice(1))); - safe_push(b.id(binding.node.name)); - } else if ( + push_unique(b.id(binding.node.name)); + binding = /** @type {import('#compiler').Binding} */ ( + scope.get(binding.node.name.slice(1)) + ); + } + + if ( // If it's a destructured derived binding, then we can extract the derived signal reference and use that. binding.expression !== null && typeof binding.expression !== 'function' && @@ -486,7 +489,7 @@ function get_hoistable_params(node, context) { binding.expression.object.callee.name === '$.get' && binding.expression.object.arguments[0].type === 'Identifier' ) { - safe_push(b.id(binding.expression.object.arguments[0].name)); + push_unique(b.id(binding.expression.object.arguments[0].name)); } else if ( // If we are referencing a simple $$props value, then we need to reference the object property instead (binding.kind === 'prop' || binding.kind === 'bindable_prop') && @@ -494,14 +497,10 @@ function get_hoistable_params(node, context) { binding.initial === null && !context.state.analysis.accessors ) { - // Handle $$props.something use-cases - if (!added_props) { - added_props = true; - safe_push(b.id('$$props')); - } + push_unique(b.id('$$props')); } else { // create a copy to remove start/end tags which would mess up source maps - safe_push(b.id(binding.node.name)); + push_unique(b.id(binding.node.name)); } } } diff --git a/packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/_config.js b/packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/_config.js new file mode 100644 index 0000000000..bd5593eb08 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/_config.js @@ -0,0 +1,18 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + async test({ assert, target }) { + const button = target.querySelector('button'); + await button?.click(); + + assert.htmlEqual( + target.innerHTML, + ` + + ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/child.svelte b/packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/child.svelte new file mode 100644 index 0000000000..77a04f3e9f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/child.svelte @@ -0,0 +1,8 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/main.svelte b/packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/main.svelte new file mode 100644 index 0000000000..0e7390d164 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-from-props-hoisting/main.svelte @@ -0,0 +1,7 @@ + + + \ No newline at end of file