diff --git a/packages/svelte/src/internal/client/block.js b/packages/svelte/src/internal/client/block.js index 5ca2bdd850..5db5d748bf 100644 --- a/packages/svelte/src/internal/client/block.js +++ b/packages/svelte/src/internal/client/block.js @@ -32,32 +32,6 @@ export function create_root_block(intro) { }; } -/** @returns {import('./types.js').IfBlock} */ -export function create_if_block() { - return { - // alternate transitions - a: null, - // alternate effect - ae: null, - // consequent transitions - c: null, - // consequent effect - ce: null, - // dom - d: null, - // effect - e: null, - // parent - p: /** @type {import('./types.js').Block} */ (current_block), - // transition - r: null, - // type - t: IF_BLOCK, - // value - v: false - }; -} - /** @returns {import('./types.js').KeyBlock} */ export function create_key_block() { return { diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js new file mode 100644 index 0000000000..50b0f0e040 --- /dev/null +++ b/packages/svelte/src/internal/client/dom/blocks/if.js @@ -0,0 +1,186 @@ +import { IF_BLOCK } from '../../block.js'; +import { + current_hydration_fragment, + hydrate_block_anchor, + hydrating, + set_current_hydration_fragment +} from '../../hydration.js'; +import { remove } from '../../reconciler.js'; +import { + current_block, + destroy_signal, + execute_effect, + push_destroy_fn, + render_effect +} from '../../runtime.js'; +import { trigger_transitions } from '../../transitions.js'; + +/** @returns {import('../../types.js').IfBlock} */ +function create_if_block() { + return { + // alternate transitions + a: null, + // alternate effect + ae: null, + // consequent transitions + c: null, + // consequent effect + ce: null, + // dom + d: null, + // effect + e: null, + // parent + p: /** @type {import('../../types.js').Block} */ (current_block), + // transition + r: null, + // type + t: IF_BLOCK, + // value + v: false + }; +} + +/** + * @param {Comment} anchor_node + * @param {() => boolean} condition_fn + * @param {(anchor: Node) => void} consequent_fn + * @param {null | ((anchor: Node) => void)} alternate_fn + * @returns {void} + */ +export default function (anchor_node, condition_fn, consequent_fn, alternate_fn) { + const block = create_if_block(); + hydrate_block_anchor(anchor_node); + /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */ + let mismatch = false; + + /** @type {null | import('../../types.js').TemplateNode | Array} */ + let consequent_dom = null; + /** @type {null | import('../../types.js').TemplateNode | Array} */ + let alternate_dom = null; + let has_mounted = false; + /** + * @type {import('../../types.js').EffectSignal | null} + */ + let current_branch_effect = null; + + const if_effect = render_effect( + () => { + const result = !!condition_fn(); + if (block.v !== result || !has_mounted) { + block.v = result; + if (has_mounted) { + const consequent_transitions = block.c; + const alternate_transitions = block.a; + if (result) { + if (alternate_transitions === null || alternate_transitions.size === 0) { + execute_effect(alternate_effect); + } else { + trigger_transitions(alternate_transitions, 'out'); + } + if (consequent_transitions === null || consequent_transitions.size === 0) { + execute_effect(consequent_effect); + } else { + trigger_transitions(consequent_transitions, 'in'); + } + } else { + if (consequent_transitions === null || consequent_transitions.size === 0) { + execute_effect(consequent_effect); + } else { + trigger_transitions(consequent_transitions, 'out'); + } + if (alternate_transitions === null || alternate_transitions.size === 0) { + execute_effect(alternate_effect); + } else { + trigger_transitions(alternate_transitions, 'in'); + } + } + } else if (hydrating) { + const comment_text = /** @type {Comment} */ (current_hydration_fragment?.[0])?.data; + if ( + !comment_text || + (comment_text === 'ssr:if:true' && !result) || + (comment_text === 'ssr:if:false' && result) + ) { + // Hydration mismatch: remove everything inside the anchor and start fresh. + // This could happen using when `{#if browser} .. {/if}` in SvelteKit. + remove(current_hydration_fragment); + set_current_hydration_fragment(null); + mismatch = true; + } else { + // Remove the ssr:if comment node or else it will confuse the subsequent hydration algorithm + current_hydration_fragment.shift(); + } + } + has_mounted = true; + } + }, + block, + false + ); + // Managed effect + const consequent_effect = render_effect( + ( + /** @type {any} */ _, + /** @type {import('../../types.js').EffectSignal | null} */ consequent_effect + ) => { + const result = block.v; + if (!result && consequent_dom !== null) { + remove(consequent_dom); + consequent_dom = null; + } + if (result && current_branch_effect !== consequent_effect) { + consequent_fn(anchor_node); + if (mismatch && current_branch_effect === null) { + // Set fragment so that Svelte continues to operate in hydration mode + set_current_hydration_fragment([]); + } + current_branch_effect = consequent_effect; + consequent_dom = block.d; + } + block.d = null; + }, + block, + true + ); + block.ce = consequent_effect; + // Managed effect + const alternate_effect = render_effect( + ( + /** @type {any} */ _, + /** @type {import('../../types.js').EffectSignal | null} */ alternate_effect + ) => { + const result = block.v; + if (result && alternate_dom !== null) { + remove(alternate_dom); + alternate_dom = null; + } + if (!result && current_branch_effect !== alternate_effect) { + if (alternate_fn !== null) { + alternate_fn(anchor_node); + } + if (mismatch && current_branch_effect === null) { + // Set fragment so that Svelte continues to operate in hydration mode + set_current_hydration_fragment([]); + } + current_branch_effect = alternate_effect; + alternate_dom = block.d; + } + block.d = null; + }, + block, + true + ); + block.ae = alternate_effect; + push_destroy_fn(if_effect, () => { + if (consequent_dom !== null) { + remove(consequent_dom); + } + if (alternate_dom !== null) { + remove(alternate_dom); + } + destroy_signal(consequent_effect); + destroy_signal(alternate_effect); + }); + block.e = if_effect; +} diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 84b7c529b1..f334fb0312 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -12,7 +12,6 @@ import { } from './operations.js'; import { create_root_block, - create_if_block, create_key_block, create_await_block, create_dynamic_element_block, @@ -1453,151 +1452,6 @@ export function slot(anchor_node, slot_fn, slot_props, fallback_fn) { } } -/** - * @param {Comment} anchor_node - * @param {() => boolean} condition_fn - * @param {(anchor: Node) => void} consequent_fn - * @param {null | ((anchor: Node) => void)} alternate_fn - * @returns {void} - */ -function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) { - const block = create_if_block(); - hydrate_block_anchor(anchor_node); - /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */ - let mismatch = false; - - /** @type {null | import('./types.js').TemplateNode | Array} */ - let consequent_dom = null; - /** @type {null | import('./types.js').TemplateNode | Array} */ - let alternate_dom = null; - let has_mounted = false; - /** - * @type {import("./types.js").EffectSignal | null} - */ - let current_branch_effect = null; - - const if_effect = render_effect( - () => { - const result = !!condition_fn(); - if (block.v !== result || !has_mounted) { - block.v = result; - if (has_mounted) { - const consequent_transitions = block.c; - const alternate_transitions = block.a; - if (result) { - if (alternate_transitions === null || alternate_transitions.size === 0) { - execute_effect(alternate_effect); - } else { - trigger_transitions(alternate_transitions, 'out'); - } - if (consequent_transitions === null || consequent_transitions.size === 0) { - execute_effect(consequent_effect); - } else { - trigger_transitions(consequent_transitions, 'in'); - } - } else { - if (consequent_transitions === null || consequent_transitions.size === 0) { - execute_effect(consequent_effect); - } else { - trigger_transitions(consequent_transitions, 'out'); - } - if (alternate_transitions === null || alternate_transitions.size === 0) { - execute_effect(alternate_effect); - } else { - trigger_transitions(alternate_transitions, 'in'); - } - } - } else if (hydrating) { - const comment_text = /** @type {Comment} */ (current_hydration_fragment?.[0])?.data; - if ( - !comment_text || - (comment_text === 'ssr:if:true' && !result) || - (comment_text === 'ssr:if:false' && result) - ) { - // Hydration mismatch: remove everything inside the anchor and start fresh. - // This could happen using when `{#if browser} .. {/if}` in SvelteKit. - remove(current_hydration_fragment); - set_current_hydration_fragment(null); - mismatch = true; - } else { - // Remove the ssr:if comment node or else it will confuse the subsequent hydration algorithm - current_hydration_fragment.shift(); - } - } - has_mounted = true; - } - }, - block, - false - ); - // Managed effect - const consequent_effect = render_effect( - ( - /** @type {any} */ _, - /** @type {import("./types.js").EffectSignal | null} */ consequent_effect - ) => { - const result = block.v; - if (!result && consequent_dom !== null) { - remove(consequent_dom); - consequent_dom = null; - } - if (result && current_branch_effect !== consequent_effect) { - consequent_fn(anchor_node); - if (mismatch && current_branch_effect === null) { - // Set fragment so that Svelte continues to operate in hydration mode - set_current_hydration_fragment([]); - } - current_branch_effect = consequent_effect; - consequent_dom = block.d; - } - block.d = null; - }, - block, - true - ); - block.ce = consequent_effect; - // Managed effect - const alternate_effect = render_effect( - ( - /** @type {any} */ _, - /** @type {import("./types.js").EffectSignal | null} */ alternate_effect - ) => { - const result = block.v; - if (result && alternate_dom !== null) { - remove(alternate_dom); - alternate_dom = null; - } - if (!result && current_branch_effect !== alternate_effect) { - if (alternate_fn !== null) { - alternate_fn(anchor_node); - } - if (mismatch && current_branch_effect === null) { - // Set fragment so that Svelte continues to operate in hydration mode - set_current_hydration_fragment([]); - } - current_branch_effect = alternate_effect; - alternate_dom = block.d; - } - block.d = null; - }, - block, - true - ); - block.ae = alternate_effect; - push_destroy_fn(if_effect, () => { - if (consequent_dom !== null) { - remove(consequent_dom); - } - if (alternate_dom !== null) { - remove(alternate_dom); - } - destroy_signal(consequent_effect); - destroy_signal(alternate_effect); - }); - block.e = if_effect; -} -export { if_block as if }; - /** * @param {(anchor: Node | null) => void} render_fn * @returns {void} diff --git a/packages/svelte/src/internal/index.js b/packages/svelte/src/internal/index.js index 9077585a33..4c4bbb9519 100644 --- a/packages/svelte/src/internal/index.js +++ b/packages/svelte/src/internal/index.js @@ -40,6 +40,7 @@ export { freeze, init } from './client/runtime.js'; +export { default as if } from './client/dom/blocks/if.js'; export * from './client/each.js'; export * from './client/render.js'; export * from './client/validate.js';