svelte 5.57.0 → 5.57.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 +8 -8
- package/src/compiler/errors.js +10 -0
- package/src/compiler/migrate/index.js +15 -4
- package/src/compiler/phases/1-parse/acorn.js +36 -2
- package/src/compiler/phases/1-parse/index.js +23 -22
- package/src/compiler/phases/1-parse/read/context.js +3 -18
- package/src/compiler/phases/1-parse/read/expression.js +110 -2
- package/src/compiler/phases/1-parse/read/script.js +1 -1
- package/src/compiler/phases/1-parse/read/style.js +3 -5
- package/src/compiler/phases/1-parse/state/element.js +68 -40
- package/src/compiler/phases/1-parse/state/tag.js +8 -6
- package/src/compiler/phases/1-parse/utils/html.js +2 -0
- package/src/compiler/phases/1-parse/utils/whitespace.js +18 -0
- package/src/compiler/phases/2-analyze/css/css-prune.js +7 -6
- package/src/compiler/phases/2-analyze/index.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/BindDirective.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/ClassDeclaration.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/FunctionDeclaration.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/Identifier.js +1 -2
- package/src/compiler/phases/2-analyze/visitors/LetDirective.js +22 -0
- package/src/compiler/phases/2-analyze/visitors/SvelteBody.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/SvelteDocument.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/SvelteWindow.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/VariableDeclarator.js +2 -2
- package/src/compiler/phases/2-analyze/visitors/shared/a11y/constants.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js +3 -3
- package/src/compiler/phases/2-analyze/visitors/shared/utils.js +0 -38
- package/src/compiler/phases/3-transform/client/utils.js +0 -38
- package/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +2 -2
- package/src/compiler/phases/3-transform/client/visitors/AwaitExpression.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/CallExpression.js +1 -2
- package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +3 -8
- package/src/compiler/phases/3-transform/client/visitors/shared/events.js +3 -2
- package/src/compiler/phases/3-transform/server/visitors/shared/element.js +8 -0
- package/src/compiler/phases/3-transform/utils.js +1 -1
- package/src/compiler/phases/nodes.js +30 -0
- package/src/compiler/phases/scope.js +85 -2
- package/src/compiler/print/index.js +3 -1
- package/src/compiler/state.js +9 -5
- package/src/compiler/utils/ast.js +2 -5
- package/src/compiler/utils/compile_diagnostic.js +1 -1
- package/src/compiler/utils/extract_svelte_ignore.js +1 -1
- package/src/compiler/utils/mapped_code.js +13 -1
- package/src/index-client.js +1 -1
- package/src/internal/client/constants.js +0 -9
- package/src/internal/client/dev/debug.js +0 -2
- package/src/internal/client/dev/tracing.js +3 -1
- package/src/internal/client/dom/blocks/boundary.js +39 -8
- package/src/internal/client/dom/blocks/each.js +8 -6
- package/src/internal/client/dom/blocks/svelte-element.js +3 -1
- package/src/internal/client/dom/css.js +1 -1
- package/src/internal/client/dom/elements/attributes.js +168 -148
- package/src/internal/client/dom/elements/bindings/document.js +3 -2
- package/src/internal/client/dom/elements/events.js +6 -1
- package/src/internal/client/dom/elements/transitions.js +3 -3
- package/src/internal/client/error-handling.js +5 -1
- package/src/internal/client/errors.js +0 -16
- package/src/internal/client/proxy.js +12 -7
- package/src/internal/client/reactivity/async.js +5 -0
- package/src/internal/client/reactivity/batch.js +103 -75
- package/src/internal/client/reactivity/deriveds.js +0 -3
- package/src/internal/client/reactivity/sources.js +31 -19
- package/src/internal/client/reactivity/utils.js +2 -23
- package/src/internal/client/runtime.js +18 -25
- package/src/internal/server/context.js +9 -2
- package/src/internal/server/index.js +9 -2
- package/src/internal/server/renderer.js +101 -69
- package/src/internal/shared/errors.js +16 -0
- package/src/reactivity/date.js +2 -2
- package/src/reactivity/url-search-params.js +1 -1
- package/src/version.js +1 -1
- package/types/index.d.ts.map +1 -1
- package/src/compiler/phases/1-parse/utils/create.js +0 -16
- package/src/compiler/phases/2-analyze/visitors/shared/special-element.js +0 -16
- package/src/compiler/utils/push_array.js +0 -13
- /package/src/compiler/{phases/2-analyze/utils → utils}/check_graph_for_cycles.js +0 -0
- /package/src/compiler/{phases/1-parse/utils → utils}/fuzzymatch.js +0 -0
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
import { async_mode_flag } from '../../flags/index.js';
|
|
23
23
|
import { deferred, define_property, includes } from '../../shared/utils.js';
|
|
24
24
|
import {
|
|
25
|
-
active_effect,
|
|
26
25
|
active_reaction,
|
|
27
26
|
get,
|
|
28
27
|
increment_write_version,
|
|
@@ -38,7 +37,6 @@ import { eager_effect, teardown, unlink_effect } from './effects.js';
|
|
|
38
37
|
import { defer_effect } from './utils.js';
|
|
39
38
|
import { UNINITIALIZED } from '../../../constants.js';
|
|
40
39
|
import { set_signal_status } from './status.js';
|
|
41
|
-
import { legacy_is_updating_store } from './store.js';
|
|
42
40
|
import { invariant } from '../../shared/dev.js';
|
|
43
41
|
import { log_effect_tree } from '../dev/debug.js';
|
|
44
42
|
import { OBSOLETE } from './deriveds.js';
|
|
@@ -160,10 +158,17 @@ export class Batch {
|
|
|
160
158
|
#deferred = null;
|
|
161
159
|
|
|
162
160
|
/**
|
|
163
|
-
*
|
|
161
|
+
* Effects that were scheduled in this batch but not yet 'resolved' into the
|
|
162
|
+
* root effects that need to be flushed. Resolving — the upwards traversal that
|
|
163
|
+
* marks the path to each effect on the shared effect tree (see #resolve) — is
|
|
164
|
+
* deferred until the batch is processed, so that the markers are created and
|
|
165
|
+
* consumed within a single traversal. Scheduling into other batches (which can
|
|
166
|
+
* happen concurrently, e.g. while a batch is committed) can therefore never
|
|
167
|
+
* observe (and be confused by) this batch's markers.
|
|
168
|
+
* May contain duplicates — deduplication happens during resolving
|
|
164
169
|
* @type {Effect[]}
|
|
165
170
|
*/
|
|
166
|
-
#
|
|
171
|
+
#scheduled = [];
|
|
167
172
|
|
|
168
173
|
/**
|
|
169
174
|
* Effects created while this batch was active.
|
|
@@ -273,14 +278,57 @@ export class Batch {
|
|
|
273
278
|
this.#unskipped_branches.add(effect);
|
|
274
279
|
}
|
|
275
280
|
|
|
276
|
-
|
|
277
|
-
|
|
281
|
+
/**
|
|
282
|
+
* Convert the effects that were scheduled in this batch into the root effects
|
|
283
|
+
* that need to be traversed, marking the path to each effect (by clearing the
|
|
284
|
+
* `CLEAN` flag on ancestor branches) so that the traversal can find them.
|
|
285
|
+
* This happens right before traversal rather than at scheduling time, so that
|
|
286
|
+
* the markers left on the (shared) effect tree are created and consumed within
|
|
287
|
+
* a single traversal — scheduling into other batches can never observe them
|
|
288
|
+
* @returns {Effect[]}
|
|
289
|
+
*/
|
|
290
|
+
#resolve() {
|
|
291
|
+
/** @type {Effect[]} */
|
|
292
|
+
var roots = [];
|
|
278
293
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
294
|
+
for (const effect of this.#scheduled) {
|
|
295
|
+
// skip effects that are destroyed, or that already ran (e.g. because
|
|
296
|
+
// they were reached by the traversal that preceded a drain iteration,
|
|
297
|
+
// or because they were scheduled twice)
|
|
298
|
+
if ((effect.f & DESTROYED) !== 0 || (effect.f & (DIRTY | MAYBE_DIRTY)) === 0) continue;
|
|
299
|
+
|
|
300
|
+
var e = effect;
|
|
301
|
+
var covered = false;
|
|
302
|
+
|
|
303
|
+
while (e.parent !== null) {
|
|
304
|
+
e = e.parent;
|
|
305
|
+
var flags = e.f;
|
|
306
|
+
|
|
307
|
+
if ((flags & (ROOT_EFFECT | BRANCH_EFFECT)) !== 0) {
|
|
308
|
+
if ((flags & CLEAN) === 0) {
|
|
309
|
+
// the path to the root was already marked, meaning the
|
|
310
|
+
// root was already collected — nothing left to do
|
|
311
|
+
covered = true;
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
e.f ^= CLEAN;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (!covered) {
|
|
320
|
+
roots.push(e);
|
|
321
|
+
}
|
|
282
322
|
}
|
|
283
323
|
|
|
324
|
+
this.#scheduled = [];
|
|
325
|
+
|
|
326
|
+
return roots;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
#process() {
|
|
330
|
+
this.#started = true;
|
|
331
|
+
|
|
284
332
|
if (DEV) {
|
|
285
333
|
// track all the values that were updated during this flush,
|
|
286
334
|
// so that they can be reset afterwards
|
|
@@ -304,9 +352,6 @@ export class Batch {
|
|
|
304
352
|
this.schedule(e);
|
|
305
353
|
}
|
|
306
354
|
|
|
307
|
-
const roots = this.#roots;
|
|
308
|
-
this.#roots = [];
|
|
309
|
-
|
|
310
355
|
this.apply();
|
|
311
356
|
|
|
312
357
|
/** @type {Effect[]} */
|
|
@@ -321,18 +366,28 @@ export class Batch {
|
|
|
321
366
|
*/
|
|
322
367
|
var updates = (legacy_updates = []);
|
|
323
368
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
//
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
369
|
+
// Effects can be scheduled during traversal (e.g. because a parent each/await/etc
|
|
370
|
+
// block updated an internal source, or because an effect invalidated itself)
|
|
371
|
+
// hence we loop until there are no more scheduled effects.
|
|
372
|
+
while (this.#scheduled.length > 0) {
|
|
373
|
+
if (flush_count++ > 1000) {
|
|
374
|
+
this.#unlink();
|
|
375
|
+
infinite_loop_guard(); // TODO try to reset_all() here?
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
for (const root of this.#resolve()) {
|
|
379
|
+
try {
|
|
380
|
+
this.#traverse(root, effects, render_effects);
|
|
381
|
+
} catch (e) {
|
|
382
|
+
reset_all(root);
|
|
383
|
+
// If there's no async work left, this branch is now dead and needs
|
|
384
|
+
// to be discarded to not become a zombie that is never cleaned up.
|
|
385
|
+
// See https://github.com/sveltejs/svelte/issues/18221#issuecomment-4497918414
|
|
386
|
+
// for a (non-minimal) reproduction that demonstrates a case where this is necessary
|
|
387
|
+
// to not get follow-up false-positives via "batch has scheduled roots" invariant errors.
|
|
388
|
+
if (!this.#is_deferred()) this.discard();
|
|
389
|
+
throw e;
|
|
390
|
+
}
|
|
336
391
|
}
|
|
337
392
|
}
|
|
338
393
|
|
|
@@ -393,7 +448,7 @@ export class Batch {
|
|
|
393
448
|
|
|
394
449
|
var next_batch = /** @type {Batch | null} */ (/** @type {unknown} */ (current_batch));
|
|
395
450
|
|
|
396
|
-
if (this.#pending === 0 && (this.#
|
|
451
|
+
if (this.#pending === 0 && (this.#scheduled.length === 0 || next_batch !== null)) {
|
|
397
452
|
this.#unlink();
|
|
398
453
|
|
|
399
454
|
// Order matters here - we need to commit and THEN continue flushing new batches, not the other way around,
|
|
@@ -408,12 +463,15 @@ export class Batch {
|
|
|
408
463
|
}
|
|
409
464
|
|
|
410
465
|
// Edge case: During traversal new branches might create effects that run immediately and set state,
|
|
411
|
-
// causing an effect
|
|
466
|
+
// causing an effect to be scheduled again. We need to traverse the current batch
|
|
412
467
|
// once more in that case - most of the time this will just clean up dirty branches.
|
|
413
|
-
if (this.#
|
|
468
|
+
if (this.#scheduled.length > 0) {
|
|
414
469
|
if (next_batch !== null) {
|
|
415
|
-
const
|
|
416
|
-
|
|
470
|
+
for (const e of this.#scheduled) {
|
|
471
|
+
next_batch.#scheduled.push(e);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
this.#scheduled = [];
|
|
417
475
|
} else {
|
|
418
476
|
next_batch = this;
|
|
419
477
|
}
|
|
@@ -712,7 +770,7 @@ export class Batch {
|
|
|
712
770
|
// The microtask queue can contain the batch already scheduled to run right
|
|
713
771
|
// after this one is finished, so throwing the invariant would be wrong here.
|
|
714
772
|
if (DEV && !batch.#decrement_queued) {
|
|
715
|
-
invariant(batch.#
|
|
773
|
+
invariant(batch.#scheduled.length === 0, 'Batch has scheduled effects');
|
|
716
774
|
}
|
|
717
775
|
|
|
718
776
|
// A batch was unskipped in a later batch -> tell prior batches to unskip it, too
|
|
@@ -768,14 +826,12 @@ export class Batch {
|
|
|
768
826
|
|
|
769
827
|
// Only apply and traverse when we know we triggered async work with marking the effects
|
|
770
828
|
// and know this won't run anyway right afterwards
|
|
771
|
-
if (batch.#
|
|
829
|
+
if (batch.#scheduled.length > 0 && !batch.#decrement_queued) {
|
|
772
830
|
batch.apply();
|
|
773
831
|
|
|
774
|
-
for (var root of batch.#
|
|
832
|
+
for (var root of batch.#resolve()) {
|
|
775
833
|
batch.#traverse(root, [], []);
|
|
776
834
|
}
|
|
777
|
-
|
|
778
|
-
batch.#roots = [];
|
|
779
835
|
}
|
|
780
836
|
|
|
781
837
|
batch.deactivate();
|
|
@@ -939,43 +995,7 @@ export class Batch {
|
|
|
939
995
|
return;
|
|
940
996
|
}
|
|
941
997
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
while (e.parent !== null) {
|
|
945
|
-
e = e.parent;
|
|
946
|
-
var flags = e.f;
|
|
947
|
-
|
|
948
|
-
// if the effect is being scheduled because a parent (each/await/etc) block
|
|
949
|
-
// updated an internal source, or because a branch is being unskipped,
|
|
950
|
-
// bail out or we'll cause a second flush
|
|
951
|
-
if (collected_effects !== null && e === active_effect) {
|
|
952
|
-
if (async_mode_flag) return;
|
|
953
|
-
|
|
954
|
-
// in sync mode, render effects run during traversal. in an extreme edge case
|
|
955
|
-
// — namely that we're setting a value inside a derived read during traversal —
|
|
956
|
-
// they can be made dirty after they have already been visited, in which
|
|
957
|
-
// case we shouldn't bail out. we also shouldn't bail out if we're
|
|
958
|
-
// updating a store inside a `$:`, since this might invalidate
|
|
959
|
-
// effects that were already visited
|
|
960
|
-
if (
|
|
961
|
-
(active_reaction === null || (active_reaction.f & DERIVED) === 0) &&
|
|
962
|
-
!legacy_is_updating_store
|
|
963
|
-
) {
|
|
964
|
-
return;
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
if ((flags & (ROOT_EFFECT | BRANCH_EFFECT)) !== 0) {
|
|
969
|
-
if ((flags & CLEAN) === 0) {
|
|
970
|
-
// branch is already dirty, bail
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
e.f ^= CLEAN;
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
this.#roots.push(e);
|
|
998
|
+
this.#scheduled.push(effect);
|
|
979
999
|
}
|
|
980
1000
|
|
|
981
1001
|
#unlink() {
|
|
@@ -1012,15 +1032,15 @@ export class Batch {
|
|
|
1012
1032
|
*/
|
|
1013
1033
|
export function flushSync(fn) {
|
|
1014
1034
|
var was_flushing_sync = is_flushing_sync;
|
|
1035
|
+
var prev_previous_batch = previous_batch;
|
|
1036
|
+
previous_batch = null;
|
|
1015
1037
|
is_flushing_sync = true;
|
|
1016
1038
|
|
|
1017
1039
|
try {
|
|
1018
1040
|
var result;
|
|
1019
1041
|
|
|
1020
1042
|
if (fn) {
|
|
1021
|
-
|
|
1022
|
-
current_batch.flush();
|
|
1023
|
-
}
|
|
1043
|
+
flushSync(); // flush anything pending through the while loop below
|
|
1024
1044
|
|
|
1025
1045
|
result = fn();
|
|
1026
1046
|
}
|
|
@@ -1036,6 +1056,7 @@ export function flushSync(fn) {
|
|
|
1036
1056
|
}
|
|
1037
1057
|
} finally {
|
|
1038
1058
|
is_flushing_sync = was_flushing_sync;
|
|
1059
|
+
previous_batch = prev_previous_batch;
|
|
1039
1060
|
}
|
|
1040
1061
|
}
|
|
1041
1062
|
|
|
@@ -1275,6 +1296,10 @@ export function eager(fn) {
|
|
|
1275
1296
|
let version = version_map.get(parent) ?? source(0);
|
|
1276
1297
|
version_map.set(parent, version);
|
|
1277
1298
|
|
|
1299
|
+
if (DEV) {
|
|
1300
|
+
version.label ??= '$state.eager version';
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1278
1303
|
teardown(() => {
|
|
1279
1304
|
if (parent.f & DESTROYING) version_map.delete(parent);
|
|
1280
1305
|
});
|
|
@@ -1382,6 +1407,8 @@ export function fork(fn) {
|
|
|
1382
1407
|
e.fork_timing();
|
|
1383
1408
|
}
|
|
1384
1409
|
|
|
1410
|
+
flushSync();
|
|
1411
|
+
|
|
1385
1412
|
var batch = Batch.ensure();
|
|
1386
1413
|
batch.is_fork = true;
|
|
1387
1414
|
batch_values = new Map();
|
|
@@ -1389,7 +1416,8 @@ export function fork(fn) {
|
|
|
1389
1416
|
var committed = false;
|
|
1390
1417
|
var settled = batch.settled();
|
|
1391
1418
|
|
|
1392
|
-
|
|
1419
|
+
fn();
|
|
1420
|
+
flushSync();
|
|
1393
1421
|
|
|
1394
1422
|
return {
|
|
1395
1423
|
commit: async () => {
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
EFFECT_PRESERVED,
|
|
10
10
|
STALE_REACTION,
|
|
11
11
|
ASYNC,
|
|
12
|
-
WAS_MARKED,
|
|
13
12
|
DESTROYED,
|
|
14
13
|
CLEAN,
|
|
15
14
|
REACTION_RAN,
|
|
@@ -365,7 +364,6 @@ export function execute_derived(derived) {
|
|
|
365
364
|
|
|
366
365
|
stack.push(derived);
|
|
367
366
|
|
|
368
|
-
derived.f &= ~WAS_MARKED;
|
|
369
367
|
destroy_derived_effects(derived);
|
|
370
368
|
value = update_reaction(derived);
|
|
371
369
|
} finally {
|
|
@@ -375,7 +373,6 @@ export function execute_derived(derived) {
|
|
|
375
373
|
}
|
|
376
374
|
} else {
|
|
377
375
|
try {
|
|
378
|
-
derived.f &= ~WAS_MARKED;
|
|
379
376
|
destroy_derived_effects(derived);
|
|
380
377
|
value = update_reaction(derived);
|
|
381
378
|
} finally {
|
|
@@ -25,10 +25,7 @@ import {
|
|
|
25
25
|
MAYBE_DIRTY,
|
|
26
26
|
BLOCK_EFFECT,
|
|
27
27
|
ROOT_EFFECT,
|
|
28
|
-
ASYNC
|
|
29
|
-
WAS_MARKED,
|
|
30
|
-
CONNECTED,
|
|
31
|
-
REACTION_IS_UPDATING
|
|
28
|
+
ASYNC
|
|
32
29
|
} from '#client/constants';
|
|
33
30
|
import * as e from '../errors.js';
|
|
34
31
|
import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js';
|
|
@@ -49,7 +46,7 @@ import { set_signal_status, update_derived_status } from './status.js';
|
|
|
49
46
|
/** @type {Set<Effect>} */
|
|
50
47
|
export let eager_effects = new Set();
|
|
51
48
|
|
|
52
|
-
/** @type {Map<
|
|
49
|
+
/** @type {Map<Value, any>} */
|
|
53
50
|
export const old_values = new Map();
|
|
54
51
|
|
|
55
52
|
/**
|
|
@@ -144,7 +141,7 @@ export function mutate(source, value) {
|
|
|
144
141
|
|
|
145
142
|
/**
|
|
146
143
|
* @template V
|
|
147
|
-
* @param {
|
|
144
|
+
* @param {Value<V>} source
|
|
148
145
|
* @param {V} value
|
|
149
146
|
* @param {boolean} [should_proxy]
|
|
150
147
|
* @returns {V}
|
|
@@ -171,9 +168,20 @@ export function set(source, value, should_proxy = false) {
|
|
|
171
168
|
return internal_set(source, new_value, legacy_updates);
|
|
172
169
|
}
|
|
173
170
|
|
|
171
|
+
/**
|
|
172
|
+
* A set of signals we have already seen while traversing in mark_reactions.
|
|
173
|
+
* Not always set to balance the common case of sources only having a couple
|
|
174
|
+
* of (transitive) dependencies (where always creating a Set would be bad for perf)
|
|
175
|
+
* with the edge case of extremely deep or wide dependency arrays with cycles.
|
|
176
|
+
* @type {Set<any> | null}
|
|
177
|
+
*/
|
|
178
|
+
var seen = null;
|
|
179
|
+
/** Number of transitive dependencies, see {@link seen} for more info */
|
|
180
|
+
var count_deps = 0;
|
|
181
|
+
|
|
174
182
|
/**
|
|
175
183
|
* @template V
|
|
176
|
-
* @param {
|
|
184
|
+
* @param {Value<V>} source
|
|
177
185
|
* @param {V} value
|
|
178
186
|
* @param {Effect[] | null} [updated_during_traversal]
|
|
179
187
|
* @returns {V}
|
|
@@ -240,7 +248,10 @@ export function internal_set(source, value, updated_during_traversal = null) {
|
|
|
240
248
|
|
|
241
249
|
// For debugging, in case you want to know which reactions are being scheduled:
|
|
242
250
|
// log_reactions(source);
|
|
251
|
+
seen = null;
|
|
252
|
+
count_deps = 0;
|
|
243
253
|
mark_reactions(source, DIRTY, updated_during_traversal);
|
|
254
|
+
seen = null;
|
|
244
255
|
|
|
245
256
|
// It's possible that the current reaction might not have up-to-date dependencies
|
|
246
257
|
// whilst it's actively running. So in the case of ensuring it registers the reaction
|
|
@@ -347,6 +358,18 @@ function mark_reactions(signal, status, updated_during_traversal) {
|
|
|
347
358
|
var runes = is_runes();
|
|
348
359
|
var length = reactions.length;
|
|
349
360
|
|
|
361
|
+
count_deps += length;
|
|
362
|
+
// Activate the `seen` Set if we think from the unusually high number of deps that
|
|
363
|
+
// there might be cycles in the graph, to avoid repeated lookups for reactions
|
|
364
|
+
// Example: https://github.com/sveltejs/svelte/issues/16658 has a graph with one source
|
|
365
|
+
// reaching ~10000 distinct deriveds/effects each, resulting in 65 million walks through repeated visits.
|
|
366
|
+
if (count_deps > 100000 && seen === null) seen = new Set();
|
|
367
|
+
|
|
368
|
+
if (seen !== null) {
|
|
369
|
+
if (seen.has(signal)) return;
|
|
370
|
+
seen.add(signal);
|
|
371
|
+
}
|
|
372
|
+
|
|
350
373
|
for (var i = 0; i < length; i++) {
|
|
351
374
|
var reaction = reactions[i];
|
|
352
375
|
var flags = reaction.f;
|
|
@@ -370,18 +393,7 @@ function mark_reactions(signal, status, updated_during_traversal) {
|
|
|
370
393
|
var derived = /** @type {Derived} */ (reaction);
|
|
371
394
|
|
|
372
395
|
batch_values?.delete(derived);
|
|
373
|
-
|
|
374
|
-
if ((flags & WAS_MARKED) === 0) {
|
|
375
|
-
// Only connected deriveds being executed outside the update cycle can be reliably unmarked right away
|
|
376
|
-
if (
|
|
377
|
-
flags & CONNECTED &&
|
|
378
|
-
(active_effect === null || (active_effect.f & REACTION_IS_UPDATING) === 0)
|
|
379
|
-
) {
|
|
380
|
-
reaction.f |= WAS_MARKED;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
mark_reactions(derived, MAYBE_DIRTY, updated_during_traversal);
|
|
384
|
-
}
|
|
396
|
+
mark_reactions(derived, MAYBE_DIRTY, updated_during_traversal);
|
|
385
397
|
} else if (not_dirty) {
|
|
386
398
|
var effect = /** @type {Effect} */ (reaction);
|
|
387
399
|
|
|
@@ -1,24 +1,7 @@
|
|
|
1
|
-
/** @import {
|
|
2
|
-
import { CLEAN,
|
|
1
|
+
/** @import { Effect } from '#client' */
|
|
2
|
+
import { CLEAN, DIRTY, MAYBE_DIRTY } from '#client/constants';
|
|
3
3
|
import { set_signal_status } from './status.js';
|
|
4
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
5
|
/**
|
|
23
6
|
* @param {Effect} effect
|
|
24
7
|
* @param {Set<Effect>} dirty_effects
|
|
@@ -31,10 +14,6 @@ export function defer_effect(effect, dirty_effects, maybe_dirty_effects) {
|
|
|
31
14
|
maybe_dirty_effects.add(effect);
|
|
32
15
|
}
|
|
33
16
|
|
|
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
17
|
// mark as clean so they get scheduled if they depend on pending async state
|
|
39
18
|
set_signal_status(effect, CLEAN);
|
|
40
19
|
}
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
REACTION_IS_UPDATING,
|
|
22
22
|
STALE_REACTION,
|
|
23
23
|
ERROR_VALUE,
|
|
24
|
-
WAS_MARKED,
|
|
25
24
|
MANAGED_EFFECT,
|
|
26
25
|
REACTION_RAN
|
|
27
26
|
} from './constants.js';
|
|
@@ -61,11 +60,6 @@ import { without_reactive_context } from './dom/elements/bindings/shared.js';
|
|
|
61
60
|
import { set_signal_status, update_derived_status } from './reactivity/status.js';
|
|
62
61
|
import * as w from './warnings.js';
|
|
63
62
|
|
|
64
|
-
/**
|
|
65
|
-
* True if updating in an effect context that is reactive (i.e. not branch/root effects)
|
|
66
|
-
*/
|
|
67
|
-
let is_updating_effect = false;
|
|
68
|
-
|
|
69
63
|
export let is_destroying_effect = false;
|
|
70
64
|
|
|
71
65
|
/** @param {boolean} value */
|
|
@@ -94,13 +88,17 @@ export function set_active_effect(effect) {
|
|
|
94
88
|
/**
|
|
95
89
|
* When sources are created within a reaction, reading and writing
|
|
96
90
|
* them within that reaction should not cause a re-run
|
|
97
|
-
* @type {null | Set<
|
|
91
|
+
* @type {null | Set<Value>}
|
|
98
92
|
*/
|
|
99
93
|
export let current_sources = null;
|
|
100
94
|
|
|
101
95
|
/** @param {Value} value */
|
|
102
96
|
export function push_reaction_value(value) {
|
|
103
|
-
if (
|
|
97
|
+
if (
|
|
98
|
+
active_reaction !== null &&
|
|
99
|
+
((!async_mode_flag && (active_reaction.f & REACTION_IS_UPDATING) !== 0) ||
|
|
100
|
+
(active_reaction.f & DERIVED) !== 0)
|
|
101
|
+
) {
|
|
104
102
|
(current_sources ??= new Set()).add(value);
|
|
105
103
|
}
|
|
106
104
|
}
|
|
@@ -118,11 +116,11 @@ export let skipped_deps = 0;
|
|
|
118
116
|
/**
|
|
119
117
|
* Tracks writes that the effect it's executed in doesn't listen to yet,
|
|
120
118
|
* so that the dependency can be added to the effect later on if it then reads it
|
|
121
|
-
* @type {null |
|
|
119
|
+
* @type {null | Value[]}
|
|
122
120
|
*/
|
|
123
121
|
export let untracked_writes = null;
|
|
124
122
|
|
|
125
|
-
/** @param {null |
|
|
123
|
+
/** @param {null | Value[]} value */
|
|
126
124
|
export function set_untracked_writes(value) {
|
|
127
125
|
untracked_writes = value;
|
|
128
126
|
}
|
|
@@ -160,10 +158,6 @@ export function is_dirty(reaction) {
|
|
|
160
158
|
return true;
|
|
161
159
|
}
|
|
162
160
|
|
|
163
|
-
if (flags & DERIVED) {
|
|
164
|
-
reaction.f &= ~WAS_MARKED;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
161
|
if ((flags & MAYBE_DIRTY) !== 0) {
|
|
168
162
|
var dependencies = /** @type {Value[]} */ (reaction.deps);
|
|
169
163
|
var length = dependencies.length;
|
|
@@ -404,17 +398,14 @@ function remove_reaction(signal, dependency) {
|
|
|
404
398
|
) {
|
|
405
399
|
var derived = /** @type {Derived} */ (dependency);
|
|
406
400
|
|
|
407
|
-
// If we are working with a derived that is owned by an effect, then mark it as being
|
|
408
|
-
// disconnected and remove the mark flag, as it cannot be reliably removed otherwise
|
|
409
401
|
if ((derived.f & CONNECTED) !== 0) {
|
|
410
402
|
derived.f ^= CONNECTED;
|
|
411
|
-
derived.f &= ~WAS_MARKED;
|
|
412
403
|
}
|
|
413
404
|
|
|
414
405
|
// In a fork it's possible that a derived is executed and gets reactions, then commits, but is
|
|
415
406
|
// never re-executed. This is possible when the derived is only executed once in the context
|
|
416
407
|
// of a new branch which happens before fork.commit() runs. In this case, the derived still has
|
|
417
|
-
// UNINITIALIZED as its value, and then when it's
|
|
408
|
+
// UNINITIALIZED as its value, and then when it's losing its reactions we need to ensure it stays
|
|
418
409
|
// DIRTY so it is reexecuted once someone wants its value again.
|
|
419
410
|
if (derived.v !== UNINITIALIZED) {
|
|
420
411
|
update_derived_status(derived);
|
|
@@ -466,10 +457,8 @@ export function update_effect(effect) {
|
|
|
466
457
|
set_signal_status(effect, CLEAN);
|
|
467
458
|
|
|
468
459
|
var previous_effect = active_effect;
|
|
469
|
-
var was_updating_effect = is_updating_effect;
|
|
470
460
|
|
|
471
461
|
active_effect = effect;
|
|
472
|
-
is_updating_effect = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0; // Branch/root effects are not reactive contexts
|
|
473
462
|
|
|
474
463
|
if (DEV) {
|
|
475
464
|
var previous_component_fn = dev_current_component_function;
|
|
@@ -502,7 +491,6 @@ export function update_effect(effect) {
|
|
|
502
491
|
}
|
|
503
492
|
}
|
|
504
493
|
} finally {
|
|
505
|
-
is_updating_effect = was_updating_effect;
|
|
506
494
|
active_effect = previous_effect;
|
|
507
495
|
|
|
508
496
|
if (DEV) {
|
|
@@ -684,13 +672,12 @@ export function get(signal) {
|
|
|
684
672
|
return value;
|
|
685
673
|
}
|
|
686
674
|
|
|
687
|
-
// connect disconnected deriveds
|
|
688
|
-
// or inside another derived that is already connected
|
|
675
|
+
// connect disconnected deriveds when reading them inside a connected reaction
|
|
689
676
|
var should_connect =
|
|
690
677
|
(derived.f & CONNECTED) === 0 &&
|
|
691
678
|
!untracking &&
|
|
692
679
|
active_reaction !== null &&
|
|
693
|
-
(
|
|
680
|
+
(active_reaction.f & CONNECTED) !== 0;
|
|
694
681
|
|
|
695
682
|
var is_new = (derived.f & REACTION_RAN) === 0;
|
|
696
683
|
|
|
@@ -732,7 +719,13 @@ function reconnect(derived) {
|
|
|
732
719
|
if (derived.deps === null) return;
|
|
733
720
|
|
|
734
721
|
for (const dep of derived.deps) {
|
|
735
|
-
|
|
722
|
+
var reactions = dep.reactions;
|
|
723
|
+
|
|
724
|
+
if (reactions === null) {
|
|
725
|
+
dep.reactions = [derived];
|
|
726
|
+
} else if (!includes.call(reactions, derived)) {
|
|
727
|
+
reactions.push(derived);
|
|
728
|
+
}
|
|
736
729
|
|
|
737
730
|
if ((dep.f & DERIVED) !== 0 && (dep.f & CONNECTED) === 0) {
|
|
738
731
|
unfreeze_derived_effects(/** @type {Derived} */ (dep));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @import { SSRContext } from '#server' */
|
|
2
2
|
import { DEV } from 'esm-env';
|
|
3
3
|
import { create_context, get_or_init_context_map } from '../shared/context.js';
|
|
4
|
+
import * as e from './errors.js';
|
|
4
5
|
|
|
5
6
|
/** @type {SSRContext | null} */
|
|
6
7
|
export var ssr_context = null;
|
|
@@ -40,7 +41,13 @@ export function getContext(key) {
|
|
|
40
41
|
* @returns {T}
|
|
41
42
|
*/
|
|
42
43
|
export function setContext(key, context) {
|
|
43
|
-
get_or_init_context_map(ssr_context, 'setContext')
|
|
44
|
+
const context_map = get_or_init_context_map(ssr_context, 'setContext');
|
|
45
|
+
|
|
46
|
+
if (/** @type {SSRContext} */ (ssr_context).i) {
|
|
47
|
+
e.set_context_after_init();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
context_map.set(key, context);
|
|
44
51
|
return context;
|
|
45
52
|
}
|
|
46
53
|
|
|
@@ -61,7 +68,7 @@ export function getAllContexts() {
|
|
|
61
68
|
* @param {Function} [fn]
|
|
62
69
|
*/
|
|
63
70
|
export function push(fn) {
|
|
64
|
-
ssr_context = { p: ssr_context, c: null, r: null };
|
|
71
|
+
ssr_context = { p: ssr_context, c: null, r: null, i: false };
|
|
65
72
|
|
|
66
73
|
if (DEV) {
|
|
67
74
|
ssr_context.function = fn;
|
|
@@ -146,8 +146,10 @@ export function attributes(attrs, css_hash, classes, styles, flags = 0) {
|
|
|
146
146
|
const is_html = (flags & ELEMENT_IS_NAMESPACED) === 0;
|
|
147
147
|
const lowercase = (flags & ELEMENT_PRESERVE_ATTRIBUTE_CASE) === 0;
|
|
148
148
|
const is_input = (flags & ELEMENT_IS_INPUT) !== 0;
|
|
149
|
+
const names = Object.keys(attrs);
|
|
149
150
|
|
|
150
|
-
for (
|
|
151
|
+
outer: for (let i = 0; i < names.length; i++) {
|
|
152
|
+
name = names[i];
|
|
151
153
|
// omit functions, internal svelte properties and invalid attribute names
|
|
152
154
|
if (typeof attrs[name] === 'function') continue;
|
|
153
155
|
if (name[0] === '$' && name[1] === '$') continue; // faster than name.startsWith('$$')
|
|
@@ -163,8 +165,13 @@ export function attributes(attrs, css_hash, classes, styles, flags = 0) {
|
|
|
163
165
|
|
|
164
166
|
if (is_input) {
|
|
165
167
|
if (name === 'defaultvalue' || name === 'defaultchecked') {
|
|
168
|
+
// value/checked takes precedence over defaultValue/defaultChecked
|
|
166
169
|
name = name === 'defaultvalue' ? 'value' : 'checked';
|
|
167
|
-
if (attrs
|
|
170
|
+
if (name in attrs) continue;
|
|
171
|
+
// We're checking prior entries as well because "name in attrs" is not enough as the attributes may have different casing
|
|
172
|
+
for (let j = 0; j < names.length; j++) {
|
|
173
|
+
if (names[j].toLowerCase() === name) continue outer;
|
|
174
|
+
}
|
|
168
175
|
}
|
|
169
176
|
}
|
|
170
177
|
|