Skip to content

Commit c7bdef5

Browse files
fix: store from props hoist wrong param (#11367)
Fixes #11355 --------- Co-authored-by: Simon Holthausen <[email protected]>
1 parent 68071f7 commit c7bdef5

File tree

5 files changed

+51
-14
lines changed

5 files changed

+51
-14
lines changed

.changeset/tiny-moose-kiss.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"svelte": patch
3+
---
4+
5+
fix: ensure store from props is hoisted correctly

packages/svelte/src/compiler/phases/3-transform/client/utils.js

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -456,27 +456,30 @@ function get_hoistable_params(node, context) {
456456

457457
/** @type {import('estree').Identifier[]} */
458458
const params = [];
459-
let added_props = false;
460459

461460
/**
462-
* we only want to push if it's not already present to avoid name clashing
461+
* We only want to push if it's not already present to avoid name clashing
463462
* @param {import('estree').Identifier} id
464463
*/
465-
function safe_push(id) {
464+
function push_unique(id) {
466465
if (!params.find((param) => param.name === id.name)) {
467466
params.push(id);
468467
}
469468
}
470469

471470
for (const [reference] of scope.references) {
472-
const binding = scope.get(reference);
471+
let binding = scope.get(reference);
473472

474473
if (binding !== null && !scope.declarations.has(reference) && binding.initial !== node) {
475474
if (binding.kind === 'store_sub') {
476475
// We need both the subscription for getting the value and the store for updating
477-
safe_push(b.id(binding.node.name.slice(1)));
478-
safe_push(b.id(binding.node.name));
479-
} else if (
476+
push_unique(b.id(binding.node.name));
477+
binding = /** @type {import('#compiler').Binding} */ (
478+
scope.get(binding.node.name.slice(1))
479+
);
480+
}
481+
482+
if (
480483
// If it's a destructured derived binding, then we can extract the derived signal reference and use that.
481484
binding.expression !== null &&
482485
typeof binding.expression !== 'function' &&
@@ -486,22 +489,18 @@ function get_hoistable_params(node, context) {
486489
binding.expression.object.callee.name === '$.get' &&
487490
binding.expression.object.arguments[0].type === 'Identifier'
488491
) {
489-
safe_push(b.id(binding.expression.object.arguments[0].name));
492+
push_unique(b.id(binding.expression.object.arguments[0].name));
490493
} else if (
491494
// If we are referencing a simple $$props value, then we need to reference the object property instead
492495
(binding.kind === 'prop' || binding.kind === 'bindable_prop') &&
493496
!binding.reassigned &&
494497
binding.initial === null &&
495498
!context.state.analysis.accessors
496499
) {
497-
// Handle $$props.something use-cases
498-
if (!added_props) {
499-
added_props = true;
500-
safe_push(b.id('$$props'));
501-
}
500+
push_unique(b.id('$$props'));
502501
} else {
503502
// create a copy to remove start/end tags which would mess up source maps
504-
safe_push(b.id(binding.node.name));
503+
push_unique(b.id(binding.node.name));
505504
}
506505
}
507506
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
compileOptions: {
5+
dev: true
6+
},
7+
async test({ assert, target }) {
8+
const button = target.querySelector('button');
9+
await button?.click();
10+
11+
assert.htmlEqual(
12+
target.innerHTML,
13+
`
14+
<button>1</button>
15+
`
16+
);
17+
}
18+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script>
2+
const { attrs } = $props();
3+
function increment() {
4+
$attrs.count++;
5+
}
6+
</script>
7+
8+
<button onclick={increment}>{$attrs.count}</button>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
import { writable } from "svelte/store";
3+
import Child from "./child.svelte";
4+
const attrs = writable({ count: 0 });
5+
</script>
6+
7+
<Child {attrs} />

0 commit comments

Comments
 (0)