From bd5480b9d08ef0022c03345b914694aa73e30d33 Mon Sep 17 00:00:00 2001
From: Simon H <5968653+dummdidumm@users.noreply.github.com>
Date: Tue, 10 Feb 2026 15:45:34 +0100
Subject: [PATCH] fix: reduce if block nesting (#17662)
* fix: reduce if block nesting
This reduces if block nesting similar to how we did it in #15250 (which got lost during the `await` feature introduction): If the if expression doesn't contain an await expression or is not dependent on a blocker that is not already resolved, then we can avoid creating a separate `$.if()` statement. The one trade-off is that we'll do re-invocations for all the conditions leading up to the condition that matches. Therefore non-simple if expressions are wrapper in `$.derived` to avoid excessive recomputations.
closes #17659 (~320 markers in prod mode possible now; less in dev because of our "wrap this component with devtime info" method)
helps with #15200
* tweak
* feedback
---
.changeset/wild-dolls-hang.md | 5 +
.../phases/2-analyze/visitors/IfBlock.js | 19 ++
.../3-transform/client/visitors/Fragment.js | 6 +-
.../3-transform/client/visitors/IfBlock.js | 85 +++++---
.../3-transform/server/visitors/IfBlock.js | 34 ++-
packages/svelte/src/compiler/phases/nodes.js | 13 ++
.../svelte/src/compiler/types/template.d.ts | 2 +
.../src/internal/client/dom/blocks/if.js | 36 ++--
.../src/internal/client/dom/hydration.js | 7 +-
.../samples/async-if-nested/_config.js | 12 ++
.../samples/async-if-nested/main.svelte | 21 ++
.../async-state-new-branch/Child.svelte | 7 +
.../samples/async-state-new-branch/_config.js | 47 ++++
.../async-state-new-branch/main.svelte | 28 +++
.../samples/async-if-chain/_config.js | 3 +
.../_expected/client/index.svelte.js | 201 ++++++++++++++++++
.../_expected/server/index.svelte.js | 110 ++++++++++
.../samples/async-if-chain/index.svelte | 59 +++++
18 files changed, 647 insertions(+), 48 deletions(-)
create mode 100644 .changeset/wild-dolls-hang.md
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-if-nested/_config.js
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-if-nested/main.svelte
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-state-new-branch/Child.svelte
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-state-new-branch/_config.js
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-state-new-branch/main.svelte
create mode 100644 packages/svelte/tests/snapshot/samples/async-if-chain/_config.js
create mode 100644 packages/svelte/tests/snapshot/samples/async-if-chain/_expected/client/index.svelte.js
create mode 100644 packages/svelte/tests/snapshot/samples/async-if-chain/_expected/server/index.svelte.js
create mode 100644 packages/svelte/tests/snapshot/samples/async-if-chain/index.svelte
diff --git a/.changeset/wild-dolls-hang.md b/.changeset/wild-dolls-hang.md
new file mode 100644
index 0000000000..a7b3436d69
--- /dev/null
+++ b/.changeset/wild-dolls-hang.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: reduce if block nesting
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js
index dcdae3587f..64855abdaf 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js
@@ -24,4 +24,23 @@ export function IfBlock(node, context) {
context.visit(node.consequent);
if (node.alternate) context.visit(node.alternate);
+
+ // Check if we can flatten branches
+ const alt = node.alternate;
+
+ if (alt && alt.nodes.length === 1 && alt.nodes[0].type === 'IfBlock' && alt.nodes[0].elseif) {
+ const elseif = alt.nodes[0];
+
+ // Don't flatten if this else-if has an await expression or new blockers
+ // TODO would be nice to check the await expression itself to see if it's awaiting the same thing as a previous if expression
+ if (
+ !elseif.metadata.expression.has_await &&
+ !elseif.metadata.expression.has_more_blockers_than(node.metadata.expression)
+ ) {
+ // Roll the existing flattened branches (if any) into this one, then delete those of the else-if block
+ // to avoid processing them multiple times as we walk down the chain during code transformation.
+ node.metadata.flattened = [elseif, ...(elseif.metadata.flattened ?? [])];
+ elseif.metadata.flattened = undefined;
+ }
+ }
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js
index 00b0cfaa2e..be919d380c 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js
@@ -47,7 +47,11 @@ export function Fragment(node, context) {
const is_single_element = trimmed.length === 1 && trimmed[0].type === 'RegularElement';
const is_single_child_not_needing_template =
trimmed.length === 1 &&
- (trimmed[0].type === 'SvelteFragment' || trimmed[0].type === 'TitleElement');
+ (trimmed[0].type === 'SvelteFragment' ||
+ trimmed[0].type === 'TitleElement' ||
+ (trimmed[0].type === 'IfBlock' &&
+ trimmed[0].elseif &&
+ /** @type {AST.IfBlock} */ (parent).metadata.flattened?.includes(trimmed[0])));
const template_name = context.state.scope.root.unique('root'); // TODO infer name from parent
/** @type {Statement[]} */
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js
index c0e66635df..0d31c42d11 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js
@@ -1,4 +1,4 @@
-/** @import { BlockStatement, Expression } from 'estree' */
+/** @import { BlockStatement, Expression, IfStatement, Statement } from 'estree' */
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */
import * as b from '#compiler/builders';
@@ -10,40 +10,75 @@ import { build_expression, add_svelte_meta } from './shared/utils.js';
*/
export function IfBlock(node, context) {
context.state.template.push_comment();
+
+ /** @type {Statement[]} */
const statements = [];
- const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent));
- const consequent_id = b.id(context.state.scope.generate('consequent'));
+ const has_await = node.metadata.expression.has_await;
+ const has_blockers = node.metadata.expression.has_blockers();
+ const expression = build_expression(context, node.test, node.metadata.expression);
+
+ // Build the if/else-if/else chain
+ let index = 0;
+ /** @type {IfStatement | undefined} */
+ let first_if;
+ /** @type {IfStatement | undefined} */
+ let last_if;
+ /** @type {AST.IfBlock | undefined} */
+ let last_alt;
- statements.push(b.var(consequent_id, b.arrow([b.id('$$anchor')], consequent)));
+ for (const branch of [node, ...(node.metadata.flattened ?? [])]) {
+ const consequent = /** @type {BlockStatement} */ (context.visit(branch.consequent));
+ const consequent_id = b.id(context.state.scope.generate('consequent'));
+ statements.push(b.var(consequent_id, b.arrow([b.id('$$anchor')], consequent)));
- let alternate_id;
+ // Build the test expression for this branch
+ /** @type {Expression} */
+ let test;
- if (node.alternate) {
- const alternate = /** @type {BlockStatement} */ (context.visit(node.alternate));
- alternate_id = b.id(context.state.scope.generate('alternate'));
- statements.push(b.var(alternate_id, b.arrow([b.id('$$anchor')], alternate)));
+ if (branch.metadata.expression.has_await) {
+ // Top-level condition with await: already resolved by $.async wrapper
+ test = b.call('$.get', b.id('$$condition'));
+ } else {
+ const expression = build_expression(context, branch.test, branch.metadata.expression);
+
+ if (branch.metadata.expression.has_call) {
+ const derived_id = b.id(context.state.scope.generate('d'));
+ statements.push(b.var(derived_id, b.call('$.derived', b.arrow([], expression))));
+ test = b.call('$.get', derived_id);
+ } else {
+ test = expression;
+ }
+ }
+
+ const render_call = b.stmt(b.call('$$render', consequent_id, index > 0 && b.literal(index)));
+ const new_if = b.if(test, render_call);
+
+ if (last_if) {
+ last_if.alternate = new_if;
+ } else {
+ first_if = new_if;
+ }
+
+ last_alt = branch;
+ last_if = new_if;
+ index++;
}
- const has_await = node.metadata.expression.has_await;
- const has_blockers = node.metadata.expression.has_blockers();
+ // Handle final alternate (else branch, remaining async chain, or nothing)
+ if (last_if && last_alt?.alternate) {
+ const alternate = /** @type {BlockStatement} */ (context.visit(last_alt.alternate));
+ const alternate_id = b.id(context.state.scope.generate('alternate'));
+ statements.push(b.var(alternate_id, b.arrow([b.id('$$anchor')], alternate)));
- const expression = build_expression(context, node.test, node.metadata.expression);
- const test = has_await ? b.call('$.get', b.id('$$condition')) : expression;
+ last_if.alternate = b.stmt(b.call('$$render', alternate_id, b.literal(false)));
+ }
+ // Build $.if() arguments
/** @type {Expression[]} */
const args = [
context.state.node,
- b.arrow(
- [b.id('$$render')],
- b.block([
- b.if(
- test,
- b.stmt(b.call('$$render', consequent_id)),
- alternate_id && b.stmt(b.call('$$render', alternate_id, b.literal(false)))
- )
- ])
- )
+ b.arrow([b.id('$$render')], first_if ? b.block([first_if]) : b.block([]))
];
if (node.elseif) {
@@ -67,7 +102,9 @@ export function IfBlock(node, context) {
//
// ...even though they're logically equivalent. In the first case, the
// transition will only play when `y` changes, but in the second it
- // should play when `x` or `y` change — both are considered 'local'
+ // should play when `x` or `y` change — both are considered 'local'.
+ // This could also be a non-flattened elseif (because it has an async expression).
+ // In both cases mark as elseif so the runtime uses EFFECT_TRANSPARENT for transitions.
args.push(b.true);
}
diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js
index 06b1b1e966..e3f5f88705 100644
--- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js
+++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js
@@ -1,4 +1,4 @@
-/** @import { BlockStatement, Expression, Statement } from 'estree' */
+/** @import { BlockStatement, Expression, IfStatement, Statement } from 'estree' */
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types.js' */
import * as b from '#compiler/builders';
@@ -9,23 +9,37 @@ import { block_close, block_open, block_open_else, create_child_block } from './
* @param {ComponentContext} context
*/
export function IfBlock(node, context) {
- const test = /** @type {Expression} */ (context.visit(node.test));
const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent));
+ consequent.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), block_open)));
- const alternate = node.alternate
- ? /** @type {BlockStatement} */ (context.visit(node.alternate))
- : b.block([]);
+ /** @type {IfStatement} */
+ let if_statement = b.if(/** @type {Expression} */ (context.visit(node.test)), consequent);
- consequent.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), block_open)));
+ let index = 1;
+ let current_if = if_statement;
+ let alt = node.alternate;
+
+ // Walk the else-if chain, flattening branches
+ for (const elseif of node.metadata.flattened ?? []) {
+ const branch = /** @type {BlockStatement} */ (context.visit(elseif.consequent));
+ branch.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), b.literal(``))));
+
+ current_if = current_if.alternate = b.if(
+ /** @type {Expression} */ (context.visit(elseif.test)),
+ branch
+ );
+ alt = elseif.alternate;
+ }
- alternate.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), block_open_else)));
+ // Handle final else (or remaining async chain)
+ const final_alternate = alt ? /** @type {BlockStatement} */ (context.visit(alt)) : b.block([]);
- /** @type {Statement} */
- let statement = b.if(test, consequent, alternate);
+ final_alternate.body.unshift(b.stmt(b.call(b.id('$$renderer.push'), block_open_else)));
+ current_if.alternate = final_alternate;
context.state.template.push(
...create_child_block(
- [statement],
+ [if_statement],
node.metadata.expression.blockers(),
node.metadata.expression.has_await
),
diff --git a/packages/svelte/src/compiler/phases/nodes.js b/packages/svelte/src/compiler/phases/nodes.js
index f302603409..2e54f333a8 100644
--- a/packages/svelte/src/compiler/phases/nodes.js
+++ b/packages/svelte/src/compiler/phases/nodes.js
@@ -118,6 +118,19 @@ export class ExpressionMetadata {
return this.#get_blockers().size > 0;
}
+ /**
+ * @param {ExpressionMetadata} other
+ */
+ has_more_blockers_than(other) {
+ for (const blocker of this.#get_blockers()) {
+ if (!other.#get_blockers().has(blocker)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
is_async() {
return this.has_await || this.#get_blockers().size > 0;
}
diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts
index b924406d0f..d44a31349a 100644
--- a/packages/svelte/src/compiler/types/template.d.ts
+++ b/packages/svelte/src/compiler/types/template.d.ts
@@ -483,6 +483,8 @@ export namespace AST {
alternate: Fragment | null;
/** @internal */
metadata: {
+ /** List of else-if blocks that can be flattened into this if block */
+ flattened?: IfBlock[];
expression: ExpressionMetadata;
};
}
diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js
index 7fa5ca464d..f2b1cf80a5 100644
--- a/packages/svelte/src/internal/client/dom/blocks/if.js
+++ b/packages/svelte/src/internal/client/dom/blocks/if.js
@@ -9,14 +9,12 @@ import {
set_hydrating
} from '../hydration.js';
import { block } from '../../reactivity/effects.js';
-import { HYDRATION_START_ELSE } from '../../../../constants.js';
import { BranchManager } from './branches.js';
-
-// TODO reinstate https://github.com/sveltejs/svelte/pull/15250
+import { HYDRATION_START, HYDRATION_START_ELSE } from '../../../../constants.js';
/**
* @param {TemplateNode} node
- * @param {(branch: (fn: (anchor: Node) => void, flag?: boolean) => void) => void} fn
+ * @param {(branch: (fn: (anchor: Node) => void, key?: number | false) => void) => void} fn
* @param {boolean} [elseif] True if this is an `{:else if ...}` block rather than an `{#if ...}`, as that affects which transitions are considered 'local'
* @returns {void}
*/
@@ -29,14 +27,28 @@ export function if_block(node, fn, elseif = false) {
var flags = elseif ? EFFECT_TRANSPARENT : 0;
/**
- * @param {boolean} condition,
+ * @param {number | false} key
* @param {null | ((anchor: Node) => void)} fn
*/
- function update_branch(condition, fn) {
+ function update_branch(key, fn) {
if (hydrating) {
- const is_else = read_hydration_instruction(node) === HYDRATION_START_ELSE;
+ const data = read_hydration_instruction(node);
+
+ /**
+ * @type {number | false}
+ * "[" = branch 0, "[1" = branch 1, "[2" = branch 2, ..., "[!" = else (false)
+ */
+ var hydrated_key;
+
+ if (data === HYDRATION_START) {
+ hydrated_key = 0;
+ } else if (data === HYDRATION_START_ELSE) {
+ hydrated_key = false;
+ } else {
+ hydrated_key = parseInt(data.substring(1)); // "[1", "[2", etc.
+ }
- if (condition === is_else) {
+ if (key !== hydrated_key) {
// Hydration mismatch: remove everything inside the anchor and start fresh.
// This could happen with `{#if browser}...{/if}`, for example
var anchor = skip_nodes();
@@ -45,22 +57,22 @@ export function if_block(node, fn, elseif = false) {
branches.anchor = anchor;
set_hydrating(false);
- branches.ensure(condition, fn);
+ branches.ensure(key, fn);
set_hydrating(true);
return;
}
}
- branches.ensure(condition, fn);
+ branches.ensure(key, fn);
}
block(() => {
var has_branch = false;
- fn((fn, flag = true) => {
+ fn((fn, key = 0) => {
has_branch = true;
- update_branch(flag, fn);
+ update_branch(key, fn);
});
if (!has_branch) {
diff --git a/packages/svelte/src/internal/client/dom/hydration.js b/packages/svelte/src/internal/client/dom/hydration.js
index 7a9ef33d1b..83833f493d 100644
--- a/packages/svelte/src/internal/client/dom/hydration.js
+++ b/packages/svelte/src/internal/client/dom/hydration.js
@@ -95,7 +95,12 @@ export function skip_nodes(remove = true) {
if (data === HYDRATION_END) {
if (depth === 0) return node;
depth -= 1;
- } else if (data === HYDRATION_START || data === HYDRATION_START_ELSE) {
+ } else if (
+ data === HYDRATION_START ||
+ data === HYDRATION_START_ELSE ||
+ // "[1", "[2", etc. for if blocks
+ (data[0] === '[' && !isNaN(Number(data.slice(1))))
+ ) {
depth += 1;
}
}
diff --git a/packages/svelte/tests/runtime-runes/samples/async-if-nested/_config.js b/packages/svelte/tests/runtime-runes/samples/async-if-nested/_config.js
new file mode 100644
index 0000000000..49cae28cc4
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-if-nested/_config.js
@@ -0,0 +1,12 @@
+import { tick } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ mode: ['async-server', 'hydrate', 'client'],
+ ssrHtml: `bar blocking`,
+
+ async test({ assert, target }) {
+ await tick();
+ assert.htmlEqual(target.innerHTML, 'bar blocking');
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/async-if-nested/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-if-nested/main.svelte
new file mode 100644
index 0000000000..cfb17e3c76
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-if-nested/main.svelte
@@ -0,0 +1,21 @@
+
+
+{#if foo}
+ foo
+{:else if await bar}
+ bar
+{:else}
+ else
+{/if}
+
+{#if foo}
+ foo
+{:else if !blocking}
+ blocking
+{:else}
+ else
+{/if}
diff --git a/packages/svelte/tests/runtime-runes/samples/async-state-new-branch/Child.svelte b/packages/svelte/tests/runtime-runes/samples/async-state-new-branch/Child.svelte
new file mode 100644
index 0000000000..6b765526c8
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-state-new-branch/Child.svelte
@@ -0,0 +1,7 @@
+
+
+{x}
diff --git a/packages/svelte/tests/runtime-runes/samples/async-state-new-branch/_config.js b/packages/svelte/tests/runtime-runes/samples/async-state-new-branch/_config.js
new file mode 100644
index 0000000000..f2091eb6ab
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-state-new-branch/_config.js
@@ -0,0 +1,47 @@
+import { tick } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ skip: true, // this fails on main, too; skip for now
+ async test({ assert, target, logs }) {
+ const [x, y, resolve] = target.querySelectorAll('button');
+
+ x.click();
+ await tick();
+ assert.deepEqual(logs, ['universe']);
+
+ y.click();
+ await tick();
+ assert.deepEqual(logs, ['universe', 'world', '$effect: world']);
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+ world
+ `
+ );
+
+ resolve.click();
+ await tick();
+ assert.deepEqual(logs, [
+ 'universe',
+ 'world',
+ '$effect: world',
+ '$effect: universe',
+ '$effect: universe'
+ ]);
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
+
+
+ universe
+ universe
+ universe
+ `
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/async-state-new-branch/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-state-new-branch/main.svelte
new file mode 100644
index 0000000000..8edc718de2
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-state-new-branch/main.svelte
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+{#if x === 'universe'}
+ {await delay(x)}
+
+{/if}
+
+{#if y > 0}
+
+{/if}
diff --git a/packages/svelte/tests/snapshot/samples/async-if-chain/_config.js b/packages/svelte/tests/snapshot/samples/async-if-chain/_config.js
new file mode 100644
index 0000000000..2e30bbeb16
--- /dev/null
+++ b/packages/svelte/tests/snapshot/samples/async-if-chain/_config.js
@@ -0,0 +1,3 @@
+import { test } from '../../test';
+
+export default test({ compileOptions: { experimental: { async: true } } });
diff --git a/packages/svelte/tests/snapshot/samples/async-if-chain/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-if-chain/_expected/client/index.svelte.js
new file mode 100644
index 0000000000..b55e3010b8
--- /dev/null
+++ b/packages/svelte/tests/snapshot/samples/async-if-chain/_expected/client/index.svelte.js
@@ -0,0 +1,201 @@
+import 'svelte/internal/disclose-version';
+import 'svelte/internal/flags/async';
+import * as $ from 'svelte/internal/client';
+
+var root = $.from_html(` `, 1);
+
+export default function Async_if_chain($$anchor) {
+ function complex1() {
+ return 1;
+ }
+
+ let foo = true;
+ var blocking;
+ var $$promises = $.run([async () => blocking = await $.async_derived(() => foo)]);
+ var fragment = root();
+ var node = $.first_child(fragment);
+
+ $.async(node, [$$promises[0]], void 0, (node) => {
+ var consequent = ($$anchor) => {
+ var text = $.text('foo');
+
+ $.append($$anchor, text);
+ };
+
+ var consequent_1 = ($$anchor) => {
+ var text_1 = $.text('bar');
+
+ $.append($$anchor, text_1);
+ };
+
+ var alternate = ($$anchor) => {
+ var text_2 = $.text('else');
+
+ $.append($$anchor, text_2);
+ };
+
+ $.if(node, ($$render) => {
+ if (foo) $$render(consequent); else if (bar) $$render(consequent_1, 1); else $$render(alternate, false);
+ });
+ });
+
+ var node_1 = $.sibling(node, 2);
+
+ $.async(node_1, [$$promises[0]], [() => foo], (node_1, $$condition) => {
+ var consequent_2 = ($$anchor) => {
+ var text_3 = $.text('foo');
+
+ $.append($$anchor, text_3);
+ };
+
+ var consequent_3 = ($$anchor) => {
+ var text_4 = $.text('bar');
+
+ $.append($$anchor, text_4);
+ };
+
+ var alternate_2 = ($$anchor) => {
+ var fragment_1 = $.comment();
+ var node_2 = $.first_child(fragment_1);
+
+ $.async(node_2, [], [() => baz], (node_2, $$condition) => {
+ var consequent_4 = ($$anchor) => {
+ var text_5 = $.text('baz');
+
+ $.append($$anchor, text_5);
+ };
+
+ var alternate_1 = ($$anchor) => {
+ var text_6 = $.text('else');
+
+ $.append($$anchor, text_6);
+ };
+
+ $.if(
+ node_2,
+ ($$render) => {
+ if ($.get($$condition)) $$render(consequent_4); else $$render(alternate_1, false);
+ },
+ true
+ );
+ });
+
+ $.append($$anchor, fragment_1);
+ };
+
+ $.if(node_1, ($$render) => {
+ if ($.get($$condition)) $$render(consequent_2); else if (bar) $$render(consequent_3, 1); else $$render(alternate_2, false);
+ });
+ });
+
+ var node_3 = $.sibling(node_1, 2);
+
+ $.async(node_3, [$$promises[0]], [async () => (await $.save(foo))() > 10], (node_3, $$condition) => {
+ var consequent_5 = ($$anchor) => {
+ var text_7 = $.text('foo');
+
+ $.append($$anchor, text_7);
+ };
+
+ var consequent_6 = ($$anchor) => {
+ var text_8 = $.text('bar');
+
+ $.append($$anchor, text_8);
+ };
+
+ var alternate_4 = ($$anchor) => {
+ var fragment_2 = $.comment();
+ var node_4 = $.first_child(fragment_2);
+
+ $.async(node_4, [$$promises[0]], [async () => (await $.save(foo))() > 5], (node_4, $$condition) => {
+ var consequent_7 = ($$anchor) => {
+ var text_9 = $.text('baz');
+
+ $.append($$anchor, text_9);
+ };
+
+ var alternate_3 = ($$anchor) => {
+ var text_10 = $.text('else');
+
+ $.append($$anchor, text_10);
+ };
+
+ $.if(
+ node_4,
+ ($$render) => {
+ if ($.get($$condition)) $$render(consequent_7); else $$render(alternate_3, false);
+ },
+ true
+ );
+ });
+
+ $.append($$anchor, fragment_2);
+ };
+
+ $.if(node_3, ($$render) => {
+ if ($.get($$condition)) $$render(consequent_5); else if (bar) $$render(consequent_6, 1); else $$render(alternate_4, false);
+ });
+ });
+
+ var node_5 = $.sibling(node_3, 2);
+
+ {
+ var consequent_8 = ($$anchor) => {
+ var text_11 = $.text('foo');
+
+ $.append($$anchor, text_11);
+ };
+
+ var consequent_9 = ($$anchor) => {
+ var text_12 = $.text('bar');
+
+ $.append($$anchor, text_12);
+ };
+
+ var consequent_10 = ($$anchor) => {
+ var text_13 = $.text('baz');
+
+ $.append($$anchor, text_13);
+ };
+
+ var d = $.derived(() => complex1() * complex2 > 100);
+
+ var alternate_5 = ($$anchor) => {
+ var text_14 = $.text('else');
+
+ $.append($$anchor, text_14);
+ };
+
+ $.if(node_5, ($$render) => {
+ if (simple1) $$render(consequent_8); else if (simple2 > 10) $$render(consequent_9, 1); else if ($.get(d)) $$render(consequent_10, 2); else $$render(alternate_5, false);
+ });
+ }
+
+ var node_6 = $.sibling(node_5, 2);
+
+ $.async(node_6, [$$promises[0]], void 0, (node_6) => {
+ var consequent_11 = ($$anchor) => {
+ var text_15 = $.text('foo');
+
+ $.append($$anchor, text_15);
+ };
+
+ var consequent_12 = ($$anchor) => {
+ var text_16 = $.text('bar');
+
+ $.append($$anchor, text_16);
+ };
+
+ var alternate_6 = ($$anchor) => {
+ var text_17 = $.text('else');
+
+ $.append($$anchor, text_17);
+ };
+
+ $.if(node_6, ($$render) => {
+ if ($.get(blocking) > 10) $$render(consequent_11); else if ($.get(blocking) > 5) $$render(consequent_12, 1); else $$render(alternate_6, false);
+ });
+ });
+
+ $.append($$anchor, fragment);
+}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/async-if-chain/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-if-chain/_expected/server/index.svelte.js
new file mode 100644
index 0000000000..91315edb94
--- /dev/null
+++ b/packages/svelte/tests/snapshot/samples/async-if-chain/_expected/server/index.svelte.js
@@ -0,0 +1,110 @@
+import 'svelte/internal/flags/async';
+import * as $ from 'svelte/internal/server';
+
+export default function Async_if_chain($$renderer) {
+ function complex1() {
+ return 1;
+ }
+
+ let foo = true;
+ var blocking;
+ var $$promises = $$renderer.run([async () => blocking = await foo]);
+
+ $$renderer.async_block([$$promises[0]], ($$renderer) => {
+ if (foo) {
+ $$renderer.push('');
+ $$renderer.push(`foo`);
+ } else if (bar) {
+ $$renderer.push('');
+ $$renderer.push(`bar`);
+ } else {
+ $$renderer.push('');
+ $$renderer.push(`else`);
+ }
+ });
+
+ $$renderer.push(` `);
+
+ $$renderer.async_block([$$promises[0]], async ($$renderer) => {
+ if ((await $.save(foo))()) {
+ $$renderer.push('');
+ $$renderer.push(`foo`);
+ } else if (bar) {
+ $$renderer.push('');
+ $$renderer.push(`bar`);
+ } else {
+ $$renderer.push('');
+
+ $$renderer.child_block(async ($$renderer) => {
+ if ((await $.save(baz))()) {
+ $$renderer.push('');
+ $$renderer.push(`baz`);
+ } else {
+ $$renderer.push('');
+ $$renderer.push(`else`);
+ }
+ });
+
+ $$renderer.push(``);
+ }
+ });
+
+ $$renderer.push(` `);
+
+ $$renderer.async_block([$$promises[0]], async ($$renderer) => {
+ if ((await $.save(foo))() > 10) {
+ $$renderer.push('');
+ $$renderer.push(`foo`);
+ } else if (bar) {
+ $$renderer.push('');
+ $$renderer.push(`bar`);
+ } else {
+ $$renderer.push('');
+
+ $$renderer.async_block([$$promises[0]], async ($$renderer) => {
+ if ((await $.save(foo))() > 5) {
+ $$renderer.push('');
+ $$renderer.push(`baz`);
+ } else {
+ $$renderer.push('');
+ $$renderer.push(`else`);
+ }
+ });
+
+ $$renderer.push(``);
+ }
+ });
+
+ $$renderer.push(` `);
+
+ if (simple1) {
+ $$renderer.push('');
+ $$renderer.push(`foo`);
+ } else if (simple2 > 10) {
+ $$renderer.push('');
+ $$renderer.push(`bar`);
+ } else if (complex1() * complex2 > 100) {
+ $$renderer.push('');
+ $$renderer.push(`baz`);
+ } else {
+ $$renderer.push('');
+ $$renderer.push(`else`);
+ }
+
+ $$renderer.push(` `);
+
+ $$renderer.async_block([$$promises[0]], ($$renderer) => {
+ if (blocking > 10) {
+ $$renderer.push('');
+ $$renderer.push(`foo`);
+ } else if (blocking > 5) {
+ $$renderer.push('');
+ $$renderer.push(`bar`);
+ } else {
+ $$renderer.push('');
+ $$renderer.push(`else`);
+ }
+ });
+
+ $$renderer.push(``);
+}
\ No newline at end of file
diff --git a/packages/svelte/tests/snapshot/samples/async-if-chain/index.svelte b/packages/svelte/tests/snapshot/samples/async-if-chain/index.svelte
new file mode 100644
index 0000000000..1f216af0b0
--- /dev/null
+++ b/packages/svelte/tests/snapshot/samples/async-if-chain/index.svelte
@@ -0,0 +1,59 @@
+
+
+
+{#if foo}
+ foo
+{:else if bar}
+ bar
+{:else}
+ else
+{/if}
+
+
+{#if await foo}
+ foo
+{:else if bar}
+ bar
+{:else if await baz}
+ baz
+{:else}
+ else
+{/if}
+
+
+{#if await foo > 10}
+ foo
+{:else if bar}
+ bar
+{:else if await foo > 5}
+ baz
+{:else}
+ else
+{/if}
+
+
+{#if simple1}
+ foo
+{:else if simple2 > 10}
+ bar
+{:else if complex1() * complex2 > 100}
+ baz
+{:else}
+ else
+{/if}
+
+
+{#if blocking > 10}
+ foo
+{:else if blocking > 5}
+ bar
+{:else}
+ else
+{/if}