Skip to content

Commit 4fcedb2

Browse files
authored
chore: simplify hydration (#10943)
* WIP * unused * unused * fix head hydration * working * simplify * tighten up * css props * fix treeshaking * add a comment
1 parent afe589e commit 4fcedb2

File tree

26 files changed

+113
-230
lines changed

26 files changed

+113
-230
lines changed

packages/svelte/scripts/check-treeshakeability.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ const bundle = await bundle_code(
109109
).js.code
110110
);
111111

112-
if (!bundle.includes('hydrate_nodes')) {
112+
if (!bundle.includes('hydrate_nodes') && !bundle.includes('hydrate_anchor')) {
113113
// eslint-disable-next-line no-console
114114
console.error(`✅ Hydration code treeshakeable`);
115115
} else {

packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,7 +1045,7 @@ function create_block(parent, name, nodes, context) {
10451045
);
10461046

10471047
/** @type {import('estree').Expression[]} */
1048-
const args = [b.id('$$anchor'), template_name];
1048+
const args = [template_name];
10491049

10501050
if (state.metadata.context.template_needs_import_node) {
10511051
args.push(b.false);
@@ -1089,7 +1089,7 @@ function create_block(parent, name, nodes, context) {
10891089

10901090
if (use_comment_template) {
10911091
// special case — we can use `$.comment` instead of creating a unique template
1092-
body.push(b.var(id, b.call('$.comment', b.id('$$anchor'))));
1092+
body.push(b.var(id, b.call('$.comment')));
10931093
} else {
10941094
state.hoisted.push(
10951095
b.var(
@@ -1103,7 +1103,7 @@ function create_block(parent, name, nodes, context) {
11031103
);
11041104

11051105
/** @type {import('estree').Expression[]} */
1106-
const args = [b.id('$$anchor'), template_name];
1106+
const args = [template_name];
11071107

11081108
if (state.metadata.context.template_needs_import_node) {
11091109
args.push(b.false);

packages/svelte/src/internal/client/dom/blocks/await.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { is_promise } from '../../../common.js';
2-
import { hydrate_block_anchor } from '../hydration.js';
32
import { remove } from '../reconciler.js';
43
import {
54
current_component_context,
@@ -23,8 +22,6 @@ import { INERT } from '../../constants.js';
2322
export function await_block(anchor, get_input, pending_fn, then_fn, catch_fn) {
2423
const component_context = current_component_context;
2524

26-
hydrate_block_anchor(anchor);
27-
2825
/** @type {any} */
2926
let input;
3027

packages/svelte/src/internal/client/dom/blocks/css-props.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { namespace_svg } from '../../../../constants.js';
2-
import { hydrate_nodes, hydrate_block_anchor, hydrating } from '../hydration.js';
2+
import { hydrate_anchor, hydrate_nodes, hydrating } from '../hydration.js';
33
import { empty } from '../operations.js';
44
import { render_effect } from '../../reactivity/effects.js';
55
import { remove } from '../reconciler.js';
@@ -12,8 +12,6 @@ import { remove } from '../reconciler.js';
1212
* @returns {void}
1313
*/
1414
export function css_props(anchor, is_html, props, component) {
15-
hydrate_block_anchor(anchor);
16-
1715
/** @type {HTMLElement | SVGElement} */
1816
let element;
1917

@@ -24,7 +22,9 @@ export function css_props(anchor, is_html, props, component) {
2422
// Hydration: css props element is surrounded by a ssr comment ...
2523
element = /** @type {HTMLElement | SVGElement} */ (hydrate_nodes[0]);
2624
// ... and the child(ren) of the css props element is also surround by a ssr comment
27-
component_anchor = /** @type {Comment} */ (element.firstChild);
25+
component_anchor = /** @type {Comment} */ (
26+
hydrate_anchor(/** @type {Comment} */ (element.firstChild))
27+
);
2828
} else {
2929
if (is_html) {
3030
element = document.createElement('div');

packages/svelte/src/internal/client/dom/blocks/each.js

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,7 @@ import {
66
EACH_ITEM_REACTIVE,
77
EACH_KEYED
88
} from '../../../../constants.js';
9-
import {
10-
hydrate_nodes,
11-
hydrate_block_anchor,
12-
hydrating,
13-
set_hydrating,
14-
update_hydrate_nodes
15-
} from '../hydration.js';
9+
import { hydrate_anchor, hydrate_nodes, hydrating, set_hydrating } from '../hydration.js';
1610
import { empty } from '../operations.js';
1711
import { insert, remove } from '../reconciler.js';
1812
import { untrack } from '../../runtime.js';
@@ -59,11 +53,15 @@ function each(anchor, flags, get_collection, get_key, render_fn, fallback_fn, re
5953
var state = { flags, items: [] };
6054

6155
var is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
62-
hydrate_block_anchor(is_controlled ? /** @type {Node} */ (anchor.firstChild) : anchor);
6356

6457
if (is_controlled) {
6558
var parent_node = /** @type {Element} */ (anchor);
66-
parent_node.append((anchor = empty()));
59+
60+
anchor = hydrating
61+
? /** @type {Comment | Text} */ (
62+
hydrate_anchor(/** @type {Comment | Text} */ (parent_node.firstChild))
63+
)
64+
: parent_node.appendChild(empty());
6765
}
6866

6967
/** @type {import('#client').Effect | null} */
@@ -115,32 +113,31 @@ function each(anchor, flags, get_collection, get_key, render_fn, fallback_fn, re
115113
if (hydrating) {
116114
var b_items = [];
117115

118-
// Hydrate block
119-
var hydration_list = /** @type {import('#client').TemplateNode[]} */ (hydrate_nodes);
120-
var hydrating_node = hydration_list[0];
116+
/** @type {Node} */
117+
var child_anchor = hydrate_nodes[0];
121118

122119
for (var i = 0; i < length; i++) {
123-
var nodes = update_hydrate_nodes(hydrating_node);
124-
125-
if (nodes === null) {
120+
if (child_anchor.nodeType !== 8 || /** @type {Comment} */ (child_anchor).data !== '[') {
126121
// If `nodes` is null, then that means that the server rendered fewer items than what
127122
// expected, so break out and continue appending non-hydrated items
128123
mismatch = true;
129124
set_hydrating(false);
130125
break;
131126
}
132127

128+
child_anchor = hydrate_anchor(child_anchor);
133129
b_items[i] = create_item(array[i], keys?.[i], i, render_fn, flags);
134-
135-
// TODO helperise this
136-
hydrating_node = /** @type {import('#client').TemplateNode} */ (
137-
/** @type {Node} */ (
138-
/** @type {Node} */ (nodes[nodes.length - 1] || hydrating_node).nextSibling
139-
).nextSibling
140-
);
130+
child_anchor = /** @type {Comment} */ (child_anchor.nextSibling);
141131
}
142132

143-
remove_excess_hydration_nodes(hydration_list, hydrating_node);
133+
// remove excess nodes
134+
if (length > 0) {
135+
while (child_anchor !== anchor) {
136+
var next = /** @type {import('#client').TemplateNode} */ (child_anchor.nextSibling);
137+
/** @type {import('#client').TemplateNode} */ (child_anchor).remove();
138+
child_anchor = next;
139+
}
140+
}
144141

145142
state.items = b_items;
146143
}
@@ -440,20 +437,6 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
440437
});
441438
}
442439

443-
/**
444-
* The server could have rendered more list items than the client specifies.
445-
* In that case, we need to remove the remaining server-rendered nodes.
446-
* @param {import('#client').TemplateNode[]} hydration_list
447-
* @param {import('#client').TemplateNode | null} next_node
448-
*/
449-
function remove_excess_hydration_nodes(hydration_list, next_node) {
450-
if (next_node === null) return;
451-
var idx = hydration_list.indexOf(next_node);
452-
if (idx !== -1 && hydration_list.length > idx + 1) {
453-
remove(hydration_list.slice(idx));
454-
}
455-
}
456-
457440
/**
458441
* Longest Increased Subsequence algorithm
459442
* @param {Int32Array} a

packages/svelte/src/internal/client/dom/blocks/if.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { IS_ELSEIF } from '../../constants.js';
2-
import { hydrate_nodes, hydrate_block_anchor, hydrating, set_hydrating } from '../hydration.js';
2+
import { hydrate_nodes, hydrating, set_hydrating } from '../hydration.js';
33
import { remove } from '../reconciler.js';
44
import {
55
destroy_effect,
@@ -23,8 +23,6 @@ export function if_block(
2323
alternate_fn = null,
2424
elseif = false
2525
) {
26-
hydrate_block_anchor(anchor);
27-
2826
/** @type {import('#client').Effect | null} */
2927
let consequent_effect = null;
3028

packages/svelte/src/internal/client/dom/blocks/key.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { UNINITIALIZED } from '../../constants.js';
2-
import { hydrate_block_anchor } from '../hydration.js';
32
import { remove } from '../reconciler.js';
43
import { pause_effect, render_effect } from '../../reactivity/effects.js';
54
import { safe_not_equal } from '../../reactivity/equality.js';
@@ -12,8 +11,6 @@ import { safe_not_equal } from '../../reactivity/equality.js';
1211
* @returns {void}
1312
*/
1413
export function key_block(anchor, get_key, render_fn) {
15-
hydrate_block_anchor(anchor);
16-
1714
/** @type {V | typeof UNINITIALIZED} */
1815
let key = UNINITIALIZED;
1916

packages/svelte/src/internal/client/dom/blocks/svelte-component.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { hydrate_block_anchor } from '../hydration.js';
21
import { pause_effect, render_effect } from '../../reactivity/effects.js';
32
import { remove } from '../reconciler.js';
43
import { current_effect } from '../../runtime.js';
@@ -14,8 +13,6 @@ import { current_effect } from '../../runtime.js';
1413
* @returns {void}
1514
*/
1615
export function component(anchor, get_component, render_fn) {
17-
hydrate_block_anchor(anchor);
18-
1916
/** @type {C} */
2017
let component;
2118

packages/svelte/src/internal/client/dom/blocks/svelte-element.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { namespace_svg } from '../../../../constants.js';
2-
import { hydrate_nodes, hydrate_block_anchor, hydrating } from '../hydration.js';
2+
import { hydrate_anchor, hydrate_nodes, hydrating } from '../hydration.js';
33
import { empty } from '../operations.js';
44
import {
55
destroy_effect,
@@ -44,8 +44,6 @@ function swap_block_dom(effect, from, to) {
4444
export function element(anchor, get_tag, is_svg, render_fn) {
4545
const parent_effect = /** @type {import('#client').Effect} */ (current_effect);
4646

47-
hydrate_block_anchor(anchor);
48-
4947
/** @type {string | null} */
5048
let tag;
5149

@@ -114,7 +112,7 @@ export function element(anchor, get_tag, is_svg, render_fn) {
114112
// If hydrating, use the existing ssr comment as the anchor so that the
115113
// inner open and close methods can pick up the existing nodes correctly
116114
var child_anchor = hydrating
117-
? /** @type {Comment} */ (element.firstChild)
115+
? element.firstChild && hydrate_anchor(/** @type {Comment} */ (element.firstChild))
118116
: element.appendChild(empty());
119117

120118
if (child_anchor) {

packages/svelte/src/internal/client/dom/blocks/svelte-head.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { hydrate_nodes, hydrating, set_hydrate_nodes, update_hydrate_nodes } from '../hydration.js';
1+
import { hydrate_anchor, hydrate_nodes, hydrating, set_hydrate_nodes } from '../hydration.js';
22
import { empty } from '../operations.js';
33
import { render_effect } from '../../reactivity/effects.js';
44
import { remove } from '../reconciler.js';
@@ -15,7 +15,13 @@ export function head(render_fn) {
1515

1616
if (hydrating) {
1717
previous_hydrate_nodes = hydrate_nodes;
18-
update_hydrate_nodes(document.head.firstChild);
18+
19+
let anchor = /** @type {import('#client').TemplateNode} */ (document.head.firstChild);
20+
while (anchor.nodeType !== 8 || /** @type {Comment} */ (anchor).data !== '[') {
21+
anchor = /** @type {import('#client').TemplateNode} */ (anchor.nextSibling);
22+
}
23+
24+
anchor = /** @type {import('#client').TemplateNode} */ (hydrate_anchor(anchor));
1925
}
2026

2127
var anchor = document.head.appendChild(empty());

packages/svelte/src/internal/client/dom/elements/custom-element.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ if (typeof HTMLElement === 'function') {
9898
* @param {Element} anchor
9999
*/
100100
return (anchor) => {
101-
const node = open(anchor, () => {
101+
const node = open(() => {
102102
const slot = document.createElement('slot');
103103
if (name !== 'default') {
104104
slot.name = name;

0 commit comments

Comments
 (0)