svelte 5.46.0 → 5.46.3
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/1-parse/read/script.js +8 -1
- package/src/compiler/phases/2-analyze/index.js +2 -1
- package/src/compiler/phases/2-analyze/visitors/EachBlock.js +3 -1
- package/src/compiler/phases/3-transform/client/transform-client.js +2 -8
- package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +3 -1
- package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +5 -6
- package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +1 -1
- package/src/compiler/phases/3-transform/shared/transform-async.js +8 -1
- package/src/internal/client/dev/hmr.js +25 -14
- package/src/internal/client/dom/blocks/async.js +1 -1
- package/src/internal/client/dom/blocks/boundary.js +52 -17
- package/src/internal/client/index.js +0 -1
- package/src/internal/client/reactivity/async.js +4 -54
- package/src/internal/client/reactivity/batch.js +55 -87
- package/src/internal/client/reactivity/deriveds.js +18 -12
- package/src/internal/client/reactivity/effects.js +2 -2
- package/src/internal/client/reactivity/sources.js +5 -3
- package/src/internal/client/reactivity/status.js +25 -0
- package/src/internal/client/reactivity/utils.js +40 -0
- package/src/internal/client/runtime.js +45 -45
- package/src/legacy/legacy-client.js +2 -1
- package/src/version.js +1 -1
- package/types/index.d.ts +4 -4
- package/types/index.d.ts.map +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** @import { Fork } from 'svelte' */
|
|
2
2
|
/** @import { Derived, Effect, Reaction, Source, Value } from '#client' */
|
|
3
|
+
/** @import { Boundary } from '../dom/blocks/boundary' */
|
|
3
4
|
import {
|
|
4
5
|
BLOCK_EFFECT,
|
|
5
6
|
BRANCH_EFFECT,
|
|
@@ -17,7 +18,6 @@ import {
|
|
|
17
18
|
EAGER_EFFECT,
|
|
18
19
|
HEAD_EFFECT,
|
|
19
20
|
ERROR_VALUE,
|
|
20
|
-
WAS_MARKED,
|
|
21
21
|
MANAGED_EFFECT
|
|
22
22
|
} from '#client/constants';
|
|
23
23
|
import { async_mode_flag } from '../../flags/index.js';
|
|
@@ -25,10 +25,10 @@ import { deferred, define_property } from '../../shared/utils.js';
|
|
|
25
25
|
import {
|
|
26
26
|
active_effect,
|
|
27
27
|
get,
|
|
28
|
+
increment_write_version,
|
|
28
29
|
is_dirty,
|
|
29
30
|
is_updating_effect,
|
|
30
31
|
set_is_updating_effect,
|
|
31
|
-
set_signal_status,
|
|
32
32
|
update_effect
|
|
33
33
|
} from '../runtime.js';
|
|
34
34
|
import * as e from '../errors.js';
|
|
@@ -37,15 +37,9 @@ import { DEV } from 'esm-env';
|
|
|
37
37
|
import { invoke_error_boundary } from '../error-handling.js';
|
|
38
38
|
import { flush_eager_effects, old_values, set_eager_effects, source, update } from './sources.js';
|
|
39
39
|
import { eager_effect, unlink_effect } from './effects.js';
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
* parent: EffectTarget | null;
|
|
44
|
-
* effect: Effect | null;
|
|
45
|
-
* effects: Effect[];
|
|
46
|
-
* render_effects: Effect[];
|
|
47
|
-
* }} EffectTarget
|
|
48
|
-
*/
|
|
40
|
+
import { defer_effect } from './utils.js';
|
|
41
|
+
import { UNINITIALIZED } from '../../../constants.js';
|
|
42
|
+
import { set_signal_status } from './status.js';
|
|
49
43
|
|
|
50
44
|
/** @type {Set<Batch>} */
|
|
51
45
|
const batches = new Set();
|
|
@@ -161,16 +155,14 @@ export class Batch {
|
|
|
161
155
|
|
|
162
156
|
this.apply();
|
|
163
157
|
|
|
164
|
-
/** @type {
|
|
165
|
-
var
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
render_effects: []
|
|
170
|
-
};
|
|
158
|
+
/** @type {Effect[]} */
|
|
159
|
+
var effects = [];
|
|
160
|
+
|
|
161
|
+
/** @type {Effect[]} */
|
|
162
|
+
var render_effects = [];
|
|
171
163
|
|
|
172
164
|
for (const root of root_effects) {
|
|
173
|
-
this.#traverse_effect_tree(root,
|
|
165
|
+
this.#traverse_effect_tree(root, effects, render_effects);
|
|
174
166
|
// Note: #traverse_effect_tree runs block effects eagerly, which can schedule effects,
|
|
175
167
|
// which means queued_root_effects now may be filled again.
|
|
176
168
|
|
|
@@ -183,16 +175,16 @@ export class Batch {
|
|
|
183
175
|
}
|
|
184
176
|
|
|
185
177
|
if (this.is_deferred()) {
|
|
186
|
-
this.#defer_effects(
|
|
187
|
-
this.#defer_effects(
|
|
178
|
+
this.#defer_effects(render_effects);
|
|
179
|
+
this.#defer_effects(effects);
|
|
188
180
|
} else {
|
|
189
181
|
// If sources are written to, then work needs to happen in a separate batch, else prior sources would be mixed with
|
|
190
182
|
// newly updated sources, which could lead to infinite loops when effects run over and over again.
|
|
191
183
|
previous_batch = this;
|
|
192
184
|
current_batch = null;
|
|
193
185
|
|
|
194
|
-
flush_queued_effects(
|
|
195
|
-
flush_queued_effects(
|
|
186
|
+
flush_queued_effects(render_effects);
|
|
187
|
+
flush_queued_effects(effects);
|
|
196
188
|
|
|
197
189
|
previous_batch = null;
|
|
198
190
|
|
|
@@ -206,13 +198,17 @@ export class Batch {
|
|
|
206
198
|
* Traverse the effect tree, executing effects or stashing
|
|
207
199
|
* them for later execution as appropriate
|
|
208
200
|
* @param {Effect} root
|
|
209
|
-
* @param {
|
|
201
|
+
* @param {Effect[]} effects
|
|
202
|
+
* @param {Effect[]} render_effects
|
|
210
203
|
*/
|
|
211
|
-
#traverse_effect_tree(root,
|
|
204
|
+
#traverse_effect_tree(root, effects, render_effects) {
|
|
212
205
|
root.f ^= CLEAN;
|
|
213
206
|
|
|
214
207
|
var effect = root.first;
|
|
215
208
|
|
|
209
|
+
/** @type {Effect | null} */
|
|
210
|
+
var pending_boundary = null;
|
|
211
|
+
|
|
216
212
|
while (effect !== null) {
|
|
217
213
|
var flags = effect.f;
|
|
218
214
|
var is_branch = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) !== 0;
|
|
@@ -220,24 +216,32 @@ export class Batch {
|
|
|
220
216
|
|
|
221
217
|
var skip = is_skippable_branch || (flags & INERT) !== 0 || this.skipped_effects.has(effect);
|
|
222
218
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
219
|
+
// Inside a `<svelte:boundary>` with a pending snippet,
|
|
220
|
+
// all effects are deferred until the boundary resolves
|
|
221
|
+
// (except block/async effects, which run immediately)
|
|
222
|
+
if (
|
|
223
|
+
async_mode_flag &&
|
|
224
|
+
pending_boundary === null &&
|
|
225
|
+
(flags & BOUNDARY_EFFECT) !== 0 &&
|
|
226
|
+
effect.b?.is_pending
|
|
227
|
+
) {
|
|
228
|
+
pending_boundary = effect;
|
|
230
229
|
}
|
|
231
230
|
|
|
232
231
|
if (!skip && effect.fn !== null) {
|
|
233
232
|
if (is_branch) {
|
|
234
233
|
effect.f ^= CLEAN;
|
|
234
|
+
} else if (
|
|
235
|
+
pending_boundary !== null &&
|
|
236
|
+
(flags & (EFFECT | RENDER_EFFECT | MANAGED_EFFECT)) !== 0
|
|
237
|
+
) {
|
|
238
|
+
/** @type {Boundary} */ (pending_boundary.b).defer_effect(effect);
|
|
235
239
|
} else if ((flags & EFFECT) !== 0) {
|
|
236
|
-
|
|
240
|
+
effects.push(effect);
|
|
237
241
|
} else if (async_mode_flag && (flags & (RENDER_EFFECT | MANAGED_EFFECT)) !== 0) {
|
|
238
|
-
|
|
242
|
+
render_effects.push(effect);
|
|
239
243
|
} else if (is_dirty(effect)) {
|
|
240
|
-
if ((
|
|
244
|
+
if ((flags & BLOCK_EFFECT) !== 0) this.#dirty_effects.add(effect);
|
|
241
245
|
update_effect(effect);
|
|
242
246
|
}
|
|
243
247
|
|
|
@@ -253,14 +257,8 @@ export class Batch {
|
|
|
253
257
|
effect = effect.next;
|
|
254
258
|
|
|
255
259
|
while (effect === null && parent !== null) {
|
|
256
|
-
if (parent ===
|
|
257
|
-
|
|
258
|
-
// could we just attach the effects _to_ the pending boundary and schedule them
|
|
259
|
-
// once the boundary is ready?
|
|
260
|
-
this.#defer_effects(target.effects);
|
|
261
|
-
this.#defer_effects(target.render_effects);
|
|
262
|
-
|
|
263
|
-
target = /** @type {EffectTarget} */ (target.parent);
|
|
260
|
+
if (parent === pending_boundary) {
|
|
261
|
+
pending_boundary = null;
|
|
264
262
|
}
|
|
265
263
|
|
|
266
264
|
effect = parent.next;
|
|
@@ -273,36 +271,8 @@ export class Batch {
|
|
|
273
271
|
* @param {Effect[]} effects
|
|
274
272
|
*/
|
|
275
273
|
#defer_effects(effects) {
|
|
276
|
-
for (
|
|
277
|
-
|
|
278
|
-
this.#dirty_effects.add(e);
|
|
279
|
-
} else if ((e.f & MAYBE_DIRTY) !== 0) {
|
|
280
|
-
this.#maybe_dirty_effects.add(e);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Since we're not executing these effects now, we need to clear any WAS_MARKED flags
|
|
284
|
-
// so that other batches can correctly reach these effects during their own traversal
|
|
285
|
-
this.#clear_marked(e.deps);
|
|
286
|
-
|
|
287
|
-
// mark as clean so they get scheduled if they depend on pending async state
|
|
288
|
-
set_signal_status(e, CLEAN);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* @param {Value[] | null} deps
|
|
294
|
-
*/
|
|
295
|
-
#clear_marked(deps) {
|
|
296
|
-
if (deps === null) return;
|
|
297
|
-
|
|
298
|
-
for (const dep of deps) {
|
|
299
|
-
if ((dep.f & DERIVED) === 0 || (dep.f & WAS_MARKED) === 0) {
|
|
300
|
-
continue;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
dep.f ^= WAS_MARKED;
|
|
304
|
-
|
|
305
|
-
this.#clear_marked(/** @type {Derived} */ (dep).deps);
|
|
274
|
+
for (var i = 0; i < effects.length; i += 1) {
|
|
275
|
+
defer_effect(effects[i], this.#dirty_effects, this.#maybe_dirty_effects);
|
|
306
276
|
}
|
|
307
277
|
}
|
|
308
278
|
|
|
@@ -313,7 +283,7 @@ export class Batch {
|
|
|
313
283
|
* @param {any} value
|
|
314
284
|
*/
|
|
315
285
|
capture(source, value) {
|
|
316
|
-
if (!this.previous.has(source)) {
|
|
286
|
+
if (value !== UNINITIALIZED && !this.previous.has(source)) {
|
|
317
287
|
this.previous.set(source, value);
|
|
318
288
|
}
|
|
319
289
|
|
|
@@ -383,14 +353,6 @@ export class Batch {
|
|
|
383
353
|
var previous_batch_values = batch_values;
|
|
384
354
|
var is_earlier = true;
|
|
385
355
|
|
|
386
|
-
/** @type {EffectTarget} */
|
|
387
|
-
var dummy_target = {
|
|
388
|
-
parent: null,
|
|
389
|
-
effect: null,
|
|
390
|
-
effects: [],
|
|
391
|
-
render_effects: []
|
|
392
|
-
};
|
|
393
|
-
|
|
394
356
|
for (const batch of batches) {
|
|
395
357
|
if (batch === this) {
|
|
396
358
|
is_earlier = false;
|
|
@@ -439,10 +401,10 @@ export class Batch {
|
|
|
439
401
|
batch.apply();
|
|
440
402
|
|
|
441
403
|
for (const root of queued_root_effects) {
|
|
442
|
-
batch.#traverse_effect_tree(root,
|
|
404
|
+
batch.#traverse_effect_tree(root, [], []);
|
|
443
405
|
}
|
|
444
406
|
|
|
445
|
-
// TODO do we need to do anything with
|
|
407
|
+
// TODO do we need to do anything with the dummy effect arrays?
|
|
446
408
|
|
|
447
409
|
batch.deactivate();
|
|
448
410
|
}
|
|
@@ -959,13 +921,18 @@ export function fork(fn) {
|
|
|
959
921
|
|
|
960
922
|
flushSync(fn);
|
|
961
923
|
|
|
962
|
-
batch_values = null;
|
|
963
|
-
|
|
964
924
|
// revert state changes
|
|
965
925
|
for (var [source, value] of batch.previous) {
|
|
966
926
|
source.v = value;
|
|
967
927
|
}
|
|
968
928
|
|
|
929
|
+
// make writable deriveds dirty, so they recalculate correctly
|
|
930
|
+
for (source of batch.current.keys()) {
|
|
931
|
+
if ((source.f & DERIVED) !== 0) {
|
|
932
|
+
set_signal_status(source, DIRTY);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
969
936
|
return {
|
|
970
937
|
commit: async () => {
|
|
971
938
|
if (committed) {
|
|
@@ -981,9 +948,10 @@ export function fork(fn) {
|
|
|
981
948
|
|
|
982
949
|
batch.is_fork = false;
|
|
983
950
|
|
|
984
|
-
// apply changes
|
|
951
|
+
// apply changes and update write versions so deriveds see the change
|
|
985
952
|
for (var [source, value] of batch.current) {
|
|
986
953
|
source.v = value;
|
|
954
|
+
source.wv = increment_write_version();
|
|
987
955
|
}
|
|
988
956
|
|
|
989
957
|
// trigger any `$state.eager(...)` expressions with the new state.
|
|
@@ -3,21 +3,18 @@
|
|
|
3
3
|
import { DEV } from 'esm-env';
|
|
4
4
|
import {
|
|
5
5
|
ERROR_VALUE,
|
|
6
|
-
CLEAN,
|
|
7
6
|
DERIVED,
|
|
8
7
|
DIRTY,
|
|
9
8
|
EFFECT_PRESERVED,
|
|
10
|
-
MAYBE_DIRTY,
|
|
11
9
|
STALE_REACTION,
|
|
12
10
|
ASYNC,
|
|
13
11
|
WAS_MARKED,
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
DESTROYED,
|
|
13
|
+
CLEAN
|
|
16
14
|
} from '#client/constants';
|
|
17
15
|
import {
|
|
18
16
|
active_reaction,
|
|
19
17
|
active_effect,
|
|
20
|
-
set_signal_status,
|
|
21
18
|
update_reaction,
|
|
22
19
|
increment_write_version,
|
|
23
20
|
set_active_effect,
|
|
@@ -37,6 +34,7 @@ import { UNINITIALIZED } from '../../../constants.js';
|
|
|
37
34
|
import { batch_values, current_batch } from './batch.js';
|
|
38
35
|
import { unset_context } from './async.js';
|
|
39
36
|
import { deferred } from '../../shared/utils.js';
|
|
37
|
+
import { set_signal_status, update_derived_status } from './status.js';
|
|
40
38
|
|
|
41
39
|
/** @type {Effect | null} */
|
|
42
40
|
export let current_async_effect = null;
|
|
@@ -93,11 +91,12 @@ export function derived(fn) {
|
|
|
93
91
|
/**
|
|
94
92
|
* @template V
|
|
95
93
|
* @param {() => V | Promise<V>} fn
|
|
94
|
+
* @param {string} [label]
|
|
96
95
|
* @param {string} [location] If provided, print a warning if the value is not read immediately after update
|
|
97
96
|
* @returns {Promise<Source<V>>}
|
|
98
97
|
*/
|
|
99
98
|
/*#__NO_SIDE_EFFECTS__*/
|
|
100
|
-
export function async_derived(fn, location) {
|
|
99
|
+
export function async_derived(fn, label, location) {
|
|
101
100
|
let parent = /** @type {Effect | null} */ (active_effect);
|
|
102
101
|
|
|
103
102
|
if (parent === null) {
|
|
@@ -109,6 +108,8 @@ export function async_derived(fn, location) {
|
|
|
109
108
|
var promise = /** @type {Promise<V>} */ (/** @type {unknown} */ (undefined));
|
|
110
109
|
var signal = source(/** @type {V} */ (UNINITIALIZED));
|
|
111
110
|
|
|
111
|
+
if (DEV) signal.label = label;
|
|
112
|
+
|
|
112
113
|
// only suspend in async deriveds created on initialisation
|
|
113
114
|
var should_suspend = !active_reaction;
|
|
114
115
|
|
|
@@ -147,7 +148,7 @@ export function async_derived(fn, location) {
|
|
|
147
148
|
var batch = /** @type {Batch} */ (current_batch);
|
|
148
149
|
|
|
149
150
|
if (should_suspend) {
|
|
150
|
-
var blocking =
|
|
151
|
+
var blocking = boundary.is_rendered();
|
|
151
152
|
|
|
152
153
|
boundary.update_pending_count(1);
|
|
153
154
|
batch.increment(blocking);
|
|
@@ -356,15 +357,21 @@ export function update_derived(derived) {
|
|
|
356
357
|
var value = execute_derived(derived);
|
|
357
358
|
|
|
358
359
|
if (!derived.equals(value)) {
|
|
360
|
+
derived.wv = increment_write_version();
|
|
361
|
+
|
|
359
362
|
// in a fork, we don't update the underlying value, just `batch_values`.
|
|
360
363
|
// the underlying value will be updated when the fork is committed.
|
|
361
364
|
// otherwise, the next time we get here after a 'real world' state
|
|
362
365
|
// change, `derived.equals` may incorrectly return `true`
|
|
363
|
-
if (!current_batch?.is_fork) {
|
|
366
|
+
if (!current_batch?.is_fork || derived.deps === null) {
|
|
364
367
|
derived.v = value;
|
|
365
|
-
}
|
|
366
368
|
|
|
367
|
-
|
|
369
|
+
// deriveds without dependencies should never be recomputed
|
|
370
|
+
if (derived.deps === null) {
|
|
371
|
+
set_signal_status(derived, CLEAN);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
368
375
|
}
|
|
369
376
|
|
|
370
377
|
// don't mark derived clean if we're reading it inside a
|
|
@@ -382,7 +389,6 @@ export function update_derived(derived) {
|
|
|
382
389
|
batch_values.set(derived, value);
|
|
383
390
|
}
|
|
384
391
|
} else {
|
|
385
|
-
|
|
386
|
-
set_signal_status(derived, status);
|
|
392
|
+
update_derived_status(derived);
|
|
387
393
|
}
|
|
388
394
|
}
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
remove_reactions,
|
|
10
10
|
set_active_reaction,
|
|
11
11
|
set_is_destroying_effect,
|
|
12
|
-
set_signal_status,
|
|
13
12
|
untrack,
|
|
14
13
|
untracking
|
|
15
14
|
} from '../runtime.js';
|
|
@@ -44,6 +43,7 @@ import { component_context, dev_current_component_function, dev_stack } from '..
|
|
|
44
43
|
import { Batch, current_batch, schedule_effect } from './batch.js';
|
|
45
44
|
import { flatten } from './async.js';
|
|
46
45
|
import { without_reactive_context } from '../dom/elements/bindings/shared.js';
|
|
46
|
+
import { set_signal_status } from './status.js';
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* @param {'$effect' | '$effect.pre' | '$inspect'} rune
|
|
@@ -328,7 +328,7 @@ export function legacy_pre_effect_reset() {
|
|
|
328
328
|
|
|
329
329
|
// If the effect is CLEAN, then make it MAYBE_DIRTY. This ensures we traverse through
|
|
330
330
|
// the effects dependencies and correctly ensure each dependency is up-to-date.
|
|
331
|
-
if ((effect.f & CLEAN) !== 0) {
|
|
331
|
+
if ((effect.f & CLEAN) !== 0 && effect.deps !== null) {
|
|
332
332
|
set_signal_status(effect, MAYBE_DIRTY);
|
|
333
333
|
}
|
|
334
334
|
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
untracked_writes,
|
|
7
7
|
get,
|
|
8
8
|
set_untracked_writes,
|
|
9
|
-
set_signal_status,
|
|
10
9
|
untrack,
|
|
11
10
|
increment_write_version,
|
|
12
11
|
update_effect,
|
|
@@ -40,6 +39,7 @@ import { component_context, is_runes } from '../context.js';
|
|
|
40
39
|
import { Batch, batch_values, eager_block_effects, schedule_effect } from './batch.js';
|
|
41
40
|
import { proxy } from '../proxy.js';
|
|
42
41
|
import { execute_derived } from './deriveds.js';
|
|
42
|
+
import { set_signal_status, update_derived_status } from './status.js';
|
|
43
43
|
|
|
44
44
|
/** @type {Set<any>} */
|
|
45
45
|
export let eager_effects = new Set();
|
|
@@ -218,12 +218,14 @@ export function internal_set(source, value) {
|
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
if ((source.f & DERIVED) !== 0) {
|
|
221
|
+
const derived = /** @type {Derived} */ (source);
|
|
222
|
+
|
|
221
223
|
// if we are assigning to a dirty derived we set it to clean/maybe dirty but we also eagerly execute it to track the dependencies
|
|
222
224
|
if ((source.f & DIRTY) !== 0) {
|
|
223
|
-
execute_derived(
|
|
225
|
+
execute_derived(derived);
|
|
224
226
|
}
|
|
225
227
|
|
|
226
|
-
|
|
228
|
+
update_derived_status(derived);
|
|
227
229
|
}
|
|
228
230
|
|
|
229
231
|
source.wv = increment_write_version();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** @import { Derived, Signal } from '#client' */
|
|
2
|
+
import { CLEAN, CONNECTED, DIRTY, MAYBE_DIRTY } from '#client/constants';
|
|
3
|
+
|
|
4
|
+
const STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN);
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {Signal} signal
|
|
8
|
+
* @param {number} status
|
|
9
|
+
*/
|
|
10
|
+
export function set_signal_status(signal, status) {
|
|
11
|
+
signal.f = (signal.f & STATUS_MASK) | status;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Set a derived's status to CLEAN or MAYBE_DIRTY based on its connection state.
|
|
16
|
+
* @param {Derived} derived
|
|
17
|
+
*/
|
|
18
|
+
export function update_derived_status(derived) {
|
|
19
|
+
// Only mark as MAYBE_DIRTY if disconnected and has dependencies.
|
|
20
|
+
if ((derived.f & CONNECTED) !== 0 || derived.deps === null) {
|
|
21
|
+
set_signal_status(derived, CLEAN);
|
|
22
|
+
} else {
|
|
23
|
+
set_signal_status(derived, MAYBE_DIRTY);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** @import { Derived, Effect, Value } from '#client' */
|
|
2
|
+
import { CLEAN, DERIVED, DIRTY, MAYBE_DIRTY, WAS_MARKED } from '#client/constants';
|
|
3
|
+
import { set_signal_status } from './status.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {Value[] | null} deps
|
|
7
|
+
*/
|
|
8
|
+
function clear_marked(deps) {
|
|
9
|
+
if (deps === null) return;
|
|
10
|
+
|
|
11
|
+
for (const dep of deps) {
|
|
12
|
+
if ((dep.f & DERIVED) === 0 || (dep.f & WAS_MARKED) === 0) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
dep.f ^= WAS_MARKED;
|
|
17
|
+
|
|
18
|
+
clear_marked(/** @type {Derived} */ (dep).deps);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {Effect} effect
|
|
24
|
+
* @param {Set<Effect>} dirty_effects
|
|
25
|
+
* @param {Set<Effect>} maybe_dirty_effects
|
|
26
|
+
*/
|
|
27
|
+
export function defer_effect(effect, dirty_effects, maybe_dirty_effects) {
|
|
28
|
+
if ((effect.f & DIRTY) !== 0) {
|
|
29
|
+
dirty_effects.add(effect);
|
|
30
|
+
} else if ((effect.f & MAYBE_DIRTY) !== 0) {
|
|
31
|
+
maybe_dirty_effects.add(effect);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Since we're not executing these effects now, we need to clear any WAS_MARKED flags
|
|
35
|
+
// so that other batches can correctly reach these effects during their own traversal
|
|
36
|
+
clear_marked(effect.deps);
|
|
37
|
+
|
|
38
|
+
// mark as clean so they get scheduled if they depend on pending async state
|
|
39
|
+
set_signal_status(effect, CLEAN);
|
|
40
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { Derived, Effect, Reaction,
|
|
1
|
+
/** @import { Derived, Effect, Reaction, Source, Value } from '#client' */
|
|
2
2
|
import { DEV } from 'esm-env';
|
|
3
3
|
import { get_descriptors, get_prototype_of, index_of } from '../shared/utils.js';
|
|
4
4
|
import {
|
|
@@ -28,7 +28,6 @@ import { old_values } from './reactivity/sources.js';
|
|
|
28
28
|
import {
|
|
29
29
|
destroy_derived_effects,
|
|
30
30
|
execute_derived,
|
|
31
|
-
current_async_effect,
|
|
32
31
|
recent_async_deriveds,
|
|
33
32
|
update_derived
|
|
34
33
|
} from './reactivity/deriveds.js';
|
|
@@ -44,7 +43,6 @@ import {
|
|
|
44
43
|
set_dev_current_component_function,
|
|
45
44
|
set_dev_stack
|
|
46
45
|
} from './context.js';
|
|
47
|
-
import * as w from './warnings.js';
|
|
48
46
|
import {
|
|
49
47
|
Batch,
|
|
50
48
|
batch_values,
|
|
@@ -56,6 +54,7 @@ import { handle_error } from './error-handling.js';
|
|
|
56
54
|
import { UNINITIALIZED } from '../../constants.js';
|
|
57
55
|
import { captured_signals } from './legacy.js';
|
|
58
56
|
import { without_reactive_context } from './dom/elements/bindings/shared.js';
|
|
57
|
+
import { set_signal_status, update_derived_status } from './reactivity/status.js';
|
|
59
58
|
|
|
60
59
|
export let is_updating_effect = false;
|
|
61
60
|
|
|
@@ -167,21 +166,18 @@ export function is_dirty(reaction) {
|
|
|
167
166
|
}
|
|
168
167
|
|
|
169
168
|
if ((flags & MAYBE_DIRTY) !== 0) {
|
|
170
|
-
var dependencies = reaction.deps;
|
|
169
|
+
var dependencies = /** @type {Value[]} */ (reaction.deps);
|
|
170
|
+
var length = dependencies.length;
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
var
|
|
172
|
+
for (var i = 0; i < length; i++) {
|
|
173
|
+
var dependency = dependencies[i];
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (is_dirty(/** @type {Derived} */ (dependency))) {
|
|
179
|
-
update_derived(/** @type {Derived} */ (dependency));
|
|
180
|
-
}
|
|
175
|
+
if (is_dirty(/** @type {Derived} */ (dependency))) {
|
|
176
|
+
update_derived(/** @type {Derived} */ (dependency));
|
|
177
|
+
}
|
|
181
178
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
179
|
+
if (dependency.wv > reaction.wv) {
|
|
180
|
+
return true;
|
|
185
181
|
}
|
|
186
182
|
}
|
|
187
183
|
|
|
@@ -374,16 +370,20 @@ function remove_reaction(signal, dependency) {
|
|
|
374
370
|
// allows us to skip the expensive work of disconnecting and immediately reconnecting it
|
|
375
371
|
(new_deps === null || !new_deps.includes(dependency))
|
|
376
372
|
) {
|
|
377
|
-
|
|
373
|
+
var derived = /** @type {Derived} */ (dependency);
|
|
374
|
+
|
|
378
375
|
// If we are working with a derived that is owned by an effect, then mark it as being
|
|
379
376
|
// disconnected and remove the mark flag, as it cannot be reliably removed otherwise
|
|
380
|
-
if ((
|
|
381
|
-
|
|
382
|
-
|
|
377
|
+
if ((derived.f & CONNECTED) !== 0) {
|
|
378
|
+
derived.f ^= CONNECTED;
|
|
379
|
+
derived.f &= ~WAS_MARKED;
|
|
383
380
|
}
|
|
381
|
+
|
|
382
|
+
update_derived_status(derived);
|
|
383
|
+
|
|
384
384
|
// Disconnect any reactions owned by this reaction
|
|
385
|
-
destroy_derived_effects(
|
|
386
|
-
remove_reactions(
|
|
385
|
+
destroy_derived_effects(derived);
|
|
386
|
+
remove_reactions(derived, 0);
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
|
|
@@ -596,14 +596,14 @@ export function get(signal) {
|
|
|
596
596
|
}
|
|
597
597
|
}
|
|
598
598
|
|
|
599
|
-
if (is_destroying_effect) {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
}
|
|
599
|
+
if (is_destroying_effect && old_values.has(signal)) {
|
|
600
|
+
return old_values.get(signal);
|
|
601
|
+
}
|
|
603
602
|
|
|
604
|
-
|
|
605
|
-
|
|
603
|
+
if (is_derived) {
|
|
604
|
+
var derived = /** @type {Derived} */ (signal);
|
|
606
605
|
|
|
606
|
+
if (is_destroying_effect) {
|
|
607
607
|
var value = derived.v;
|
|
608
608
|
|
|
609
609
|
// if the derived is dirty and has reactions, or depends on the values that just changed, re-execute
|
|
@@ -619,17 +619,28 @@ export function get(signal) {
|
|
|
619
619
|
|
|
620
620
|
return value;
|
|
621
621
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
622
|
+
|
|
623
|
+
// connect disconnected deriveds if we are reading them inside an effect,
|
|
624
|
+
// or inside another derived that is already connected
|
|
625
|
+
var should_connect =
|
|
626
|
+
(derived.f & CONNECTED) === 0 &&
|
|
627
|
+
!untracking &&
|
|
628
|
+
active_reaction !== null &&
|
|
629
|
+
(is_updating_effect || (active_reaction.f & CONNECTED) !== 0);
|
|
630
|
+
|
|
631
|
+
var is_new = derived.deps === null;
|
|
627
632
|
|
|
628
633
|
if (is_dirty(derived)) {
|
|
634
|
+
if (should_connect) {
|
|
635
|
+
// set the flag before `update_derived`, so that the derived
|
|
636
|
+
// is added as a reaction to its dependencies
|
|
637
|
+
derived.f |= CONNECTED;
|
|
638
|
+
}
|
|
639
|
+
|
|
629
640
|
update_derived(derived);
|
|
630
641
|
}
|
|
631
642
|
|
|
632
|
-
if (
|
|
643
|
+
if (should_connect && !is_new) {
|
|
633
644
|
reconnect(derived);
|
|
634
645
|
}
|
|
635
646
|
}
|
|
@@ -653,7 +664,7 @@ export function get(signal) {
|
|
|
653
664
|
function reconnect(derived) {
|
|
654
665
|
if (derived.deps === null) return;
|
|
655
666
|
|
|
656
|
-
derived.f
|
|
667
|
+
derived.f |= CONNECTED;
|
|
657
668
|
|
|
658
669
|
for (const dep of derived.deps) {
|
|
659
670
|
(dep.reactions ??= []).push(derived);
|
|
@@ -718,17 +729,6 @@ export function untrack(fn) {
|
|
|
718
729
|
}
|
|
719
730
|
}
|
|
720
731
|
|
|
721
|
-
const STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN);
|
|
722
|
-
|
|
723
|
-
/**
|
|
724
|
-
* @param {Signal} signal
|
|
725
|
-
* @param {number} status
|
|
726
|
-
* @returns {void}
|
|
727
|
-
*/
|
|
728
|
-
export function set_signal_status(signal, status) {
|
|
729
|
-
signal.f = (signal.f & STATUS_MASK) | status;
|
|
730
|
-
}
|
|
731
|
-
|
|
732
732
|
/**
|
|
733
733
|
* @param {Record<string | symbol, unknown>} obj
|
|
734
734
|
* @param {Array<string | symbol>} keys
|
|
@@ -3,7 +3,7 @@ import { DIRTY, LEGACY_PROPS, MAYBE_DIRTY } from '../internal/client/constants.j
|
|
|
3
3
|
import { user_pre_effect } from '../internal/client/reactivity/effects.js';
|
|
4
4
|
import { mutable_source, set } from '../internal/client/reactivity/sources.js';
|
|
5
5
|
import { hydrate, mount, unmount } from '../internal/client/render.js';
|
|
6
|
-
import { active_effect, get
|
|
6
|
+
import { active_effect, get } from '../internal/client/runtime.js';
|
|
7
7
|
import { flushSync } from '../internal/client/reactivity/batch.js';
|
|
8
8
|
import { define_property, is_array } from '../internal/shared/utils.js';
|
|
9
9
|
import * as e from '../internal/client/errors.js';
|
|
@@ -12,6 +12,7 @@ import { DEV } from 'esm-env';
|
|
|
12
12
|
import { FILENAME } from '../constants.js';
|
|
13
13
|
import { component_context, dev_current_component_function } from '../internal/client/context.js';
|
|
14
14
|
import { async_mode_flag } from '../internal/flags/index.js';
|
|
15
|
+
import { set_signal_status } from '../internal/client/reactivity/status.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Takes the same options as a Svelte 4 component and the component function and returns a Svelte 4 compatible component.
|
package/src/version.js
CHANGED