Skip to content

Commit 1942f87

Browse files
trueadmRich-Harris
andauthored
fix: improve prop binding warning validation for stores (#12745)
* fix: improve prop binding warning validation for stores * ts * address feedback * add comment * failing test * fix/simplify --------- Co-authored-by: Rich Harris <[email protected]>
1 parent d06174e commit 1942f87

File tree

7 files changed

+61
-28
lines changed

7 files changed

+61
-28
lines changed

.changeset/healthy-dancers-play.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: improve prop binding warning validation for stores

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as b from '../../../../utils/builders.js';
77
import { binding_properties } from '../../../bindings.js';
88
import { build_setter } from '../utils.js';
99
import { build_attribute_value } from './shared/element.js';
10-
import { build_bind_this, build_validate_binding } from './shared/utils.js';
10+
import { build_bind_this, validate_binding } from './shared/utils.js';
1111

1212
/**
1313
* @param {BindDirective} node
@@ -30,12 +30,10 @@ export function BindDirective(node, context) {
3030
)) &&
3131
!is_ignored(node, 'binding_property_non_reactive')
3232
) {
33-
context.state.init.push(
34-
build_validate_binding(
35-
context.state,
36-
node,
37-
/**@type {MemberExpression} */ (context.visit(expression))
38-
)
33+
validate_binding(
34+
context.state,
35+
node,
36+
/**@type {MemberExpression} */ (context.visit(expression))
3937
);
4038
}
4139

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { get_attribute_chunks } from '../../../../../utils/ast.js';
66
import * as b from '../../../../../utils/builders.js';
77
import { is_element_node } from '../../../../nodes.js';
88
import { create_derived, build_setter } from '../../utils.js';
9-
import { build_bind_this, build_validate_binding } from '../shared/utils.js';
9+
import { build_bind_this, validate_binding } from '../shared/utils.js';
1010
import { build_attribute_value } from '../shared/element.js';
1111
import { build_event_handler } from './events.js';
1212

@@ -151,7 +151,7 @@ export function build_component(node, component_name, context, anchor = context.
151151
context.state.analysis.runes &&
152152
!is_ignored(node, 'binding_property_non_reactive')
153153
) {
154-
context.state.init.push(build_validate_binding(context.state, attribute, expression));
154+
validate_binding(context.state, attribute, expression);
155155
}
156156

157157
if (attribute.name === 'this') {

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

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -204,28 +204,30 @@ export function build_bind_this(expression, value, { state, visit }) {
204204
* @param {BindDirective} binding
205205
* @param {MemberExpression} expression
206206
*/
207-
export function build_validate_binding(state, binding, expression) {
208-
const string = state.analysis.source.slice(binding.start, binding.end);
209-
210-
const get_object = b.thunk(/** @type {Expression} */ (expression.object));
211-
const get_property = b.thunk(
212-
/** @type {Expression} */ (
213-
expression.computed
214-
? expression.property
215-
: b.literal(/** @type {Identifier} */ (expression.property).name)
216-
)
217-
);
207+
export function validate_binding(state, binding, expression) {
208+
// If we are referencing a $store.foo then we don't need to add validation
209+
const left = object(binding.expression);
210+
const left_binding = left && state.scope.get(left.name);
211+
if (left_binding?.kind === 'store_sub') return;
218212

219213
const loc = locator(binding.start);
220214

221-
return b.stmt(
222-
b.call(
223-
'$.validate_binding',
224-
b.literal(string),
225-
get_object,
226-
get_property,
227-
loc && b.literal(loc.line),
228-
loc && b.literal(loc.column)
215+
state.init.push(
216+
b.stmt(
217+
b.call(
218+
'$.validate_binding',
219+
b.literal(state.analysis.source.slice(binding.start, binding.end)),
220+
b.thunk(/** @type {Expression} */ (expression.object)),
221+
b.thunk(
222+
/** @type {Expression} */ (
223+
expression.computed
224+
? expression.property
225+
: b.literal(/** @type {Identifier} */ (expression.property).name)
226+
)
227+
),
228+
loc && b.literal(loc.line),
229+
loc && b.literal(loc.column)
230+
)
229231
)
230232
);
231233
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let { value = $bindable() } = $props();
3+
</script>
4+
5+
<input type="number" bind:value />
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
mode: ['client'],
5+
compileOptions: {
6+
dev: true
7+
},
8+
async test({ warnings, assert }) {
9+
assert.deepEqual(warnings, []);
10+
}
11+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
import { writable } from 'svelte/store';
3+
import Child from './Child.svelte';
4+
5+
let a = writable({ value: 0 });
6+
let b = writable({ nested: { value: 0 } });
7+
</script>
8+
9+
<Child bind:value={$a.value} />
10+
<Child bind:value={$b.nested.value} />
11+
<p>{$a.value}</p>
12+
<p>{$b.nested.value}</p>

0 commit comments

Comments
 (0)