From e0fca584be978ca0afbd7fe4afd1e1862ff9b2a2 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Thu, 13 Nov 2025 20:17:14 +0100 Subject: [PATCH] fix: take blockers of components into account Fixes #17122 --- .changeset/ready-flies-invite.md | 5 +++++ .../compiler/phases/1-parse/state/element.js | 5 +++++ .../phases/2-analyze/visitors/Component.js | 6 ++++++ .../2-analyze/visitors/SvelteComponent.js | 2 +- .../client/visitors/shared/component.js | 5 +++++ .../server/visitors/shared/component.js | 17 +++++++++++++---- .../svelte/src/compiler/types/template.d.ts | 2 ++ .../async-dynamic-component/Component.svelte | 1 + .../samples/async-dynamic-component/_config.js | 11 +++++++++++ .../samples/async-dynamic-component/main.svelte | 10 ++++++++++ 10 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 .changeset/ready-flies-invite.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-dynamic-component/Component.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-dynamic-component/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-dynamic-component/main.svelte diff --git a/.changeset/ready-flies-invite.md b/.changeset/ready-flies-invite.md new file mode 100644 index 0000000000..660fa0d67e --- /dev/null +++ b/.changeset/ready-flies-invite.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: take blockers of components into account diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index 7b0950ae82..9167888e37 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -242,6 +242,10 @@ export default function element(parser) { parser.allow_whitespace(); } + if (element.type === 'Component') { + element.metadata.expression = new ExpressionMetadata(); + } + if (element.type === 'SvelteComponent') { const index = element.attributes.findIndex( /** @param {any} attr */ @@ -257,6 +261,7 @@ export default function element(parser) { } element.expression = get_attribute_expression(definition); + element.metadata.expression = new ExpressionMetadata(); } if (element.type === 'SvelteElement') { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Component.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Component.js index 350719dedd..5a4d3fce3f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Component.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Component.js @@ -16,5 +16,11 @@ export function Component(node, context) { binding !== null && (binding.kind !== 'normal' || node.name.includes('.')); + if (binding) { + node.metadata.expression.has_state = node.metadata.dynamic; + node.metadata.expression.dependencies.add(binding); + node.metadata.expression.references.add(binding); + } + visit_component(node, context); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteComponent.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteComponent.js index 3c7630cb25..18500f6154 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteComponent.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteComponent.js @@ -12,7 +12,7 @@ export function SvelteComponent(node, context) { w.svelte_component_deprecated(node); } - context.visit(node.expression); + context.visit(node.expression, { ...context.state, expression: node.metadata.expression }); visit_component(node, context); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index 2b57f61c77..b3139a29aa 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -451,6 +451,11 @@ export function build_component(node, component_name, context) { }; } + if (node.type !== 'SvelteSelf') { + // Component name itself could be blocked on async values + memoizer.check_blockers(node.metadata.expression); + } + const statements = [...snippet_declarations, ...memoizer.deriveds(context.state.analysis.runes)]; if (is_component_dynamic) { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js index 70c790fc57..2e7b4a186c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js @@ -325,17 +325,26 @@ export function build_inline_component(node, expression, context) { ); } + if (node.type !== 'SvelteSelf') { + // Component name itself could be blocked on async values + optimiser.check_blockers(node.metadata.expression); + } + const is_async = optimiser.is_async(); if (is_async) { statement = create_async_block( - b.block([optimiser.apply(), statement]), + b.block([ + optimiser.apply(), + dynamic && custom_css_props.length === 0 + ? b.stmt(b.call('$$renderer.push', empty_comment)) + : b.empty, + statement + ]), optimiser.blockers(), optimiser.has_await ); - } - - if (dynamic && custom_css_props.length === 0) { + } else if (dynamic && custom_css_props.length === 0) { context.state.template.push(empty_comment); } diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index f9bbe28333..3960c95c8f 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -308,6 +308,7 @@ export namespace AST { type: 'Component'; /** @internal */ metadata: { + expression: ExpressionMetadata; scopes: Record; dynamic: boolean; /** The set of locally-defined snippets that this component tag could render, @@ -355,6 +356,7 @@ export namespace AST { expression: Expression; /** @internal */ metadata: { + expression: ExpressionMetadata; scopes: Record; /** The set of locally-defined snippets that this component tag could render, * used for CSS pruning purposes */ diff --git a/packages/svelte/tests/runtime-runes/samples/async-dynamic-component/Component.svelte b/packages/svelte/tests/runtime-runes/samples/async-dynamic-component/Component.svelte new file mode 100644 index 0000000000..40816a2b5a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-dynamic-component/Component.svelte @@ -0,0 +1 @@ +Hi \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/async-dynamic-component/_config.js b/packages/svelte/tests/runtime-runes/samples/async-dynamic-component/_config.js new file mode 100644 index 0000000000..bf13a9b120 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-dynamic-component/_config.js @@ -0,0 +1,11 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['async-server', 'client', 'hydrate'], + ssrHtml: 'Hi Hi Hi Hi', + async test({ assert, target }) { + await tick(); + assert.htmlEqual(target.innerHTML, 'Hi Hi Hi Hi'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-dynamic-component/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-dynamic-component/main.svelte new file mode 100644 index 0000000000..d959f80694 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-dynamic-component/main.svelte @@ -0,0 +1,10 @@ + + + + + +