diff --git a/.changeset/cuddly-pianos-drop.md b/.changeset/cuddly-pianos-drop.md new file mode 100644 index 0000000000..7993ba6289 --- /dev/null +++ b/.changeset/cuddly-pianos-drop.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: improve bundle code size diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 9f05591753..d5a06d4cc8 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -53,12 +53,10 @@ import { flushSync, expose, safe_not_equal, - managed_pre_effect, current_block, set_signal_value, source, managed_effect, - mark_subtree_inert, safe_equal, push, current_component_context, @@ -73,7 +71,7 @@ import { } from './hydration.js'; import { array_from, define_property, get_descriptor, get_descriptors, is_array } from './utils.js'; import { is_promise } from '../common.js'; -import { bind_transition } from './transitions.js'; +import { bind_transition, remove_in_transitions, trigger_transitions } from './transitions.js'; /** @type {Set} */ const all_registerd_events = new Set(); @@ -82,7 +80,7 @@ const all_registerd_events = new Set(); const root_event_handles = new Set(); /** @returns {Text} */ -function empty() { +export function empty() { return document.createTextNode(''); } @@ -1370,11 +1368,7 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) { consequent_transitions.add(transition); transition.finished(() => { consequent_transitions.delete(transition); - for (let other of consequent_transitions) { - if (other.direction === 'in') { - consequent_transitions.delete(other); - } - } + remove_in_transitions(consequent_transitions); if (consequent_transitions.size === 0) { execute_effect(consequent_effect); } @@ -1383,11 +1377,7 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) { alternate_transitions.add(transition); transition.finished(() => { alternate_transitions.delete(transition); - for (let other of alternate_transitions) { - if (other.direction === 'in') { - alternate_transitions.delete(other); - } - } + remove_in_transitions(alternate_transitions); if (alternate_transitions.size === 0) { execute_effect(alternate_effect); } @@ -1675,11 +1665,7 @@ export function component(anchor_node, component_fn, render_fn) { transitions.add(transition); transition.finished(() => { transitions.delete(transition); - for (let other of transitions) { - if (other.direction === 'in') { - transitions.delete(other); - } - } + remove_in_transitions(transitions); if (transitions.size === 0) { if (render.effect !== null) { if (render.dom !== null) { @@ -1806,11 +1792,7 @@ function await_block(anchor_node, input, pending_fn, then_fn, catch_fn) { transitions.add(transition); transition.finished(() => { transitions.delete(transition); - for (let other of transitions) { - if (other.direction === 'in') { - transitions.delete(other); - } - } + remove_in_transitions(transitions); if (transitions.size === 0) { if (render.effect !== null) { if (render.dom !== null) { @@ -1864,6 +1846,7 @@ function await_block(anchor_node, input, pending_fn, then_fn, catch_fn) { return; } const transitions = render.transitions; + remove_in_transitions(transitions); if (transitions.size === 0) { if (render.dom !== null) { remove(render.dom); @@ -1968,11 +1951,7 @@ export function key(anchor_node, key, render_fn) { transitions.add(transition); transition.finished(() => { transitions.delete(transition); - for (let other of transitions) { - if (other.direction === 'in') { - transitions.delete(other); - } - } + remove_in_transitions(transitions); if (transitions.size === 0) { if (render.effect !== null) { if (render.dom !== null) { @@ -2013,6 +1992,7 @@ export function key(anchor_node, key, render_fn) { return; } const transitions = render.transitions; + remove_in_transitions(transitions); if (transitions.size === 0) { if (render.dom !== null) { remove(render.dom); @@ -2140,96 +2120,6 @@ export function destroy_each_item_block( } } -/** - * @this {import('./types.js').EachItemBlock} - * @param {import('./types.js').Transition} transition - * @returns {void} - */ -function each_item_transition(transition) { - const block = this; - const each_block = block.parent; - const is_controlled = (each_block.flags & EACH_IS_CONTROLLED) !== 0; - // Disable optimization - if (is_controlled) { - const anchor = empty(); - each_block.flags ^= EACH_IS_CONTROLLED; - append_child(/** @type {Element} */ (each_block.anchor), anchor); - each_block.anchor = anchor; - } - if (transition.direction === 'key' && (each_block.flags & EACH_IS_ANIMATED) === 0) { - each_block.flags |= EACH_IS_ANIMATED; - } - let transitions = block.transitions; - if (transitions === null) { - block.transitions = transitions = new Set(); - } - transition.finished(() => { - if (transitions !== null) { - transitions.delete(transition); - if (transition.direction !== 'key') { - for (let other of transitions) { - if (other.direction === 'key' || other.direction === 'in') { - transitions.delete(other); - } - } - if (transitions.size === 0) { - block.transitions = null; - destroy_each_item_block(block, null, true); - } - } - } - }); - transitions.add(transition); -} - -/** - * @param {Set} transitions - * @param {'in' | 'out' | 'key'} target_direction - * @param {DOMRect} [from] - * @returns {void} - */ -export function trigger_transitions(transitions, target_direction, from) { - /** @type {Array<() => void>} */ - const outros = []; - for (const transition of transitions) { - const direction = transition.direction; - if (target_direction === 'in') { - if (direction === 'in' || direction === 'both') { - if (direction === 'in') { - transition.cancel(); - } - transition.in(); - } else { - transition.cancel(); - } - transition.dom.inert = false; - mark_subtree_inert(transition.effect, false); - } else if (target_direction === 'key') { - if (direction === 'key') { - transition.payload = transition.init(/** @type {DOMRect} */ (from)); - transition.in(); - } - } else { - if (direction === 'out' || direction === 'both') { - transition.payload = transition.init(); - outros.push(transition.out); - } - transition.dom.inert = true; - mark_subtree_inert(transition.effect, true); - } - } - if (outros.length > 0) { - // Defer the outros to a microtask - const e = managed_pre_effect(() => { - destroy_signal(e); - const e2 = managed_effect(() => { - destroy_signal(e2); - outros.forEach(/** @param {any} o */ (o) => o()); - }); - }, false); - } -} - /** * @template V * @param {V} item @@ -2243,7 +2133,6 @@ export function each_item_block(item, key, index, render_fn, flags) { const item_value = (flags & EACH_ITEM_REACTIVE) === 0 ? item : source(item); const index_value = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index); const block = create_each_item_block(item_value, index_value, key); - block.transition = each_item_transition; const effect = render_effect( /** @param {import('./types.js').EachItemBlock} block */ (block) => { @@ -2291,11 +2180,7 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re transitions.add(transition); transition.finished(() => { transitions.delete(transition); - for (let other of transitions) { - if (other.direction === 'in') { - transitions.delete(other); - } - } + remove_in_transitions(transitions); if (transitions.size === 0) { if (fallback.effect !== null) { if (fallback.dom !== null) { diff --git a/packages/svelte/src/internal/client/transitions.js b/packages/svelte/src/internal/client/transitions.js index edd80fec38..4413af63c9 100644 --- a/packages/svelte/src/internal/client/transitions.js +++ b/packages/svelte/src/internal/client/transitions.js @@ -1,3 +1,4 @@ +import { EACH_IS_ANIMATED, EACH_IS_CONTROLLED } from '../../constants.js'; import { AWAIT_BLOCK, DYNAMIC_COMPONENT_BLOCK, @@ -7,12 +8,16 @@ import { KEY_BLOCK, ROOT_BLOCK } from './block.js'; +import { append_child } from './operations.js'; +import { destroy_each_item_block, empty } from './render.js'; import { current_block, current_effect, destroy_signal, effect, + managed_effect, managed_pre_effect, + mark_subtree_inert, untrack } from './runtime.js'; import { raf } from './timing.js'; @@ -414,6 +419,8 @@ export function bind_transition(dom, transition_fn, props_fn, direction, global) while (transition_block !== null) { if (is_transition_block(transition_block)) { if (transition_block.type === EACH_ITEM_BLOCK) { + // Lazily apply the each block transition + transition_block.transition = each_item_transition; transition_block = transition_block.parent; } else if (transition_block.type === AWAIT_BLOCK && transition_block.pending) { skip_intro = false; @@ -496,3 +503,104 @@ export function bind_transition(dom, transition_fn, props_fn, direction, global) }); } } + +/** + * @param {Set} transitions + */ +export function remove_in_transitions(transitions) { + for (let other of transitions) { + if (other.direction === 'in') { + transitions.delete(other); + } + } +} + +/** + * @param {Set} transitions + * @param {'in' | 'out' | 'key'} target_direction + * @param {DOMRect} [from] + * @returns {void} + */ +export function trigger_transitions(transitions, target_direction, from) { + /** @type {Array<() => void>} */ + const outros = []; + for (const transition of transitions) { + const direction = transition.direction; + if (target_direction === 'in') { + if (direction === 'in' || direction === 'both') { + if (direction === 'in') { + transition.cancel(); + } + transition.in(); + } else { + transition.cancel(); + } + transition.dom.inert = false; + mark_subtree_inert(transition.effect, false); + } else if (target_direction === 'key') { + if (direction === 'key') { + transition.payload = transition.init(/** @type {DOMRect} */ (from)); + transition.in(); + } + } else { + if (direction === 'out' || direction === 'both') { + transition.payload = transition.init(); + outros.push(transition.out); + } + transition.dom.inert = true; + mark_subtree_inert(transition.effect, true); + } + } + if (outros.length > 0) { + // Defer the outros to a microtask + const e = managed_pre_effect(() => { + destroy_signal(e); + const e2 = managed_effect(() => { + destroy_signal(e2); + outros.forEach(/** @param {any} o */ (o) => o()); + }); + }, false); + } +} + +/** + * @this {import('./types.js').EachItemBlock} + * @param {import('./types.js').Transition} transition + * @returns {void} + */ +function each_item_transition(transition) { + const block = this; + const each_block = block.parent; + const is_controlled = (each_block.flags & EACH_IS_CONTROLLED) !== 0; + // Disable optimization + if (is_controlled) { + const anchor = empty(); + each_block.flags ^= EACH_IS_CONTROLLED; + append_child(/** @type {Element} */ (each_block.anchor), anchor); + each_block.anchor = anchor; + } + if (transition.direction === 'key' && (each_block.flags & EACH_IS_ANIMATED) === 0) { + each_block.flags |= EACH_IS_ANIMATED; + } + let transitions = block.transitions; + if (transitions === null) { + block.transitions = transitions = new Set(); + } + transition.finished(() => { + if (transitions !== null) { + transitions.delete(transition); + if (transition.direction !== 'key') { + for (let other of transitions) { + if (other.direction === 'key' || other.direction === 'in') { + transitions.delete(other); + } + } + if (transitions.size === 0) { + block.transitions = null; + destroy_each_item_block(block, null, true); + } + } + } + }); + transitions.add(transition); +}