fix: ensure function calls to identifiers are made reactive (#13264)

* fix: ensure function calls to identifiers are made reactive

* update test

* we have has_local at home

* add note

* make it a TODO

* Update .changeset/red-ladybugs-turn.md

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/13257/head
Dominic Gannaway 2 months ago committed by GitHub
parent 9864138022
commit 3c97c0a1a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: treat pure call expressions as potentially reactive if they reference local bindings

@ -143,11 +143,6 @@ export function CallExpression(node, context) {
break; break;
} }
if (context.state.expression && !is_pure(node.callee, context)) {
context.state.expression.has_call = true;
context.state.expression.has_state = true;
}
if (context.state.render_tag) { if (context.state.render_tag) {
// Find out which of the render tag arguments contains this call expression // Find out which of the render tag arguments contains this call expression
const arg_idx = unwrap_optional(context.state.render_tag.expression).arguments.findIndex( const arg_idx = unwrap_optional(context.state.render_tag.expression).arguments.findIndex(
@ -174,4 +169,15 @@ export function CallExpression(node, context) {
} else { } else {
context.next(); context.next();
} }
if (context.state.expression) {
// TODO We assume that any dependencies are stateful, which isn't necessarily the case — see
// https://github.com/sveltejs/svelte/issues/13266. This check also includes dependencies
// outside the call expression itself (e.g. `{blah && pure()}`) resulting in additional
// false positives, but for now we accept that trade-off
if (!is_pure(node.callee, context) || context.state.expression.dependencies.size > 0) {
context.state.expression.has_call = true;
context.state.expression.has_state = true;
}
}
} }

@ -1,3 +1,3 @@
<script module> <script module>
let foo = true; let foo = true;
</script> </script>

@ -0,0 +1,11 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
const [b1] = target.querySelectorAll('button');
b1.click();
flushSync();
assert.htmlEqual(target.innerHTML, `<button>Replace</button>\n9,10,11`);
}
});

@ -0,0 +1,11 @@
<script>
import { obj } from "./Data.svelte.js";
function replaceProp() {
Object.assign(obj, {a:9, b:10, c:11});
}
</script>
<button onclick={replaceProp}>Replace</button>
{Object.values(obj)}

@ -7,4 +7,4 @@ const __DECLARED_ASSET_3__ = "__VITE_ASSET__2AM7_y_g__";
export default function Inline_module_vars($$payload) { export default function Inline_module_vars($$payload) {
$$payload.out += `<picture><source${$.attr("srcset", __DECLARED_ASSET_0__)} type="image/avif"> <source${$.attr("srcset", __DECLARED_ASSET_1__)} type="image/webp"> <source${$.attr("srcset", __DECLARED_ASSET_2__)} type="image/png"> <img${$.attr("src", __DECLARED_ASSET_3__)} alt="production test" width="1440" height="1440"></picture>`; $$payload.out += `<picture><source${$.attr("srcset", __DECLARED_ASSET_0__)} type="image/avif"> <source${$.attr("srcset", __DECLARED_ASSET_1__)} type="image/webp"> <source${$.attr("srcset", __DECLARED_ASSET_2__)} type="image/png"> <img${$.attr("src", __DECLARED_ASSET_3__)} alt="production test" width="1440" height="1440"></picture>`;
} }

@ -4,14 +4,10 @@ import * as $ from "svelte/internal/client";
var root = $.template(`<p></p> <p></p> <!>`, 1); var root = $.template(`<p></p> <p></p> <!>`, 1);
export default function Purity($$anchor) { export default function Purity($$anchor) {
let min = 0;
let max = 100;
let number = 50;
let value = 'hello';
var fragment = root(); var fragment = root();
var p = $.first_child(fragment); var p = $.first_child(fragment);
p.textContent = Math.max(min, Math.min(max, number)); p.textContent = Math.max(0, Math.min(0, 100));
var p_1 = $.sibling(p, 2); var p_1 = $.sibling(p, 2);
@ -19,6 +15,6 @@ export default function Purity($$anchor) {
var node = $.sibling(p_1, 2); var node = $.sibling(p_1, 2);
Child(node, { prop: encodeURIComponent(value) }); Child(node, { prop: encodeURIComponent('hello') });
$.append($$anchor, fragment); $.append($$anchor, fragment);
} }

@ -1,12 +1,7 @@
import * as $ from "svelte/internal/server"; import * as $ from "svelte/internal/server";
export default function Purity($$payload) { export default function Purity($$payload) {
let min = 0; $$payload.out += `<p>${$.escape(Math.max(0, Math.min(0, 100)))}</p> <p>${$.escape(location.href)}</p> `;
let max = 100; Child($$payload, { prop: encodeURIComponent('hello') });
let number = 50;
let value = 'hello';
$$payload.out += `<p>${$.escape(Math.max(min, Math.min(max, number)))}</p> <p>${$.escape(location.href)}</p> `;
Child($$payload, { prop: encodeURIComponent(value) });
$$payload.out += `<!---->`; $$payload.out += `<!---->`;
} }

@ -1,12 +1,4 @@
<script> <p>{Math.max(0, Math.min(0, 100))}</p>
let min = 0;
let max = 100;
let number = 50;
let value = 'hello';
</script>
<p>{Math.max(min, Math.min(max, number))}</p>
<p>{location.href}</p> <p>{location.href}</p>
<Child prop={encodeURIComponent(value)} /> <Child prop={encodeURIComponent('hello')} />

Loading…
Cancel
Save