From c2f75dc7ba0169edd70747514b66a08dcf6bf3c7 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 16 May 2024 17:59:04 +0200 Subject: [PATCH] fix: use coarse-grained updates for derived expressions in legacy mode (#11652) fixes #11648 Also deduplicates the code a bit --- .changeset/fresh-beds-wash.md | 5 +++ .../3-transform/client/visitors/template.js | 33 +++++++++---------- .../component-prop-object/Child.svelte | 5 +++ .../samples/component-prop-object/_config.js | 10 ++++++ .../samples/component-prop-object/main.svelte | 11 +++++++ 5 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 .changeset/fresh-beds-wash.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/component-prop-object/Child.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/component-prop-object/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/component-prop-object/main.svelte diff --git a/.changeset/fresh-beds-wash.md b/.changeset/fresh-beds-wash.md new file mode 100644 index 0000000000..a0cb72d402 --- /dev/null +++ b/.changeset/fresh-beds-wash.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: use coarse-grained updates for derived expressions passed to props in legacy mode diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index e6f80d0000..4cc0c836bf 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -632,6 +632,15 @@ function collect_parent_each_blocks(context) { ); } +/** + * Svelte legacy mode should use safe equals in most places, runes mode shouldn't + * @param {import('../types.js').ComponentClientTransformState} state + * @param {import('estree').Expression} arg + */ +function create_derived(state, arg) { + return b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', arg); +} + /** * @param {import('#compiler').Component | import('#compiler').SvelteComponent | import('#compiler').SvelteSelf} node * @param {string} component_name @@ -745,7 +754,7 @@ function serialize_inline_component(node, component_name, context) { if (should_wrap_in_derived) { const id = b.id(context.state.scope.generate(attribute.name)); - context.state.init.push(b.var(id, b.call('$.derived', b.thunk(value)))); + context.state.init.push(b.var(id, create_derived(context.state, b.thunk(value)))); arg = b.call('$.get', id); } @@ -1649,9 +1658,8 @@ function serialize_template_literal(values, visit, state) { state.init.push( b.const( id, - b.call( - // In runes mode, we want things to be fine-grained - but not in legacy mode - state.analysis.runes ? '$.derived' : '$.derived_safe_equal', + create_derived( + state, b.thunk(/** @type {import('estree').Expression} */ (visit(node.expression))) ) ) @@ -1701,9 +1709,8 @@ export const template_visitors = { state.init.push( b.const( declaration.id, - b.call( - // In runes mode, we want things to be fine-grained - but not in legacy mode - state.analysis.runes ? '$.derived' : '$.derived_safe_equal', + create_derived( + state, b.thunk(/** @type {import('estree').Expression} */ (visit(declaration.init))) ) ) @@ -1738,10 +1745,7 @@ export const template_visitors = { ]) ); - state.init.push( - // In runes mode, we want things to be fine-grained - but not in legacy mode - b.const(tmp, b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', fn)) - ); + state.init.push(b.const(tmp, create_derived(state, fn))); // we need to eagerly evaluate the expression in order to hit any // 'Cannot access x before initialization' errors @@ -2995,12 +2999,7 @@ export const template_visitors = { const name = node.expression === null ? node.name : node.expression.name; return b.const( name, - b.call( - // in legacy mode, sources can be mutated but they're not fine-grained. - // Using the safe-equal derived version ensures the slot is still updated - state.analysis.runes ? '$.derived' : '$.derived_safe_equal', - b.thunk(b.member(b.id('$$slotProps'), b.id(node.name))) - ) + create_derived(state, b.thunk(b.member(b.id('$$slotProps'), b.id(node.name)))) ); } }, diff --git a/packages/svelte/tests/runtime-legacy/samples/component-prop-object/Child.svelte b/packages/svelte/tests/runtime-legacy/samples/component-prop-object/Child.svelte new file mode 100644 index 0000000000..fd191fa695 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/component-prop-object/Child.svelte @@ -0,0 +1,5 @@ + + +child: {x.y} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-legacy/samples/component-prop-object/_config.js b/packages/svelte/tests/runtime-legacy/samples/component-prop-object/_config.js new file mode 100644 index 0000000000..d37a72a2ee --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/component-prop-object/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + html: `child: 0 parent: 0 `, + + async test({ assert, target }) { + await target.querySelector('button')?.click(); + assert.htmlEqual(target.innerHTML, `child: 1 parent: 1 `); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/component-prop-object/main.svelte b/packages/svelte/tests/runtime-legacy/samples/component-prop-object/main.svelte new file mode 100644 index 0000000000..82aa5864e6 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/component-prop-object/main.svelte @@ -0,0 +1,11 @@ + + + + +parent: {x.y} + \ No newline at end of file