svelte 5.55.4 → 5.55.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.4",
5
+ "version": "5.55.6",
6
6
  "type": "module",
7
7
  "types": "./types/index.d.ts",
8
8
  "engines": {
@@ -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
 
@@ -48,7 +48,8 @@ export const EFFECT_OFFSCREEN = 1 << 25;
48
48
  /**
49
49
  * Tells that we marked this derived and its reactions as visited during the "mark as (maybe) dirty"-phase.
50
50
  * Will be lifted during execution of the derived and during checking its dirty state (both are necessary
51
- * because a derived might be checked but not executed).
51
+ * because a derived might be checked but not executed). This is a pure performance optimization flag and
52
+ * should not be used for any other purpose!
52
53
  */
53
54
  export const WAS_MARKED = 1 << 16;
54
55
 
@@ -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);
@@ -584,7 +584,7 @@ function get_setters(element) {
584
584
  var element_proto = Element.prototype;
585
585
 
586
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
587
+ // Do not use constructor.name here as that's unreliable in some browser environments
588
588
  while (element_proto !== proto) {
589
589
  descriptors = get_descriptors(proto);
590
590
 
@@ -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.
@@ -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;
@@ -115,10 +115,17 @@ export function animation(element, get_fn, get_params) {
115
115
  ) {
116
116
  const options = get_fn()(this.element, { from, to }, get_params?.());
117
117
 
118
- animation = animate(this.element, options, undefined, 1, () => {
119
- animation?.abort();
120
- animation = undefined;
121
- });
118
+ animation = animate(
119
+ this.element,
120
+ options,
121
+ undefined,
122
+ 1,
123
+ () => {},
124
+ () => {
125
+ animation?.abort();
126
+ animation = undefined;
127
+ }
128
+ );
122
129
  }
123
130
  },
124
131
  fix() {
@@ -239,15 +246,24 @@ export function transition(flags, element, get_fn, get_params) {
239
246
  intro?.abort();
240
247
  }
241
248
 
242
- intro = animate(element, get_options(), outro, 1, () => {
243
- dispatch_event(element, 'introend');
244
-
245
- // Ensure we cancel the animation to prevent leaking
246
- intro?.abort();
247
- intro = current_options = undefined;
248
-
249
- element.style.overflow = overflow;
250
- });
249
+ intro = animate(
250
+ element,
251
+ get_options(),
252
+ outro,
253
+ 1,
254
+ () => {
255
+ dispatch_event(element, 'introstart');
256
+ },
257
+ () => {
258
+ dispatch_event(element, 'introend');
259
+
260
+ // Ensure we cancel the animation to prevent leaking
261
+ intro?.abort();
262
+ intro = current_options = undefined;
263
+
264
+ element.style.overflow = overflow;
265
+ }
266
+ );
251
267
  },
252
268
  out(fn) {
253
269
  if (!is_outro) {
@@ -258,10 +274,19 @@ export function transition(flags, element, get_fn, get_params) {
258
274
 
259
275
  element.inert = true;
260
276
 
261
- outro = animate(element, get_options(), intro, 0, () => {
262
- dispatch_event(element, 'outroend');
263
- fn?.();
264
- });
277
+ outro = animate(
278
+ element,
279
+ get_options(),
280
+ intro,
281
+ 0,
282
+ () => {
283
+ dispatch_event(element, 'outrostart');
284
+ },
285
+ () => {
286
+ dispatch_event(element, 'outroend');
287
+ fn?.();
288
+ }
289
+ );
265
290
  },
266
291
  stop: () => {
267
292
  intro?.abort();
@@ -306,10 +331,11 @@ export function transition(flags, element, get_fn, get_params) {
306
331
  * @param {AnimationConfig | ((opts: { direction: 'in' | 'out' }) => AnimationConfig)} options
307
332
  * @param {Animation | undefined} counterpart The corresponding intro/outro to this outro/intro
308
333
  * @param {number} t2 The target `t` value — `1` for intro, `0` for outro
334
+ * @param {(() => void)} on_begin Called just before beginning the animation
309
335
  * @param {(() => void)} on_finish Called after successfully completing the animation
310
336
  * @returns {Animation}
311
337
  */
312
- function animate(element, options, counterpart, t2, on_finish) {
338
+ function animate(element, options, counterpart, t2, on_begin, on_finish) {
313
339
  var is_intro = t2 === 1;
314
340
 
315
341
  if (is_function(options)) {
@@ -323,7 +349,7 @@ function animate(element, options, counterpart, t2, on_finish) {
323
349
  queue_micro_task(() => {
324
350
  if (aborted) return;
325
351
  var o = options({ direction: is_intro ? 'in' : 'out' });
326
- a = animate(element, o, counterpart, t2, on_finish);
352
+ a = animate(element, o, counterpart, t2, on_begin, on_finish);
327
353
  });
328
354
 
329
355
  // ...but we want to do so without using `async`/`await` everywhere, so
@@ -342,7 +368,7 @@ function animate(element, options, counterpart, t2, on_finish) {
342
368
  counterpart?.deactivate();
343
369
 
344
370
  if (!options?.duration && !options?.delay) {
345
- dispatch_event(element, is_intro ? 'introstart' : 'outrostart');
371
+ on_begin();
346
372
  on_finish();
347
373
 
348
374
  return {
@@ -382,7 +408,7 @@ function animate(element, options, counterpart, t2, on_finish) {
382
408
  // remove dummy animation from the stack to prevent conflict with main animation
383
409
  animation.cancel();
384
410
 
385
- dispatch_event(element, is_intro ? 'introstart' : 'outrostart');
411
+ on_begin();
386
412
 
387
413
  // for bidirectional transitions, we start from the current position,
388
414
  // rather than doing a full intro/outro
@@ -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
  }