svelte 5.56.10 → 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 (81) 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/phases/1-parse/acorn.js +1 -1
  5. package/src/compiler/phases/1-parse/utils/html.js +12 -7
  6. package/src/compiler/phases/2-analyze/css/css-prune.js +10 -0
  7. package/src/compiler/phases/2-analyze/index.js +26 -3
  8. package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +10 -4
  9. package/src/compiler/phases/2-analyze/visitors/CallExpression.js +1 -1
  10. package/src/compiler/phases/2-analyze/visitors/DeclarationTag.js +2 -1
  11. package/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +10 -10
  12. package/src/compiler/phases/2-analyze/visitors/SvelteBody.js +4 -4
  13. package/src/compiler/phases/2-analyze/visitors/SvelteDocument.js +4 -4
  14. package/src/compiler/phases/2-analyze/visitors/SvelteWindow.js +4 -4
  15. package/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js +14 -2
  16. package/src/compiler/phases/2-analyze/visitors/shared/element.js +2 -8
  17. package/src/compiler/phases/2-analyze/visitors/shared/utils.js +23 -1
  18. package/src/compiler/phases/3-transform/client/transform-client.js +2 -2
  19. package/src/compiler/phases/3-transform/client/transform-template/index.js +6 -0
  20. package/src/compiler/phases/3-transform/client/utils.js +35 -8
  21. package/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +6 -6
  22. package/src/compiler/phases/3-transform/client/visitors/BindDirective.js +10 -13
  23. package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +19 -8
  24. package/src/compiler/phases/3-transform/client/visitors/DeclarationTag.js +3 -2
  25. package/src/compiler/phases/3-transform/client/visitors/EachBlock.js +4 -1
  26. package/src/compiler/phases/3-transform/client/visitors/Fragment.js +2 -7
  27. package/src/compiler/phases/3-transform/client/visitors/HtmlTag.js +2 -1
  28. package/src/compiler/phases/3-transform/client/visitors/IfBlock.js +2 -1
  29. package/src/compiler/phases/3-transform/client/visitors/KeyBlock.js +2 -1
  30. package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +106 -44
  31. package/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +2 -1
  32. package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +11 -4
  33. package/src/compiler/phases/3-transform/client/visitors/shared/element.js +6 -1
  34. package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +1 -6
  35. package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +5 -5
  36. package/src/compiler/phases/3-transform/server/transform-server.js +20 -9
  37. package/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js +1 -1
  38. package/src/compiler/phases/3-transform/server/visitors/DeclarationTag.js +1 -1
  39. package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +8 -2
  40. package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +4 -4
  41. package/src/compiler/phases/3-transform/server/visitors/RegularElement.js +1 -1
  42. package/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +9 -4
  43. package/src/compiler/phases/3-transform/server/visitors/shared/element.js +5 -4
  44. package/src/compiler/phases/3-transform/server/visitors/shared/utils.js +30 -0
  45. package/src/compiler/phases/bindings.js +3 -1
  46. package/src/compiler/phases/nodes.js +4 -0
  47. package/src/compiler/print/index.js +21 -16
  48. package/src/easing/index.js +62 -0
  49. package/src/internal/client/constants.js +9 -0
  50. package/src/internal/client/context.js +15 -5
  51. package/src/internal/client/dev/hmr.js +15 -4
  52. package/src/internal/client/dom/blocks/boundary.js +21 -12
  53. package/src/internal/client/dom/blocks/slot.js +19 -0
  54. package/src/internal/client/dom/blocks/svelte-head.js +8 -0
  55. package/src/internal/client/dom/elements/attributes.js +35 -34
  56. package/src/internal/client/dom/elements/bindings/select.js +99 -9
  57. package/src/internal/client/dom/elements/bindings/this.js +7 -2
  58. package/src/internal/client/dom/elements/transitions.js +81 -68
  59. package/src/internal/client/dom/operations.js +21 -1
  60. package/src/internal/client/error-handling.js +10 -2
  61. package/src/internal/client/index.js +9 -2
  62. package/src/internal/client/proxy.js +8 -3
  63. package/src/internal/client/reactivity/async.js +23 -3
  64. package/src/internal/client/reactivity/effects.js +14 -1
  65. package/src/internal/client/render.js +4 -8
  66. package/src/internal/client/runtime.js +45 -33
  67. package/src/internal/client/warnings.js +0 -11
  68. package/src/internal/server/abort-signal.js +8 -9
  69. package/src/internal/server/context.js +2 -2
  70. package/src/internal/server/index.js +1 -1
  71. package/src/internal/server/renderer.js +163 -52
  72. package/src/internal/shared/attributes.js +2 -1
  73. package/src/internal/shared/clone.js +2 -0
  74. package/src/internal/shared/context.js +3 -2
  75. package/src/legacy/legacy-server.js +1 -1
  76. package/src/reactivity/map.js +22 -0
  77. package/src/reactivity/url.js +2 -0
  78. package/src/transition/index.js +18 -7
  79. package/src/version.js +1 -1
  80. package/types/index.d.ts +135 -37
  81. package/types/index.d.ts.map +2 -2
@@ -26,7 +26,12 @@ import { set_class } from './class.js';
26
26
  import { set_style } from './style.js';
27
27
  import { ATTACHMENT_KEY, NAMESPACE_HTML, UNINITIALIZED } from '../../../../constants.js';
28
28
  import { branch, destroy_effect, effect, managed } from '../../reactivity/effects.js';
29
- import { init_select, select_option } from './bindings/select.js';
29
+ import {
30
+ init_select,
31
+ select_option,
32
+ set_default_select_value,
33
+ set_selected
34
+ } from './bindings/select.js';
30
35
  import { flatten } from '../../reactivity/async.js';
31
36
 
32
37
  export const CLASS = Symbol('class');
@@ -122,25 +127,6 @@ export function set_checked(element, checked) {
122
127
  element.checked = checked;
123
128
  }
124
129
 
125
- /**
126
- * Sets the `selected` attribute on an `option` element.
127
- * Not set through the property because that doesn't reflect to the DOM,
128
- * which means it wouldn't be taken into account when a form is reset.
129
- * @param {HTMLOptionElement} element
130
- * @param {boolean} selected
131
- */
132
- export function set_selected(element, selected) {
133
- if (selected) {
134
- // The selected option could've changed via user selection, and
135
- // setting the value without this check would set it back.
136
- if (!element.hasAttribute('selected')) {
137
- element.setAttribute('selected', '');
138
- }
139
- } else {
140
- element.removeAttribute('selected');
141
- }
142
- }
143
-
144
130
  /**
145
131
  * Applies the default checked property without influencing the current checked property.
146
132
  * @param {HTMLInputElement} element
@@ -201,7 +187,7 @@ export function set_attribute(element, attribute, value, skip_warning) {
201
187
 
202
188
  if (value == null) {
203
189
  element.removeAttribute(attribute);
204
- } else if (typeof value !== 'string' && get_setters(element).includes(attribute)) {
190
+ } else if (typeof value !== 'string' && get_setters(element).has(attribute)) {
205
191
  // @ts-ignore
206
192
  element[attribute] = value;
207
193
  } else {
@@ -252,7 +238,7 @@ export function set_custom_element_data(node, prop, value) {
252
238
  // customElements may not be available in browser extension contexts
253
239
  !customElements ||
254
240
  customElements.get(node.getAttribute('is') || node.nodeName.toLowerCase())
255
- ? get_setters(node).includes(prop)
241
+ ? get_setters(node).has(prop)
256
242
  : value && typeof value === 'object')
257
243
  ) {
258
244
  // @ts-expect-error
@@ -291,11 +277,8 @@ function set_attributes(
291
277
  skip_warning = false
292
278
  ) {
293
279
  if (hydrating && should_remove_defaults && element.nodeName === INPUT_TAG) {
294
- var input = /** @type {HTMLInputElement} */ (element);
295
- var attribute = input.type === 'checkbox' ? 'defaultChecked' : 'defaultValue';
296
-
297
- if (!(attribute in next)) {
298
- remove_input_defaults(input);
280
+ if (!('defaultValue' in next || 'defaultChecked' in next)) {
281
+ remove_input_defaults(/** @type {HTMLInputElement} */ (element));
299
282
  }
300
283
  }
301
284
 
@@ -313,6 +296,7 @@ function set_attributes(
313
296
 
314
297
  var current = prev || {};
315
298
  var is_option_element = element.nodeName === OPTION_TAG;
299
+ var is_select_element = element.nodeName === SELECT_TAG;
316
300
 
317
301
  for (var key in prev) {
318
302
  // don't null our internal $$onX listeners
@@ -449,6 +433,9 @@ function set_attributes(
449
433
 
450
434
  var is_default = name === 'defaultValue' || name === 'defaultChecked';
451
435
 
436
+ // A select's default value is represented by selected options, not a property.
437
+ if (is_select_element && name === 'defaultValue') continue;
438
+
452
439
  if (value == null && !is_custom_element && !is_default) {
453
440
  attributes[key] = null;
454
441
 
@@ -473,7 +460,7 @@ function set_attributes(
473
460
  }
474
461
  } else if (
475
462
  is_default ||
476
- (setters.includes(name) && (is_custom_element || typeof value !== 'string'))
463
+ ((is_custom_element || typeof value !== 'string') && setters.has(name))
477
464
  ) {
478
465
  // @ts-ignore
479
466
  element[name] = value;
@@ -534,8 +521,16 @@ export function attribute_effect(
534
521
  skip_warning
535
522
  );
536
523
 
537
- if (inited && is_select && 'value' in next) {
538
- select_option(/** @type {HTMLSelectElement} */ (element), next.value);
524
+ if (inited && is_select) {
525
+ var select = /** @type {HTMLSelectElement} */ (element);
526
+
527
+ if ('defaultValue' in next) {
528
+ set_default_select_value(select, next.defaultValue);
529
+ }
530
+
531
+ if ('value' in next) {
532
+ select_option(select, next.value);
533
+ }
539
534
  }
540
535
 
541
536
  for (let symbol of Object.getOwnPropertySymbols(effects)) {
@@ -560,7 +555,13 @@ export function attribute_effect(
560
555
  var select = /** @type {HTMLSelectElement} */ (element);
561
556
 
562
557
  effect(() => {
563
- select_option(select, /** @type {Record<string | symbol, any>} */ (prev).value, true);
558
+ var attrs = /** @type {Record<string | symbol, any>} */ (prev);
559
+
560
+ if ('defaultValue' in attrs) {
561
+ set_default_select_value(select, attrs.defaultValue);
562
+ }
563
+
564
+ select_option(select, attrs.value, true);
564
565
  init_select(select);
565
566
  });
566
567
  }
@@ -582,7 +583,7 @@ function get_attributes(element) {
582
583
  );
583
584
  }
584
585
 
585
- /** @type {Map<string, string[]>} */
586
+ /** @type {Map<string, Set<string>>} */
586
587
  var setters_cache = new Map();
587
588
 
588
589
  /** @param {Element} element */
@@ -590,7 +591,7 @@ function get_setters(element) {
590
591
  var cache_key = element.getAttribute('is') || element.nodeName;
591
592
  var setters = setters_cache.get(cache_key);
592
593
  if (setters) return setters;
593
- setters_cache.set(cache_key, (setters = []));
594
+ setters_cache.set(cache_key, (setters = new Set()));
594
595
 
595
596
  var descriptors;
596
597
  var proto = element; // In the case of custom elements there might be setters on the instance
@@ -609,7 +610,7 @@ function get_setters(element) {
609
610
  key !== 'textContent' &&
610
611
  key !== 'innerText'
611
612
  ) {
612
- setters.push(key);
613
+ setters.add(key);
613
614
  }
614
615
  }
615
616
 
@@ -6,6 +6,71 @@ import * as w from '../../../warnings.js';
6
6
  import { Batch, current_batch, previous_batch } from '../../../reactivity/batch.js';
7
7
  import { async_mode_flag } from '../../../../flags/index.js';
8
8
 
9
+ /**
10
+ * Sets the `selected` attribute on an option so form reset can restore it.
11
+ * @param {HTMLOptionElement} option
12
+ * @param {boolean} selected
13
+ */
14
+ export function set_selected(option, selected) {
15
+ if (selected) {
16
+ if (!option.hasAttribute('selected')) option.setAttribute('selected', '');
17
+ } else {
18
+ option.removeAttribute('selected');
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Sets the options a form reset should restore. The first call selects
24
+ * them if nothing has set a value, later calls leave the current selection alone.
25
+ * @param {HTMLSelectElement} select
26
+ * @param {any} value
27
+ */
28
+ export function set_default_select_value(select, value) {
29
+ var mounting = !('__defaultValue' in select);
30
+ // @ts-expect-error
31
+ if (!mounting && select.__defaultValue === value) return;
32
+ // @ts-expect-error
33
+ select.__defaultValue = value;
34
+ apply_default_select_value(select, !mounting || '__value' in select);
35
+ }
36
+
37
+ /**
38
+ * Marks the options matching `__defaultValue` as selected. Without `preserve`
39
+ * a newly matching option gets selected, as an inserted `<option selected>` would.
40
+ * @param {HTMLSelectElement} select
41
+ * @param {boolean} preserve
42
+ */
43
+ function apply_default_select_value(select, preserve) {
44
+ // @ts-expect-error
45
+ var value = select.__defaultValue;
46
+ var multiple = select.multiple;
47
+ var values = multiple ? value ?? [] : null;
48
+
49
+ if (multiple && !is_array(values)) return;
50
+
51
+ var index = select.selectedIndex;
52
+ var selected = preserve && multiple ? new Set(select.selectedOptions) : null;
53
+
54
+ for (var option of select.options) {
55
+ var option_value = get_option_value(option);
56
+ set_selected(
57
+ option,
58
+ multiple ? /** @type {any[]} */ (values).includes(option_value) : is(option_value, value)
59
+ );
60
+ }
61
+
62
+ if (!preserve) return;
63
+
64
+ if (selected !== null) {
65
+ for (option of select.options) {
66
+ var was_selected = selected.has(option);
67
+ if (option.selected !== was_selected) option.selected = was_selected;
68
+ }
69
+ } else if (select.selectedIndex !== index) {
70
+ select.selectedIndex = index;
71
+ }
72
+ }
73
+
9
74
  /**
10
75
  * Selects the correct option(s) (depending on whether this is a multiple select)
11
76
  * @template V
@@ -47,17 +112,25 @@ export function select_option(select, value, mounting = false) {
47
112
  }
48
113
 
49
114
  /**
50
- * Selects the correct option(s) if `value` is given,
51
- * and then sets up a mutation observer to sync the
52
- * current selection to the dom when it changes. Such
53
- * changes could for example occur when options are
54
- * inside an `#each` block.
115
+ * Sets up a mutation observer to sync the current selection
116
+ * and default to the dom when the options change, for example
117
+ * when they are inside an `#each` block. Called once per `<select>`,
118
+ * by the compiled output or by `attribute_effect` for spreads.
55
119
  * @param {HTMLSelectElement} select
56
120
  */
57
121
  export function init_select(select) {
58
- var observer = new MutationObserver(() => {
122
+ var observer = new MutationObserver((entries) => {
123
+ // Mutations related to `<selectedcontent>` can never affect the option list.
124
+ // Reacting to them could revert a user-initiated selection change, because the
125
+ // records are delivered as soon as any listener returns (e.g. a delegated `input`
126
+ // handler), which can happen before the `change` handler has updated `__value`
127
+ if (entries.every(is_selectedcontent_mutation)) return;
128
+
129
+ if ('__defaultValue' in select) {
130
+ apply_default_select_value(select, false);
131
+ }
132
+
59
133
  if ('__value' in select) {
60
- // @ts-ignore
61
134
  select_option(select, select.__value);
62
135
  }
63
136
  // Deliberately don't update the potential binding value,
@@ -151,8 +224,6 @@ export function bind_select_value(select, get, set = get) {
151
224
  select.__value = value;
152
225
  mounting = false;
153
226
  });
154
-
155
- init_select(select);
156
227
  }
157
228
 
158
229
  /** @param {HTMLOptionElement} option */
@@ -164,3 +235,22 @@ function get_option_value(option) {
164
235
  return option.value;
165
236
  }
166
237
  }
238
+
239
+ /**
240
+ * Returns `true` if the mutation stems from the browser mirroring the selected
241
+ * option's content into `<selectedcontent>`, or from us replacing the
242
+ * `<selectedcontent>` element with a clone of itself
243
+ * @param {MutationRecord} entry
244
+ */
245
+ function is_selectedcontent_mutation(entry) {
246
+ if (/** @type {Element} */ (entry.target).closest('selectedcontent') !== null) {
247
+ return true;
248
+ }
249
+
250
+ if (entry.type === 'childList') {
251
+ var nodes = [...entry.addedNodes, ...entry.removedNodes];
252
+ return nodes.length > 0 && nodes.every((node) => node.nodeName === 'SELECTEDCONTENT');
253
+ }
254
+
255
+ return false;
256
+ }
@@ -1,6 +1,6 @@
1
1
  /** @import { ComponentContext, Effect } from '#client' */
2
2
  import { DESTROYING, STATE_SYMBOL } from '#client/constants';
3
- import { component_context } from '../../../context.js';
3
+ import { component_context, mark_as_component } from '../../../context.js';
4
4
  import { effect, render_effect } from '../../../reactivity/effects.js';
5
5
  import { active_effect, untrack } from '../../../runtime.js';
6
6
 
@@ -23,7 +23,12 @@ function is_bound_this(bound_value, element_or_component) {
23
23
  * returns all the parts of the each block context that are used in the expression
24
24
  * @returns {void}
25
25
  */
26
- export function bind_this(element_or_component = {}, update, get_value, get_parts) {
26
+ export function bind_this(
27
+ element_or_component = mark_as_component(),
28
+ update,
29
+ get_value,
30
+ get_parts
31
+ ) {
27
32
  var component_effect = /** @type {ComponentContext} */ (component_context).r;
28
33
  var parent = /** @type {Effect} */ (active_effect);
29
34
 
@@ -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