chore: use template_effect for html tags (#15779)

pull/15772/head
Rich Harris 5 months ago committed by GitHub
parent 19836e29f2
commit 80f62b5b10
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,6 +1,6 @@
/** @import { Effect, TemplateNode } from '#client' */ /** @import { Effect, TemplateNode } from '#client' */
import { FILENAME, HYDRATION_ERROR } from '../../../../constants.js'; import { FILENAME, HYDRATION_ERROR } from '../../../../constants.js';
import { block, branch, destroy_effect } from '../../reactivity/effects.js'; import { remove_effect_dom, template_effect } from '../../reactivity/effects.js';
import { hydrate_next, hydrate_node, hydrating, set_hydrate_node } from '../hydration.js'; import { hydrate_next, hydrate_node, hydrating, set_hydrate_node } from '../hydration.js';
import { create_fragment_from_html } from '../reconciler.js'; import { create_fragment_from_html } from '../reconciler.js';
import { assign_nodes } from '../template.js'; import { assign_nodes } from '../template.js';
@ -9,6 +9,7 @@ import { hash, sanitize_location } from '../../../../utils.js';
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { dev_current_component_function } from '../../context.js'; import { dev_current_component_function } from '../../context.js';
import { get_first_child, get_next_sibling } from '../operations.js'; import { get_first_child, get_next_sibling } from '../operations.js';
import { active_effect } from '../../runtime.js';
/** /**
* @param {Element} element * @param {Element} element
@ -44,79 +45,71 @@ export function html(node, get_value, svg = false, mathml = false, skip_warning
var value = ''; var value = '';
/** @type {Effect | undefined} */ template_effect(() => {
var effect; var effect = /** @type {Effect} */ (active_effect);
block(() => {
if (value === (value = get_value() ?? '')) { if (value === (value = get_value() ?? '')) {
if (hydrating) { if (hydrating) hydrate_next();
hydrate_next();
}
return; return;
} }
if (effect !== undefined) { if (effect.nodes_start !== null) {
destroy_effect(effect); remove_effect_dom(effect.nodes_start, /** @type {TemplateNode} */ (effect.nodes_end));
effect = undefined; effect.nodes_start = effect.nodes_end = null;
} }
if (value === '') return; if (value === '') return;
effect = branch(() => { if (hydrating) {
if (hydrating) { // We're deliberately not trying to repair mismatches between server and client,
// We're deliberately not trying to repair mismatches between server and client, // as it's costly and error-prone (and it's an edge case to have a mismatch anyway)
// as it's costly and error-prone (and it's an edge case to have a mismatch anyway) var hash = /** @type {Comment} */ (hydrate_node).data;
var hash = /** @type {Comment} */ (hydrate_node).data; var next = hydrate_next();
var next = hydrate_next(); var last = next;
var last = next;
while (
next !== null &&
(next.nodeType !== 8 || /** @type {Comment} */ (next).data !== '')
) {
last = next;
next = /** @type {TemplateNode} */ (get_next_sibling(next));
}
if (next === null) {
w.hydration_mismatch();
throw HYDRATION_ERROR;
}
if (DEV && !skip_warning) {
check_hash(/** @type {Element} */ (next.parentNode), hash, value);
}
assign_nodes(hydrate_node, last);
anchor = set_hydrate_node(next);
return;
}
var html = value + ''; while (next !== null && (next.nodeType !== 8 || /** @type {Comment} */ (next).data !== '')) {
if (svg) html = `<svg>${html}</svg>`; last = next;
else if (mathml) html = `<math>${html}</math>`; next = /** @type {TemplateNode} */ (get_next_sibling(next));
}
// Don't use create_fragment_with_script_from_html here because that would mean script tags are executed. if (next === null) {
// @html is basically `.innerHTML = ...` and that doesn't execute scripts either due to security reasons. w.hydration_mismatch();
/** @type {DocumentFragment | Element} */ throw HYDRATION_ERROR;
var node = create_fragment_from_html(html); }
if (svg || mathml) { if (DEV && !skip_warning) {
node = /** @type {Element} */ (get_first_child(node)); check_hash(/** @type {Element} */ (next.parentNode), hash, value);
} }
assign_nodes( assign_nodes(hydrate_node, last);
/** @type {TemplateNode} */ (get_first_child(node)), anchor = set_hydrate_node(next);
/** @type {TemplateNode} */ (node.lastChild) return;
); }
if (svg || mathml) { var html = value + '';
while (get_first_child(node)) { if (svg) html = `<svg>${html}</svg>`;
anchor.before(/** @type {Node} */ (get_first_child(node))); else if (mathml) html = `<math>${html}</math>`;
}
} else { // Don't use create_fragment_with_script_from_html here because that would mean script tags are executed.
anchor.before(node); // @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} */ (get_first_child(node));
}
assign_nodes(
/** @type {TemplateNode} */ (get_first_child(node)),
/** @type {TemplateNode} */ (node.lastChild)
);
if (svg || mathml) {
while (get_first_child(node)) {
anchor.before(/** @type {Node} */ (get_first_child(node)));
} }
}); } else {
anchor.before(node);
}
}); });
} }

@ -427,18 +427,7 @@ export function destroy_effect(effect, remove_dom = true) {
var removed = false; var removed = false;
if ((remove_dom || (effect.f & HEAD_EFFECT) !== 0) && effect.nodes_start !== null) { if ((remove_dom || (effect.f & HEAD_EFFECT) !== 0) && effect.nodes_start !== null) {
/** @type {TemplateNode | null} */ remove_effect_dom(effect.nodes_start, /** @type {TemplateNode} */ (effect.nodes_end));
var node = effect.nodes_start;
var end = effect.nodes_end;
while (node !== null) {
/** @type {TemplateNode | null} */
var next = node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node));
node.remove();
node = next;
}
removed = true; removed = true;
} }
@ -480,6 +469,21 @@ export function destroy_effect(effect, remove_dom = true) {
null; null;
} }
/**
*
* @param {TemplateNode | null} node
* @param {TemplateNode} end
*/
export function remove_effect_dom(node, end) {
while (node !== null) {
/** @type {TemplateNode | null} */
var next = node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node));
node.remove();
node = next;
}
}
/** /**
* Detach an effect from the effect tree, freeing up memory and * Detach an effect from the effect tree, freeing up memory and
* reducing the amount of work that happens on subsequent traversals * reducing the amount of work that happens on subsequent traversals

Loading…
Cancel
Save