From eb0845e365856e7c20c62a27c328159573d8ce61 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Thu, 6 Nov 2025 20:50:32 +0100 Subject: [PATCH] await dependencies of style directives --- .changeset/huge-walls-hang.md | 5 ++++ .../2-analyze/visitors/StyleDirective.js | 7 +++-- .../client/visitors/RegularElement.js | 19 ++---------- .../client/visitors/shared/element.js | 4 +-- packages/svelte/src/compiler/phases/nodes.js | 15 ++++++++++ .../async-style-after-await/_config.js | 30 +++++++++++++++++++ .../async-style-after-await/main.svelte | 25 ++++++++++++++++ 7 files changed, 83 insertions(+), 22 deletions(-) create mode 100644 .changeset/huge-walls-hang.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-style-after-await/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-style-after-await/main.svelte diff --git a/.changeset/huge-walls-hang.md b/.changeset/huge-walls-hang.md new file mode 100644 index 0000000000..63dd6c21c4 --- /dev/null +++ b/.changeset/huge-walls-hang.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: await dependencies of style directives diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/StyleDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/StyleDirective.js index 9699d3c03b..8f4036cf89 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/StyleDirective.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/StyleDirective.js @@ -23,6 +23,9 @@ export function StyleDirective(node, context) { if (binding.kind !== 'normal') { node.metadata.expression.has_state = true; } + if (binding.blocker) { + node.metadata.expression.dependencies.add(binding); + } } } else { context.next(); @@ -30,9 +33,7 @@ export function StyleDirective(node, context) { for (const chunk of get_attribute_chunks(node.value)) { if (chunk.type !== 'ExpressionTag') continue; - node.metadata.expression.has_state ||= chunk.metadata.expression.has_state; - node.metadata.expression.has_call ||= chunk.metadata.expression.has_call; - node.metadata.expression.has_await ||= chunk.metadata.expression.has_await; + node.metadata.expression.merge(chunk.metadata.expression); } } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 3998770a71..971f93e1d8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -484,21 +484,6 @@ function setup_select_synchronization(value_binding, context) { ); } -/** - * @param {ExpressionMetadata} target - * @param {ExpressionMetadata} source - */ -function merge_metadata(target, source) { - target.has_assignment ||= source.has_assignment; - target.has_await ||= source.has_await; - target.has_call ||= source.has_call; - target.has_member_expression ||= source.has_member_expression; - target.has_state ||= source.has_state; - - for (const r of source.references) target.references.add(r); - for (const b of source.dependencies) target.dependencies.add(b); -} - /** * @param {AST.ClassDirective[]} class_directives * @param {ComponentContext} context @@ -514,7 +499,7 @@ export function build_class_directives_object( const metadata = new ExpressionMetadata(); for (const d of class_directives) { - merge_metadata(metadata, d.metadata.expression); + metadata.merge(d.metadata.expression); const expression = /** @type Expression */ (context.visit(d.expression)); properties.push(b.init(d.name, expression)); @@ -541,7 +526,7 @@ export function build_style_directives_object( const metadata = new ExpressionMetadata(); for (const d of style_directives) { - merge_metadata(metadata, d.metadata.expression); + metadata.merge(d.metadata.expression); const expression = d.value === true diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index d199cfd66b..58ed88855f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -170,7 +170,7 @@ export function build_set_class(element, node_id, attribute, class_directives, c if (class_directives.length) { next = build_class_directives_object(class_directives, context); has_state ||= class_directives.some( - (d) => d.metadata.expression.has_state || d.metadata.expression.has_await + (d) => d.metadata.expression.has_state || d.metadata.expression.is_async() ); if (has_state) { @@ -240,7 +240,7 @@ export function build_set_style(node_id, attribute, style_directives, context) { if (style_directives.length) { next = build_style_directives_object(style_directives, context); has_state ||= style_directives.some( - (d) => d.metadata.expression.has_state || d.metadata.expression.has_await + (d) => d.metadata.expression.has_state || d.metadata.expression.is_async() ); if (has_state) { diff --git a/packages/svelte/src/compiler/phases/nodes.js b/packages/svelte/src/compiler/phases/nodes.js index 2c7c3fb66b..9d1a8f1890 100644 --- a/packages/svelte/src/compiler/phases/nodes.js +++ b/packages/svelte/src/compiler/phases/nodes.js @@ -115,6 +115,21 @@ export class ExpressionMetadata { is_async() { return this.has_await || this.#get_blockers().size > 0; } + + /** + * @param {ExpressionMetadata} source + */ + merge(source) { + this.has_state ||= source.has_state; + this.has_call ||= source.has_call; + this.has_await ||= source.has_await; + this.has_member_expression ||= source.has_member_expression; + this.has_assignment ||= source.has_assignment; + this.#blockers = null; // so that blockers are recalculated + + for (const r of source.references) this.references.add(r); + for (const b of source.dependencies) this.dependencies.add(b); + } } /** diff --git a/packages/svelte/tests/runtime-runes/samples/async-style-after-await/_config.js b/packages/svelte/tests/runtime-runes/samples/async-style-after-await/_config.js new file mode 100644 index 0000000000..472e7076c3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-style-after-await/_config.js @@ -0,0 +1,30 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['async-server', 'client', 'hydrate'], + ssrHtml: ` +
+
+ +
+
+ + `, + + async test({ assert, target }) { + await tick(); + + assert.htmlEqual( + target.innerHTML, + ` +
+
+ +
+
+ + ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-style-after-await/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-style-after-await/main.svelte new file mode 100644 index 0000000000..7c1abeda2c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-style-after-await/main.svelte @@ -0,0 +1,25 @@ + + + + + +{#if color} +
+
+ +{/if} + + +{#if color} +
+
+ +{/if}