svelte 5.41.0 → 5.41.2

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 (37) hide show
  1. package/compiler/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/compiler/phases/2-analyze/index.js +3 -3
  4. package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +13 -6
  5. package/src/compiler/phases/2-analyze/visitors/CallExpression.js +1 -1
  6. package/src/compiler/phases/2-analyze/visitors/ConstTag.js +3 -1
  7. package/src/compiler/phases/2-analyze/visitors/EachBlock.js +2 -1
  8. package/src/compiler/phases/2-analyze/visitors/VariableDeclarator.js +0 -6
  9. package/src/compiler/phases/3-transform/client/transform-client.js +4 -1
  10. package/src/compiler/phases/3-transform/client/visitors/CallExpression.js +21 -3
  11. package/src/compiler/phases/3-transform/client/visitors/Fragment.js +7 -2
  12. package/src/compiler/phases/3-transform/client/visitors/LetDirective.js +21 -17
  13. package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +1 -1
  14. package/src/compiler/phases/3-transform/client/visitors/SlotElement.js +1 -1
  15. package/src/compiler/phases/3-transform/client/visitors/SvelteFragment.js +1 -1
  16. package/src/compiler/phases/3-transform/client/visitors/shared/component.js +2 -2
  17. package/src/compiler/phases/3-transform/server/visitors/CallExpression.js +10 -4
  18. package/src/compiler/phases/3-transform/utils.js +14 -25
  19. package/src/internal/client/dev/inspect.js +14 -4
  20. package/src/internal/client/dev/tracing.js +10 -1
  21. package/src/internal/client/dom/blocks/async.js +5 -0
  22. package/src/internal/client/dom/blocks/await.js +71 -137
  23. package/src/internal/client/dom/blocks/boundary.js +7 -26
  24. package/src/internal/client/dom/blocks/branches.js +185 -0
  25. package/src/internal/client/dom/blocks/if.js +28 -107
  26. package/src/internal/client/dom/blocks/key.js +12 -58
  27. package/src/internal/client/dom/blocks/snippet.js +6 -22
  28. package/src/internal/client/dom/blocks/svelte-component.js +7 -63
  29. package/src/internal/client/dom/blocks/svelte-element.js +34 -45
  30. package/src/internal/client/reactivity/async.js +27 -16
  31. package/src/internal/client/reactivity/batch.js +134 -69
  32. package/src/internal/client/reactivity/deriveds.js +19 -10
  33. package/src/internal/client/reactivity/effects.js +20 -2
  34. package/src/internal/client/reactivity/sources.js +1 -1
  35. package/src/internal/server/index.js +0 -9
  36. package/src/version.js +1 -1
  37. package/types/index.d.ts.map +1 -1
@@ -1,12 +1,9 @@
1
- /** @import { Effect, Source, TemplateNode } from '#client' */
2
- import { DEV } from 'esm-env';
1
+ /** @import { Source, TemplateNode } from '#client' */
3
2
  import { is_promise } from '../../../shared/utils.js';
4
- import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js';
3
+ import { block } from '../../reactivity/effects.js';
5
4
  import { internal_set, mutable_source, source } from '../../reactivity/sources.js';
6
- import { set_active_effect, set_active_reaction } from '../../runtime.js';
7
5
  import {
8
6
  hydrate_next,
9
- hydrate_node,
10
7
  hydrating,
11
8
  skip_nodes,
12
9
  set_hydrate_node,
@@ -14,15 +11,10 @@ import {
14
11
  } from '../hydration.js';
15
12
  import { queue_micro_task } from '../task.js';
16
13
  import { HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js';
17
- import {
18
- component_context,
19
- dev_stack,
20
- is_runes,
21
- set_component_context,
22
- set_dev_current_component_function,
23
- set_dev_stack
24
- } from '../../context.js';
14
+ import { is_runes } from '../../context.js';
25
15
  import { flushSync, is_flushing_sync } from '../../reactivity/batch.js';
16
+ import { BranchManager } from './branches.js';
17
+ import { capture, unset_context } from '../../reactivity/async.js';
26
18
 
27
19
  const PENDING = 0;
28
20
  const THEN = 1;
@@ -33,7 +25,7 @@ const CATCH = 2;
33
25
  /**
34
26
  * @template V
35
27
  * @param {TemplateNode} node
36
- * @param {(() => Promise<V>)} get_input
28
+ * @param {(() => any)} get_input
37
29
  * @param {null | ((anchor: Node) => void)} pending_fn
38
30
  * @param {null | ((anchor: Node, value: Source<V>) => void)} then_fn
39
31
  * @param {null | ((anchor: Node, error: unknown) => void)} catch_fn
@@ -44,149 +36,94 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
44
36
  hydrate_next();
45
37
  }
46
38
 
47
- var anchor = node;
48
39
  var runes = is_runes();
49
- var active_component_context = component_context;
50
-
51
- /** @type {any} */
52
- var component_function = DEV ? component_context?.function : null;
53
- var dev_original_stack = DEV ? dev_stack : null;
54
-
55
- /** @type {V | Promise<V> | typeof UNINITIALIZED} */
56
- var input = UNINITIALIZED;
57
-
58
- /** @type {Effect | null} */
59
- var pending_effect;
60
-
61
- /** @type {Effect | null} */
62
- var then_effect;
63
-
64
- /** @type {Effect | null} */
65
- var catch_effect;
66
-
67
- var input_source = runes
68
- ? source(/** @type {V} */ (undefined))
69
- : mutable_source(/** @type {V} */ (undefined), false, false);
70
- var error_source = runes ? source(undefined) : mutable_source(undefined, false, false);
71
- var resolved = false;
72
- /**
73
- * @param {AwaitState} state
74
- * @param {boolean} restore
75
- */
76
- function update(state, restore) {
77
- resolved = true;
78
-
79
- if (restore) {
80
- set_active_effect(effect);
81
- set_active_reaction(effect); // TODO do we need both?
82
- set_component_context(active_component_context);
83
- if (DEV) {
84
- set_dev_current_component_function(component_function);
85
- set_dev_stack(dev_original_stack);
86
- }
87
- }
88
-
89
- try {
90
- if (state === PENDING && pending_fn) {
91
- if (pending_effect) resume_effect(pending_effect);
92
- else pending_effect = branch(() => pending_fn(anchor));
93
- }
94
-
95
- if (state === THEN && then_fn) {
96
- if (then_effect) resume_effect(then_effect);
97
- else then_effect = branch(() => then_fn(anchor, input_source));
98
- }
99
-
100
- if (state === CATCH && catch_fn) {
101
- if (catch_effect) resume_effect(catch_effect);
102
- else catch_effect = branch(() => catch_fn(anchor, error_source));
103
- }
104
-
105
- if (state !== PENDING && pending_effect) {
106
- pause_effect(pending_effect, () => (pending_effect = null));
107
- }
108
-
109
- if (state !== THEN && then_effect) {
110
- pause_effect(then_effect, () => (then_effect = null));
111
- }
112
-
113
- if (state !== CATCH && catch_effect) {
114
- pause_effect(catch_effect, () => (catch_effect = null));
115
- }
116
- } finally {
117
- if (restore) {
118
- if (DEV) {
119
- set_dev_current_component_function(null);
120
- set_dev_stack(null);
121
- }
122
40
 
123
- set_component_context(null);
124
- set_active_reaction(null);
125
- set_active_effect(null);
41
+ var v = /** @type {V} */ (UNINITIALIZED);
42
+ var value = runes ? source(v) : mutable_source(v, false, false);
43
+ var error = runes ? source(v) : mutable_source(v, false, false);
126
44
 
127
- // without this, the DOM does not update until two ticks after the promise
128
- // resolves, which is unexpected behaviour (and somewhat irksome to test)
129
- if (!is_flushing_sync) flushSync();
130
- }
131
- }
132
- }
45
+ var branches = new BranchManager(node);
133
46
 
134
- var effect = block(() => {
135
- if (input === (input = get_input())) return;
47
+ block(() => {
48
+ var input = get_input();
49
+ var destroyed = false;
136
50
 
137
51
  /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
138
- // @ts-ignore coercing `anchor` to a `Comment` causes TypeScript and Prettier to fight
139
- let mismatch = hydrating && is_promise(input) === (anchor.data === HYDRATION_START_ELSE);
52
+ // @ts-ignore coercing `node` to a `Comment` causes TypeScript and Prettier to fight
53
+ let mismatch = hydrating && is_promise(input) === (node.data === HYDRATION_START_ELSE);
140
54
 
141
55
  if (mismatch) {
142
56
  // Hydration mismatch: remove everything inside the anchor and start fresh
143
- anchor = skip_nodes();
144
-
145
- set_hydrate_node(anchor);
57
+ set_hydrate_node(skip_nodes());
146
58
  set_hydrating(false);
147
- mismatch = true;
148
59
  }
149
60
 
150
61
  if (is_promise(input)) {
151
- var promise = input;
62
+ var restore = capture();
63
+ var resolved = false;
64
+
65
+ /**
66
+ * @param {() => void} fn
67
+ */
68
+ const resolve = (fn) => {
69
+ if (destroyed) return;
70
+
71
+ resolved = true;
72
+ restore();
73
+
74
+ if (hydrating) {
75
+ // `restore()` could set `hydrating` to `true`, which we very much
76
+ // don't want — we want to restore everything _except_ this
77
+ set_hydrating(false);
78
+ }
152
79
 
153
- resolved = false;
80
+ try {
81
+ fn();
82
+ } finally {
83
+ unset_context();
154
84
 
155
- promise.then(
156
- (value) => {
157
- if (promise !== input) return;
158
- // we technically could use `set` here since it's on the next microtick
159
- // but let's use internal_set for consistency and just to be safe
160
- internal_set(input_source, value);
161
- update(THEN, true);
85
+ // without this, the DOM does not update until two ticks after the promise
86
+ // resolves, which is unexpected behaviour (and somewhat irksome to test)
87
+ if (!is_flushing_sync) flushSync();
88
+ }
89
+ };
90
+
91
+ input.then(
92
+ (v) => {
93
+ resolve(() => {
94
+ internal_set(value, v);
95
+ branches.ensure(THEN, then_fn && ((target) => then_fn(target, value)));
96
+ });
162
97
  },
163
- (error) => {
164
- if (promise !== input) return;
165
- // we technically could use `set` here since it's on the next microtick
166
- // but let's use internal_set for consistency and just to be safe
167
- internal_set(error_source, error);
168
- update(CATCH, true);
169
- if (!catch_fn) {
170
- // Rethrow the error if no catch block exists
171
- throw error_source.v;
172
- }
98
+ (e) => {
99
+ resolve(() => {
100
+ internal_set(error, e);
101
+ branches.ensure(THEN, catch_fn && ((target) => catch_fn(target, error)));
102
+
103
+ if (!catch_fn) {
104
+ // Rethrow the error if no catch block exists
105
+ throw error.v;
106
+ }
107
+ });
173
108
  }
174
109
  );
175
110
 
176
111
  if (hydrating) {
177
- if (pending_fn) {
178
- pending_effect = branch(() => pending_fn(anchor));
179
- }
112
+ branches.ensure(PENDING, pending_fn);
180
113
  } else {
181
114
  // Wait a microtask before checking if we should show the pending state as
182
- // the promise might have resolved by the next microtask.
115
+ // the promise might have resolved by then
183
116
  queue_micro_task(() => {
184
- if (!resolved) update(PENDING, true);
117
+ if (!resolved) {
118
+ resolve(() => {
119
+ branches.ensure(PENDING, pending_fn);
120
+ });
121
+ }
185
122
  });
186
123
  }
187
124
  } else {
188
- internal_set(input_source, input);
189
- update(THEN, false);
125
+ internal_set(value, input);
126
+ branches.ensure(THEN, then_fn && ((target) => then_fn(target, value)));
190
127
  }
191
128
 
192
129
  if (mismatch) {
@@ -194,11 +131,8 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
194
131
  set_hydrating(true);
195
132
  }
196
133
 
197
- // Set the input to something else, in order to disable the promise callbacks
198
- return () => (input = UNINITIALIZED);
134
+ return () => {
135
+ destroyed = true;
136
+ };
199
137
  });
200
-
201
- if (hydrating) {
202
- anchor = hydrate_node;
203
- }
204
138
  }
@@ -8,7 +8,13 @@ import {
8
8
  import { HYDRATION_START_ELSE } from '../../../../constants.js';
9
9
  import { component_context, set_component_context } from '../../context.js';
10
10
  import { handle_error, invoke_error_boundary } from '../../error-handling.js';
11
- import { block, branch, destroy_effect, pause_effect } from '../../reactivity/effects.js';
11
+ import {
12
+ block,
13
+ branch,
14
+ destroy_effect,
15
+ move_effect,
16
+ pause_effect
17
+ } from '../../reactivity/effects.js';
12
18
  import {
13
19
  active_effect,
14
20
  active_reaction,
@@ -285,13 +291,6 @@ export class Boundary {
285
291
  this.#anchor.before(this.#offscreen_fragment);
286
292
  this.#offscreen_fragment = null;
287
293
  }
288
-
289
- // TODO this feels like a little bit of a kludge, but until we
290
- // overhaul the boundary/batch relationship it's probably
291
- // the most pragmatic solution available to us
292
- queue_micro_task(() => {
293
- Batch.ensure().flush();
294
- });
295
294
  }
296
295
  }
297
296
 
@@ -425,24 +424,6 @@ export class Boundary {
425
424
  }
426
425
  }
427
426
 
428
- /**
429
- *
430
- * @param {Effect} effect
431
- * @param {DocumentFragment} fragment
432
- */
433
- function move_effect(effect, fragment) {
434
- var node = effect.nodes_start;
435
- var end = effect.nodes_end;
436
-
437
- while (node !== null) {
438
- /** @type {TemplateNode | null} */
439
- var next = node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node));
440
-
441
- fragment.append(node);
442
- node = next;
443
- }
444
- }
445
-
446
427
  export function get_boundary() {
447
428
  return /** @type {Boundary} */ (/** @type {Effect} */ (active_effect).b);
448
429
  }
@@ -0,0 +1,185 @@
1
+ /** @import { Effect, TemplateNode } from '#client' */
2
+ import { is_runes } from '../../context.js';
3
+ import { Batch, current_batch } from '../../reactivity/batch.js';
4
+ import {
5
+ branch,
6
+ destroy_effect,
7
+ move_effect,
8
+ pause_effect,
9
+ resume_effect
10
+ } from '../../reactivity/effects.js';
11
+ import { set_should_intro, should_intro } from '../../render.js';
12
+ import { hydrate_node, hydrating } from '../hydration.js';
13
+ import { create_text, should_defer_append } from '../operations.js';
14
+
15
+ /**
16
+ * @typedef {{ effect: Effect, fragment: DocumentFragment }} Branch
17
+ */
18
+
19
+ /**
20
+ * @template Key
21
+ */
22
+ export class BranchManager {
23
+ /** @type {TemplateNode} */
24
+ anchor;
25
+
26
+ /** @type {Map<Batch, Key>} */
27
+ #batches = new Map();
28
+
29
+ /** @type {Map<Key, Effect>} */
30
+ #onscreen = new Map();
31
+
32
+ /** @type {Map<Key, Branch>} */
33
+ #offscreen = new Map();
34
+
35
+ /**
36
+ * Whether to pause (i.e. outro) on change, or destroy immediately.
37
+ * This is necessary for `<svelte:element>`
38
+ */
39
+ #transition = true;
40
+
41
+ /**
42
+ * @param {TemplateNode} anchor
43
+ * @param {boolean} transition
44
+ */
45
+ constructor(anchor, transition = true) {
46
+ this.anchor = anchor;
47
+ this.#transition = transition;
48
+ }
49
+
50
+ #commit = () => {
51
+ var batch = /** @type {Batch} */ (current_batch);
52
+
53
+ // if this batch was made obsolete, bail
54
+ if (!this.#batches.has(batch)) return;
55
+
56
+ var key = /** @type {Key} */ (this.#batches.get(batch));
57
+
58
+ var onscreen = this.#onscreen.get(key);
59
+
60
+ if (onscreen) {
61
+ // effect is already in the DOM — abort any current outro
62
+ resume_effect(onscreen);
63
+ } else {
64
+ // effect is currently offscreen. put it in the DOM
65
+ var offscreen = this.#offscreen.get(key);
66
+
67
+ if (offscreen) {
68
+ this.#onscreen.set(key, offscreen.effect);
69
+ this.#offscreen.delete(key);
70
+
71
+ // remove the anchor...
72
+ /** @type {TemplateNode} */ (offscreen.fragment.lastChild).remove();
73
+
74
+ // ...and append the fragment
75
+ this.anchor.before(offscreen.fragment);
76
+ onscreen = offscreen.effect;
77
+ }
78
+ }
79
+
80
+ for (const [b, k] of this.#batches) {
81
+ this.#batches.delete(b);
82
+
83
+ if (b === batch) {
84
+ // keep values for newer batches
85
+ break;
86
+ }
87
+
88
+ const offscreen = this.#offscreen.get(k);
89
+
90
+ if (offscreen) {
91
+ // for older batches, destroy offscreen effects
92
+ // as they will never be committed
93
+ destroy_effect(offscreen.effect);
94
+ this.#offscreen.delete(k);
95
+ }
96
+ }
97
+
98
+ // outro/destroy all onscreen effects...
99
+ for (const [k, effect] of this.#onscreen) {
100
+ // ...except the one that was just committed
101
+ if (k === key) continue;
102
+
103
+ const on_destroy = () => {
104
+ const keys = Array.from(this.#batches.values());
105
+
106
+ if (keys.includes(k)) {
107
+ // keep the effect offscreen, as another batch will need it
108
+ var fragment = document.createDocumentFragment();
109
+ move_effect(effect, fragment);
110
+
111
+ fragment.append(create_text()); // TODO can we avoid this?
112
+
113
+ this.#offscreen.set(k, { effect, fragment });
114
+ } else {
115
+ destroy_effect(effect);
116
+ }
117
+
118
+ this.#onscreen.delete(k);
119
+ };
120
+
121
+ if (this.#transition || !onscreen) {
122
+ pause_effect(effect, on_destroy, false);
123
+ } else {
124
+ on_destroy();
125
+ }
126
+ }
127
+ };
128
+
129
+ /**
130
+ *
131
+ * @param {any} key
132
+ * @param {null | ((target: TemplateNode) => void)} fn
133
+ */
134
+ ensure(key, fn) {
135
+ var batch = /** @type {Batch} */ (current_batch);
136
+ var defer = should_defer_append();
137
+
138
+ if (fn && !this.#onscreen.has(key) && !this.#offscreen.has(key)) {
139
+ if (defer) {
140
+ var fragment = document.createDocumentFragment();
141
+ var target = create_text();
142
+
143
+ fragment.append(target);
144
+
145
+ this.#offscreen.set(key, {
146
+ effect: branch(() => fn(target)),
147
+ fragment
148
+ });
149
+ } else {
150
+ this.#onscreen.set(
151
+ key,
152
+ branch(() => fn(this.anchor))
153
+ );
154
+ }
155
+ }
156
+
157
+ this.#batches.set(batch, key);
158
+
159
+ if (defer) {
160
+ for (const [k, effect] of this.#onscreen) {
161
+ if (k === key) {
162
+ batch.skipped_effects.delete(effect);
163
+ } else {
164
+ batch.skipped_effects.add(effect);
165
+ }
166
+ }
167
+
168
+ for (const [k, branch] of this.#offscreen) {
169
+ if (k === key) {
170
+ batch.skipped_effects.delete(branch.effect);
171
+ } else {
172
+ batch.skipped_effects.add(branch.effect);
173
+ }
174
+ }
175
+
176
+ batch.add_callback(this.#commit);
177
+ } else {
178
+ if (hydrating) {
179
+ this.anchor = hydrate_node;
180
+ }
181
+
182
+ this.#commit();
183
+ }
184
+ }
185
+ }
@@ -1,19 +1,16 @@
1
- /** @import { Effect, TemplateNode } from '#client' */
2
- /** @import { Batch } from '../../reactivity/batch.js'; */
1
+ /** @import { TemplateNode } from '#client' */
3
2
  import { EFFECT_TRANSPARENT } from '#client/constants';
4
3
  import {
5
4
  hydrate_next,
6
- hydrate_node,
7
5
  hydrating,
8
6
  read_hydration_instruction,
9
7
  skip_nodes,
10
8
  set_hydrate_node,
11
9
  set_hydrating
12
10
  } from '../hydration.js';
13
- import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js';
14
- import { HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js';
15
- import { create_text, should_defer_append } from '../operations.js';
16
- import { current_batch } from '../../reactivity/batch.js';
11
+ import { block } from '../../reactivity/effects.js';
12
+ import { HYDRATION_START_ELSE } from '../../../../constants.js';
13
+ import { BranchManager } from './branches.js';
17
14
 
18
15
  // TODO reinstate https://github.com/sveltejs/svelte/pull/15250
19
16
 
@@ -28,122 +25,46 @@ export function if_block(node, fn, elseif = false) {
28
25
  hydrate_next();
29
26
  }
30
27
 
31
- var anchor = node;
32
-
33
- /** @type {Effect | null} */
34
- var consequent_effect = null;
35
-
36
- /** @type {Effect | null} */
37
- var alternate_effect = null;
38
-
39
- /** @type {typeof UNINITIALIZED | boolean | null} */
40
- var condition = UNINITIALIZED;
41
-
28
+ var branches = new BranchManager(node);
42
29
  var flags = elseif ? EFFECT_TRANSPARENT : 0;
43
30
 
44
- var has_branch = false;
45
-
46
- const set_branch = (/** @type {(anchor: Node) => void} */ fn, flag = true) => {
47
- has_branch = true;
48
- update_branch(flag, fn);
49
- };
50
-
51
- /** @type {DocumentFragment | null} */
52
- var offscreen_fragment = null;
53
-
54
- function commit() {
55
- if (offscreen_fragment !== null) {
56
- // remove the anchor
57
- /** @type {Text} */ (offscreen_fragment.lastChild).remove();
58
-
59
- anchor.before(offscreen_fragment);
60
- offscreen_fragment = null;
61
- }
62
-
63
- var active = condition ? consequent_effect : alternate_effect;
64
- var inactive = condition ? alternate_effect : consequent_effect;
65
-
66
- if (active) {
67
- resume_effect(active);
68
- }
69
-
70
- if (inactive) {
71
- pause_effect(inactive, () => {
72
- if (condition) {
73
- alternate_effect = null;
74
- } else {
75
- consequent_effect = null;
76
- }
77
- });
78
- }
79
- }
80
-
81
- const update_branch = (
82
- /** @type {boolean | null} */ new_condition,
83
- /** @type {null | ((anchor: Node) => void)} */ fn
84
- ) => {
85
- if (condition === (condition = new_condition)) return;
86
-
87
- /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
88
- let mismatch = false;
89
-
31
+ /**
32
+ * @param {boolean} condition,
33
+ * @param {null | ((anchor: Node) => void)} fn
34
+ */
35
+ function update_branch(condition, fn) {
90
36
  if (hydrating) {
91
- const is_else = read_hydration_instruction(anchor) === HYDRATION_START_ELSE;
37
+ const is_else = read_hydration_instruction(node) === HYDRATION_START_ELSE;
92
38
 
93
- if (!!condition === is_else) {
39
+ if (condition === is_else) {
94
40
  // Hydration mismatch: remove everything inside the anchor and start fresh.
95
41
  // This could happen with `{#if browser}...{/if}`, for example
96
- anchor = skip_nodes();
42
+ var anchor = skip_nodes();
97
43
 
98
44
  set_hydrate_node(anchor);
99
- set_hydrating(false);
100
- mismatch = true;
101
- }
102
- }
45
+ branches.anchor = anchor;
103
46
 
104
- var defer = should_defer_append();
105
- var target = anchor;
106
-
107
- if (defer) {
108
- offscreen_fragment = document.createDocumentFragment();
109
- offscreen_fragment.append((target = create_text()));
110
- }
47
+ set_hydrating(false);
48
+ branches.ensure(condition, fn);
49
+ set_hydrating(true);
111
50
 
112
- if (condition) {
113
- consequent_effect ??= fn && branch(() => fn(target));
114
- } else {
115
- alternate_effect ??= fn && branch(() => fn(target));
51
+ return;
52
+ }
116
53
  }
117
54
 
118
- if (defer) {
119
- var batch = /** @type {Batch} */ (current_batch);
120
-
121
- var active = condition ? consequent_effect : alternate_effect;
122
- var inactive = condition ? alternate_effect : consequent_effect;
123
-
124
- if (active) batch.skipped_effects.delete(active);
125
- if (inactive) batch.skipped_effects.add(inactive);
55
+ branches.ensure(condition, fn);
56
+ }
126
57
 
127
- batch.add_callback(commit);
128
- } else {
129
- commit();
130
- }
58
+ block(() => {
59
+ var has_branch = false;
131
60
 
132
- if (mismatch) {
133
- // continue in hydration mode
134
- set_hydrating(true);
135
- }
136
- };
61
+ fn((fn, flag = true) => {
62
+ has_branch = true;
63
+ update_branch(flag, fn);
64
+ });
137
65
 
138
- block(() => {
139
- has_branch = false;
140
- fn(set_branch);
141
66
  if (!has_branch) {
142
- update_branch(null, null);
67
+ update_branch(false, null);
143
68
  }
144
69
  }, flags);
145
-
146
- if (hydrating) {
147
- anchor = hydrate_node;
148
- }
149
70
  }