From c08bd161036faab19573ac4145b1392b06dc63ea Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 26 Mar 2024 15:30:12 -0400 Subject: [PATCH] tidy up --- .../client/dom/blocks/svelte-element.js | 192 +++++++++--------- .../internal/client/dom/blocks/svelte-head.js | 7 +- 2 files changed, 99 insertions(+), 100 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js index aed52029df..5dff16f014 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js @@ -6,6 +6,7 @@ import { branch, destroy_effect, pause_effect, + render_effect, resume_effect } from '../../reactivity/effects.js'; import { remove } from '../reconciler.js'; @@ -45,110 +46,105 @@ function swap_block_dom(effect, from, to) { export function element(anchor, get_tag, is_svg, render_fn) { const parent_effect = /** @type {import('#client').Effect} */ (current_effect); - /** @type {string | null} */ - let tag; - - /** @type {string | null} */ - let current_tag; - - /** @type {null | Element} */ - let element = null; - - /** @type {import('#client').Effect | null} */ - let effect; - - /** - * The keyed `{#each ...}` item block, if any, that this element is inside. - * We track this so we can set it when changing the element, allowing any - * `animate:` directive to bind itself to the correct block - */ - let each_item_block = current_each_item; - - const wrapper = block(() => { - const next_tag = get_tag() || null; - if (next_tag === tag) return; - - // See explanation of `each_item_block` above - var previous_each_item = current_each_item; - set_current_each_item(each_item_block); - - // We try our best infering the namespace in case it's not possible to determine statically, - // but on the first render on the client (without hydration) the parent will be undefined, - // since the anchor is not attached to its parent / the dom yet. - const ns = - is_svg || next_tag === 'svg' - ? namespace_svg - : is_svg === false || anchor.parentElement?.tagName === 'foreignObject' - ? null - : anchor.parentElement?.namespaceURI ?? null; - - if (effect) { - if (next_tag === null) { - // start outro - pause_effect(effect, () => { - effect = null; - current_tag = null; - element?.remove(); // TODO this should be unnecessary - }); - } else if (next_tag === current_tag) { - // same tag as is currently rendered — abort outro - resume_effect(effect); - } else { - // tag is changing — destroy immediately, render contents without intro transitions - destroy_effect(effect); - set_should_intro(false); + render_effect(() => { + /** @type {string | null} */ + let tag; + + /** @type {string | null} */ + let current_tag; + + /** @type {null | Element} */ + let element = null; + + /** @type {import('#client').Effect | null} */ + let effect; + + /** + * The keyed `{#each ...}` item block, if any, that this element is inside. + * We track this so we can set it when changing the element, allowing any + * `animate:` directive to bind itself to the correct block + */ + let each_item_block = current_each_item; + + block(() => { + const next_tag = get_tag() || null; + if (next_tag === tag) return; + + // See explanation of `each_item_block` above + var previous_each_item = current_each_item; + set_current_each_item(each_item_block); + + // We try our best infering the namespace in case it's not possible to determine statically, + // but on the first render on the client (without hydration) the parent will be undefined, + // since the anchor is not attached to its parent / the dom yet. + const ns = + is_svg || next_tag === 'svg' + ? namespace_svg + : is_svg === false || anchor.parentElement?.tagName === 'foreignObject' + ? null + : anchor.parentElement?.namespaceURI ?? null; + + if (effect) { + if (next_tag === null) { + // start outro + pause_effect(effect, () => { + effect = null; + current_tag = null; + element?.remove(); // TODO this should be unnecessary + }); + } else if (next_tag === current_tag) { + // same tag as is currently rendered — abort outro + resume_effect(effect); + } else { + // tag is changing — destroy immediately, render contents without intro transitions + destroy_effect(effect); + set_should_intro(false); + } } - } - if (next_tag && next_tag !== current_tag) { - effect = branch(() => { - const prev_element = element; - element = hydrating - ? /** @type {Element} */ (hydrate_nodes[0]) - : ns - ? document.createElementNS(ns, next_tag) - : document.createElement(next_tag); - - if (render_fn) { - // If hydrating, use the existing ssr comment as the anchor so that the - // inner open and close methods can pick up the existing nodes correctly - var child_anchor = hydrating - ? element.firstChild && hydrate_anchor(/** @type {Comment} */ (element.firstChild)) - : element.appendChild(empty()); - - if (child_anchor) { - // `child_anchor` can be undefined if this is a void element with children, - // i.e. `...`. This is - // user error, but we warn on it elsewhere (in dev) so here we just - // silently ignore it - render_fn(element, child_anchor); + if (next_tag && next_tag !== current_tag) { + effect = branch(() => { + const prev_element = element; + element = hydrating + ? /** @type {Element} */ (hydrate_nodes[0]) + : ns + ? document.createElementNS(ns, next_tag) + : document.createElement(next_tag); + + if (render_fn) { + // If hydrating, use the existing ssr comment as the anchor so that the + // inner open and close methods can pick up the existing nodes correctly + var child_anchor = hydrating + ? element.firstChild && hydrate_anchor(/** @type {Comment} */ (element.firstChild)) + : element.appendChild(empty()); + + if (child_anchor) { + // `child_anchor` can be undefined if this is a void element with children, + // i.e. `...`. This is + // user error, but we warn on it elsewhere (in dev) so here we just + // silently ignore it + render_fn(element, child_anchor); + } } - } - anchor.before(element); - - if (prev_element) { - swap_block_dom(parent_effect, prev_element, element); - prev_element.remove(); - } - }); - } + anchor.before(element); - tag = next_tag; - if (tag) current_tag = tag; - set_should_intro(true); + if (prev_element) { + swap_block_dom(parent_effect, prev_element, element); + prev_element.remove(); + } + }); + } - set_current_each_item(previous_each_item); - }); + tag = next_tag; + if (tag) current_tag = tag; + set_should_intro(true); - wrapper.ondestroy = () => { - if (element !== null) { - remove(element); - element = null; - } + set_current_each_item(previous_each_item); + }); - if (effect) { - destroy_effect(effect); - } - }; + return () => { + element?.remove(); + }; + }); } diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-head.js b/packages/svelte/src/internal/client/dom/blocks/svelte-head.js index af942c5b35..07d02d2c06 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-head.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-head.js @@ -12,6 +12,9 @@ export function head(render_fn) { let previous_hydrate_nodes = null; let was_hydrating = hydrating; + /** @type {Comment | Text} */ + var anchor; + if (hydrating) { previous_hydrate_nodes = hydrate_nodes; @@ -21,10 +24,10 @@ export function head(render_fn) { } anchor = /** @type {import('#client').TemplateNode} */ (hydrate_anchor(anchor)); + } else { + anchor = document.head.appendChild(empty()); } - var anchor = document.head.appendChild(empty()); - try { block(() => render_fn(anchor)); } finally {