From 27b38cdd9ff8bc9493880a1154e492cf87165ab4 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 27 Jun 2024 11:09:53 -0400 Subject: [PATCH] more --- .../src/internal/client/dom/blocks/html.js | 67 ++++++++++++++----- .../src/internal/client/dom/operations.js | 14 ++-- .../src/internal/client/reactivity/effects.js | 9 +-- 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/blocks/html.js b/packages/svelte/src/internal/client/dom/blocks/html.js index 369bec70f9..9c6c8b8e9a 100644 --- a/packages/svelte/src/internal/client/dom/blocks/html.js +++ b/packages/svelte/src/internal/client/dom/blocks/html.js @@ -1,5 +1,5 @@ import { derived } from '../../reactivity/deriveds.js'; -import { render_effect } from '../../reactivity/effects.js'; +import { block, branch, destroy_effect, render_effect } from '../../reactivity/effects.js'; import { get } from '../../runtime.js'; import { is_array } from '../../utils.js'; import { hydrate_nodes, hydrating } from '../hydration.js'; @@ -14,22 +14,53 @@ import { assign_nodes } from '../template.js'; * @returns {void} */ export function html(anchor, get_value, svg, mathml) { - let value = derived(get_value); + var value = ''; - render_effect(() => { - var dom = html_to_dom(anchor, get(value), svg, mathml); + /** @type {import('#client').Effect | null} */ + var effect; - if (dom) { - if (is_array(dom)) { - assign_nodes(dom[0], dom[dom.length - 1]); - } else { - assign_nodes(dom, dom); - } + block(anchor, 0, () => { + if (value === (value = get_value())) return; - return () => { - remove(dom); - }; + if (effect) { + destroy_effect(effect); + effect = null; } + + if (value === '') return; + + effect = branch(() => { + if (hydrating) { + assign_nodes(hydrate_nodes[0], hydrate_nodes[hydrate_nodes.length - 1]); + return; + } + + var html = value + ''; + if (svg) html = `${html}`; + else if (mathml) html = `${html}`; + + // Don't use create_fragment_with_script_from_html here because that would mean script tags are executed. + // @html is basically `.innerHTML = ...` and that doesn't execute scripts either due to security reasons. + /** @type {DocumentFragment | Element} */ + var node = create_fragment_from_html(html); + + if (svg || mathml) { + node = /** @type {Element} */ (node.firstChild); + } + + assign_nodes( + /** @type {import('#client').TemplateNode} */ (node.firstChild), + /** @type {import('#client').TemplateNode} */ (node.lastChild) + ); + + if (svg || mathml) { + while (node.firstChild) { + anchor.before(node.firstChild); + } + } else { + anchor.before(node); + } + }); }); } @@ -37,13 +68,13 @@ export function html(anchor, get_value, svg, mathml) { * Creates the content for a `@html` tag from its string value, * inserts it before the target anchor and returns the new nodes. * @template V - * @param {Element | Text | Comment} target + * @param {Element | Text | Comment} anchor * @param {V} value * @param {boolean} svg * @param {boolean} mathml * @returns {Element | Comment | (Element | Comment | Text)[]} */ -function html_to_dom(target, value, svg, mathml) { +function html_to_dom(anchor, value, svg, mathml) { if (hydrating) return hydrate_nodes; var html = value + ''; @@ -61,7 +92,7 @@ function html_to_dom(target, value, svg, mathml) { if (node.childNodes.length === 1) { var child = /** @type {Text | Element | Comment} */ (node.firstChild); - target.before(child); + anchor.before(child); return child; } @@ -69,10 +100,10 @@ function html_to_dom(target, value, svg, mathml) { if (svg || mathml) { while (node.firstChild) { - target.before(node.firstChild); + anchor.before(node.firstChild); } } else { - target.before(node); + anchor.before(node); } return nodes; diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index c1fdbd0e75..a28207395d 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -85,11 +85,15 @@ export function first_child(fragment, is_text) { // text node to hydrate — we must therefore create one if (is_text && hydrate_start?.nodeType !== 3) { var text = empty(); - var dom = /** @type {import('#client').TemplateNode[]} */ ( - /** @type {import('#client').Effect} */ (current_effect).dom - ); + // var dom = /** @type {import('#client').TemplateNode[]} */ ( + // /** @type {import('#client').Effect} */ (current_effect).dom + // ); - dom.unshift(text); + if (current_effect.nodes.start === hydrate_start) { + current_effect.nodes.start = text; + } + + // dom.unshift(text); hydrate_start?.before(text); return text; @@ -126,6 +130,8 @@ export function sibling(node, is_text = false) { /** @type {import('#client').Effect} */ (current_effect).dom ); + // TODO something needs to happen here but i'm not quite sure what + dom.unshift(text); next_sibling?.before(text); diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index b4c1118e0d..4a72d63bde 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -121,7 +121,7 @@ function create_effect(type, fn, sync) { sync && effect.deps === null && effect.first === null && - effect.dom === null && + effect.nodes === null && effect.teardown === null; if (!inert && !is_root) { @@ -339,8 +339,6 @@ export function execute_effect_teardown(effect) { * @returns {void} */ export function destroy_effect(effect, remove_dom = true) { - var dom = effect.dom; - var removed = false; if (remove_dom) { @@ -351,10 +349,6 @@ export function destroy_effect(effect, remove_dom = true) { removed = true; } - if (dom !== null && remove_dom) { - // remove(dom); - } - destroy_effect_children(effect, remove_dom && !removed); remove_reactions(effect, 0); set_signal_status(effect, DESTROYED); @@ -379,7 +373,6 @@ export function destroy_effect(effect, remove_dom = true) { effect.prev = effect.teardown = effect.ctx = - effect.dom = effect.deps = effect.parent = effect.fn =