svelte 5.43.7 → 5.43.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/compiler/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/compiler/phases/1-parse/utils/create.js +1 -2
  4. package/src/compiler/phases/2-analyze/index.js +269 -187
  5. package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +0 -4
  6. package/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +29 -15
  7. package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +50 -14
  8. package/src/compiler/phases/3-transform/client/visitors/Fragment.js +5 -14
  9. package/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +2 -8
  10. package/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +12 -2
  11. package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +1 -1
  12. package/src/compiler/phases/3-transform/server/visitors/ConstTag.js +25 -1
  13. package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +2 -6
  14. package/src/compiler/phases/3-transform/server/visitors/Fragment.js +8 -1
  15. package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +1 -5
  16. package/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +0 -5
  17. package/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +3 -10
  18. package/src/compiler/phases/3-transform/server/visitors/shared/component.js +1 -6
  19. package/src/compiler/phases/nodes.js +4 -0
  20. package/src/compiler/phases/scope.js +8 -4
  21. package/src/compiler/utils/builders.js +1 -1
  22. package/src/internal/client/dom/blocks/each.js +156 -236
  23. package/src/internal/client/reactivity/deriveds.js +5 -2
  24. package/src/internal/client/reactivity/effects.js +33 -37
  25. package/src/internal/client/reactivity/sources.js +17 -9
  26. package/src/version.js +1 -1
@@ -38,7 +38,7 @@ import { source, mutable_source, internal_set } from '../../reactivity/sources.j
38
38
  import { array_from, is_array } from '../../../shared/utils.js';
39
39
  import { COMMENT_NODE, INERT } from '#client/constants';
40
40
  import { queue_micro_task } from '../task.js';
41
- import { active_effect, get } from '../../runtime.js';
41
+ import { get } from '../../runtime.js';
42
42
  import { DEV } from 'esm-env';
43
43
  import { derived_safe_equal } from '../../reactivity/deriveds.js';
44
44
  import { current_batch } from '../../reactivity/batch.js';
@@ -67,41 +67,51 @@ export function index(_, i) {
67
67
  * Pause multiple effects simultaneously, and coordinate their
68
68
  * subsequent destruction. Used in each blocks
69
69
  * @param {EachState} state
70
- * @param {EachItem[]} items
70
+ * @param {EachItem[]} to_destroy
71
71
  * @param {null | Node} controlled_anchor
72
72
  */
73
- function pause_effects(state, items, controlled_anchor) {
74
- var items_map = state.items;
75
-
73
+ function pause_effects(state, to_destroy, controlled_anchor) {
76
74
  /** @type {TransitionManager[]} */
77
75
  var transitions = [];
78
- var length = items.length;
76
+ var length = to_destroy.length;
79
77
 
80
78
  for (var i = 0; i < length; i++) {
81
- pause_children(items[i].e, transitions, true);
82
- }
83
-
84
- var is_controlled = length > 0 && transitions.length === 0 && controlled_anchor !== null;
85
- // If we have a controlled anchor, it means that the each block is inside a single
86
- // DOM element, so we can apply a fast-path for clearing the contents of the element.
87
- if (is_controlled) {
88
- var parent_node = /** @type {Element} */ (
89
- /** @type {Element} */ (controlled_anchor).parentNode
90
- );
91
- clear_text_content(parent_node);
92
- parent_node.append(/** @type {Element} */ (controlled_anchor));
93
- items_map.clear();
94
- link(state, items[0].prev, items[length - 1].next);
79
+ pause_children(to_destroy[i].e, transitions, true);
95
80
  }
96
81
 
97
82
  run_out_transitions(transitions, () => {
83
+ // If we're in a controlled each block (i.e. the block is the only child of an
84
+ // element), and we are removing all items, _and_ there are no out transitions,
85
+ // we can use the fast path — emptying the element and replacing the anchor
86
+ var fast_path = transitions.length === 0 && controlled_anchor !== null;
87
+
88
+ // TODO only destroy effects if no pending batch needs them. otherwise,
89
+ // just set `item.o` back to `false`
90
+
91
+ if (fast_path) {
92
+ var anchor = /** @type {Element} */ (controlled_anchor);
93
+ var parent_node = /** @type {Element} */ (anchor.parentNode);
94
+
95
+ clear_text_content(parent_node);
96
+ parent_node.append(anchor);
97
+
98
+ state.items.clear();
99
+ link(state, to_destroy[0].prev, to_destroy[length - 1].next);
100
+ }
101
+
98
102
  for (var i = 0; i < length; i++) {
99
- var item = items[i];
100
- if (!is_controlled) {
101
- items_map.delete(item.k);
103
+ var item = to_destroy[i];
104
+
105
+ if (!fast_path) {
106
+ state.items.delete(item.k);
102
107
  link(state, item.prev, item.next);
103
108
  }
104
- destroy_effect(item.e, !is_controlled);
109
+
110
+ destroy_effect(item.e, !fast_path);
111
+ }
112
+
113
+ if (state.first === to_destroy[0]) {
114
+ state.first = to_destroy[0].prev;
105
115
  }
106
116
  });
107
117
  }
@@ -123,6 +133,8 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
123
133
  var state = { flags, items: new Map(), first: null };
124
134
 
125
135
  var is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
136
+ var is_reactive_value = (flags & EACH_ITEM_REACTIVE) !== 0;
137
+ var is_reactive_index = (flags & EACH_INDEX_REACTIVE) !== 0;
126
138
 
127
139
  if (is_controlled) {
128
140
  var parent_node = /** @type {Element} */ (node);
@@ -136,14 +148,9 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
136
148
  hydrate_next();
137
149
  }
138
150
 
139
- /** @type {Effect | null} */
151
+ /** @type {{ fragment: DocumentFragment | null, effect: Effect } | null} */
140
152
  var fallback = null;
141
153
 
142
- var was_empty = false;
143
-
144
- /** @type {Map<any, EachItem>} */
145
- var offscreen_items = new Map();
146
-
147
154
  // TODO: ideally we could use derived for runes mode but because of the ability
148
155
  // to use a store which can be mutated, we can't do that here as mutating a store
149
156
  // will still result in the collection array being the same from the store
@@ -156,51 +163,36 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
156
163
  /** @type {V[]} */
157
164
  var array;
158
165
 
159
- /** @type {Effect} */
160
- var each_effect;
166
+ var first_run = true;
161
167
 
162
168
  function commit() {
163
- reconcile(
164
- each_effect,
165
- array,
166
- state,
167
- offscreen_items,
168
- anchor,
169
- render_fn,
170
- flags,
171
- get_key,
172
- get_collection
173
- );
174
-
175
- if (fallback_fn !== null) {
169
+ reconcile(each_effect, array, state, anchor, flags, get_key);
170
+
171
+ if (fallback !== null) {
176
172
  if (array.length === 0) {
177
- if (fallback) {
178
- resume_effect(fallback);
173
+ if (fallback.fragment) {
174
+ anchor.before(fallback.fragment);
175
+ fallback.fragment = null;
179
176
  } else {
180
- fallback = branch(() => fallback_fn(anchor));
177
+ resume_effect(fallback.effect);
181
178
  }
182
- } else if (fallback !== null) {
183
- pause_effect(fallback, () => {
179
+
180
+ each_effect.first = fallback.effect;
181
+ } else {
182
+ pause_effect(fallback.effect, () => {
183
+ // TODO only null out if no pending batch needs it,
184
+ // otherwise re-add `fallback.fragment` and move the
185
+ // effect into it
184
186
  fallback = null;
185
187
  });
186
188
  }
187
189
  }
188
190
  }
189
191
 
190
- block(() => {
191
- // store a reference to the effect so that we can update the start/end nodes in reconciliation
192
- each_effect ??= /** @type {Effect} */ (active_effect);
193
-
192
+ var each_effect = block(() => {
194
193
  array = /** @type {V[]} */ (get(each_array));
195
194
  var length = array.length;
196
195
 
197
- if (was_empty && length === 0) {
198
- // ignore updates if the array is empty,
199
- // and it already was empty on previous run
200
- return;
201
- }
202
- was_empty = length === 0;
203
-
204
196
  /** `true` if there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
205
197
  let mismatch = false;
206
198
 
@@ -217,34 +209,48 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
217
209
  }
218
210
  }
219
211
 
220
- // this is separate to the previous block because `hydrating` might change
221
- if (hydrating) {
222
- /** @type {EachItem | null} */
223
- var prev = null;
224
-
225
- /** @type {EachItem} */
226
- var item;
227
-
228
- for (var i = 0; i < length; i++) {
229
- if (
230
- hydrate_node.nodeType === COMMENT_NODE &&
231
- /** @type {Comment} */ (hydrate_node).data === HYDRATION_END
232
- ) {
233
- // The server rendered fewer items than expected,
234
- // so break out and continue appending non-hydrated items
235
- anchor = /** @type {Comment} */ (hydrate_node);
236
- mismatch = true;
237
- set_hydrating(false);
238
- break;
212
+ var keys = new Set();
213
+ var batch = /** @type {Batch} */ (current_batch);
214
+ var prev = null;
215
+ var defer = should_defer_append();
216
+
217
+ for (var i = 0; i < length; i += 1) {
218
+ if (
219
+ hydrating &&
220
+ hydrate_node.nodeType === COMMENT_NODE &&
221
+ /** @type {Comment} */ (hydrate_node).data === HYDRATION_END
222
+ ) {
223
+ // The server rendered fewer items than expected,
224
+ // so break out and continue appending non-hydrated items
225
+ anchor = /** @type {Comment} */ (hydrate_node);
226
+ mismatch = true;
227
+ set_hydrating(false);
228
+ }
229
+
230
+ var value = array[i];
231
+ var key = get_key(value, i);
232
+
233
+ var item = first_run ? null : state.items.get(key);
234
+
235
+ if (item) {
236
+ // update before reconciliation, to trigger any async updates
237
+ if (is_reactive_value) {
238
+ internal_set(item.v, value);
239
239
  }
240
240
 
241
- var value = array[i];
242
- var key = get_key(value, i);
241
+ if (is_reactive_index) {
242
+ internal_set(/** @type {Value<number>} */ (item.i), i);
243
+ } else {
244
+ item.i = i;
245
+ }
246
+
247
+ if (defer) {
248
+ batch.skipped_effects.delete(item.e);
249
+ }
250
+ } else {
243
251
  item = create_item(
244
- hydrate_node,
245
- state,
252
+ first_run ? anchor : null,
246
253
  prev,
247
- null,
248
254
  value,
249
255
  key,
250
256
  i,
@@ -252,58 +258,50 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
252
258
  flags,
253
259
  get_collection
254
260
  );
255
- state.items.set(key, item);
256
261
 
257
- prev = item;
258
- }
262
+ if (first_run) {
263
+ item.o = true;
264
+
265
+ if (prev === null) {
266
+ state.first = item;
267
+ } else {
268
+ prev.next = item;
269
+ }
259
270
 
260
- // remove excess nodes
261
- if (length > 0) {
262
- set_hydrate_node(skip_nodes());
271
+ prev = item;
272
+ }
273
+
274
+ state.items.set(key, item);
263
275
  }
276
+
277
+ keys.add(key);
264
278
  }
265
279
 
266
- if (hydrating) {
267
- if (length === 0 && fallback_fn) {
268
- fallback = branch(() => fallback_fn(anchor));
280
+ if (length === 0 && fallback_fn && !fallback) {
281
+ if (first_run) {
282
+ fallback = {
283
+ fragment: null,
284
+ effect: branch(() => fallback_fn(anchor))
285
+ };
286
+ } else {
287
+ var fragment = document.createDocumentFragment();
288
+ var target = create_text();
289
+ fragment.append(target);
290
+
291
+ fallback = {
292
+ fragment,
293
+ effect: branch(() => fallback_fn(target))
294
+ };
269
295
  }
270
- } else {
271
- if (should_defer_append()) {
272
- var keys = new Set();
273
- var batch = /** @type {Batch} */ (current_batch);
274
-
275
- for (i = 0; i < length; i += 1) {
276
- value = array[i];
277
- key = get_key(value, i);
278
-
279
- var existing = state.items.get(key) ?? offscreen_items.get(key);
280
-
281
- if (existing) {
282
- // update before reconciliation, to trigger any async updates
283
- if ((flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0) {
284
- update_item(existing, value, i, flags);
285
- }
286
- } else {
287
- item = create_item(
288
- null,
289
- state,
290
- null,
291
- null,
292
- value,
293
- key,
294
- i,
295
- render_fn,
296
- flags,
297
- get_collection,
298
- true
299
- );
300
-
301
- offscreen_items.set(key, item);
302
- }
296
+ }
303
297
 
304
- keys.add(key);
305
- }
298
+ // remove excess nodes
299
+ if (hydrating && length > 0) {
300
+ set_hydrate_node(skip_nodes());
301
+ }
306
302
 
303
+ if (!first_run) {
304
+ if (defer) {
307
305
  for (const [key, item] of state.items) {
308
306
  if (!keys.has(key)) {
309
307
  batch.skipped_effects.add(item.e);
@@ -311,6 +309,9 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
311
309
  }
312
310
 
313
311
  batch.oncommit(commit);
312
+ batch.ondiscard(() => {
313
+ // TODO presumably we need to do something here?
314
+ });
314
315
  } else {
315
316
  commit();
316
317
  }
@@ -330,6 +331,8 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
330
331
  get(each_array);
331
332
  });
332
333
 
334
+ first_run = false;
335
+
333
336
  if (hydrating) {
334
337
  anchor = hydrate_node;
335
338
  }
@@ -341,32 +344,17 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
341
344
  * @param {Effect} each_effect
342
345
  * @param {Array<V>} array
343
346
  * @param {EachState} state
344
- * @param {Map<any, EachItem>} offscreen_items
345
347
  * @param {Element | Comment | Text} anchor
346
- * @param {(anchor: Node, item: MaybeSource<V>, index: number | Source<number>, collection: () => V[]) => void} render_fn
347
348
  * @param {number} flags
348
349
  * @param {(value: V, index: number) => any} get_key
349
- * @param {() => V[]} get_collection
350
350
  * @returns {void}
351
351
  */
352
- function reconcile(
353
- each_effect,
354
- array,
355
- state,
356
- offscreen_items,
357
- anchor,
358
- render_fn,
359
- flags,
360
- get_key,
361
- get_collection
362
- ) {
352
+ function reconcile(each_effect, array, state, anchor, flags, get_key) {
363
353
  var is_animated = (flags & EACH_IS_ANIMATED) !== 0;
364
- var should_update = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0;
365
354
 
366
355
  var length = array.length;
367
356
  var items = state.items;
368
- var first = state.first;
369
- var current = first;
357
+ var current = state.first;
370
358
 
371
359
  /** @type {undefined | Set<EachItem>} */
372
360
  var seen;
@@ -399,12 +387,10 @@ function reconcile(
399
387
  for (i = 0; i < length; i += 1) {
400
388
  value = array[i];
401
389
  key = get_key(value, i);
402
- item = items.get(key);
390
+ item = /** @type {EachItem} */ (items.get(key));
403
391
 
404
- if (item !== undefined) {
405
- item.a?.measure();
406
- (to_animate ??= new Set()).add(item);
407
- }
392
+ item.a?.measure();
393
+ (to_animate ??= new Set()).add(item);
408
394
  }
409
395
  }
410
396
 
@@ -412,40 +398,20 @@ function reconcile(
412
398
  value = array[i];
413
399
  key = get_key(value, i);
414
400
 
415
- item = items.get(key);
401
+ item = /** @type {EachItem} */ (items.get(key));
416
402
 
417
- if (item === undefined) {
418
- var pending = offscreen_items.get(key);
403
+ state.first ??= item;
419
404
 
420
- if (pending !== undefined) {
421
- offscreen_items.delete(key);
422
- items.set(key, pending);
405
+ if (!item.o) {
406
+ item.o = true;
423
407
 
424
- var next = prev ? prev.next : current;
408
+ var next = prev ? prev.next : current;
425
409
 
426
- link(state, prev, pending);
427
- link(state, pending, next);
410
+ link(state, prev, item);
411
+ link(state, item, next);
428
412
 
429
- move(pending, next, anchor);
430
- prev = pending;
431
- } else {
432
- var child_anchor = current ? /** @type {TemplateNode} */ (current.e.nodes_start) : anchor;
433
-
434
- prev = create_item(
435
- child_anchor,
436
- state,
437
- prev,
438
- prev === null ? state.first : prev.next,
439
- value,
440
- key,
441
- i,
442
- render_fn,
443
- flags,
444
- get_collection
445
- );
446
- }
447
-
448
- items.set(key, prev);
413
+ move(item, next, anchor);
414
+ prev = item;
449
415
 
450
416
  matched = [];
451
417
  stashed = [];
@@ -454,10 +420,6 @@ function reconcile(
454
420
  continue;
455
421
  }
456
422
 
457
- if (should_update) {
458
- update_item(item, value, i, flags);
459
- }
460
-
461
423
  if ((item.e.f & INERT) !== 0) {
462
424
  resume_effect(item.e);
463
425
  if (is_animated) {
@@ -575,63 +537,30 @@ function reconcile(
575
537
  });
576
538
  }
577
539
 
540
+ // TODO i have an inkling that the rest of this function is wrong...
541
+ // the offscreen items need to be linked, so that they all update correctly.
542
+ // the last onscreen item should link to the first offscreen item, etc
578
543
  each_effect.first = state.first && state.first.e;
579
544
  each_effect.last = prev && prev.e;
580
545
 
581
- for (var unused of offscreen_items.values()) {
582
- destroy_effect(unused.e);
583
- }
584
-
585
- offscreen_items.clear();
586
- }
587
-
588
- /**
589
- * @param {EachItem} item
590
- * @param {any} value
591
- * @param {number} index
592
- * @param {number} type
593
- * @returns {void}
594
- */
595
- function update_item(item, value, index, type) {
596
- if ((type & EACH_ITEM_REACTIVE) !== 0) {
597
- internal_set(item.v, value);
598
- }
599
-
600
- if ((type & EACH_INDEX_REACTIVE) !== 0) {
601
- internal_set(/** @type {Value<number>} */ (item.i), index);
602
- } else {
603
- item.i = index;
546
+ if (prev) {
547
+ prev.e.next = null;
604
548
  }
605
549
  }
606
550
 
607
551
  /**
608
552
  * @template V
609
553
  * @param {Node | null} anchor
610
- * @param {EachState} state
611
554
  * @param {EachItem | null} prev
612
- * @param {EachItem | null} next
613
555
  * @param {V} value
614
556
  * @param {unknown} key
615
557
  * @param {number} index
616
558
  * @param {(anchor: Node, item: V | Source<V>, index: number | Value<number>, collection: () => V[]) => void} render_fn
617
559
  * @param {number} flags
618
560
  * @param {() => V[]} get_collection
619
- * @param {boolean} [deferred]
620
561
  * @returns {EachItem}
621
562
  */
622
- function create_item(
623
- anchor,
624
- state,
625
- prev,
626
- next,
627
- value,
628
- key,
629
- index,
630
- render_fn,
631
- flags,
632
- get_collection,
633
- deferred
634
- ) {
563
+ function create_item(anchor, prev, value, key, index, render_fn, flags, get_collection) {
635
564
  var previous_each_item = current_each_item;
636
565
  var reactive = (flags & EACH_ITEM_REACTIVE) !== 0;
637
566
  var mutable = (flags & EACH_ITEM_IMMUTABLE) === 0;
@@ -657,8 +586,9 @@ function create_item(
657
586
  a: null,
658
587
  // @ts-expect-error
659
588
  e: null,
589
+ o: false,
660
590
  prev,
661
- next
591
+ next: null
662
592
  };
663
593
 
664
594
  current_each_item = item;
@@ -669,25 +599,15 @@ function create_item(
669
599
  fragment.append((anchor = create_text()));
670
600
  }
671
601
 
672
- item.e = branch(() => render_fn(/** @type {Node} */ (anchor), v, i, get_collection), hydrating);
602
+ item.e = branch(() => render_fn(/** @type {Node} */ (anchor), v, i, get_collection));
673
603
 
674
604
  item.e.prev = prev && prev.e;
675
- item.e.next = next && next.e;
676
605
 
677
- if (prev === null) {
678
- if (!deferred) {
679
- state.first = item;
680
- }
681
- } else {
606
+ if (prev !== null) {
682
607
  prev.next = item;
683
608
  prev.e.next = item.e;
684
609
  }
685
610
 
686
- if (next !== null) {
687
- next.prev = item;
688
- next.e.prev = item.e;
689
- }
690
-
691
611
  return item;
692
612
  } finally {
693
613
  current_each_item = previous_each_item;
@@ -11,7 +11,8 @@ import {
11
11
  STALE_REACTION,
12
12
  ASYNC,
13
13
  WAS_MARKED,
14
- CONNECTED
14
+ CONNECTED,
15
+ DESTROYED
15
16
  } from '#client/constants';
16
17
  import {
17
18
  active_reaction,
@@ -296,7 +297,9 @@ function get_derived_parent_effect(derived) {
296
297
  var parent = derived.parent;
297
298
  while (parent !== null) {
298
299
  if ((parent.f & DERIVED) === 0) {
299
- return /** @type {Effect} */ (parent);
300
+ // The original parent effect might've been destroyed but the derived
301
+ // is used elsewhere now - do not return the destroyed effect in that case
302
+ return (parent.f & DESTROYED) === 0 ? /** @type {Effect} */ (parent) : null;
300
303
  }
301
304
  parent = parent.parent;
302
305
  }
@@ -80,10 +80,9 @@ function push_effect(effect, parent_effect) {
80
80
  * @param {number} type
81
81
  * @param {null | (() => void | (() => void))} fn
82
82
  * @param {boolean} sync
83
- * @param {boolean} push
84
83
  * @returns {Effect}
85
84
  */
86
- function create_effect(type, fn, sync, push = true) {
85
+ function create_effect(type, fn, sync) {
87
86
  var parent = active_effect;
88
87
 
89
88
  if (DEV) {
@@ -133,43 +132,41 @@ function create_effect(type, fn, sync, push = true) {
133
132
  schedule_effect(effect);
134
133
  }
135
134
 
136
- if (push) {
137
- /** @type {Effect | null} */
138
- var e = effect;
135
+ /** @type {Effect | null} */
136
+ var e = effect;
139
137
 
140
- // if an effect has already ran and doesn't need to be kept in the tree
141
- // (because it won't re-run, has no DOM, and has no teardown etc)
142
- // then we skip it and go to its child (if any)
143
- if (
144
- sync &&
145
- e.deps === null &&
146
- e.teardown === null &&
147
- e.nodes_start === null &&
148
- e.first === e.last && // either `null`, or a singular child
149
- (e.f & EFFECT_PRESERVED) === 0
150
- ) {
151
- e = e.first;
152
- if ((type & BLOCK_EFFECT) !== 0 && (type & EFFECT_TRANSPARENT) !== 0 && e !== null) {
153
- e.f |= EFFECT_TRANSPARENT;
154
- }
138
+ // if an effect has already ran and doesn't need to be kept in the tree
139
+ // (because it won't re-run, has no DOM, and has no teardown etc)
140
+ // then we skip it and go to its child (if any)
141
+ if (
142
+ sync &&
143
+ e.deps === null &&
144
+ e.teardown === null &&
145
+ e.nodes_start === null &&
146
+ e.first === e.last && // either `null`, or a singular child
147
+ (e.f & EFFECT_PRESERVED) === 0
148
+ ) {
149
+ e = e.first;
150
+ if ((type & BLOCK_EFFECT) !== 0 && (type & EFFECT_TRANSPARENT) !== 0 && e !== null) {
151
+ e.f |= EFFECT_TRANSPARENT;
155
152
  }
153
+ }
156
154
 
157
- if (e !== null) {
158
- e.parent = parent;
155
+ if (e !== null) {
156
+ e.parent = parent;
159
157
 
160
- if (parent !== null) {
161
- push_effect(e, parent);
162
- }
158
+ if (parent !== null) {
159
+ push_effect(e, parent);
160
+ }
163
161
 
164
- // if we're in a derived, add the effect there too
165
- if (
166
- active_reaction !== null &&
167
- (active_reaction.f & DERIVED) !== 0 &&
168
- (type & ROOT_EFFECT) === 0
169
- ) {
170
- var derived = /** @type {Derived} */ (active_reaction);
171
- (derived.effects ??= []).push(e);
172
- }
162
+ // if we're in a derived, add the effect there too
163
+ if (
164
+ active_reaction !== null &&
165
+ (active_reaction.f & DERIVED) !== 0 &&
166
+ (type & ROOT_EFFECT) === 0
167
+ ) {
168
+ var derived = /** @type {Derived} */ (active_reaction);
169
+ (derived.effects ??= []).push(e);
173
170
  }
174
171
  }
175
172
 
@@ -406,10 +403,9 @@ export function block(fn, flags = 0) {
406
403
 
407
404
  /**
408
405
  * @param {(() => void)} fn
409
- * @param {boolean} [push]
410
406
  */
411
- export function branch(fn, push = true) {
412
- return create_effect(BRANCH_EFFECT | EFFECT_PRESERVED, fn, true, push);
407
+ export function branch(fn) {
408
+ return create_effect(BRANCH_EFFECT | EFFECT_PRESERVED, fn, true);
413
409
  }
414
410
 
415
411
  /**