chore: more hydration stuff (#10896)

* this condition is always true

* rename child_frag to first_child

* no need to use is_array, it is always an array when hydrating

* simplify close_template

* spread is faster than Array.from

* avoid reassigning argument
pull/10903/head
Rich Harris 8 months ago committed by GitHub
parent 9b7331c04c
commit 32b1824198
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1202,7 +1202,7 @@ function create_block(parent, name, nodes, context) {
} else { } else {
/** @type {(is_text: boolean) => import('estree').Expression} */ /** @type {(is_text: boolean) => import('estree').Expression} */
const expression = (is_text) => const expression = (is_text) =>
is_text ? b.call('$.child_frag', id, b.true) : b.call('$.child_frag', id); is_text ? b.call('$.first_child', id, b.true) : b.call('$.first_child', id);
process_children(trimmed, expression, false, { ...context, state }); process_children(trimmed, expression, false, { ...context, state });

@ -134,18 +134,19 @@ export function child(node) {
} }
/** /**
* @template {Node | Node[]} N * @param {DocumentFragment | import('#client').TemplateNode[]} fragment
* @param {N} node
* @param {boolean} is_text * @param {boolean} is_text
* @returns {Node | null} * @returns {Node | null}
*/ */
/*#__NO_SIDE_EFFECTS__*/ /*#__NO_SIDE_EFFECTS__*/
export function child_frag(node, is_text) { export function first_child(fragment, is_text) {
if (!hydrating) { if (!hydrating) {
return first_child_get.call(/** @type {Node} */ (node)); // when not hydrating, `fragment` is a `DocumentFragment` (the result of calling `open_frag`)
return first_child_get.call(/** @type {DocumentFragment} */ (fragment));
} }
const first_node = /** @type {import('#client').TemplateNode[]} */ (node)[0]; // when we _are_ hydrating, `fragment` is an array of nodes
const first_node = /** @type {import('#client').TemplateNode[]} */ (fragment)[0];
// if an {expression} is empty during SSR, there might be no // if an {expression} is empty during SSR, there might be no
// text node to hydrate — we must therefore create one // text node to hydrate — we must therefore create one

@ -93,7 +93,7 @@ export function reconcile_html(target, value, svg) {
content = /** @type {DocumentFragment} */ (/** @type {unknown} */ (content.firstChild)); content = /** @type {DocumentFragment} */ (/** @type {unknown} */ (content.firstChild));
} }
var clone = content.cloneNode(true); var clone = content.cloneNode(true);
frag_nodes = Array.from(clone.childNodes); frag_nodes = [...clone.childNodes];
frag_nodes.forEach((node) => { frag_nodes.forEach((node) => {
target.before(node); target.before(node);
}); });

@ -87,18 +87,17 @@ export function svg_template_with_script(svg, return_fragment) {
* @param {() => Node} [template_element_fn] * @param {() => Node} [template_element_fn]
* @returns {Element | DocumentFragment | Node[]} * @returns {Element | DocumentFragment | Node[]}
*/ */
/*#__NO_SIDE_EFFECTS__*/
function open_template(is_fragment, use_clone_node, anchor, template_element_fn) { function open_template(is_fragment, use_clone_node, anchor, template_element_fn) {
if (hydrating) { if (hydrating) {
if (anchor !== null) { if (anchor !== null) {
// TODO why is this sometimes null and sometimes not? needs clear documentation
hydrate_block_anchor(anchor); hydrate_block_anchor(anchor);
} }
// In ssr+hydration optimization mode, we might remove the template_element,
// so we need to is_fragment flag to properly handle hydrated content accordingly. return is_fragment ? hydrate_nodes : /** @type {Element} */ (hydrate_nodes[0]);
const nodes = hydrate_nodes;
if (nodes !== null) {
return is_fragment ? nodes : /** @type {Element} */ (nodes[0]);
}
} }
return use_clone_node return use_clone_node
? clone_node(/** @type {() => Element} */ (template_element_fn)(), true) ? clone_node(/** @type {() => Element} */ (template_element_fn)(), true)
: document.importNode(/** @type {() => Element} */ (template_element_fn)(), true); : document.importNode(/** @type {() => Element} */ (template_element_fn)(), true);
@ -108,11 +107,10 @@ function open_template(is_fragment, use_clone_node, anchor, template_element_fn)
* @param {null | Text | Comment | Element} anchor * @param {null | Text | Comment | Element} anchor
* @param {() => Node} template_element_fn * @param {() => Node} template_element_fn
* @param {boolean} [use_clone_node] * @param {boolean} [use_clone_node]
* @returns {Element | DocumentFragment | Node[]} * @returns {Element}
*/ */
/*#__NO_SIDE_EFFECTS__*/
export function open(anchor, template_element_fn, use_clone_node = true) { export function open(anchor, template_element_fn, use_clone_node = true) {
return open_template(false, use_clone_node, anchor, template_element_fn); return /** @type {Element} */ (open_template(false, use_clone_node, anchor, template_element_fn));
} }
/** /**
@ -121,7 +119,6 @@ export function open(anchor, template_element_fn, use_clone_node = true) {
* @param {boolean} [use_clone_node] * @param {boolean} [use_clone_node]
* @returns {Element | DocumentFragment | Node[]} * @returns {Element | DocumentFragment | Node[]}
*/ */
/*#__NO_SIDE_EFFECTS__*/
export function open_frag(anchor, template_element_fn, use_clone_node = true) { export function open_frag(anchor, template_element_fn, use_clone_node = true) {
return open_template(true, use_clone_node, anchor, template_element_fn); return open_template(true, use_clone_node, anchor, template_element_fn);
} }
@ -175,21 +172,25 @@ export function comment(anchor) {
/** /**
* Assign the created (or in hydration mode, traversed) dom elements to the current block * Assign the created (or in hydration mode, traversed) dom elements to the current block
* and insert the elements into the dom (in client mode). * and insert the elements into the dom (in client mode).
* @param {Element | Text} dom * @param {import('#client').Dom} dom
* @param {boolean} is_fragment * @param {boolean} is_fragment
* @param {null | Text | Comment | Element} anchor * @param {null | Text | Comment | Element} anchor
* @returns {import('#client').Dom} * @returns {import('#client').Dom}
*/ */
function close_template(dom, is_fragment, anchor) { function close_template(dom, is_fragment, anchor) {
/** @type {import('#client').Dom} */ var current = dom;
var current = is_fragment
? is_array(dom) if (!hydrating) {
? dom if (is_fragment) {
: /** @type {import('#client').TemplateNode[]} */ (Array.from(dom.childNodes)) // if hydrating, `dom` is already an array of nodes, but if not then
: dom; // we need to create an array to store it on the current effect
current = /** @type {import('#client').Dom} */ ([.../** @type {Node} */ (dom).childNodes]);
if (!hydrating && anchor !== null) { }
insert(current, anchor);
if (anchor !== null) {
// TODO as with `open_template — why is this sometimes null and sometimes not?
insert(current, anchor);
}
} }
/** @type {import('#client').Effect} */ (current_effect).dom = current; /** @type {import('#client').Effect} */ (current_effect).dom = current;

@ -64,7 +64,7 @@ export { proxy, unstate } from './client/proxy.js';
export { create_custom_element } from './client/dom/elements/custom-element.js'; export { create_custom_element } from './client/dom/elements/custom-element.js';
export { export {
child, child,
child_frag, first_child,
sibling, sibling,
$window as window, $window as window,
$document as document $document as document

@ -9,9 +9,9 @@ export default function Bind_this($$anchor, $$props) {
/* Init */ /* Init */
var fragment = $.comment($$anchor); var fragment = $.comment($$anchor);
var node = $.child_frag(fragment); var node = $.first_child(fragment);
$.bind_this(Foo(node, {}), ($$value) => foo = $$value, () => foo); $.bind_this(Foo(node, {}), ($$value) => foo = $$value, () => foo);
$.close_frag($$anchor, fragment); $.close_frag($$anchor, fragment);
$.pop(); $.pop();
} }

@ -13,7 +13,7 @@ export default function Main($$anchor, $$props) {
let y = () => 'test'; let y = () => 'test';
/* Init */ /* Init */
var fragment = $.open_frag($$anchor, frag, false); var fragment = $.open_frag($$anchor, frag, false);
var div = $.child_frag(fragment); var div = $.first_child(fragment);
var svg = $.sibling($.sibling(div, true)); var svg = $.sibling($.sibling(div, true));
var custom_element = $.sibling($.sibling(svg, true)); var custom_element = $.sibling($.sibling(svg, true));
var div_1 = $.sibling($.sibling(custom_element, true)); var div_1 = $.sibling($.sibling(custom_element, true));

@ -9,7 +9,7 @@ export default function Each_string_template($$anchor, $$props) {
/* Init */ /* Init */
var fragment = $.comment($$anchor); var fragment = $.comment($$anchor);
var node = $.child_frag(fragment); var node = $.first_child(fragment);
$.each_indexed( $.each_indexed(
node, node,

@ -15,7 +15,7 @@ export default function Function_prop_no_getter($$anchor, $$props) {
const plusOne = (num) => num + 1; const plusOne = (num) => num + 1;
/* Init */ /* Init */
var fragment = $.comment($$anchor); var fragment = $.comment($$anchor);
var node = $.child_frag(fragment); var node = $.first_child(fragment);
Button(node, { Button(node, {
onmousedown: () => $.set(count, $.get(count) + 1), onmousedown: () => $.set(count, $.get(count) + 1),

@ -19,7 +19,7 @@ export default function State_proxy_literal($$anchor, $$props) {
let tpl = $.source(``); let tpl = $.source(``);
/* Init */ /* Init */
var fragment = $.open_frag($$anchor, frag); var fragment = $.open_frag($$anchor, frag);
var input = $.child_frag(fragment); var input = $.first_child(fragment);
$.remove_input_attr_defaults(input); $.remove_input_attr_defaults(input);

@ -9,9 +9,9 @@ export default function Svelte_element($$anchor, $$props) {
let tag = $.prop($$props, "tag", 3, 'hr'); let tag = $.prop($$props, "tag", 3, 'hr');
/* Init */ /* Init */
var fragment = $.comment($$anchor); var fragment = $.comment($$anchor);
var node = $.child_frag(fragment); var node = $.first_child(fragment);
$.element(node, tag, false); $.element(node, tag, false);
$.close_frag($$anchor, fragment); $.close_frag($$anchor, fragment);
$.pop(); $.pop();
} }

Loading…
Cancel
Save