diff --git a/.changeset/quiet-jars-search.md b/.changeset/quiet-jars-search.md new file mode 100644 index 0000000000..4d229da74a --- /dev/null +++ b/.changeset/quiet-jars-search.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: properly lazily evaluate RHS when checking for `assignment_value_stale` 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 1379669e77..5282f1ed64 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 @@ -179,7 +179,7 @@ function build_assignment(operator, left, right, context) { // in cases like `(object.items ??= []).push(value)`, we may need to warn // if the value gets proxified, since the proxy _isn't_ the thing that // will be pushed to. we do this by transforming it to something like - // `$.assign_nullish(object, 'items', [])` + // `$.assign_nullish(object, 'items', () => [])` let should_transform = dev && path.at(-1) !== 'ExpressionStatement' && @@ -236,7 +236,7 @@ function build_assignment(operator, left, right, context) { ? left.property : b.literal(/** @type {Identifier} */ (left.property).name) ), - right, + b.arrow([], right), b.literal(locate_node(left)) ) ) diff --git a/packages/svelte/src/internal/client/dev/assign.js b/packages/svelte/src/internal/client/dev/assign.js index 3b48e736b5..1cda7044b5 100644 --- a/packages/svelte/src/internal/client/dev/assign.js +++ b/packages/svelte/src/internal/client/dev/assign.js @@ -21,12 +21,12 @@ function compare(a, b, property, location) { /** * @param {any} object * @param {string} property - * @param {any} value + * @param {() => any} rhs_getter * @param {string} location */ -export function assign(object, property, value, location) { +export function assign(object, property, rhs_getter, location) { return compare( - (object[property] = value), + (object[property] = rhs_getter()), untrack(() => object[property]), property, location @@ -36,12 +36,12 @@ export function assign(object, property, value, location) { /** * @param {any} object * @param {string} property - * @param {any} value + * @param {() => any} rhs_getter * @param {string} location */ -export function assign_and(object, property, value, location) { +export function assign_and(object, property, rhs_getter, location) { return compare( - (object[property] &&= value), + (object[property] &&= rhs_getter()), untrack(() => object[property]), property, location @@ -51,12 +51,12 @@ export function assign_and(object, property, value, location) { /** * @param {any} object * @param {string} property - * @param {any} value + * @param {() => any} rhs_getter * @param {string} location */ -export function assign_or(object, property, value, location) { +export function assign_or(object, property, rhs_getter, location) { return compare( - (object[property] ||= value), + (object[property] ||= rhs_getter()), untrack(() => object[property]), property, location @@ -66,12 +66,12 @@ export function assign_or(object, property, value, location) { /** * @param {any} object * @param {string} property - * @param {any} value + * @param {() => any} rhs_getter * @param {string} location */ -export function assign_nullish(object, property, value, location) { +export function assign_nullish(object, property, rhs_getter, location) { return compare( - (object[property] ??= value), + (object[property] ??= rhs_getter()), untrack(() => object[property]), property, location diff --git a/packages/svelte/tests/runtime-runes/samples/assignment-value-stale-lazy-rhs/_config.js b/packages/svelte/tests/runtime-runes/samples/assignment-value-stale-lazy-rhs/_config.js new file mode 100644 index 0000000000..729eb1813a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/assignment-value-stale-lazy-rhs/_config.js @@ -0,0 +1,19 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + async test({ assert, target }) { + const button = /** @type {HTMLElement} */ (target.querySelector('button')); + await tick(); + assert.htmlEqual(target.innerHTML, `
count1: 0, count2: 0
`); + button.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `count1: 1, count2: 1
`); + button.click(); + await tick(); + assert.htmlEqual(target.innerHTML, `count1: 2, count2: 1
`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/assignment-value-stale-lazy-rhs/main.svelte b/packages/svelte/tests/runtime-runes/samples/assignment-value-stale-lazy-rhs/main.svelte new file mode 100644 index 0000000000..9937dc9713 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/assignment-value-stale-lazy-rhs/main.svelte @@ -0,0 +1,18 @@ + + + +count1: {count1}, count2: {count2}