From edcbb0e6403e5b90e72c3ef1f46a938a52cc2d53 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 1 Apr 2026 04:36:26 -0400 Subject: [PATCH] fix: invalidate `@const` tags based on visible references in legacy mode (#18041) closes #17992 --- .changeset/const-tags.md | 5 +++ .../2-analyze/visitors/shared/function.js | 2 +- .../const-tag-depends-on-const-tag/_config.js | 34 +++++++++++++++++++ .../main.svelte | 15 ++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 .changeset/const-tags.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/const-tag-depends-on-const-tag/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/const-tag-depends-on-const-tag/main.svelte diff --git a/.changeset/const-tags.md b/.changeset/const-tags.md new file mode 100644 index 0000000000..82765c5038 --- /dev/null +++ b/.changeset/const-tags.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: invalidate `@const` tags based on visible references in legacy mode diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/function.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/function.js index 4d93cd44e0..7bdb2243f2 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/function.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/function.js @@ -10,7 +10,7 @@ export function visit_function(node, context) { for (const [name] of context.state.scope.references) { const binding = context.state.scope.get(name); - if (binding && binding.scope.function_depth < context.state.scope.function_depth) { + if (binding && binding.scope !== context.state.scope) { context.state.expression.references.add(binding); } } diff --git a/packages/svelte/tests/runtime-legacy/samples/const-tag-depends-on-const-tag/_config.js b/packages/svelte/tests/runtime-legacy/samples/const-tag-depends-on-const-tag/_config.js new file mode 100644 index 0000000000..6282d36f75 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/const-tag-depends-on-const-tag/_config.js @@ -0,0 +1,34 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ` + +

hello

+

hello

+ `, + + ssrHtml: ` + +

hello

+

hello

+ `, + + async test({ assert, target }) { + const [input] = target.querySelectorAll('input'); + + flushSync(() => { + input.value = 'goodbye'; + input.dispatchEvent(new InputEvent('input', { bubbles: true })); + }); + + assert.htmlEqual( + target.innerHTML, + ` + +

goodbye

+

goodbye

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/const-tag-depends-on-const-tag/main.svelte b/packages/svelte/tests/runtime-legacy/samples/const-tag-depends-on-const-tag/main.svelte new file mode 100644 index 0000000000..a32010717c --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/const-tag-depends-on-const-tag/main.svelte @@ -0,0 +1,15 @@ + + + + + + +{#if true} + {@const m1 = message} + {@const m2 = (() => m1)()} + +

{m1}

+

{m2}

+{/if}