diff --git a/.changeset/plain-dancers-double.md b/.changeset/plain-dancers-double.md new file mode 100644 index 0000000000..ab47b8e4c7 --- /dev/null +++ b/.changeset/plain-dancers-double.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: more conservative assignment_value_stale warnings diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js index 731569aaae..0f6a619357 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js @@ -162,7 +162,10 @@ function build_assignment(operator, left, right, context) { // will be pushed to. we do this by transforming it to something like // `$.assign_nullish(object, 'items', [])` let should_transform = - dev && path.at(-1) !== 'ExpressionStatement' && is_non_coercive_operator(operator); + dev && + path.at(-1) !== 'ExpressionStatement' && + is_non_coercive_operator(operator) && + !context.state.scope.evaluate(right).is_primitive; // special case — ignore `onclick={() => (...)}` if ( diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 2a0c76f756..52efd93210 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -228,6 +228,13 @@ class Evaluation { */ is_number = true; + /** + * True if the value is known to be a primitive + * @readonly + * @type {boolean} + */ + is_primitive = true; + /** * True if the value is known to be a function * @readonly @@ -577,6 +584,7 @@ class Evaluation { if (value === UNKNOWN) { this.has_unknown = true; + this.is_primitive = false; } } diff --git a/packages/svelte/src/internal/client/dev/assign.js b/packages/svelte/src/internal/client/dev/assign.js index d9ef7497d5..3b48e736b5 100644 --- a/packages/svelte/src/internal/client/dev/assign.js +++ b/packages/svelte/src/internal/client/dev/assign.js @@ -1,3 +1,4 @@ +import { STATE_SYMBOL } from '#client/constants'; import { sanitize_location } from '../../../utils.js'; import { untrack } from '../runtime.js'; import * as w from '../warnings.js'; @@ -10,7 +11,7 @@ import * as w from '../warnings.js'; * @param {string} location */ function compare(a, b, property, location) { - if (a !== b) { + if (a !== b && typeof b === 'object' && STATE_SYMBOL in b) { w.assignment_value_stale(property, /** @type {string} */ (sanitize_location(location))); } diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js index 4462f492fa..2ff2634822 100644 --- a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js @@ -6,31 +6,26 @@ export default test({ dev: true }, - html: `
x
`, - test({ assert, target, warnings }) { - const btn = target.querySelector('button'); - ok(btn); + const [button1, button2, button3] = target.querySelectorAll('button'); + ok(button1); - flushSync(() => btn.click()); - assert.htmlEqual( - target.innerHTML, - `
x
` - ); + flushSync(() => button1.click()); + assert.htmlEqual(button1.innerHTML, `items: []`); - flushSync(() => btn.click()); - assert.htmlEqual( - target.innerHTML, - `
x
` - ); + flushSync(() => button1.click()); + assert.htmlEqual(button1.innerHTML, `items: [0]`); const input = target.querySelector('input'); ok(input); input.checked = true; flushSync(() => input.dispatchEvent(new Event('change', { bubbles: true }))); + flushSync(() => button2.click()); + flushSync(() => button3.click()); + assert.deepEqual(warnings, [ - 'Assignment to `items` property (main.svelte:9:24) will evaluate to the right-hand side, not the value of `items` following the assignment. This may result in unexpected behaviour.' + 'Assignment to `items` property (main.svelte:17:24) will evaluate to the right-hand side, not the value of `items` following the assignment. This may result in unexpected behaviour.' ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte index a79fe873b7..02320a3128 100644 --- a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte @@ -1,9 +1,17 @@ +