Skip to content

Commit 1f3f182

Browse files
authored
chore: move DOM-related effect properties to effect.nodes (#17293)
* WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * changeset * unused
1 parent 8dd8e3e commit 1f3f182

File tree

13 files changed

+105
-107
lines changed

13 files changed

+105
-107
lines changed

.changeset/petite-mammals-talk.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
chore: move DOM-related effect properties to `effect.nodes`

packages/svelte/src/internal/client/dev/debug.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,13 @@ export function log_effect_tree(effect, depth = 0) {
110110
console.groupEnd();
111111
}
112112

113-
if (effect.nodes_start && effect.nodes_end) {
113+
if (effect.nodes) {
114114
// eslint-disable-next-line no-console
115-
console.log(effect.nodes_start);
115+
console.log(effect.nodes.start);
116116

117-
if (effect.nodes_start !== effect.nodes_end) {
117+
if (effect.nodes.start !== effect.nodes.end) {
118118
// eslint-disable-next-line no-console
119-
console.log(effect.nodes_end);
119+
console.log(effect.nodes.end);
120120
}
121121
}
122122

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

Lines changed: 23 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** @import { EachItem, EachState, Effect, MaybeSource, Source, TemplateNode, TransitionManager, Value } from '#client' */
1+
/** @import { EachItem, EachState, Effect, EffectNodes, MaybeSource, Source, TemplateNode, TransitionManager, Value } from '#client' */
22
/** @import { Batch } from '../../reactivity/batch.js'; */
33
import {
44
EACH_INDEX_REACTIVE,
@@ -43,18 +43,6 @@ import { DEV } from 'esm-env';
4343
import { derived_safe_equal } from '../../reactivity/deriveds.js';
4444
import { current_batch } from '../../reactivity/batch.js';
4545

46-
/**
47-
* The row of a keyed each block that is currently updating. We track this
48-
* so that `animate:` directives have something to attach themselves to
49-
* @type {EachItem | null}
50-
*/
51-
export let current_each_item = null;
52-
53-
/** @param {EachItem | null} item */
54-
export function set_current_each_item(item) {
55-
current_each_item = item;
56-
}
57-
5846
/**
5947
* @param {any} _
6048
* @param {number} i
@@ -395,7 +383,7 @@ function reconcile(state, array, anchor, flags, get_key) {
395383
// offscreen == coming in now, no animation in that case,
396384
// else this would happen https://github.com/sveltejs/svelte/issues/17181
397385
if (item.o) {
398-
item.a?.measure();
386+
item.e.nodes?.a?.measure();
399387
(to_animate ??= new Set()).add(item);
400388
}
401389
}
@@ -430,7 +418,7 @@ function reconcile(state, array, anchor, flags, get_key) {
430418
if ((item.e.f & INERT) !== 0) {
431419
resume_effect(item.e);
432420
if (is_animated) {
433-
item.a?.unfix();
421+
item.e.nodes?.a?.unfix();
434422
(to_animate ??= new Set()).delete(item);
435423
}
436424
}
@@ -527,11 +515,11 @@ function reconcile(state, array, anchor, flags, get_key) {
527515

528516
if (is_animated) {
529517
for (i = 0; i < destroy_length; i += 1) {
530-
to_destroy[i].a?.measure();
518+
to_destroy[i].e.nodes?.a?.measure();
531519
}
532520

533521
for (i = 0; i < destroy_length; i += 1) {
534-
to_destroy[i].a?.fix();
522+
to_destroy[i].e.nodes?.a?.fix();
535523
}
536524
}
537525

@@ -555,7 +543,7 @@ function reconcile(state, array, anchor, flags, get_key) {
555543
queue_micro_task(() => {
556544
if (to_animate === undefined) return;
557545
for (item of to_animate) {
558-
item.a?.apply();
546+
item.e.nodes?.a?.apply();
559547
}
560548
});
561549
}
@@ -574,7 +562,6 @@ function reconcile(state, array, anchor, flags, get_key) {
574562
* @returns {EachItem}
575563
*/
576564
function create_item(anchor, prev, value, key, index, render_fn, flags, get_collection) {
577-
var previous_each_item = current_each_item;
578565
var reactive = (flags & EACH_ITEM_REACTIVE) !== 0;
579566
var mutable = (flags & EACH_ITEM_IMMUTABLE) === 0;
580567

@@ -596,35 +583,28 @@ function create_item(anchor, prev, value, key, index, render_fn, flags, get_coll
596583
i,
597584
v,
598585
k: key,
599-
a: null,
600586
// @ts-expect-error
601587
e: null,
602588
o: false,
603589
prev,
604590
next: null
605591
};
606592

607-
current_each_item = item;
608-
609-
try {
610-
if (anchor === null) {
611-
var fragment = document.createDocumentFragment();
612-
fragment.append((anchor = create_text()));
613-
}
614-
615-
item.e = branch(() => render_fn(/** @type {Node} */ (anchor), v, i, get_collection));
593+
if (anchor === null) {
594+
var fragment = document.createDocumentFragment();
595+
fragment.append((anchor = create_text()));
596+
}
616597

617-
if (prev !== null) {
618-
// we only need to set `prev.next = item`, because
619-
// `item.prev = prev` was set on initialization.
620-
// the effects themselves are already linked
621-
prev.next = item;
622-
}
598+
item.e = branch(() => render_fn(/** @type {Node} */ (anchor), v, i, get_collection));
623599

624-
return item;
625-
} finally {
626-
current_each_item = previous_each_item;
600+
if (prev !== null) {
601+
// we only need to set `prev.next = item`, because
602+
// `item.prev = prev` was set on initialization.
603+
// the effects themselves are already linked
604+
prev.next = item;
627605
}
606+
607+
return item;
628608
}
629609

630610
/**
@@ -633,10 +613,12 @@ function create_item(anchor, prev, value, key, index, render_fn, flags, get_coll
633613
* @param {Text | Element | Comment} anchor
634614
*/
635615
function move(item, next, anchor) {
636-
var end = item.next ? /** @type {TemplateNode} */ (item.next.e.nodes_start) : anchor;
616+
if (!item.e.nodes) return;
617+
618+
var end = item.next ? /** @type {EffectNodes} */ (item.next.e.nodes).start : anchor;
637619

638-
var dest = next ? /** @type {TemplateNode} */ (next.e.nodes_start) : anchor;
639-
var node = /** @type {TemplateNode} */ (item.e.nodes_start);
620+
var dest = next ? /** @type {EffectNodes} */ (next.e.nodes).start : anchor;
621+
var node = /** @type {TemplateNode} */ (item.e.nodes.start);
640622

641623
while (node !== null && node !== end) {
642624
var next_node = /** @type {TemplateNode} */ (get_next_sibling(node));

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ export function html(node, get_value, svg = false, mathml = false, skip_warning
5454
return;
5555
}
5656

57-
if (effect.nodes_start !== null) {
58-
remove_effect_dom(effect.nodes_start, /** @type {TemplateNode} */ (effect.nodes_end));
59-
effect.nodes_start = effect.nodes_end = null;
57+
if (effect.nodes !== null) {
58+
remove_effect_dom(effect.nodes.start, /** @type {TemplateNode} */ (effect.nodes.end));
59+
effect.nodes = null;
6060
}
6161

6262
if (value === '') return;

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

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** @import { Effect, TemplateNode } from '#client' */
1+
/** @import { Effect, EffectNodes, TemplateNode } from '#client' */
22
import { FILENAME, NAMESPACE_SVG } from '../../../../constants.js';
33
import {
44
hydrate_next,
@@ -10,14 +10,14 @@ import {
1010
import { create_text, get_first_child } from '../operations.js';
1111
import { block, teardown } from '../../reactivity/effects.js';
1212
import { set_should_intro } from '../../render.js';
13-
import { current_each_item, set_current_each_item } from './each.js';
1413
import { active_effect } from '../../runtime.js';
1514
import { component_context, dev_stack } from '../../context.js';
1615
import { DEV } from 'esm-env';
1716
import { EFFECT_TRANSPARENT, ELEMENT_NODE } from '#client/constants';
1817
import { assign_nodes } from '../template.js';
1918
import { is_raw_text_element } from '../../../../utils.js';
2019
import { BranchManager } from './branches.js';
20+
import { set_animation_effect_override } from '../elements/transitions.js';
2121

2222
/**
2323
* @param {Comment | Element} node
@@ -48,11 +48,10 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
4848
var anchor = /** @type {TemplateNode} */ (hydrating ? hydrate_node : node);
4949

5050
/**
51-
* The keyed `{#each ...}` item block, if any, that this element is inside.
5251
* We track this so we can set it when changing the element, allowing any
5352
* `animate:` directive to bind itself to the correct block
5453
*/
55-
var each_item_block = current_each_item;
54+
var parent_effect = /** @type {Effect} */ (active_effect);
5655

5756
var branches = new BranchManager(anchor, false);
5857

@@ -67,10 +66,6 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
6766
}
6867

6968
branches.ensure(next_tag, (anchor) => {
70-
// See explanation of `each_item_block` above
71-
var previous_each_item = current_each_item;
72-
set_current_each_item(each_item_block);
73-
7469
if (next_tag) {
7570
element = hydrating
7671
? /** @type {Element} */ (element)
@@ -112,21 +107,23 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
112107
}
113108
}
114109

110+
set_animation_effect_override(parent_effect);
111+
115112
// `child_anchor` is undefined if this is a void element, but we still
116113
// need to call `render_fn` in order to run actions etc. If the element
117114
// contains children, it's a user error (which is warned on elsewhere)
118115
// and the DOM will be silently discarded
119116
render_fn(element, child_anchor);
117+
118+
set_animation_effect_override(null);
120119
}
121120

122121
// we do this after calling `render_fn` so that child effects don't override `nodes.end`
123-
/** @type {Effect} */ (active_effect).nodes_end = element;
122+
/** @type {Effect & { nodes: EffectNodes }} */ (active_effect).nodes.end = element;
124123

125124
anchor.before(element);
126125
}
127126

128-
set_current_each_item(previous_each_item);
129-
130127
if (hydrating) {
131128
set_hydrate_node(anchor);
132129
}

packages/svelte/src/internal/client/dom/elements/transitions.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
/** @import { AnimateFn, Animation, AnimationConfig, EachItem, Effect, TransitionFn, TransitionManager } from '#client' */
1+
/** @import { AnimateFn, Animation, AnimationConfig, EachItem, Effect, EffectNodes, TransitionFn, TransitionManager } from '#client' */
22
import { noop, is_function } from '../../../shared/utils.js';
33
import { effect } from '../../reactivity/effects.js';
44
import { active_effect, untrack } from '../../runtime.js';
55
import { loop } from '../../loop.js';
66
import { should_intro } from '../../render.js';
7-
import { current_each_item } from '../blocks/each.js';
87
import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../constants.js';
98
import { BLOCK_EFFECT, EFFECT_RAN, EFFECT_TRANSPARENT } from '#client/constants';
109
import { queue_micro_task } from '../task.js';
@@ -66,6 +65,14 @@ function css_to_keyframe(css) {
6665
/** @param {number} t */
6766
const linear = (t) => t;
6867

68+
/** @type {Effect | null} */
69+
let animation_effect_override = null;
70+
71+
/** @param {Effect | null} v */
72+
export function set_animation_effect_override(v) {
73+
animation_effect_override = v;
74+
}
75+
6976
/**
7077
* Called inside keyed `{#each ...}` blocks (as `$.animation(...)`). This creates an animation manager
7178
* and attaches it to the block, so that moves can be animated following reconciliation.
@@ -75,7 +82,8 @@ const linear = (t) => t;
7582
* @param {(() => P) | null} get_params
7683
*/
7784
export function animation(element, get_fn, get_params) {
78-
var item = /** @type {EachItem} */ (current_each_item);
85+
var effect = animation_effect_override ?? /** @type {Effect} */ (active_effect);
86+
var nodes = /** @type {EffectNodes} */ (effect.nodes);
7987

8088
/** @type {DOMRect} */
8189
var from;
@@ -89,7 +97,7 @@ export function animation(element, get_fn, get_params) {
8997
/** @type {null | { position: string, width: string, height: string, transform: string }} */
9098
var original_styles = null;
9199

92-
item.a ??= {
100+
nodes.a ??= {
93101
element,
94102
measure() {
95103
from = this.element.getBoundingClientRect();
@@ -161,7 +169,7 @@ export function animation(element, get_fn, get_params) {
161169
// when an animation manager already exists, if the tag changes. in that case, we need to
162170
// swap out the element rather than creating a new manager, in case it happened at the same
163171
// moment as a reconciliation
164-
item.a.element = element;
172+
nodes.a.element = element;
165173
}
166174

167175
/**
@@ -265,9 +273,9 @@ export function transition(flags, element, get_fn, get_params) {
265273
}
266274
};
267275

268-
var e = /** @type {Effect} */ (active_effect);
276+
var e = /** @type {Effect & { nodes: EffectNodes }} */ (active_effect);
269277

270-
(e.transitions ??= []).push(transition);
278+
(e.nodes.t ??= []).push(transition);
271279

272280
// if this is a local transition, we only want to run it if the parent (branch) effect's
273281
// parent (block) effect is where the state change happened. we can determine that by

packages/svelte/src/internal/client/dom/template.js

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** @import { Effect, TemplateNode } from '#client' */
1+
/** @import { Effect, EffectNodes, TemplateNode } from '#client' */
22
/** @import { TemplateStructure } from './types' */
33
import { hydrate_next, hydrate_node, hydrating, set_hydrate_node } from './hydration.js';
44
import {
@@ -28,9 +28,8 @@ import { COMMENT_NODE, DOCUMENT_FRAGMENT_NODE, EFFECT_RAN, TEXT_NODE } from '#cl
2828
*/
2929
export function assign_nodes(start, end) {
3030
var effect = /** @type {Effect} */ (active_effect);
31-
if (effect.nodes_start === null) {
32-
effect.nodes_start = start;
33-
effect.nodes_end = end;
31+
if (effect.nodes === null) {
32+
effect.nodes = { start, end, a: null, t: null };
3433
}
3534
}
3635

@@ -270,7 +269,8 @@ function run_scripts(node) {
270269
/** @type {HTMLElement} */ (node).tagName === 'SCRIPT'
271270
? [/** @type {HTMLScriptElement} */ (node)]
272271
: node.querySelectorAll('script');
273-
const effect = /** @type {Effect} */ (active_effect);
272+
273+
const effect = /** @type {Effect & { nodes: EffectNodes }} */ (active_effect);
274274

275275
for (const script of scripts) {
276276
const clone = document.createElement('script');
@@ -282,10 +282,10 @@ function run_scripts(node) {
282282

283283
// The script has changed - if it's at the edges, the effect now points at dead nodes
284284
if (is_fragment ? node.firstChild === script : node === script) {
285-
effect.nodes_start = clone;
285+
effect.nodes.start = clone;
286286
}
287287
if (is_fragment ? node.lastChild === script : node === script) {
288-
effect.nodes_end = clone;
288+
effect.nodes.end = clone;
289289
}
290290

291291
script.replaceWith(clone);
@@ -344,13 +344,15 @@ export function comment() {
344344
*/
345345
export function append(anchor, dom) {
346346
if (hydrating) {
347-
var effect = /** @type {Effect} */ (active_effect);
347+
var effect = /** @type {Effect & { nodes: EffectNodes }} */ (active_effect);
348+
348349
// When hydrating and outer component and an inner component is async, i.e. blocked on a promise,
349350
// then by the time the inner resolves we have already advanced to the end of the hydrated nodes
350351
// of the parent component. Check for defined for that reason to avoid rewinding the parent's end marker.
351-
if ((effect.f & EFFECT_RAN) === 0 || effect.nodes_end === null) {
352-
effect.nodes_end = hydrate_node;
352+
if ((effect.f & EFFECT_RAN) === 0 || effect.nodes.end === null) {
353+
effect.nodes.end = hydrate_node;
353354
}
355+
354356
hydrate_next();
355357
return;
356358
}

packages/svelte/src/internal/client/reactivity/async.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import {
2626
} from './deriveds.js';
2727
import { aborted } from './effects.js';
2828
import { hydrate_next, hydrating, set_hydrate_node, skip_nodes } from '../dom/hydration.js';
29-
import { current_each_item, set_current_each_item } from '../dom/blocks/each.js';
3029

3130
/**
3231
* @param {Array<Promise<void>>} blockers
@@ -90,11 +89,7 @@ export function flatten(blockers, sync, async, fn) {
9089
* @param {(values: Value[]) => any} fn
9190
*/
9291
export function run_after_blockers(blockers, fn) {
93-
var each_item = current_each_item; // TODO should this be part of capture?
94-
flatten(blockers, [], [], (v) => {
95-
set_current_each_item(each_item);
96-
fn(v);
97-
});
92+
flatten(blockers, [], [], fn);
9893
}
9994

10095
/**

0 commit comments

Comments
 (0)