From 52947304862d481db5014dd9e073fc69f27c2d51 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 25 Jan 2025 18:06:19 -0500 Subject: [PATCH] if blocks --- .../svelte/src/internal/client/constants.js | 2 + .../src/internal/client/dom/blocks/if.js | 47 ++++++++++++++++++- packages/svelte/src/internal/client/fork.js | 8 +++- .../svelte/src/internal/client/types.d.ts | 2 + 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js index 5018887d7f..82d6733f25 100644 --- a/packages/svelte/src/internal/client/constants.js +++ b/packages/svelte/src/internal/client/constants.js @@ -30,3 +30,5 @@ export const STATE_SYMBOL = Symbol('$state'); export const STATE_SYMBOL_METADATA = Symbol('$state metadata'); export const LEGACY_PROPS = Symbol('legacy props'); export const LOADING_ATTR_SYMBOL = Symbol(''); + +export const FORK_ROOT = 1 << 1; diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js index 36790c05c1..db68d19dda 100644 --- a/packages/svelte/src/internal/client/dom/blocks/if.js +++ b/packages/svelte/src/internal/client/dom/blocks/if.js @@ -1,5 +1,5 @@ /** @import { Effect, TemplateNode } from '#client' */ -import { EFFECT_TRANSPARENT } from '../../constants.js'; +import { EFFECT_TRANSPARENT, FORK_ROOT } from '../../constants.js'; import { hydrate_next, hydrate_node, @@ -10,6 +10,7 @@ import { } from '../hydration.js'; import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; import { HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js'; +import { active_fork } from '../../fork.js'; /** * @param {TemplateNode} node @@ -65,6 +66,50 @@ export function if_block(node, fn, elseif = false) { } } + if (active_fork !== null && (active_fork.f & FORK_ROOT) !== 0) { + active_fork.f ^= FORK_ROOT; + + const fragment = document.createDocumentFragment(); + const offscreen_condition = condition; + const offscreen_anchor = document.createComment(''); + + fragment.append(offscreen_anchor); + + const offscreen_effect = fn && branch(() => fn(offscreen_anchor)); + + active_fork.branches.push(() => { + anchor.before(fragment); + + if (condition) { + if (consequent_effect) { + resume_effect(consequent_effect); + } else { + consequent_effect = offscreen_effect; + + if (alternate_effect) { + pause_effect(alternate_effect, () => { + alternate_effect = null; + }); + } + } + } else { + if (alternate_effect) { + resume_effect(alternate_effect); + } else { + alternate_effect = offscreen_effect; + + if (consequent_effect) { + pause_effect(consequent_effect, () => { + consequent_effect = null; + }); + } + } + } + }); + + return; + } + if (condition) { if (consequent_effect) { resume_effect(consequent_effect); diff --git a/packages/svelte/src/internal/client/fork.js b/packages/svelte/src/internal/client/fork.js index 0bc547eeff..d416959f67 100644 --- a/packages/svelte/src/internal/client/fork.js +++ b/packages/svelte/src/internal/client/fork.js @@ -1,5 +1,5 @@ /** @import { Derived, Effect, Fork, Value } from '#client' */ -import { BLOCK_EFFECT, DERIVED, DIRTY, TEMPLATE_EFFECT } from './constants.js'; +import { BLOCK_EFFECT, DERIVED, DIRTY, FORK_ROOT, TEMPLATE_EFFECT } from './constants.js'; import { queue_micro_task } from './dom/task.js'; import { flush_sync, schedule_effect, set_signal_status } from './runtime.js'; @@ -19,8 +19,10 @@ export function set_active_fork(fork) { */ function create_fork(callback) { return { + f: FORK_ROOT, pending: 0, sources: new Map(), + branches: [], callback }; } @@ -80,6 +82,10 @@ function apply_fork(fork) { mark_effects(source); } + + for (const fn of fork.branches) { + fn(); + } } /** diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index 95a9fafefb..87c02c80c1 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -185,8 +185,10 @@ export interface SourceFork { } export interface Fork { + f: number; sources: Map; pending: number; + branches: Array<() => void>; callback: (error?: Error) => void; }