fix: take blockers of components into account

Fixes #17122
component-async-blockers-fix
Simon Holthausen 22 hours ago
parent e238e6611e
commit e0fca584be

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: take blockers of components into account

@ -242,6 +242,10 @@ export default function element(parser) {
parser.allow_whitespace(); parser.allow_whitespace();
} }
if (element.type === 'Component') {
element.metadata.expression = new ExpressionMetadata();
}
if (element.type === 'SvelteComponent') { if (element.type === 'SvelteComponent') {
const index = element.attributes.findIndex( const index = element.attributes.findIndex(
/** @param {any} attr */ /** @param {any} attr */
@ -257,6 +261,7 @@ export default function element(parser) {
} }
element.expression = get_attribute_expression(definition); element.expression = get_attribute_expression(definition);
element.metadata.expression = new ExpressionMetadata();
} }
if (element.type === 'SvelteElement') { if (element.type === 'SvelteElement') {

@ -16,5 +16,11 @@ export function Component(node, context) {
binding !== null && binding !== null &&
(binding.kind !== 'normal' || node.name.includes('.')); (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); visit_component(node, context);
} }

@ -12,7 +12,7 @@ export function SvelteComponent(node, context) {
w.svelte_component_deprecated(node); w.svelte_component_deprecated(node);
} }
context.visit(node.expression); context.visit(node.expression, { ...context.state, expression: node.metadata.expression });
visit_component(node, context); visit_component(node, context);
} }

@ -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)]; const statements = [...snippet_declarations, ...memoizer.deriveds(context.state.analysis.runes)];
if (is_component_dynamic) { if (is_component_dynamic) {

@ -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(); const is_async = optimiser.is_async();
if (is_async) { if (is_async) {
statement = create_async_block( 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.blockers(),
optimiser.has_await optimiser.has_await
); );
} } else if (dynamic && custom_css_props.length === 0) {
if (dynamic && custom_css_props.length === 0) {
context.state.template.push(empty_comment); context.state.template.push(empty_comment);
} }

@ -308,6 +308,7 @@ export namespace AST {
type: 'Component'; type: 'Component';
/** @internal */ /** @internal */
metadata: { metadata: {
expression: ExpressionMetadata;
scopes: Record<string, Scope>; scopes: Record<string, Scope>;
dynamic: boolean; dynamic: boolean;
/** The set of locally-defined snippets that this component tag could render, /** The set of locally-defined snippets that this component tag could render,
@ -355,6 +356,7 @@ export namespace AST {
expression: Expression; expression: Expression;
/** @internal */ /** @internal */
metadata: { metadata: {
expression: ExpressionMetadata;
scopes: Record<string, Scope>; scopes: Record<string, Scope>;
/** The set of locally-defined snippets that this component tag could render, /** The set of locally-defined snippets that this component tag could render,
* used for CSS pruning purposes */ * used for CSS pruning purposes */

@ -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');
}
});

@ -0,0 +1,10 @@
<script>
import Component from './Component.svelte'
const X = $derived(await Promise.resolve(Component))
const Y = await Promise.resolve(Component)
</script>
<X />
<svelte:component this={X} />
<Y />
<svelte:component this={Y} />
Loading…
Cancel
Save