svelte 5.56.9 → 5.57.0

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 (94) hide show
  1. package/compiler/index.js +1 -1
  2. package/elements.d.ts +3 -0
  3. package/package.json +3 -3
  4. package/src/compiler/migrate/index.js +6 -1
  5. package/src/compiler/phases/1-parse/acorn.js +1 -1
  6. package/src/compiler/phases/1-parse/read/style.js +16 -6
  7. package/src/compiler/phases/1-parse/utils/html.js +12 -7
  8. package/src/compiler/phases/2-analyze/css/css-prune.js +10 -0
  9. package/src/compiler/phases/2-analyze/index.js +45 -18
  10. package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +10 -4
  11. package/src/compiler/phases/2-analyze/visitors/CallExpression.js +1 -1
  12. package/src/compiler/phases/2-analyze/visitors/DeclarationTag.js +2 -1
  13. package/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +10 -10
  14. package/src/compiler/phases/2-analyze/visitors/SvelteBody.js +4 -4
  15. package/src/compiler/phases/2-analyze/visitors/SvelteDocument.js +4 -4
  16. package/src/compiler/phases/2-analyze/visitors/SvelteWindow.js +4 -4
  17. package/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js +14 -2
  18. package/src/compiler/phases/2-analyze/visitors/shared/element.js +2 -8
  19. package/src/compiler/phases/2-analyze/visitors/shared/utils.js +23 -1
  20. package/src/compiler/phases/3-transform/client/transform-client.js +2 -2
  21. package/src/compiler/phases/3-transform/client/transform-template/index.js +6 -0
  22. package/src/compiler/phases/3-transform/client/utils.js +35 -8
  23. package/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +16 -3
  24. package/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +6 -6
  25. package/src/compiler/phases/3-transform/client/visitors/BindDirective.js +10 -13
  26. package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +19 -8
  27. package/src/compiler/phases/3-transform/client/visitors/DeclarationTag.js +3 -2
  28. package/src/compiler/phases/3-transform/client/visitors/EachBlock.js +4 -1
  29. package/src/compiler/phases/3-transform/client/visitors/Fragment.js +2 -7
  30. package/src/compiler/phases/3-transform/client/visitors/HtmlTag.js +2 -1
  31. package/src/compiler/phases/3-transform/client/visitors/IfBlock.js +2 -1
  32. package/src/compiler/phases/3-transform/client/visitors/KeyBlock.js +2 -1
  33. package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +119 -46
  34. package/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +2 -1
  35. package/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js +0 -1
  36. package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +11 -4
  37. package/src/compiler/phases/3-transform/client/visitors/shared/element.js +6 -1
  38. package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +1 -6
  39. package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +5 -5
  40. package/src/compiler/phases/3-transform/css/index.js +5 -1
  41. package/src/compiler/phases/3-transform/server/transform-server.js +20 -9
  42. package/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js +1 -1
  43. package/src/compiler/phases/3-transform/server/visitors/DeclarationTag.js +1 -1
  44. package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +8 -2
  45. package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +4 -4
  46. package/src/compiler/phases/3-transform/server/visitors/LabeledStatement.js +1 -0
  47. package/src/compiler/phases/3-transform/server/visitors/RegularElement.js +1 -1
  48. package/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +9 -4
  49. package/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +4 -2
  50. package/src/compiler/phases/3-transform/server/visitors/shared/element.js +53 -47
  51. package/src/compiler/phases/3-transform/server/visitors/shared/utils.js +31 -1
  52. package/src/compiler/phases/bindings.js +3 -1
  53. package/src/compiler/phases/nodes.js +4 -0
  54. package/src/compiler/print/index.js +95 -24
  55. package/src/easing/index.js +62 -0
  56. package/src/internal/client/constants.js +9 -0
  57. package/src/internal/client/context.js +33 -56
  58. package/src/internal/client/dev/hmr.js +15 -4
  59. package/src/internal/client/dom/blocks/boundary.js +21 -12
  60. package/src/internal/client/dom/blocks/css-props.js +3 -3
  61. package/src/internal/client/dom/blocks/slot.js +19 -0
  62. package/src/internal/client/dom/blocks/svelte-head.js +8 -0
  63. package/src/internal/client/dom/css.js +6 -1
  64. package/src/internal/client/dom/elements/attributes.js +37 -35
  65. package/src/internal/client/dom/elements/bindings/select.js +99 -9
  66. package/src/internal/client/dom/elements/bindings/this.js +7 -2
  67. package/src/internal/client/dom/elements/events.js +19 -1
  68. package/src/internal/client/dom/elements/transitions.js +81 -68
  69. package/src/internal/client/dom/operations.js +21 -1
  70. package/src/internal/client/error-handling.js +10 -2
  71. package/src/internal/client/index.js +9 -2
  72. package/src/internal/client/proxy.js +8 -3
  73. package/src/internal/client/reactivity/async.js +23 -3
  74. package/src/internal/client/reactivity/batch.js +1 -0
  75. package/src/internal/client/reactivity/effects.js +14 -1
  76. package/src/internal/client/reactivity/sources.js +7 -1
  77. package/src/internal/client/render.js +4 -8
  78. package/src/internal/client/runtime.js +45 -33
  79. package/src/internal/client/warnings.js +0 -11
  80. package/src/internal/server/abort-signal.js +8 -9
  81. package/src/internal/server/context.js +9 -48
  82. package/src/internal/server/index.js +1 -1
  83. package/src/internal/server/renderer.js +164 -52
  84. package/src/internal/shared/attributes.js +4 -2
  85. package/src/internal/shared/clone.js +2 -0
  86. package/src/internal/shared/context.js +53 -0
  87. package/src/internal/shared/errors.js +2 -2
  88. package/src/legacy/legacy-server.js +1 -1
  89. package/src/reactivity/map.js +22 -0
  90. package/src/reactivity/url.js +2 -0
  91. package/src/transition/index.js +18 -7
  92. package/src/version.js +1 -1
  93. package/types/index.d.ts +148 -46
  94. package/types/index.d.ts.map +2 -2
@@ -337,6 +337,7 @@ export function transition(flags, element, get_fn, get_params) {
337
337
  */
338
338
  function animate(element, options, counterpart, t2, on_begin, on_finish) {
339
339
  var is_intro = t2 === 1;
340
+ var aborted = false;
340
341
 
341
342
  if (is_function(options)) {
342
343
  // In the case of a deferred transition (such as `crossfade`), `option` will be
@@ -344,7 +345,6 @@ function animate(element, options, counterpart, t2, on_begin, on_finish) {
344
345
  // once the DOM has been updated...
345
346
  /** @type {Animation} */
346
347
  var a;
347
- var aborted = false;
348
348
 
349
349
  queue_micro_task(() => {
350
350
  if (aborted) return;
@@ -381,99 +381,112 @@ function animate(element, options, counterpart, t2, on_begin, on_finish) {
381
381
 
382
382
  const { delay = 0, css, tick, easing = linear } = options;
383
383
 
384
- var keyframes = [];
384
+ /** @type {globalThis.Animation} */
385
+ var animation;
385
386
 
386
- if (is_intro && counterpart === undefined) {
387
- if (tick) {
388
- tick(0, 1); // TODO put in nested effect, to avoid interleaved reads/writes?
389
- }
387
+ var get_t = () => 1 - t2;
390
388
 
391
- if (css) {
392
- var styles = css_to_keyframe(css(0, 1));
393
- keyframes.push(styles, styles);
394
- }
395
- }
389
+ // wait a microtask before applying the initial styles and creating the dummy animation,
390
+ // so that transitions created in the same batch (e.g. on nested elements) all measure
391
+ // the DOM first (#18421). this still happens before the next paint, so the element
392
+ // won't be rendered without styles applied (#14732)
393
+ queue_micro_task(() => {
394
+ if (aborted) return;
396
395
 
397
- var get_t = () => 1 - t2;
396
+ var keyframes = [];
398
397
 
399
- // create a dummy animation that lasts as long as the delay (but with whatever devtools
400
- // multiplier is in effect). in the common case that it is `0`, we keep it anyway so that
401
- // the CSS keyframes aren't created until the DOM is updated
402
- //
403
- // fill forwards to prevent the element from rendering without styles applied
404
- // see https://github.com/sveltejs/svelte/issues/14732
405
- var animation = element.animate(keyframes, { duration: delay, fill: 'forwards' });
398
+ if (is_intro && counterpart === undefined) {
399
+ if (tick) {
400
+ tick(0, 1); // TODO put in nested effect, to avoid interleaved reads/writes?
401
+ }
406
402
 
407
- animation.onfinish = () => {
408
- // remove dummy animation from the stack to prevent conflict with main animation
409
- animation.cancel();
403
+ if (css) {
404
+ var styles = css_to_keyframe(css(0, 1));
405
+ keyframes.push(styles, styles);
406
+ }
407
+ }
410
408
 
411
- on_begin();
409
+ // create a dummy animation that lasts as long as the delay (but with whatever devtools
410
+ // multiplier is in effect). in the common case that it is `0`, we keep it anyway so that
411
+ // the CSS keyframes aren't created until the DOM is updated
412
+ //
413
+ // fill forwards to prevent the element from rendering without styles applied
414
+ // see https://github.com/sveltejs/svelte/issues/14732
415
+ animation = element.animate(keyframes, { duration: delay, fill: 'forwards' });
412
416
 
413
- // for bidirectional transitions, we start from the current position,
414
- // rather than doing a full intro/outro
415
- var t1 = counterpart?.t() ?? 1 - t2;
416
- counterpart?.abort();
417
+ animation.onfinish = () => {
418
+ // remove dummy animation from the stack to prevent conflict with main animation
419
+ animation.cancel();
417
420
 
418
- var delta = t2 - t1;
419
- var duration = /** @type {number} */ (options.duration) * Math.abs(delta);
420
- var keyframes = [];
421
+ on_begin();
421
422
 
422
- if (duration > 0) {
423
- /**
424
- * Whether or not the CSS includes `overflow: hidden`, in which case we need to
425
- * add it as an inline style to work around a Safari <18 bug
426
- * TODO 6.0 remove this, if possible
427
- */
428
- var needs_overflow_hidden = false;
423
+ // for bidirectional transitions, we start from the current position,
424
+ // rather than doing a full intro/outro
425
+ var t1 = counterpart?.t() ?? 1 - t2;
426
+ counterpart?.abort();
429
427
 
430
- if (css) {
431
- var n = Math.ceil(duration / (1000 / 60)); // `n` must be an integer, or we risk missing the `t2` value
428
+ var delta = t2 - t1;
429
+ var duration = /** @type {number} */ (options.duration) * Math.abs(delta);
430
+ var keyframes = [];
431
+
432
+ if (duration > 0) {
433
+ /**
434
+ * Whether or not the CSS includes `overflow: hidden`, in which case we need to
435
+ * add it as an inline style to work around a Safari <18 bug
436
+ * TODO 6.0 remove this, if possible
437
+ */
438
+ var needs_overflow_hidden = false;
432
439
 
433
- for (var i = 0; i <= n; i += 1) {
434
- var t = t1 + delta * easing(i / n);
435
- var styles = css_to_keyframe(css(t, 1 - t));
436
- keyframes.push(styles);
440
+ if (css) {
441
+ var n = Math.ceil(duration / (1000 / 60)); // `n` must be an integer, or we risk missing the `t2` value
437
442
 
438
- needs_overflow_hidden ||= styles.overflow === 'hidden';
443
+ for (var i = 0; i <= n; i += 1) {
444
+ var t = t1 + delta * easing(i / n);
445
+ var styles = css_to_keyframe(css(t, 1 - t));
446
+ keyframes.push(styles);
447
+
448
+ needs_overflow_hidden ||= styles.overflow === 'hidden';
449
+ }
439
450
  }
440
- }
441
451
 
442
- if (needs_overflow_hidden) {
443
- /** @type {HTMLElement} */ (element).style.overflow = 'hidden';
444
- }
452
+ if (needs_overflow_hidden) {
453
+ /** @type {HTMLElement} */ (element).style.overflow = 'hidden';
454
+ }
445
455
 
446
- get_t = () => {
447
- var time = /** @type {number} */ (
448
- /** @type {globalThis.Animation} */ (animation).currentTime
449
- );
456
+ get_t = () => {
457
+ var time = /** @type {number} */ (
458
+ /** @type {globalThis.Animation} */ (animation).currentTime
459
+ );
450
460
 
451
- return t1 + delta * easing(time / duration);
452
- };
461
+ return t1 + delta * easing(time / duration);
462
+ };
453
463
 
454
- if (tick) {
455
- loop(() => {
456
- if (animation.playState !== 'running') return false;
464
+ if (tick) {
465
+ loop(() => {
466
+ if (animation.playState !== 'running') return false;
457
467
 
458
- var t = get_t();
459
- tick(t, 1 - t);
468
+ var t = get_t();
469
+ tick(t, 1 - t);
460
470
 
461
- return true;
462
- });
471
+ return true;
472
+ });
473
+ }
463
474
  }
464
- }
465
475
 
466
- animation = element.animate(keyframes, { duration, fill: 'forwards' });
476
+ animation = element.animate(keyframes, { duration, fill: 'forwards' });
467
477
 
468
- animation.onfinish = () => {
469
- get_t = () => t2;
470
- tick?.(t2, 1 - t2);
471
- on_finish();
478
+ animation.onfinish = () => {
479
+ get_t = () => t2;
480
+ tick?.(t2, 1 - t2);
481
+ on_finish();
482
+ };
472
483
  };
473
- };
484
+ });
474
485
 
475
486
  return {
476
487
  abort: () => {
488
+ aborted = true;
489
+
477
490
  if (animation) {
478
491
  animation.cancel();
479
492
  // This prevents memory leaks in Chromium
@@ -1,5 +1,5 @@
1
1
  /** @import { Effect, TemplateNode } from '#client' */
2
- import { hydrate_node, hydrating, set_hydrate_node } from './hydration.js';
2
+ import { hydrate_node, hydrating, reset, set_hydrate_node } from './hydration.js';
3
3
  import { DEV } from 'esm-env';
4
4
  import { init_array_prototype_warnings } from '../dev/equality.js';
5
5
  import { get_descriptor, is_extensible } from '../../shared/utils.js';
@@ -165,6 +165,26 @@ export function first_child(node, is_text = false) {
165
165
  return hydrate_node;
166
166
  }
167
167
 
168
+ /**
169
+ * `child`, for the very common case of an element with exactly one child. Resetting the
170
+ * hydration cursor is part of the same step, so the compiler doesn't have to emit a
171
+ * separate `reset` call for every `<p>{text}</p>` in an app.
172
+ * Don't mark this as side-effect-free, hydration needs to walk all nodes
173
+ * @param {TemplateNode} node
174
+ * @param {boolean} [is_text]
175
+ * @returns {TemplateNode | null}
176
+ */
177
+ export function only_child(node, is_text = false) {
178
+ if (!hydrating) {
179
+ return get_first_child(node);
180
+ }
181
+
182
+ var first = child(node, is_text);
183
+ reset(node);
184
+
185
+ return first;
186
+ }
187
+
168
188
  /**
169
189
  * Don't mark this as side-effect-free, hydration needs to walk all nodes
170
190
  * @param {TemplateNode} node
@@ -3,7 +3,14 @@
3
3
  import { DEV } from 'esm-env';
4
4
  import { FILENAME } from '../../constants.js';
5
5
  import { is_firefox } from './dom/operations.js';
6
- import { ERROR_VALUE, BOUNDARY_EFFECT, REACTION_RAN, EFFECT, DESTROYED } from './constants.js';
6
+ import {
7
+ ERROR_VALUE,
8
+ BOUNDARY_EFFECT,
9
+ REACTION_RAN,
10
+ EFFECT,
11
+ DESTROYED,
12
+ DESTROYING
13
+ } from './constants.js';
7
14
  import { define_property, get_descriptor } from '../shared/utils.js';
8
15
  import { active_effect, active_reaction } from './runtime.js';
9
16
 
@@ -50,7 +57,8 @@ export function invoke_error_boundary(error, effect) {
50
57
  }
51
58
 
52
59
  while (effect !== null) {
53
- if ((effect.f & BOUNDARY_EFFECT) !== 0) {
60
+ // Skip boundaries that are destroyed/destroying and cannot meaningfully handle the error.
61
+ if ((effect.f & BOUNDARY_EFFECT) !== 0 && (effect.f & (DESTROYED | DESTROYING)) === 0) {
54
62
  if ((effect.f & REACTION_RAN) === 0) {
55
63
  // we are still creating the boundary effect
56
64
  throw error;
@@ -33,7 +33,6 @@ export {
33
33
  set_xlink_attribute,
34
34
  set_value,
35
35
  set_checked,
36
- set_selected,
37
36
  set_default_checked,
38
37
  set_default_value,
39
38
  CLASS,
@@ -62,7 +61,13 @@ export {
62
61
  } from './dom/elements/bindings/media.js';
63
62
  export { bind_online } from './dom/elements/bindings/navigator.js';
64
63
  export { bind_prop } from './dom/elements/bindings/props.js';
65
- export { bind_select_value, init_select, select_option } from './dom/elements/bindings/select.js';
64
+ export {
65
+ bind_select_value,
66
+ init_select,
67
+ select_option,
68
+ set_selected,
69
+ set_default_select_value
70
+ } from './dom/elements/bindings/select.js';
66
71
  export { bind_element_size, bind_resize_observer } from './dom/elements/bindings/size.js';
67
72
  export { bind_this } from './dom/elements/bindings/this.js';
68
73
  export {
@@ -103,6 +108,7 @@ export {
103
108
  run,
104
109
  save,
105
110
  track_reactivity_loss,
111
+ unsave,
106
112
  run_after_blockers,
107
113
  wait
108
114
  } from './reactivity/async.js';
@@ -163,6 +169,7 @@ export { proxy } from './proxy.js';
163
169
  export { create_custom_element } from './dom/elements/custom-element.js';
164
170
  export {
165
171
  child,
172
+ only_child,
166
173
  first_child,
167
174
  sibling,
168
175
  $window as window,
@@ -22,7 +22,7 @@ import {
22
22
  flush_eager_effects,
23
23
  set_eager_effects_deferred
24
24
  } from './reactivity/sources.js';
25
- import { PROXY_PATH_SYMBOL, STATE_SYMBOL } from '#client/constants';
25
+ import { COMPONENT_SYMBOL, PROXY_PATH_SYMBOL, STATE_SYMBOL } from '#client/constants';
26
26
  import { UNINITIALIZED } from '../../constants.js';
27
27
  import * as e from './errors.js';
28
28
  import { tag } from './dev/tracing.js';
@@ -38,8 +38,13 @@ const regex_is_valid_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/;
38
38
  * @returns {T}
39
39
  */
40
40
  export function proxy(value) {
41
- // if non-proxyable, or is already a proxy, return `value`
42
- if (typeof value !== 'object' || value === null || STATE_SYMBOL in value) {
41
+ // if non-proxyable, a component instance, or already a proxy, return `value`
42
+ if (
43
+ typeof value !== 'object' ||
44
+ value === null ||
45
+ STATE_SYMBOL in value ||
46
+ COMPONENT_SYMBOL in value
47
+ ) {
43
48
  return value;
44
49
  }
45
50
 
@@ -8,7 +8,6 @@ import {
8
8
  set_component_context,
9
9
  set_dev_stack
10
10
  } from '../context.js';
11
- import { Boundary } from '../dom/blocks/boundary.js';
12
11
  import { invoke_error_boundary } from '../error-handling.js';
13
12
  import {
14
13
  active_effect,
@@ -25,7 +24,6 @@ import {
25
24
  set_reactivity_loss_tracker
26
25
  } from './deriveds.js';
27
26
  import { aborted } from './effects.js';
28
- import { queue_micro_task } from '../dom/task.js';
29
27
 
30
28
  /**
31
29
  * @param {Blocker[]} blockers
@@ -156,6 +154,9 @@ export function capture() {
156
154
  };
157
155
  }
158
156
 
157
+ /** `true` between a `save` thunk restoring a context and the end of that synchronous segment */
158
+ var restored = false;
159
+
159
160
  /**
160
161
  * Wraps an `await` expression in such a way that the effect context that was
161
162
  * active before the expression evaluated can be reapplied afterwards —
@@ -166,15 +167,32 @@ export function capture() {
166
167
  */
167
168
  export async function save(promise) {
168
169
  var restore = capture();
170
+ // the context restored by an earlier `save` in this expression must not
171
+ // outlive the synchronous segment that is about to end at this `await`
172
+ unsave();
169
173
  var value = await promise;
170
174
 
171
175
  return () => {
172
176
  restore();
173
- queue_micro_task(unset_context);
177
+ restored = true;
174
178
  return value;
175
179
  };
176
180
  }
177
181
 
182
+ /**
183
+ * Unset the context if a `save` thunk restored it in the current synchronous segment,
184
+ * so that a foreign microtask can never run inside a restored reaction context.
185
+ * Called at every suspension point, and at the end of async expression bodies —
186
+ * `async () => (await $.save(a))().b` becomes `async () => { try { return (await $.save(a))().b; } finally { $.unsave(); } }`
187
+ * @template T
188
+ * @param {T} [value]
189
+ * @returns {T}
190
+ */
191
+ export function unsave(value) {
192
+ if (restored) unset_context();
193
+ return /** @type {T} */ (value);
194
+ }
195
+
178
196
  /**
179
197
  * Reset `current_async_effect` after the `promise` resolves, so
180
198
  * that we can emit `await_reactivity_loss` warnings
@@ -183,6 +201,7 @@ export async function save(promise) {
183
201
  * @returns {Promise<() => T>}
184
202
  */
185
203
  export async function track_reactivity_loss(promise) {
204
+ unsave();
186
205
  var previous_reactivity_loss_tracker = reactivity_loss_tracker;
187
206
  // Ensure that unrelated reads after an async operation is kicked off don't cause false positives
188
207
  queueMicrotask(() => {
@@ -269,6 +288,7 @@ export async function* for_await_track_reactivity_loss(iterable) {
269
288
  }
270
289
 
271
290
  export function unset_context(deactivate_batch = true) {
291
+ restored = false;
272
292
  set_active_effect(null);
273
293
  set_active_reaction(null);
274
294
  set_component_context(null);
@@ -420,6 +420,7 @@ export class Batch {
420
420
  }
421
421
 
422
422
  if (next_batch !== null) {
423
+ old_values.clear();
423
424
  next_batch.#process();
424
425
  }
425
426
  }
@@ -34,8 +34,10 @@ import {
34
34
  ASYNC,
35
35
  CONNECTED,
36
36
  MANAGED_EFFECT,
37
- DESTROYING
37
+ DESTROYING,
38
+ PAUSED
38
39
  } from '#client/constants';
40
+ import { invoke_error_boundary } from '../error-handling.js';
39
41
  import * as e from '../errors.js';
40
42
  import { DEV } from 'esm-env';
41
43
  import { define_property } from '../../shared/utils.js';
@@ -449,6 +451,11 @@ export function execute_effect_teardown(effect) {
449
451
  set_active_reaction(null);
450
452
  try {
451
453
  teardown.call(null);
454
+ } catch (error) {
455
+ // Route teardown errors through the boundary system so that a live
456
+ // ancestor <svelte:boundary> can handle them. Boundaries that are
457
+ // themselves mid-teardown are skipped by invoke_error_boundary.
458
+ invoke_error_boundary(error, effect.parent);
452
459
  } finally {
453
460
  set_is_destroying_effect(previously_destroying_effect);
454
461
  set_active_reaction(previous_reaction);
@@ -610,6 +617,7 @@ export function pause_effect(effect, callback, destroy = true) {
610
617
  /** @type {TransitionManager[]} */
611
618
  var transitions = [];
612
619
 
620
+ effect.f |= PAUSED;
613
621
  pause_children(effect, transitions, true);
614
622
 
615
623
  var fn = () => {
@@ -677,6 +685,7 @@ function pause_children(effect, transitions, local) {
677
685
  * @param {Effect} effect
678
686
  */
679
687
  export function resume_effect(effect) {
688
+ effect.f &= ~PAUSED;
680
689
  resume_children(effect, true);
681
690
  }
682
691
 
@@ -685,6 +694,10 @@ export function resume_effect(effect) {
685
694
  * @param {boolean} local
686
695
  */
687
696
  function resume_children(effect, local) {
697
+ // this subtree was paused for its own reasons (e.g. a block whose condition
698
+ // is still false) — its controller will resume or destroy it
699
+ if ((effect.f & PAUSED) !== 0) return;
700
+
688
701
  if ((effect.f & INERT) === 0) return;
689
702
  effect.f ^= INERT;
690
703
 
@@ -180,7 +180,13 @@ export function set(source, value, should_proxy = false) {
180
180
  */
181
181
  export function internal_set(source, value, updated_during_traversal = null) {
182
182
  if (!source.equals(value)) {
183
- old_values.set(source, is_destroying_effect ? value : source.v);
183
+ if (is_destroying_effect) {
184
+ old_values.set(source, value);
185
+ } else if (!old_values.has(source)) {
186
+ // only record the value from before the first write in this flush, otherwise a
187
+ // teardown would see the value from before whichever write happened to be last
188
+ old_values.set(source, source.v);
189
+ }
184
190
 
185
191
  var batch = Batch.ensure();
186
192
  batch.capture(source, value);
@@ -10,7 +10,7 @@ import {
10
10
  } from './dom/operations.js';
11
11
  import { HYDRATION_END, HYDRATION_ERROR, HYDRATION_START } from '../../constants.js';
12
12
  import { active_effect } from './runtime.js';
13
- import { push, pop, component_context } from './context.js';
13
+ import { push, pop, component_context, mark_as_component } from './context.js';
14
14
  import { component_root } from './reactivity/effects.js';
15
15
  import { hydrate_node, hydrating, set_hydrate_node, set_hydrating } from './dom/hydration.js';
16
16
  import { array_from } from '../shared/utils.js';
@@ -23,7 +23,7 @@ import * as w from './warnings.js';
23
23
  import * as e from './errors.js';
24
24
  import { assign_nodes } from './dom/template.js';
25
25
  import { is_passive_event } from '../../utils.js';
26
- import { COMMENT_NODE, STATE_SYMBOL, TEXT_CACHE } from './constants.js';
26
+ import { COMMENT_NODE, TEXT_CACHE } from './constants.js';
27
27
  import { boundary } from './dom/blocks/boundary.js';
28
28
 
29
29
  /**
@@ -193,7 +193,7 @@ function _mount(
193
193
 
194
194
  should_intro = intro;
195
195
  // @ts-expect-error the public typings are not what the actual function looks like
196
- component = Component(anchor_node, props) || {};
196
+ component = Component(anchor_node, props) || mark_as_component();
197
197
  should_intro = true;
198
198
 
199
199
  if (hydrating) {
@@ -323,11 +323,7 @@ export function unmount(component, options) {
323
323
  }
324
324
 
325
325
  if (DEV) {
326
- if (STATE_SYMBOL in component) {
327
- w.state_proxy_unmount();
328
- } else {
329
- w.lifecycle_double_unmount();
330
- }
326
+ w.lifecycle_double_unmount();
331
327
  }
332
328
 
333
329
  return Promise.resolve();
@@ -133,7 +133,7 @@ export function set_untracked_writes(value) {
133
133
  **/
134
134
  export let write_version = 1;
135
135
 
136
- /** @type {number} Used to version each read of a source of derived to avoid duplicating depedencies inside a reaction */
136
+ /** @type {number} Used to version each read of a source of derived to avoid duplicating dependencies inside a reaction */
137
137
  let read_version = 0;
138
138
 
139
139
  export let update_version = read_version;
@@ -258,37 +258,7 @@ export function update_reaction(reaction) {
258
258
  var fn = /** @type {Function} */ (reaction.fn);
259
259
  var result = fn();
260
260
  reaction.f |= REACTION_RAN;
261
- var deps = reaction.deps;
262
-
263
- // Don't remove reactions during fork;
264
- // they must remain for when fork is discarded
265
- var is_fork = current_batch?.is_fork;
266
-
267
- if (new_deps !== null) {
268
- var i;
269
-
270
- if (!is_fork) {
271
- remove_reactions(reaction, skipped_deps);
272
- }
273
-
274
- if (deps !== null && skipped_deps > 0) {
275
- deps.length = skipped_deps + new_deps.length;
276
- for (i = 0; i < new_deps.length; i++) {
277
- deps[skipped_deps + i] = new_deps[i];
278
- }
279
- } else {
280
- reaction.deps = deps = new_deps;
281
- }
282
-
283
- if (effect_tracking() && (reaction.f & CONNECTED) !== 0) {
284
- for (i = skipped_deps; i < deps.length; i++) {
285
- (deps[i].reactions ??= []).push(reaction);
286
- }
287
- }
288
- } else if (!is_fork && deps !== null && skipped_deps < deps.length) {
289
- remove_reactions(reaction, skipped_deps);
290
- deps.length = skipped_deps;
291
- }
261
+ var deps = update_dependencies(reaction);
292
262
 
293
263
  // If we're inside an effect and we have untracked writes, then we need to
294
264
  // ensure that if any of those untracked writes result in re-invalidation
@@ -300,7 +270,7 @@ export function update_reaction(reaction) {
300
270
  deps !== null &&
301
271
  (reaction.f & (DERIVED | MAYBE_DIRTY | DIRTY)) === 0
302
272
  ) {
303
- for (i = 0; i < /** @type {Source[]} */ (untracked_writes).length; i++) {
273
+ for (var i = 0; i < /** @type {Source[]} */ (untracked_writes).length; i++) {
304
274
  schedule_possible_effect_self_invalidation(
305
275
  untracked_writes[i],
306
276
  /** @type {Effect} */ (reaction)
@@ -344,6 +314,9 @@ export function update_reaction(reaction) {
344
314
 
345
315
  return result;
346
316
  } catch (error) {
317
+ // still commit the deps read before the throw, otherwise deriveds connected by this run keep no reader and the reaction never re-runs when they change
318
+ update_dependencies(reaction);
319
+
347
320
  return handle_error(error);
348
321
  } finally {
349
322
  reaction.f ^= REACTION_IS_UPDATING;
@@ -358,6 +331,45 @@ export function update_reaction(reaction) {
358
331
  }
359
332
  }
360
333
 
334
+ /**
335
+ * @param {Reaction} reaction
336
+ */
337
+ function update_dependencies(reaction) {
338
+ var deps = reaction.deps;
339
+
340
+ // Don't remove reactions during fork;
341
+ // they must remain for when fork is discarded
342
+ var is_fork = current_batch?.is_fork;
343
+
344
+ if (new_deps !== null) {
345
+ var i;
346
+
347
+ if (!is_fork) {
348
+ remove_reactions(reaction, skipped_deps);
349
+ }
350
+
351
+ if (deps !== null && skipped_deps > 0) {
352
+ deps.length = skipped_deps + new_deps.length;
353
+ for (i = 0; i < new_deps.length; i++) {
354
+ deps[skipped_deps + i] = new_deps[i];
355
+ }
356
+ } else {
357
+ reaction.deps = deps = new_deps;
358
+ }
359
+
360
+ if (effect_tracking() && (reaction.f & CONNECTED) !== 0) {
361
+ for (i = skipped_deps; i < deps.length; i++) {
362
+ (deps[i].reactions ??= []).push(reaction);
363
+ }
364
+ }
365
+ } else if (!is_fork && deps !== null && skipped_deps < deps.length) {
366
+ remove_reactions(reaction, skipped_deps);
367
+ deps.length = skipped_deps;
368
+ }
369
+
370
+ return deps;
371
+ }
372
+
361
373
  /**
362
374
  * @template V
363
375
  * @param {Reaction} signal
@@ -247,17 +247,6 @@ export function state_proxy_equality_mismatch(operator) {
247
247
  }
248
248
  }
249
249
 
250
- /**
251
- * Tried to unmount a state proxy, rather than a component
252
- */
253
- export function state_proxy_unmount() {
254
- if (DEV) {
255
- console.warn(`%c[svelte] state_proxy_unmount\n%cTried to unmount a state proxy, rather than a component\nhttps://svelte.dev/e/state_proxy_unmount`, bold, normal);
256
- } else {
257
- console.warn(`https://svelte.dev/e/state_proxy_unmount`);
258
- }
259
- }
260
-
261
250
  /**
262
251
  * A `<svelte:boundary>` `reset` function only resets the boundary the first time it is called
263
252
  */
@@ -1,13 +1,12 @@
1
- import { STALE_REACTION } from '#client/constants';
1
+ import { ssr_context } from './context.js';
2
2
 
3
- /** @type {AbortController | null} */
4
- let controller = null;
3
+ export function getAbortSignal() {
4
+ let context = ssr_context;
5
5
 
6
- export function abort() {
7
- controller?.abort(STALE_REACTION);
8
- controller = null;
9
- }
6
+ while (context !== null) {
7
+ if (context.r !== null) return context.r.global.get_abort_signal();
8
+ context = context.p;
9
+ }
10
10
 
11
- export function getAbortSignal() {
12
- return (controller ??= new AbortController()).signal;
11
+ return new AbortController().signal;
13
12
  }