fix: properly lazily evaluate RHS when checking for assignment_value_stale (#17906)

Fixes #17904 by wrapping the RHS in a `() =>`.

### Before submitting the PR, please make sure you do the following

- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [x] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).

### Tests and linting

- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
always-populate-batch-values
Conduitry 4 months ago committed by GitHub
parent 0e8f49b25f
commit 58f161dee2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: properly lazily evaluate RHS when checking for `assignment_value_stale`

@ -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))
)
)

@ -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

@ -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, `<button>go</button><p>count1: 0, count2: 0</p>`);
button.click();
await tick();
assert.htmlEqual(target.innerHTML, `<button>go</button><p>count1: 1, count2: 1</p>`);
button.click();
await tick();
assert.htmlEqual(target.innerHTML, `<button>go</button><p>count1: 2, count2: 1</p>`);
}
});

@ -0,0 +1,18 @@
<script>
let count1 = $state(0);
let count2 = $state(0);
let cache = $state({});
function go() {
count1++;
const value = cache.value ??= get_value();
}
function get_value() {
count2++;
return 42;
}
</script>
<button onclick={go}>go</button>
<p>count1: {count1}, count2: {count2}</p>
Loading…
Cancel
Save