diff --git a/.changeset/healthy-dancers-play.md b/.changeset/healthy-dancers-play.md new file mode 100644 index 0000000000..6fe572bb67 --- /dev/null +++ b/.changeset/healthy-dancers-play.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve prop binding warning validation for stores diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BindDirective.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BindDirective.js index 611e5e570d..8b9064d59a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BindDirective.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BindDirective.js @@ -7,7 +7,7 @@ import * as b from '../../../../utils/builders.js'; import { binding_properties } from '../../../bindings.js'; import { build_setter } from '../utils.js'; import { build_attribute_value } from './shared/element.js'; -import { build_bind_this, build_validate_binding } from './shared/utils.js'; +import { build_bind_this, validate_binding } from './shared/utils.js'; /** * @param {BindDirective} node @@ -30,12 +30,10 @@ export function BindDirective(node, context) { )) && !is_ignored(node, 'binding_property_non_reactive') ) { - context.state.init.push( - build_validate_binding( - context.state, - node, - /**@type {MemberExpression} */ (context.visit(expression)) - ) + validate_binding( + context.state, + node, + /**@type {MemberExpression} */ (context.visit(expression)) ); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index ab1abc82da..6416e26318 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -6,7 +6,7 @@ import { get_attribute_chunks } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; import { is_element_node } from '../../../../nodes.js'; import { create_derived, build_setter } from '../../utils.js'; -import { build_bind_this, build_validate_binding } from '../shared/utils.js'; +import { build_bind_this, validate_binding } from '../shared/utils.js'; import { build_attribute_value } from '../shared/element.js'; import { build_event_handler } from './events.js'; @@ -151,7 +151,7 @@ export function build_component(node, component_name, context, anchor = context. context.state.analysis.runes && !is_ignored(node, 'binding_property_non_reactive') ) { - context.state.init.push(build_validate_binding(context.state, attribute, expression)); + validate_binding(context.state, attribute, expression); } if (attribute.name === 'this') { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index 04b7b36186..874ebe3131 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -204,28 +204,30 @@ export function build_bind_this(expression, value, { state, visit }) { * @param {BindDirective} binding * @param {MemberExpression} expression */ -export function build_validate_binding(state, binding, expression) { - const string = state.analysis.source.slice(binding.start, binding.end); - - const get_object = b.thunk(/** @type {Expression} */ (expression.object)); - const get_property = b.thunk( - /** @type {Expression} */ ( - expression.computed - ? expression.property - : b.literal(/** @type {Identifier} */ (expression.property).name) - ) - ); +export function validate_binding(state, binding, expression) { + // If we are referencing a $store.foo then we don't need to add validation + const left = object(binding.expression); + const left_binding = left && state.scope.get(left.name); + if (left_binding?.kind === 'store_sub') return; const loc = locator(binding.start); - return b.stmt( - b.call( - '$.validate_binding', - b.literal(string), - get_object, - get_property, - loc && b.literal(loc.line), - loc && b.literal(loc.column) + state.init.push( + b.stmt( + b.call( + '$.validate_binding', + b.literal(state.analysis.source.slice(binding.start, binding.end)), + b.thunk(/** @type {Expression} */ (expression.object)), + b.thunk( + /** @type {Expression} */ ( + expression.computed + ? expression.property + : b.literal(/** @type {Identifier} */ (expression.property).name) + ) + ), + loc && b.literal(loc.line), + loc && b.literal(loc.column) + ) ) ); } diff --git a/packages/svelte/tests/runtime-runes/samples/binding-property-store/Child.svelte b/packages/svelte/tests/runtime-runes/samples/binding-property-store/Child.svelte new file mode 100644 index 0000000000..c1bd5ff3ed --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/binding-property-store/Child.svelte @@ -0,0 +1,5 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/binding-property-store/_config.js b/packages/svelte/tests/runtime-runes/samples/binding-property-store/_config.js new file mode 100644 index 0000000000..e93067eb9d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/binding-property-store/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + mode: ['client'], + compileOptions: { + dev: true + }, + async test({ warnings, assert }) { + assert.deepEqual(warnings, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/binding-property-store/main.svelte b/packages/svelte/tests/runtime-runes/samples/binding-property-store/main.svelte new file mode 100644 index 0000000000..206d0ceb44 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/binding-property-store/main.svelte @@ -0,0 +1,12 @@ + + + + +

{$a.value}

+

{$b.nested.value}