svelte 5.55.5 → 5.55.7

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 (35) hide show
  1. package/compiler/index.js +1 -1
  2. package/elements.d.ts +3 -3
  3. package/package.json +2 -2
  4. package/src/compiler/phases/1-parse/read/style.js +15 -0
  5. package/src/compiler/phases/3-transform/client/visitors/DebugTag.js +10 -4
  6. package/src/compiler/phases/3-transform/server/visitors/Component.js +5 -1
  7. package/src/compiler/phases/3-transform/server/visitors/DebugTag.js +21 -10
  8. package/src/compiler/phases/3-transform/server/visitors/shared/utils.js +2 -2
  9. package/src/internal/client/constants.js +5 -0
  10. package/src/internal/client/dev/inspect.js +2 -0
  11. package/src/internal/client/dom/blocks/async.js +1 -5
  12. package/src/internal/client/dom/blocks/boundary.js +4 -5
  13. package/src/internal/client/dom/blocks/svelte-head.js +6 -5
  14. package/src/internal/client/dom/elements/attributes.js +17 -8
  15. package/src/internal/client/dom/elements/bindings/shared.js +4 -6
  16. package/src/internal/client/dom/elements/bindings/this.js +1 -1
  17. package/src/internal/client/dom/elements/class.js +3 -4
  18. package/src/internal/client/dom/elements/events.js +2 -2
  19. package/src/internal/client/dom/elements/misc.js +2 -2
  20. package/src/internal/client/dom/elements/style.js +3 -4
  21. package/src/internal/client/dom/operations.js +12 -11
  22. package/src/internal/client/reactivity/async.js +34 -16
  23. package/src/internal/client/reactivity/batch.js +294 -127
  24. package/src/internal/client/reactivity/deriveds.js +30 -29
  25. package/src/internal/client/reactivity/effects.js +1 -9
  26. package/src/internal/client/reactivity/props.js +7 -1
  27. package/src/internal/client/reactivity/sources.js +19 -9
  28. package/src/internal/client/render.js +4 -5
  29. package/src/internal/server/hydratable.js +7 -2
  30. package/src/internal/server/index.js +1 -1
  31. package/src/internal/server/renderer.js +6 -1
  32. package/src/utils.js +1 -1
  33. package/src/version.js +1 -1
  34. package/types/index.d.ts +2 -0
  35. package/types/index.d.ts.map +1 -1
package/elements.d.ts CHANGED
@@ -2067,9 +2067,9 @@ export interface SvelteHTMLElements {
2067
2067
  };
2068
2068
  'svelte:head': { [name: string]: any };
2069
2069
  'svelte:boundary': {
2070
- onerror?: (error: unknown, reset: () => void) => void;
2071
- failed?: import('svelte').Snippet<[error: unknown, reset: () => void]>;
2072
- pending?: import('svelte').Snippet;
2070
+ onerror?: ((error: unknown, reset: () => void) => void) | null | undefined;
2071
+ failed?: import('svelte').Snippet<[error: unknown, reset: () => void]> | null | undefined;
2072
+ pending?: import('svelte').Snippet | null | undefined;
2073
2073
  };
2074
2074
 
2075
2075
  [name: string]: { [name: string]: any };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "svelte",
3
3
  "description": "Cybernetically enhanced web apps",
4
4
  "license": "MIT",
5
- "version": "5.55.5",
5
+ "version": "5.55.7",
6
6
  "type": "module",
7
7
  "types": "./types/index.d.ts",
8
8
  "engines": {
@@ -164,7 +164,7 @@
164
164
  "aria-query": "5.3.1",
165
165
  "axobject-query": "^4.1.0",
166
166
  "clsx": "^2.1.1",
167
- "devalue": "^5.6.4",
167
+ "devalue": "^5.8.1",
168
168
  "esm-env": "^1.2.1",
169
169
  "esrap": "^2.2.4",
170
170
  "is-reference": "^3.0.3",
@@ -524,6 +524,21 @@ function read_value(parser) {
524
524
  in_url = true;
525
525
  } else if ((char === ';' || char === '{' || char === '}') && !in_url && !quote_mark) {
526
526
  return value.trim();
527
+ } else if (
528
+ char === '/' &&
529
+ !in_url &&
530
+ !quote_mark &&
531
+ parser.template[parser.index + 1] === '*'
532
+ ) {
533
+ parser.index += 2;
534
+ while (parser.index < parser.template.length) {
535
+ if (parser.template[parser.index] === '*' && parser.template[parser.index + 1] === '/') {
536
+ parser.index += 2;
537
+ break;
538
+ }
539
+ parser.index++;
540
+ }
541
+ continue;
527
542
  }
528
543
 
529
544
  value += char;
@@ -8,6 +8,10 @@ import * as b from '#compiler/builders';
8
8
  * @param {ComponentContext} context
9
9
  */
10
10
  export function DebugTag(node, context) {
11
+ const blockers = node.identifiers
12
+ .map((identifier) => context.state.scope.get(identifier.name)?.blocker)
13
+ .filter((blocker) => blocker != null);
14
+
11
15
  const object = b.object(
12
16
  node.identifiers.map((identifier) => {
13
17
  const visited = b.call('$.snapshot', /** @type {Expression} */ (context.visit(identifier)));
@@ -20,9 +24,11 @@ export function DebugTag(node, context) {
20
24
  })
21
25
  );
22
26
 
23
- const call = b.call('console.log', object);
27
+ const args = [b.thunk(b.block([b.stmt(b.call('console.log', object)), b.debugger]))];
24
28
 
25
- context.state.init.push(
26
- b.stmt(b.call('$.template_effect', b.thunk(b.block([b.stmt(call), b.debugger]))))
27
- );
29
+ if (blockers.length > 0) {
30
+ args.push(b.array([]), b.array([]), b.array(blockers));
31
+ }
32
+
33
+ context.state.init.push(b.stmt(b.call('$.template_effect', ...args)));
28
34
  }
@@ -9,5 +9,9 @@ import { build_inline_component } from './shared/component.js';
9
9
  * @param {ComponentContext} context
10
10
  */
11
11
  export function Component(node, context) {
12
- build_inline_component(node, /** @type {Expression} */ (context.visit(b.id(node.name))), context);
12
+ build_inline_component(
13
+ node,
14
+ /** @type {Expression} */ (context.visit(b.member_id(node.name))),
15
+ context
16
+ );
13
17
  }
@@ -2,23 +2,34 @@
2
2
  /** @import { AST } from '#compiler' */
3
3
  /** @import { ComponentContext } from '../types.js' */
4
4
  import * as b from '#compiler/builders';
5
+ import { create_child_block } from './shared/utils.js';
5
6
 
6
7
  /**
7
8
  * @param {AST.DebugTag} node
8
9
  * @param {ComponentContext} context
9
10
  */
10
11
  export function DebugTag(node, context) {
12
+ const blockers = node.identifiers
13
+ .map((identifier) => context.state.scope.get(identifier.name)?.blocker)
14
+ .filter((blocker) => blocker != null);
15
+
11
16
  context.state.template.push(
12
- b.stmt(
13
- b.call(
14
- 'console.log',
15
- b.object(
16
- node.identifiers.map((identifier) =>
17
- b.prop('init', identifier, /** @type {Expression} */ (context.visit(identifier)))
17
+ ...create_child_block(
18
+ [
19
+ b.stmt(
20
+ b.call(
21
+ 'console.log',
22
+ b.object(
23
+ node.identifiers.map((identifier) =>
24
+ b.prop('init', identifier, /** @type {Expression} */ (context.visit(identifier)))
25
+ )
26
+ )
18
27
  )
19
- )
20
- )
21
- ),
22
- b.debugger
28
+ ),
29
+ b.debugger
30
+ ],
31
+ b.array(blockers),
32
+ false
33
+ )
23
34
  );
24
35
  }
@@ -12,7 +12,7 @@ import {
12
12
  import * as b from '#compiler/builders';
13
13
  import { sanitize_template_string } from '../../../../../utils/sanitize_template_string.js';
14
14
  import { regex_whitespaces_strict } from '../../../../patterns.js';
15
- import { has_await_expression } from '../../../../../utils/ast.js';
15
+ import { has_await_expression, save } from '../../../../../utils/ast.js';
16
16
  import { ExpressionMetadata } from '../../../../nodes.js';
17
17
 
18
18
  /** Opens an if/each block, so that we can remove nodes in the case of a mismatch */
@@ -360,7 +360,7 @@ export class PromiseOptimiser {
360
360
 
361
361
  return b.const(
362
362
  b.array_pattern(this.expressions.map((_, i) => b.id(`$$${i}`))),
363
- b.await(b.call('Promise.all', promises))
363
+ save(b.call('Promise.all', promises))
364
364
  );
365
365
  }
366
366
 
@@ -63,6 +63,11 @@ export const STATE_SYMBOL = Symbol('$state');
63
63
  export const LEGACY_PROPS = Symbol('legacy props');
64
64
  export const LOADING_ATTR_SYMBOL = Symbol('');
65
65
  export const PROXY_PATH_SYMBOL = Symbol('proxy path');
66
+ export const ATTRIBUTES_CACHE = Symbol('attributes');
67
+ export const CLASS_CACHE = Symbol('class');
68
+ export const STYLE_CACHE = Symbol('style');
69
+ export const TEXT_CACHE = Symbol('text');
70
+ export const FORM_RESET_HANDLER = Symbol('form reset');
66
71
  /** An anchor might change, via this symbol on the original anchor we can tell HMR about the updated anchor */
67
72
  export const HMR_ANCHOR = Symbol('hmr anchor');
68
73
 
@@ -20,6 +20,8 @@ export function inspect(get_value, inspector, show_stack = false) {
20
20
  // in an error (an `$inspect(object.property)` will run before the
21
21
  // `{#if object}...{/if}` that contains it)
22
22
  eager_effect(() => {
23
+ error = UNINITIALIZED;
24
+
23
25
  try {
24
26
  var value = get_value();
25
27
  } catch (e) {
@@ -1,5 +1,5 @@
1
1
  /** @import { Blocker, TemplateNode, Value } from '#client' */
2
- import { flatten, increment_pending } from '../../reactivity/async.js';
2
+ import { flatten } from '../../reactivity/async.js';
3
3
  import { get } from '../../runtime.js';
4
4
  import {
5
5
  hydrate_next,
@@ -42,8 +42,6 @@ export function async(node, blockers = [], expressions = [], fn) {
42
42
  return;
43
43
  }
44
44
 
45
- const decrement_pending = increment_pending();
46
-
47
45
  if (was_hydrating) {
48
46
  var previous_hydrate_node = hydrate_node;
49
47
  set_hydrate_node(end);
@@ -64,8 +62,6 @@ export function async(node, blockers = [], expressions = [], fn) {
64
62
  if (was_hydrating) {
65
63
  set_hydrating(false);
66
64
  }
67
-
68
- decrement_pending();
69
65
  }
70
66
  });
71
67
  }
@@ -35,19 +35,18 @@ import { queue_micro_task } from '../task.js';
35
35
  import * as e from '../../errors.js';
36
36
  import * as w from '../../warnings.js';
37
37
  import { DEV } from 'esm-env';
38
- import { Batch, current_batch, previous_batch, schedule_effect } from '../../reactivity/batch.js';
38
+ import { Batch, current_batch } from '../../reactivity/batch.js';
39
39
  import { internal_set, source } from '../../reactivity/sources.js';
40
40
  import { tag } from '../../dev/tracing.js';
41
41
  import { createSubscriber } from '../../../../reactivity/create-subscriber.js';
42
42
  import { create_text } from '../operations.js';
43
43
  import { defer_effect } from '../../reactivity/utils.js';
44
- import { set_signal_status } from '../../reactivity/status.js';
45
44
 
46
45
  /**
47
46
  * @typedef {{
48
- * onerror?: (error: unknown, reset: () => void) => void;
49
- * failed?: (anchor: Node, error: () => unknown, reset: () => () => void) => void;
50
- * pending?: (anchor: Node) => void;
47
+ * onerror?: ((error: unknown, reset: () => void) => void) | null;
48
+ * failed?: ((anchor: Node, error: () => unknown, reset: () => () => void) => void) | null;
49
+ * pending?: ((anchor: Node) => void) | null;
51
50
  * }} BoundaryProps
52
51
  */
53
52
 
@@ -1,8 +1,8 @@
1
1
  /** @import { TemplateNode } from '#client' */
2
2
  import { hydrate_node, hydrating, set_hydrate_node, set_hydrating } from '../hydration.js';
3
3
  import { create_text, get_first_child, get_next_sibling } from '../operations.js';
4
- import { block } from '../../reactivity/effects.js';
5
- import { COMMENT_NODE, EFFECT_PRESERVED, HEAD_EFFECT } from '#client/constants';
4
+ import { block, branch } from '../../reactivity/effects.js';
5
+ import { COMMENT_NODE, HEAD_EFFECT } from '#client/constants';
6
6
 
7
7
  /**
8
8
  * @param {string} hash
@@ -49,9 +49,10 @@ export function head(hash, render_fn) {
49
49
  }
50
50
 
51
51
  try {
52
- // normally a branch is the child of a block and would have the EFFECT_PRESERVED flag,
53
- // but since head blocks don't necessarily only have direct branch children we add it on the block itself
54
- block(() => render_fn(anchor), HEAD_EFFECT | EFFECT_PRESERVED);
52
+ block(() => {
53
+ var e = branch(() => render_fn(anchor));
54
+ e.f |= HEAD_EFFECT;
55
+ });
55
56
  } finally {
56
57
  if (was_hydrating) {
57
58
  set_hydrating(true);
@@ -5,7 +5,12 @@ import { get_descriptors, get_prototype_of } from '../../../shared/utils.js';
5
5
  import { create_event, delegate, delegated, event, event_symbol } from './events.js';
6
6
  import { add_form_reset_listener, autofocus } from './misc.js';
7
7
  import * as w from '../../warnings.js';
8
- import { IS_XHTML, LOADING_ATTR_SYMBOL } from '#client/constants';
8
+ import {
9
+ ATTRIBUTES_CACHE,
10
+ FORM_RESET_HANDLER,
11
+ IS_XHTML,
12
+ LOADING_ATTR_SYMBOL
13
+ } from '#client/constants';
9
14
  import { queue_micro_task } from '../task.js';
10
15
  import { is_capture_event, can_delegate_event, normalize_attribute } from '../../../../utils.js';
11
16
  import {
@@ -69,8 +74,7 @@ export function remove_input_defaults(input) {
69
74
  }
70
75
  };
71
76
 
72
- // @ts-expect-error
73
- input.__on_r = remove_defaults;
77
+ /** @type {any} */ (input)[FORM_RESET_HANDLER] = remove_defaults;
74
78
  queue_micro_task(remove_defaults);
75
79
  add_form_reset_listener();
76
80
  }
@@ -561,8 +565,7 @@ export function attribute_effect(
561
565
  */
562
566
  function get_attributes(element) {
563
567
  return /** @type {Record<string | symbol, unknown>} **/ (
564
- // @ts-expect-error
565
- element.__attributes ??= {
568
+ /** @type {any} */ (element)[ATTRIBUTES_CACHE] ??= {
566
569
  [IS_CUSTOM_ELEMENT]: element.nodeName.includes('-'),
567
570
  [IS_HTML]: element.namespaceURI === NAMESPACE_HTML
568
571
  }
@@ -583,13 +586,19 @@ function get_setters(element) {
583
586
  var proto = element; // In the case of custom elements there might be setters on the instance
584
587
  var element_proto = Element.prototype;
585
588
 
586
- // Stop at Element, from there on there's only unnecessary setters we're not interested in
587
- // Do not use contructor.name here as that's unreliable in some browser environments
589
+ // Stop at Element, from there on there's only unnecessary (and dangerous, like innerHTML) setters we're not interested in
590
+ // Do not use constructor.name here as that's unreliable in some browser environments
588
591
  while (element_proto !== proto) {
589
592
  descriptors = get_descriptors(proto);
590
593
 
591
594
  for (var key in descriptors) {
592
- if (descriptors[key].set) {
595
+ if (
596
+ descriptors[key].set &&
597
+ // better safe than sorry, we don't want spread attributes to mess with HTML content
598
+ key !== 'innerHTML' &&
599
+ key !== 'textContent' &&
600
+ key !== 'innerText'
601
+ ) {
593
602
  setters.push(key);
594
603
  }
595
604
  }
@@ -5,6 +5,7 @@ import {
5
5
  set_active_effect,
6
6
  set_active_reaction
7
7
  } from '../../../runtime.js';
8
+ import { FORM_RESET_HANDLER } from '../../../constants.js';
8
9
  import { add_form_reset_listener } from '../misc.js';
9
10
 
10
11
  /**
@@ -58,18 +59,15 @@ export function without_reactive_context(fn) {
58
59
  */
59
60
  export function listen_to_event_and_reset_event(element, event, handler, on_reset = handler) {
60
61
  element.addEventListener(event, () => without_reactive_context(handler));
61
- // @ts-expect-error
62
- const prev = element.__on_r;
62
+ const prev = /** @type {any} */ (element)[FORM_RESET_HANDLER];
63
63
  if (prev) {
64
64
  // special case for checkbox that can have multiple binds (group & checked)
65
- // @ts-expect-error
66
- element.__on_r = () => {
65
+ /** @type {any} */ (element)[FORM_RESET_HANDLER] = () => {
67
66
  prev();
68
67
  on_reset(true);
69
68
  };
70
69
  } else {
71
- // @ts-expect-error
72
- element.__on_r = () => on_reset(true);
70
+ /** @type {any} */ (element)[FORM_RESET_HANDLER] = () => on_reset(true);
73
71
  }
74
72
 
75
73
  add_form_reset_listener();
@@ -40,7 +40,7 @@ export function bind_this(element_or_component = {}, update, get_value, get_part
40
40
  parts = get_parts?.() || [];
41
41
 
42
42
  untrack(() => {
43
- if (element_or_component !== get_value(...parts)) {
43
+ if (!is_bound_this(get_value(...parts), element_or_component)) {
44
44
  update(element_or_component, ...parts);
45
45
  // If this is an effect rerun (cause: each block context changes), then nullify the binding at
46
46
  // the previous position if it isn't already taken over by a different effect.
@@ -1,4 +1,5 @@
1
1
  import { to_class } from '../../../shared/attributes.js';
2
+ import { CLASS_CACHE } from '../../constants.js';
2
3
  import { hydrating } from '../hydration.js';
3
4
 
4
5
  /**
@@ -11,8 +12,7 @@ import { hydrating } from '../hydration.js';
11
12
  * @returns {Record<string, boolean> | undefined}
12
13
  */
13
14
  export function set_class(dom, is_html, value, hash, prev_classes, next_classes) {
14
- // @ts-expect-error need to add __className to patched prototype
15
- var prev = dom.__className;
15
+ var prev = /** @type {any} */ (dom)[CLASS_CACHE];
16
16
 
17
17
  if (
18
18
  hydrating ||
@@ -35,8 +35,7 @@ export function set_class(dom, is_html, value, hash, prev_classes, next_classes)
35
35
  }
36
36
  }
37
37
 
38
- // @ts-expect-error need to add __className to patched prototype
39
- dom.__className = value;
38
+ /** @type {any} */ (dom)[CLASS_CACHE] = value;
40
39
  } else if (next_classes && prev_classes !== next_classes) {
41
40
  for (var key in next_classes) {
42
41
  var is_present = !!next_classes[key];
@@ -237,9 +237,9 @@ export function handle_event_propagation(event) {
237
237
  });
238
238
 
239
239
  // This started because of Chromium issue https://chromestatus.com/feature/5128696823545856,
240
- // where removal or moving of of the DOM can cause sync `blur` events to fire, which can cause logic
240
+ // where removal or moving of the DOM can cause sync `blur` events to fire, which can cause logic
241
241
  // to run inside the current `active_reaction`, which isn't what we want at all. However, on reflection,
242
- // it's probably best that all event handled by Svelte have this behaviour, as we don't really want
242
+ // it's probably best that all events handled by Svelte have this behaviour, as we don't really want
243
243
  // an event handler to run in the context of another reaction or effect.
244
244
  var previous_reaction = active_reaction;
245
245
  var previous_effect = active_effect;
@@ -1,6 +1,7 @@
1
1
  import { hydrating } from '../hydration.js';
2
2
  import { clear_text_content, get_first_child } from '../operations.js';
3
3
  import { queue_micro_task } from '../task.js';
4
+ import { FORM_RESET_HANDLER } from '../../constants.js';
4
5
 
5
6
  /**
6
7
  * @param {HTMLElement} dom
@@ -45,8 +46,7 @@ export function add_form_reset_listener() {
45
46
  Promise.resolve().then(() => {
46
47
  if (!evt.defaultPrevented) {
47
48
  for (const e of /**@type {HTMLFormElement} */ (evt.target).elements) {
48
- // @ts-expect-error
49
- e.__on_r?.();
49
+ /** @type {any} */ (e)[FORM_RESET_HANDLER]?.();
50
50
  }
51
51
  }
52
52
  });
@@ -1,4 +1,5 @@
1
1
  import { to_style } from '../../../shared/attributes.js';
2
+ import { STYLE_CACHE } from '../../constants.js';
2
3
  import { hydrating } from '../hydration.js';
3
4
 
4
5
  /**
@@ -28,8 +29,7 @@ function update_styles(dom, prev = {}, next, priority) {
28
29
  * @param {Record<string, any> | [Record<string, any>, Record<string, any>]} [next_styles]
29
30
  */
30
31
  export function set_style(dom, value, prev_styles, next_styles) {
31
- // @ts-expect-error
32
- var prev = dom.__style;
32
+ var prev = /** @type {any} */ (dom)[STYLE_CACHE];
33
33
 
34
34
  if (hydrating || prev !== value) {
35
35
  var next_style_attr = to_style(value, next_styles);
@@ -42,8 +42,7 @@ export function set_style(dom, value, prev_styles, next_styles) {
42
42
  }
43
43
  }
44
44
 
45
- // @ts-expect-error
46
- dom.__style = value;
45
+ /** @type {any} */ (dom)[STYLE_CACHE] = value;
47
46
  } else if (next_styles) {
48
47
  if (Array.isArray(next_styles)) {
49
48
  update_styles(dom, prev_styles?.[0], next_styles[0]);
@@ -5,7 +5,14 @@ import { init_array_prototype_warnings } from '../dev/equality.js';
5
5
  import { get_descriptor, is_extensible } from '../../shared/utils.js';
6
6
  import { active_effect } from '../runtime.js';
7
7
  import { async_mode_flag } from '../../flags/index.js';
8
- import { TEXT_NODE, REACTION_RAN } from '#client/constants';
8
+ import {
9
+ ATTRIBUTES_CACHE,
10
+ CLASS_CACHE,
11
+ REACTION_RAN,
12
+ STYLE_CACHE,
13
+ TEXT_CACHE,
14
+ TEXT_NODE
15
+ } from '#client/constants';
9
16
  import { eager_block_effects } from '../reactivity/batch.js';
10
17
  import { NAMESPACE_HTML } from '../../../constants.js';
11
18
 
@@ -48,21 +55,15 @@ export function init_operations() {
48
55
 
49
56
  if (is_extensible(element_prototype)) {
50
57
  // the following assignments improve perf of lookups on DOM nodes
51
- // @ts-expect-error
52
- element_prototype.__click = undefined;
53
- // @ts-expect-error
54
- element_prototype.__className = undefined;
55
- // @ts-expect-error
56
- element_prototype.__attributes = null;
57
- // @ts-expect-error
58
- element_prototype.__style = undefined;
58
+ /** @type {any} */ (element_prototype)[CLASS_CACHE] = undefined;
59
+ /** @type {any} */ (element_prototype)[ATTRIBUTES_CACHE] = null;
60
+ /** @type {any} */ (element_prototype)[STYLE_CACHE] = undefined;
59
61
  // @ts-expect-error
60
62
  element_prototype.__e = undefined;
61
63
  }
62
64
 
63
65
  if (is_extensible(text_prototype)) {
64
- // @ts-expect-error
65
- text_prototype.__t = undefined;
66
+ /** @type {any} */ (text_prototype)[TEXT_CACHE] = undefined;
66
67
  }
67
68
 
68
69
  if (DEV) {
@@ -55,33 +55,38 @@ export function flatten(blockers, sync, async, fn) {
55
55
 
56
56
  /** @param {Value[]} values */
57
57
  function finish(values) {
58
+ if ((parent.f & DESTROYED) !== 0) {
59
+ return;
60
+ }
61
+
58
62
  restore();
59
63
 
60
64
  try {
61
65
  fn(values);
62
66
  } catch (error) {
63
- if ((parent.f & DESTROYED) === 0) {
64
- invoke_error_boundary(error, parent);
65
- }
67
+ invoke_error_boundary(error, parent);
66
68
  }
67
69
 
68
70
  unset_context();
69
71
  }
70
72
 
73
+ var decrement_pending = increment_pending();
74
+
71
75
  // Fast path: blockers but no async expressions
72
76
  if (async.length === 0) {
73
- /** @type {Promise<any>} */ (blocker_promise).then(() => finish(sync.map(d)));
77
+ /** @type {Promise<any>} */ (blocker_promise)
78
+ .then(() => finish(sync.map(d)))
79
+ .finally(decrement_pending);
80
+
74
81
  return;
75
82
  }
76
83
 
77
- var decrement_pending = increment_pending();
78
-
79
84
  // Full path: has async expressions
80
85
  function run() {
81
86
  Promise.all(async.map((expression) => async_derived(expression)))
82
87
  .then((result) => finish([...sync.map(d), ...result]))
83
88
  .catch((error) => invoke_error_boundary(error, parent))
84
- .finally(() => decrement_pending());
89
+ .finally(decrement_pending);
85
90
  }
86
91
 
87
92
  if (blocker_promise) {
@@ -213,22 +218,35 @@ export async function* for_await_track_reactivity_loss(iterable) {
213
218
  throw new TypeError('value is not async iterable');
214
219
  }
215
220
 
216
- /** Whether the completion of the iterator was "normal", meaning it wasn't ended via `break` or a similar method */
217
- let normal_completion = false;
221
+ // eslint-disable-next-line no-useless-assignment
222
+ let invoke_return = true;
223
+
218
224
  try {
219
225
  while (true) {
220
226
  const { done, value } = (await track_reactivity_loss(iterator.next()))();
221
227
  if (done) {
222
- normal_completion = true;
228
+ invoke_return = false;
223
229
  break;
224
230
  }
225
231
  var prev = reactivity_loss_tracker;
226
- yield value;
232
+ try {
233
+ yield value;
234
+ } catch (e) {
235
+ set_reactivity_loss_tracker(prev);
236
+ // If the yield throws, we need to call `return` but not return its value, instead rethrow
237
+ if (iterator.return !== undefined) {
238
+ (await track_reactivity_loss(iterator.return()))();
239
+ }
240
+ throw e;
241
+ }
227
242
  set_reactivity_loss_tracker(prev);
228
243
  }
244
+ } catch (error) {
245
+ invoke_return = false;
246
+ throw error;
229
247
  } finally {
230
- // If the iterator had an abrupt completion and `return` is defined on the iterator, call it and return the value
231
- if (!normal_completion && iterator.return !== undefined) {
248
+ // If the iterator had an abrupt completion (break) and `return` is defined on the iterator, call it and return the value
249
+ if (invoke_return && iterator.return !== undefined) {
232
250
  // eslint-disable-next-line no-unsafe-finally
233
251
  return /** @type {TReturn} */ ((await track_reactivity_loss(iterator.return()))().value);
234
252
  }
@@ -310,7 +328,7 @@ export function run(thunks) {
310
328
  // wait one more tick, so that template effects are
311
329
  // guaranteed to run before `$effect(...)`
312
330
  .then(() => Promise.resolve())
313
- .finally(() => decrement_pending());
331
+ .finally(decrement_pending);
314
332
 
315
333
  return blockers;
316
334
  }
@@ -334,8 +352,8 @@ export function increment_pending() {
334
352
  boundary.update_pending_count(1, batch);
335
353
  batch.increment(blocking, effect);
336
354
 
337
- return (skip = false) => {
355
+ return () => {
338
356
  boundary.update_pending_count(-1, batch);
339
- batch.decrement(blocking, effect, skip);
357
+ batch.decrement(blocking, effect);
340
358
  };
341
359
  }