Skip to content

Commit 6af7c65

Browse files
committed
Merge branch 'main' into hydration-whitespace
2 parents 4b12ce0 + 3f6eff5 commit 6af7c65

File tree

4 files changed

+107
-65
lines changed

4 files changed

+107
-65
lines changed

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

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import {
77
set_signal_status,
88
mark_reactions,
99
current_skip_reaction,
10-
execute_reaction_fn
10+
execute_reaction_fn,
11+
destroy_effect_children
1112
} from '../runtime.js';
1213
import { equals, safe_equals } from './equality.js';
13-
import { destroy_effect } from './effects.js';
1414

1515
export let updating_derived = false;
1616

@@ -26,13 +26,14 @@ export function derived(fn) {
2626

2727
/** @type {import('#client').Derived<V>} */
2828
const signal = {
29-
reactions: null,
3029
deps: null,
30+
deriveds: null,
3131
equals,
3232
f: flags,
33+
first: null,
3334
fn,
34-
effects: null,
35-
deriveds: null,
35+
last: null,
36+
reactions: null,
3637
v: /** @type {V} */ (null),
3738
version: 0
3839
};
@@ -70,20 +71,12 @@ export function derived_safe_equal(fn) {
7071
* @returns {void}
7172
*/
7273
function destroy_derived_children(signal) {
73-
var effects = signal.effects;
74-
75-
// TODO: should it be possible to create effects in deriveds given they're meant to be pure?
76-
if (effects !== null) {
77-
signal.effects = null;
78-
for (var i = 0; i < effects.length; i += 1) {
79-
destroy_effect(effects[i]);
80-
}
81-
}
74+
destroy_effect_children(signal);
8275
var deriveds = signal.deriveds;
8376

8477
if (deriveds !== null) {
8578
signal.deriveds = null;
86-
for (i = 0; i < deriveds.length; i += 1) {
79+
for (var i = 0; i < deriveds.length; i += 1) {
8780
destroy_derived(deriveds[i]);
8881
}
8982
}
@@ -127,7 +120,10 @@ export function destroy_derived(signal) {
127120
remove_reactions(signal, 0);
128121
set_signal_status(signal, DESTROYED);
129122

130-
signal.effects =
123+
// TODO we need to ensure we remove the derived from any parent derives
124+
125+
signal.first =
126+
signal.last =
131127
signal.deps =
132128
signal.reactions =
133129
// @ts-expect-error `signal.fn` cannot be `null` while the signal is alive

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

Lines changed: 65 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
current_component_context,
55
current_effect,
66
current_reaction,
7-
destroy_children,
7+
destroy_effect_children,
88
execute_effect,
99
get,
1010
is_flushing_effect,
@@ -21,15 +21,30 @@ import {
2121
EFFECT,
2222
DESTROYED,
2323
INERT,
24-
IS_ELSEIF,
2524
EFFECT_RAN,
2625
BLOCK_EFFECT,
27-
ROOT_EFFECT
26+
ROOT_EFFECT,
27+
IS_ELSEIF
2828
} from '../constants.js';
2929
import { set } from './sources.js';
3030
import { noop } from '../../shared/utils.js';
3131
import { remove } from '../dom/reconciler.js';
3232

33+
/**
34+
* @param {import("#client").Effect} effect
35+
* @param {import("#client").Reaction} parent_effect
36+
*/
37+
export function push_effect(effect, parent_effect) {
38+
var parent_last = parent_effect.last;
39+
if (parent_last === null) {
40+
parent_effect.last = parent_effect.first = effect;
41+
} else {
42+
parent_last.next = effect;
43+
effect.prev = parent_last;
44+
parent_effect.last = effect;
45+
}
46+
}
47+
3348
/**
3449
* @param {number} type
3550
* @param {(() => void | (() => void))} fn
@@ -40,23 +55,22 @@ function create_effect(type, fn, sync) {
4055
var is_root = (type & ROOT_EFFECT) !== 0;
4156
/** @type {import('#client').Effect} */
4257
var effect = {
43-
parent: is_root ? null : current_effect,
44-
dom: null,
58+
ctx: current_component_context,
4559
deps: null,
60+
dom: null,
4661
f: type | DIRTY,
62+
first: null,
4763
fn,
48-
effects: null,
64+
last: null,
65+
next: null,
66+
parent: is_root ? null : current_effect,
67+
prev: null,
4968
teardown: null,
50-
ctx: current_component_context,
5169
transitions: null
5270
};
5371

5472
if (current_reaction !== null && !is_root) {
55-
if (current_reaction.effects === null) {
56-
current_reaction.effects = [effect];
57-
} else {
58-
current_reaction.effects.push(effect);
59-
}
73+
push_effect(effect, current_reaction);
6074
}
6175

6276
if (sync) {
@@ -221,7 +235,7 @@ export function branch(fn) {
221235
* @returns {void}
222236
*/
223237
export function destroy_effect(effect) {
224-
destroy_children(effect);
238+
destroy_effect_children(effect);
225239
remove_reactions(effect, 0);
226240
set_signal_status(effect, DESTROYED);
227241

@@ -239,15 +253,31 @@ export function destroy_effect(effect) {
239253

240254
var parent = effect.parent;
241255

242-
if (parent !== null && (effect.f & BRANCH_EFFECT) !== 0) {
243-
var effects = parent.effects;
244-
if (effects !== null) {
245-
var index = effects.indexOf(effect);
246-
effects.splice(index, 1);
256+
// If the parent doesn't have any children, then skip this work altogether
257+
if (parent !== null && (effect.f & BRANCH_EFFECT) !== 0 && parent.first !== null) {
258+
var previous = effect.prev;
259+
var next = effect.next;
260+
if (previous !== null) {
261+
if (next !== null) {
262+
previous.next = next;
263+
next.prev = previous;
264+
} else {
265+
previous.next = null;
266+
parent.last = previous;
267+
}
268+
} else if (next !== null) {
269+
next.prev = null;
270+
parent.first = next;
271+
} else {
272+
parent.first = null;
273+
parent.last = null;
247274
}
248275
}
249276

250-
effect.effects =
277+
effect.first =
278+
effect.last =
279+
effect.next =
280+
effect.prev =
251281
effect.teardown =
252282
effect.ctx =
253283
effect.dom =
@@ -334,11 +364,14 @@ function pause_children(effect, transitions, local) {
334364
}
335365
}
336366

337-
if (effect.effects !== null) {
338-
for (const child of effect.effects) {
339-
var transparent = (child.f & IS_ELSEIF) !== 0 || (child.f & BRANCH_EFFECT) !== 0;
340-
pause_children(child, transitions, transparent ? local : false);
341-
}
367+
var child = effect.first;
368+
369+
while (child !== null) {
370+
var sibling = child.next;
371+
var transparent = (child.f & IS_ELSEIF) !== 0 || (child.f & BRANCH_EFFECT) !== 0;
372+
// TODO we don't need to call pause_children recursively with a linked list in place
373+
pause_children(child, transitions, transparent ? local : false);
374+
child = sibling;
342375
}
343376
}
344377

@@ -365,11 +398,14 @@ function resume_children(effect, local) {
365398
execute_effect(effect);
366399
}
367400

368-
if (effect.effects !== null) {
369-
for (const child of effect.effects) {
370-
var transparent = (child.f & IS_ELSEIF) !== 0 || (child.f & BRANCH_EFFECT) !== 0;
371-
resume_children(child, transparent ? local : false);
372-
}
401+
var child = effect.first;
402+
403+
while (child !== null) {
404+
var sibling = child.next;
405+
var transparent = (child.f & IS_ELSEIF) !== 0 || (child.f & BRANCH_EFFECT) !== 0;
406+
// TODO we don't need to call resume_children recursively with a linked list in place
407+
resume_children(child, transparent ? local : false);
408+
child = sibling;
373409
}
374410

375411
if (effect.transitions !== null) {

packages/svelte/src/internal/client/reactivity/types.d.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ export interface Reaction extends Signal {
2121
fn: Function;
2222
/** Signals that this signal reads from */
2323
deps: null | Value[];
24-
/** Effects created inside this signal */
25-
effects: null | Effect[];
24+
/** First child effect created inside this signal */
25+
first: null | Effect;
26+
/** Last child effect created inside this signal */
27+
last: null | Effect;
2628
}
2729

2830
export interface Derived<V = unknown> extends Value<V>, Reaction {
@@ -43,6 +45,10 @@ export interface Effect extends Reaction {
4345
teardown: null | (() => void);
4446
/** Transition managers created with `$.transition` */
4547
transitions: null | TransitionManager[];
48+
/** Next sibling child effect created inside the parent signal */
49+
prev: null | Effect;
50+
/** Next sibling child effect created inside the parent signal */
51+
next: null | Effect;
4652
}
4753

4854
export interface ValueDebug<V = unknown> extends Value<V> {

packages/svelte/src/internal/client/runtime.js

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
import { flush_tasks } from './dom/task.js';
2727
import { add_owner } from './dev/ownership.js';
2828
import { mutate, set, source } from './reactivity/sources.js';
29-
import { destroy_derived, update_derived } from './reactivity/deriveds.js';
29+
import { update_derived } from './reactivity/deriveds.js';
3030

3131
const FLUSH_MICROTASK = 0;
3232
const FLUSH_SYNC = 1;
@@ -349,17 +349,18 @@ export function remove_reactions(signal, start_index) {
349349
}
350350

351351
/**
352-
* @param {import('./types.js').Effect} signal
352+
* @param {import('./types.js').Reaction} signal
353353
* @returns {void}
354354
*/
355-
export function destroy_children(signal) {
356-
var effects = signal.effects;
357-
358-
if (effects !== null) {
359-
signal.effects = null;
360-
for (var i = 0; i < effects.length; i += 1) {
361-
destroy_effect(effects[i]);
362-
}
355+
export function destroy_effect_children(signal) {
356+
let effect = signal.first;
357+
signal.first = null;
358+
signal.last = null;
359+
var sibling;
360+
while (effect !== null) {
361+
sibling = effect.next;
362+
destroy_effect(effect);
363+
effect = sibling;
363364
}
364365
}
365366

@@ -386,7 +387,7 @@ export function execute_effect(effect) {
386387

387388
try {
388389
if ((flags & BLOCK_EFFECT) === 0) {
389-
destroy_children(effect);
390+
destroy_effect_children(effect);
390391
}
391392

392393
effect.teardown?.();
@@ -499,13 +500,12 @@ export function schedule_effect(signal) {
499500
* @returns {void}
500501
*/
501502
function recursively_process_effects(effect, filter_flags, shallow, collected_user) {
502-
var effects = effect.effects;
503-
if (effects === null) return;
504-
503+
var current_child = effect.first;
505504
var user = [];
506505

507-
for (var i = 0; i < effects.length; i++) {
508-
var child = effects[i];
506+
while (current_child !== null) {
507+
var child = current_child;
508+
current_child = child.next;
509509
var flags = child.f;
510510
var is_inactive = (flags & (DESTROYED | INERT)) !== 0;
511511
if (is_inactive) continue;
@@ -521,16 +521,19 @@ function recursively_process_effects(effect, filter_flags, shallow, collected_us
521521
if ((flags & RENDER_EFFECT) !== 0) {
522522
if (is_branch) {
523523
if (shallow) continue;
524+
// TODO we don't need to call recursively_process_effects recursively with a linked list in place
524525
recursively_process_effects(child, filter_flags, false, collected_user);
525526
} else {
526527
if (check_dirtiness(child)) {
527528
execute_effect(child);
528529
}
530+
// TODO we don't need to call recursively_process_effects recursively with a linked list in place
529531
recursively_process_effects(child, filter_flags, false, collected_user);
530532
}
531533
} else if ((flags & EFFECT) !== 0) {
532534
if (is_branch || is_clean) {
533535
if (shallow) continue;
536+
// TODO we don't need to call recursively_process_effects recursively with a linked list in place
534537
recursively_process_effects(child, filter_flags, false, collected_user);
535538
} else {
536539
user.push(child);
@@ -544,7 +547,8 @@ function recursively_process_effects(effect, filter_flags, shallow, collected_us
544547
}
545548

546549
if (!shallow) {
547-
for (i = 0; i < user.length; i++) {
550+
for (var i = 0; i < user.length; i++) {
551+
// TODO we don't need to call recursively_process_effects recursively with a linked list in place
548552
recursively_process_effects(user[i], filter_flags, false, collected_user);
549553
}
550554
}
@@ -571,7 +575,7 @@ function flush_nested_effects(effect, filter_flags, shallow = false) {
571575

572576
try {
573577
// When working with custom elements, the root effects might not have a root
574-
if (effect.effects === null && (effect.f & BRANCH_EFFECT) === 0) {
578+
if (effect.first === null && (effect.f & BRANCH_EFFECT) === 0) {
575579
flush_queued_effects([effect]);
576580
} else {
577581
recursively_process_effects(effect, filter_flags, shallow, user_effects);

0 commit comments

Comments
 (0)