fix: don't clone node if a renderer is available

pull/18058/head
paoloricciuti 4 months ago
parent 9ae3faa09a
commit c459824a88

@ -10,7 +10,18 @@ import {
create_fragment,
create_comment,
set_attribute,
merge_text_nodes
merge_text_nodes,
get_last_child,
import_node,
clone_node,
append_child,
insert_before,
set_text_content,
replace_with,
node_type,
query_selector_all,
get_node_value,
node_name
} from './operations.js';
import { create_fragment_from_html } from './reconciler.js';
import { active_effect } from '../runtime.js';
@ -29,6 +40,7 @@ import {
REACTION_RAN,
TEXT_NODE
} from '#client/constants';
import { renderer } from '../custom-renderer/state.js';
const TEMPLATE_TAG = IS_XHTML ? 'template' : 'TEMPLATE';
const SCRIPT_TAG = IS_XHTML ? 'script' : 'SCRIPT';
@ -75,12 +87,12 @@ export function from_html(content, flags) {
}
var clone = /** @type {TemplateNode} */ (
use_import_node || is_firefox ? document.importNode(node, true) : node.cloneNode(true)
use_import_node || is_firefox ? import_node(node, true) : clone_node(node, true)
);
if (is_fragment) {
var start = /** @type {TemplateNode} */ (get_first_child(clone));
var end = /** @type {TemplateNode} */ (clone.lastChild);
var end = /** @type {TemplateNode} */ (get_last_child(clone));
assign_nodes(start, end);
} else {
@ -122,20 +134,20 @@ function from_namespace(content, flags, ns = 'svg') {
var root = /** @type {Element} */ (get_first_child(fragment));
if (is_fragment) {
node = document.createDocumentFragment();
node = create_fragment();
while (get_first_child(root)) {
node.appendChild(/** @type {TemplateNode} */ (get_first_child(root)));
append_child(node, /** @type {TemplateNode} */ (get_first_child(root)));
}
} else {
node = /** @type {Element} */ (get_first_child(root));
}
}
var clone = /** @type {TemplateNode} */ (node.cloneNode(true));
var clone = /** @type {TemplateNode} */ (clone_node(node, true));
if (is_fragment) {
var start = /** @type {TemplateNode} */ (get_first_child(clone));
var end = /** @type {TemplateNode} */ (clone.lastChild);
var end = /** @type {TemplateNode} */ (get_last_child(clone));
assign_nodes(start, end);
} else {
@ -173,13 +185,13 @@ function fragment_from_tree(structure, ns) {
for (var item of structure) {
if (typeof item === 'string') {
fragment.append(create_text(item));
append_child(fragment, create_text(item));
continue;
}
// if `preserveComments === true`, comments are represented as `['// <data>']`
if (item === undefined || item[0][0] === '/') {
fragment.append(create_comment(item ? item[0].slice(3) : ''));
append_child(fragment, create_comment(item ? item[0].slice(3) : ''));
continue;
}
@ -195,16 +207,18 @@ function fragment_from_tree(structure, ns) {
if (children.length > 0) {
var target =
element.nodeName === TEMPLATE_TAG
? /** @type {HTMLTemplateElement} */ (element).content
node_name(element) === TEMPLATE_TAG
? // TODO: DOM access
/** @type {HTMLTemplateElement} */ (element).content
: element;
target.append(
fragment_from_tree(children, element.nodeName === 'foreignObject' ? undefined : namespace)
append_child(
target,
fragment_from_tree(children, node_name(element) === 'foreignObject' ? undefined : namespace)
);
}
fragment.append(element);
append_child(fragment, element);
}
return fragment;
@ -229,7 +243,10 @@ export function from_tree(structure, flags) {
return hydrate_node;
}
if (node === undefined) {
// for the custom renderer we skip the cloning and create new nodes every time...a bit less efficient
// but save custom renderers implementors from having to implement importNode or cloneNode
// which can be a pain
if (node === undefined || renderer != null) {
const ns =
(flags & TEMPLATE_USE_SVG) !== 0
? NAMESPACE_SVG
@ -242,12 +259,16 @@ export function from_tree(structure, flags) {
}
var clone = /** @type {TemplateNode} */ (
use_import_node || is_firefox ? document.importNode(node, true) : node.cloneNode(true)
renderer != null
? node
: use_import_node || is_firefox
? import_node(node, true)
: clone_node(node, true)
);
if (is_fragment) {
var start = /** @type {TemplateNode} */ (get_first_child(clone));
var end = /** @type {TemplateNode} */ (clone.lastChild);
var end = /** @type {TemplateNode} */ (get_last_child(clone));
assign_nodes(start, end);
} else {
@ -275,31 +296,34 @@ function run_scripts(node) {
// scripts were SSR'd, in which case they will run
if (hydrating) return node;
const is_fragment = node.nodeType === DOCUMENT_FRAGMENT_NODE;
const is_fragment = node_type(node) === DOCUMENT_FRAGMENT_NODE;
const scripts =
/** @type {HTMLElement} */ (node).nodeName === SCRIPT_TAG
// TODO RENDERER: figure out what to do here
node_name(node) === SCRIPT_TAG
? [/** @type {HTMLScriptElement} */ (node)]
: node.querySelectorAll('script');
: query_selector_all(node, 'script');
const effect = /** @type {Effect & { nodes: EffectNodes }} */ (active_effect);
for (const script of scripts) {
const clone = create_element('script');
// TODO: DOM access
for (var attribute of script.attributes) {
clone.setAttribute(attribute.name, attribute.value);
set_attribute(clone, attribute.name, attribute.value);
}
clone.textContent = script.textContent;
// TODO: DOM access
set_text_content(clone, script.textContent ?? '');
// The script has changed - if it's at the edges, the effect now points at dead nodes
if (is_fragment ? node.firstChild === script : node === script) {
if (is_fragment ? get_first_child(node) === script : node === script) {
effect.nodes.start = clone;
}
if (is_fragment ? node.lastChild === script : node === script) {
if (is_fragment ? get_last_child(node) === script : node === script) {
effect.nodes.end = clone;
}
script.replaceWith(clone);
replace_with(script, clone);
}
return node;
}
@ -317,9 +341,9 @@ export function text(value = '') {
var node = hydrate_node;
if (node.nodeType !== TEXT_NODE) {
if (node_type(node) !== TEXT_NODE) {
// if an {expression} is empty during SSR, we need to insert an empty text node
node.before((node = create_text()));
insert_before(node, (node = create_text()));
set_hydrate_node(node);
} else {
merge_text_nodes(/** @type {Text} */ (node));
@ -339,10 +363,11 @@ export function comment() {
return hydrate_node;
}
var frag = document.createDocumentFragment();
var start = document.createComment('');
var frag = create_fragment();
var start = create_comment('');
var anchor = create_text();
frag.append(start, anchor);
append_child(frag, start);
append_child(frag, anchor);
assign_nodes(start, anchor);
@ -375,24 +400,26 @@ export function append(anchor, dom) {
return;
}
anchor.before(/** @type {Node} */ (dom));
insert_before(anchor, /** @type {Node} */ (dom));
}
/**
* Create (or hydrate) an unique UID for the component instance.
*/
export function props_id() {
let node_value;
if (
hydrating &&
hydrate_node &&
hydrate_node.nodeType === COMMENT_NODE &&
hydrate_node.textContent?.startsWith(`$`)
node_type(hydrate_node) === COMMENT_NODE &&
(node_value = get_node_value(hydrate_node))?.startsWith(`$`)
) {
const id = hydrate_node.textContent.substring(1);
const id = node_value.substring(1);
hydrate_next();
return id;
}
// TODO RENDERER: figure out what to do here
// @ts-expect-error This way we ensure the id is unique even across Svelte runtimes
(window.__svelte ??= {}).uid ??= 1;

Loading…
Cancel
Save