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.
- package/compiler/index.js +1 -1
- package/package.json +1 -1
- package/src/compiler/phases/2-analyze/index.js +3 -3
- package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +13 -6
- package/src/compiler/phases/2-analyze/visitors/CallExpression.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/ConstTag.js +3 -1
- package/src/compiler/phases/2-analyze/visitors/EachBlock.js +2 -1
- package/src/compiler/phases/2-analyze/visitors/VariableDeclarator.js +0 -6
- package/src/compiler/phases/3-transform/client/transform-client.js +4 -1
- package/src/compiler/phases/3-transform/client/visitors/CallExpression.js +21 -3
- package/src/compiler/phases/3-transform/client/visitors/Fragment.js +7 -2
- package/src/compiler/phases/3-transform/client/visitors/LetDirective.js +21 -17
- package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +1 -1
- package/src/compiler/phases/3-transform/client/visitors/SlotElement.js +1 -1
- package/src/compiler/phases/3-transform/client/visitors/SvelteFragment.js +1 -1
- package/src/compiler/phases/3-transform/client/visitors/shared/component.js +2 -2
- package/src/compiler/phases/3-transform/server/visitors/CallExpression.js +10 -4
- package/src/compiler/phases/3-transform/utils.js +14 -25
- package/src/internal/client/dev/inspect.js +14 -4
- package/src/internal/client/dev/tracing.js +10 -1
- package/src/internal/client/dom/blocks/async.js +5 -0
- package/src/internal/client/dom/blocks/await.js +71 -137
- package/src/internal/client/dom/blocks/boundary.js +7 -26
- package/src/internal/client/dom/blocks/branches.js +185 -0
- package/src/internal/client/dom/blocks/if.js +28 -107
- package/src/internal/client/dom/blocks/key.js +12 -58
- package/src/internal/client/dom/blocks/snippet.js +6 -22
- package/src/internal/client/dom/blocks/svelte-component.js +7 -63
- package/src/internal/client/dom/blocks/svelte-element.js +34 -45
- package/src/internal/client/reactivity/async.js +27 -16
- package/src/internal/client/reactivity/batch.js +134 -69
- package/src/internal/client/reactivity/deriveds.js +19 -10
- package/src/internal/client/reactivity/effects.js +20 -2
- package/src/internal/client/reactivity/sources.js +1 -1
- package/src/internal/server/index.js +0 -9
- package/src/version.js +1 -1
- package/types/index.d.ts.map +1 -1
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
/** @import {
|
|
2
|
-
import { DEV } from 'esm-env';
|
|
1
|
+
/** @import { Source, TemplateNode } from '#client' */
|
|
3
2
|
import { is_promise } from '../../../shared/utils.js';
|
|
4
|
-
import { block
|
|
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 {(() =>
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
|
|
135
|
-
|
|
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 `
|
|
139
|
-
let mismatch = hydrating && is_promise(input) === (
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
80
|
+
try {
|
|
81
|
+
fn();
|
|
82
|
+
} finally {
|
|
83
|
+
unset_context();
|
|
154
84
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
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
|
|
115
|
+
// the promise might have resolved by then
|
|
183
116
|
queue_micro_task(() => {
|
|
184
|
-
if (!resolved)
|
|
117
|
+
if (!resolved) {
|
|
118
|
+
resolve(() => {
|
|
119
|
+
branches.ensure(PENDING, pending_fn);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
185
122
|
});
|
|
186
123
|
}
|
|
187
124
|
} else {
|
|
188
|
-
internal_set(
|
|
189
|
-
|
|
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
|
-
|
|
198
|
-
|
|
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 {
|
|
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 {
|
|
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
|
|
14
|
-
import { HYDRATION_START_ELSE
|
|
15
|
-
import {
|
|
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
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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(
|
|
37
|
+
const is_else = read_hydration_instruction(node) === HYDRATION_START_ELSE;
|
|
92
38
|
|
|
93
|
-
if (
|
|
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
|
-
|
|
100
|
-
mismatch = true;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
45
|
+
branches.anchor = anchor;
|
|
103
46
|
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
} else {
|
|
115
|
-
alternate_effect ??= fn && branch(() => fn(target));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
116
53
|
}
|
|
117
54
|
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
commit();
|
|
130
|
-
}
|
|
58
|
+
block(() => {
|
|
59
|
+
var has_branch = false;
|
|
131
60
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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(
|
|
67
|
+
update_branch(false, null);
|
|
143
68
|
}
|
|
144
69
|
}, flags);
|
|
145
|
-
|
|
146
|
-
if (hydrating) {
|
|
147
|
-
anchor = hydrate_node;
|
|
148
|
-
}
|
|
149
70
|
}
|