Skip to content

chore: more hydration stuff #10896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1202,7 +1202,7 @@ function create_block(parent, name, nodes, context) {
} else {
/** @type {(is_text: boolean) => import('estree').Expression} */
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 });

Expand Down
11 changes: 6 additions & 5 deletions packages/svelte/src/internal/client/dom/operations.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,18 +134,19 @@ export function child(node) {
}

/**
* @template {Node | Node[]} N
* @param {N} node
* @param {DocumentFragment | import('#client').TemplateNode[]} fragment
* @param {boolean} is_text
* @returns {Node | null}
*/
/*#__NO_SIDE_EFFECTS__*/
export function child_frag(node, is_text) {
export function first_child(fragment, is_text) {
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
// text node to hydrate — we must therefore create one
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/client/dom/reconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export function reconcile_html(target, value, svg) {
content = /** @type {DocumentFragment} */ (/** @type {unknown} */ (content.firstChild));
}
var clone = content.cloneNode(true);
frag_nodes = Array.from(clone.childNodes);
frag_nodes = [...clone.childNodes];
frag_nodes.forEach((node) => {
target.before(node);
});
Expand Down
41 changes: 21 additions & 20 deletions packages/svelte/src/internal/client/dom/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,18 +87,17 @@ export function svg_template_with_script(svg, return_fragment) {
* @param {() => Node} [template_element_fn]
* @returns {Element | DocumentFragment | Node[]}
*/
/*#__NO_SIDE_EFFECTS__*/
function open_template(is_fragment, use_clone_node, anchor, template_element_fn) {
if (hydrating) {
if (anchor !== null) {
// TODO why is this sometimes null and sometimes not? needs clear documentation
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.
const nodes = hydrate_nodes;
if (nodes !== null) {
return is_fragment ? nodes : /** @type {Element} */ (nodes[0]);
}

return is_fragment ? hydrate_nodes : /** @type {Element} */ (hydrate_nodes[0]);
}

return use_clone_node
? clone_node(/** @type {() => Element} */ (template_element_fn)(), true)
: document.importNode(/** @type {() => Element} */ (template_element_fn)(), true);
Expand All @@ -108,11 +107,10 @@ function open_template(is_fragment, use_clone_node, anchor, template_element_fn)
* @param {null | Text | Comment | Element} anchor
* @param {() => Node} template_element_fn
* @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) {
return open_template(false, use_clone_node, anchor, template_element_fn);
return /** @type {Element} */ (open_template(false, use_clone_node, anchor, template_element_fn));
}

/**
Expand All @@ -121,7 +119,6 @@ export function open(anchor, template_element_fn, use_clone_node = true) {
* @param {boolean} [use_clone_node]
* @returns {Element | DocumentFragment | Node[]}
*/
/*#__NO_SIDE_EFFECTS__*/
export function open_frag(anchor, template_element_fn, use_clone_node = true) {
return open_template(true, use_clone_node, anchor, template_element_fn);
}
Expand Down Expand Up @@ -175,21 +172,25 @@ export function comment(anchor) {
/**
* Assign the created (or in hydration mode, traversed) dom elements to the current block
* and insert the elements into the dom (in client mode).
* @param {Element | Text} dom
* @param {import('#client').Dom} dom
* @param {boolean} is_fragment
* @param {null | Text | Comment | Element} anchor
* @returns {import('#client').Dom}
*/
function close_template(dom, is_fragment, anchor) {
/** @type {import('#client').Dom} */
var current = is_fragment
? is_array(dom)
? dom
: /** @type {import('#client').TemplateNode[]} */ (Array.from(dom.childNodes))
: dom;

if (!hydrating && anchor !== null) {
insert(current, anchor);
var current = dom;

if (!hydrating) {
if (is_fragment) {
// if hydrating, `dom` is already an array of nodes, but if not then
// we need to create an array to store it on the current effect
current = /** @type {import('#client').Dom} */ ([.../** @type {Node} */ (dom).childNodes]);
}

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;
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/internal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export { proxy, unstate } from './client/proxy.js';
export { create_custom_element } from './client/dom/elements/custom-element.js';
export {
child,
child_frag,
first_child,
sibling,
$window as window,
$document as document
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export default function Bind_this($$anchor, $$props) {

/* Init */
var fragment = $.comment($$anchor);
var node = $.child_frag(fragment);
var node = $.first_child(fragment);

$.bind_this(Foo(node, {}), ($$value) => foo = $$value, () => foo);
$.close_frag($$anchor, fragment);
$.pop();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function Main($$anchor, $$props) {
let y = () => 'test';
/* Init */
var fragment = $.open_frag($$anchor, frag, false);
var div = $.child_frag(fragment);
var div = $.first_child(fragment);
var svg = $.sibling($.sibling(div, true));
var custom_element = $.sibling($.sibling(svg, true));
var div_1 = $.sibling($.sibling(custom_element, true));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default function Each_string_template($$anchor, $$props) {

/* Init */
var fragment = $.comment($$anchor);
var node = $.child_frag(fragment);
var node = $.first_child(fragment);

$.each_indexed(
node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function Function_prop_no_getter($$anchor, $$props) {
const plusOne = (num) => num + 1;
/* Init */
var fragment = $.comment($$anchor);
var node = $.child_frag(fragment);
var node = $.first_child(fragment);

Button(node, {
onmousedown: () => $.set(count, $.get(count) + 1),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function State_proxy_literal($$anchor, $$props) {
let tpl = $.source(``);
/* Init */
var fragment = $.open_frag($$anchor, frag);
var input = $.child_frag(fragment);
var input = $.first_child(fragment);

$.remove_input_attr_defaults(input);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export default function Svelte_element($$anchor, $$props) {
let tag = $.prop($$props, "tag", 3, 'hr');
/* Init */
var fragment = $.comment($$anchor);
var node = $.child_frag(fragment);
var node = $.first_child(fragment);

$.element(node, tag, false);
$.close_frag($$anchor, fragment);
$.pop();
}
}