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
@@ -1,14 +1,8 @@
1
1
  /** @import { Effect, Source, TemplateNode, } from '#client' */
2
- import {
3
- BOUNDARY_EFFECT,
4
- DIRTY,
5
- EFFECT_PRESERVED,
6
- EFFECT_TRANSPARENT,
7
- MAYBE_DIRTY
8
- } from '#client/constants';
2
+ import { BOUNDARY_EFFECT, EFFECT_PRESERVED, EFFECT_TRANSPARENT } from '#client/constants';
9
3
  import { HYDRATION_START_ELSE, HYDRATION_START_FAILED } from '../../../../constants.js';
10
4
  import { component_context, set_component_context } from '../../context.js';
11
- import { handle_error, invoke_error_boundary } from '../../error-handling.js';
5
+ import { invoke_error_boundary } from '../../error-handling.js';
12
6
  import {
13
7
  block,
14
8
  branch,
@@ -271,13 +265,31 @@ export class Boundary {
271
265
  queue_micro_task(() => {
272
266
  var fragment = (this.#offscreen_fragment = document.createDocumentFragment());
273
267
  var anchor = create_text();
268
+ var handled = false;
274
269
 
275
270
  fragment.append(anchor);
276
271
 
277
272
  this.#main_effect = this.#run(() => {
278
- return branch(() => this.#children(anchor));
273
+ try {
274
+ return branch(() => this.#children(anchor));
275
+ } catch (error) {
276
+ try {
277
+ this.error(error);
278
+ handled = true;
279
+ } catch (error) {
280
+ invoke_error_boundary(error, this.#effect.parent);
281
+ }
282
+
283
+ return null;
284
+ }
279
285
  });
280
286
 
287
+ if (this.#main_effect === null) {
288
+ this.#offscreen_fragment = null;
289
+ if (handled) this.#resolve(/** @type {Batch} */ (current_batch));
290
+ return;
291
+ }
292
+
281
293
  if (this.#pending_count === 0) {
282
294
  this.#anchor.before(fragment);
283
295
  this.#offscreen_fragment = null;
@@ -362,9 +374,6 @@ export class Boundary {
362
374
  try {
363
375
  Batch.ensure();
364
376
  return fn();
365
- } catch (e) {
366
- handle_error(e);
367
- return null;
368
377
  } finally {
369
378
  set_active_effect(previous_effect);
370
379
  set_active_reaction(previous_reaction);
@@ -18,10 +18,10 @@ export function css_props(element, get_styles) {
18
18
  for (var key in styles) {
19
19
  var value = styles[key];
20
20
 
21
- if (value) {
22
- element.style.setProperty(key, value);
23
- } else {
21
+ if (value == null || value === '') {
24
22
  element.style.removeProperty(key);
23
+ } else {
24
+ element.style.setProperty(key, value);
25
25
  }
26
26
  }
27
27
  });
@@ -1,4 +1,6 @@
1
1
  import { hydrate_next, hydrating } from '../hydration.js';
2
+ import { create_element, create_text } from '../operations.js';
3
+ import { append } from '../template.js';
2
4
 
3
5
  /**
4
6
  * @param {Comment} anchor
@@ -12,6 +14,23 @@ export function slot(anchor, $$props, name, slot_props, fallback_fn) {
12
14
  hydrate_next();
13
15
  }
14
16
 
17
+ // Custom element slots are native DOM slots.
18
+ // Use the stored reference because the shadow root may be closed.
19
+ if ($$props.$$host?.$$shadowRoot) {
20
+ const element = create_element('slot');
21
+ if (name !== 'default') element.name = name;
22
+
23
+ append(anchor, element);
24
+
25
+ if (fallback_fn !== null) {
26
+ const fallback_anchor = create_text();
27
+ element.append(fallback_anchor);
28
+ fallback_fn(fallback_anchor);
29
+ }
30
+
31
+ return;
32
+ }
33
+
15
34
  var slot_fn = $$props.$$slots?.[name];
16
35
  // Interop: Can use snippets to fill slots
17
36
  var is_interop = false;
@@ -52,6 +52,14 @@ export function head(hash, render_fn) {
52
52
  block(() => {
53
53
  var e = branch(() => render_fn(anchor));
54
54
  e.f |= HEAD_EFFECT;
55
+
56
+ if (!hydrating) {
57
+ if (e.nodes === null) {
58
+ e.nodes = { start: anchor, end: anchor, a: null, t: null };
59
+ } else {
60
+ e.nodes.end = anchor;
61
+ }
62
+ }
55
63
  });
56
64
  } finally {
57
65
  if (was_hydrating) {
@@ -2,14 +2,19 @@ import { DEV } from 'esm-env';
2
2
  import { register_style } from '../dev/css.js';
3
3
  import { effect } from '../reactivity/effects.js';
4
4
  import { create_element } from './operations.js';
5
+ import { active_effect } from '../runtime.js';
5
6
 
6
7
  /**
7
8
  * @param {Node} anchor
8
9
  * @param {{ hash: string, code: string }} css
9
10
  */
10
11
  export function append_styles(anchor, css) {
11
- // Use `queue_micro_task` to ensure `anchor` is in the DOM, otherwise getRootNode() will yield wrong results
12
+ // Use an effect to ensure `anchor` is in the DOM, otherwise getRootNode() will yield wrong results
12
13
  effect(() => {
14
+ // Bit of a hack: branches.js/each.js use offscreen fragments with temporary text nodes that will
15
+ // never be connected to the real dom. Therfore walk up to the branch that has created the component
16
+ // whose styles we want to append, and check its node instead. It will be connected by the time we get here.
17
+ anchor = active_effect?.parent?.nodes?.start ?? anchor;
13
18
  var root = anchor.getRootNode();
14
19
 
15
20
  var target = /** @type {ShadowRoot} */ (root).host
@@ -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,9 +296,11 @@ 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
- if (!(key in next)) {
302
+ // don't null our internal $$onX listeners
303
+ if (!(key in next) && key[0] + key[1] !== '$$') {
319
304
  next[key] = null;
320
305
  }
321
306
  }
@@ -448,6 +433,9 @@ function set_attributes(
448
433
 
449
434
  var is_default = name === 'defaultValue' || name === 'defaultChecked';
450
435
 
436
+ // A select's default value is represented by selected options, not a property.
437
+ if (is_select_element && name === 'defaultValue') continue;
438
+
451
439
  if (value == null && !is_custom_element && !is_default) {
452
440
  attributes[key] = null;
453
441
 
@@ -472,7 +460,7 @@ function set_attributes(
472
460
  }
473
461
  } else if (
474
462
  is_default ||
475
- (setters.includes(name) && (is_custom_element || typeof value !== 'string'))
463
+ ((is_custom_element || typeof value !== 'string') && setters.has(name))
476
464
  ) {
477
465
  // @ts-ignore
478
466
  element[name] = value;
@@ -533,8 +521,16 @@ export function attribute_effect(
533
521
  skip_warning
534
522
  );
535
523
 
536
- if (inited && is_select && 'value' in next) {
537
- 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
+ }
538
534
  }
539
535
 
540
536
  for (let symbol of Object.getOwnPropertySymbols(effects)) {
@@ -559,7 +555,13 @@ export function attribute_effect(
559
555
  var select = /** @type {HTMLSelectElement} */ (element);
560
556
 
561
557
  effect(() => {
562
- 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);
563
565
  init_select(select);
564
566
  });
565
567
  }
@@ -581,7 +583,7 @@ function get_attributes(element) {
581
583
  );
582
584
  }
583
585
 
584
- /** @type {Map<string, string[]>} */
586
+ /** @type {Map<string, Set<string>>} */
585
587
  var setters_cache = new Map();
586
588
 
587
589
  /** @param {Element} element */
@@ -589,7 +591,7 @@ function get_setters(element) {
589
591
  var cache_key = element.getAttribute('is') || element.nodeName;
590
592
  var setters = setters_cache.get(cache_key);
591
593
  if (setters) return setters;
592
- setters_cache.set(cache_key, (setters = []));
594
+ setters_cache.set(cache_key, (setters = new Set()));
593
595
 
594
596
  var descriptors;
595
597
  var proto = element; // In the case of custom elements there might be setters on the instance
@@ -608,7 +610,7 @@ function get_setters(element) {
608
610
  key !== 'textContent' &&
609
611
  key !== 'innerText'
610
612
  ) {
611
- setters.push(key);
613
+ setters.add(key);
612
614
  }
613
615
  }
614
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
 
@@ -159,12 +159,15 @@ export function delegate(events) {
159
159
  }
160
160
 
161
161
  // used to store the reference to the currently propagated event
162
- // to prevent garbage collection between microtasks in Firefox
162
+ // to prevent garbage collection between microtasks in Firefox (<= 141)
163
163
  // If the event object is GCed too early, the expando __root property
164
164
  // set on the event object is lost, causing the event delegation
165
165
  // to process the event twice
166
166
  let last_propagated_event = null;
167
167
 
168
+ // whether a task is already queued to clear `last_propagated_event`
169
+ let last_propagated_event_clear_scheduled = false;
170
+
168
171
  /**
169
172
  * @this {EventTarget}
170
173
  * @param {Event} event
@@ -179,6 +182,21 @@ export function handle_event_propagation(event) {
179
182
 
180
183
  last_propagated_event = event;
181
184
 
185
+ // The reference is only needed while the event can still reach another
186
+ // delegated root, i.e. during the current (synchronous) dispatch and its
187
+ // microtask checkpoints. Clearing it in a later task preserves the
188
+ // Firefox workaround while making sure the slot doesn't retain the last
189
+ // event forever — through `event.target` it would otherwise keep the
190
+ // entire detached subtree of whatever the user last clicked in alive
191
+ // until the next delegated event happens to arrive.
192
+ if (!last_propagated_event_clear_scheduled) {
193
+ last_propagated_event_clear_scheduled = true;
194
+ setTimeout(() => {
195
+ last_propagated_event_clear_scheduled = false;
196
+ last_propagated_event = null;
197
+ });
198
+ }
199
+
182
200
  // composedPath contains list of nodes the event has propagated through.
183
201
  // We check `event_symbol` to skip all nodes below it in case this is a
184
202
  // parent of the `event_symbol` node, which indicates that there's nested