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,6 +1,6 @@
1
1
  /** @import { SSRContext } from '#server' */
2
2
  import { DEV } from 'esm-env';
3
- import * as e from './errors.js';
3
+ import { create_context, get_or_init_context_map } from '../shared/context.js';
4
4
 
5
5
  /** @type {SSRContext | null} */
6
6
  export var ssr_context = null;
@@ -12,22 +12,13 @@ export function set_ssr_context(v) {
12
12
 
13
13
  /**
14
14
  * @template T
15
- * @returns {[() => T, (context: T) => T]}
15
+ * @returns {[() => T, (context: T) => T, () => boolean]}
16
16
  * @since 5.40.0
17
17
  */
18
18
  export function createContext() {
19
- const key = {};
20
-
21
- return [
22
- () => {
23
- if (!hasContext(key)) {
24
- e.missing_context();
25
- }
26
-
27
- return getContext(key);
28
- },
29
- (context) => setContext(key, context)
30
- ];
19
+ return /** @type {[() => T, (context: T) => T, () => boolean]} */ (
20
+ create_context(getContext, setContext, hasContext)
21
+ );
31
22
  }
32
23
 
33
24
  /**
@@ -36,7 +27,7 @@ export function createContext() {
36
27
  * @returns {T}
37
28
  */
38
29
  export function getContext(key) {
39
- const context_map = get_or_init_context_map('getContext');
30
+ const context_map = get_or_init_context_map(ssr_context, 'getContext');
40
31
  const result = /** @type {T} */ (context_map.get(key));
41
32
 
42
33
  return result;
@@ -49,7 +40,7 @@ export function getContext(key) {
49
40
  * @returns {T}
50
41
  */
51
42
  export function setContext(key, context) {
52
- get_or_init_context_map('setContext').set(key, context);
43
+ get_or_init_context_map(ssr_context, 'setContext').set(key, context);
53
44
  return context;
54
45
  }
55
46
 
@@ -58,24 +49,12 @@ export function setContext(key, context) {
58
49
  * @returns {boolean}
59
50
  */
60
51
  export function hasContext(key) {
61
- return get_or_init_context_map('hasContext').has(key);
52
+ return get_or_init_context_map(ssr_context, 'hasContext').has(key);
62
53
  }
63
54
 
64
55
  /** @returns {Map<any, any>} */
65
56
  export function getAllContexts() {
66
- return get_or_init_context_map('getAllContexts');
67
- }
68
-
69
- /**
70
- * @param {string} name
71
- * @returns {Map<unknown, unknown>}
72
- */
73
- function get_or_init_context_map(name) {
74
- if (ssr_context === null) {
75
- e.lifecycle_outside_component(name);
76
- }
77
-
78
- return (ssr_context.c ??= new Map(get_parent_context(ssr_context) || undefined));
57
+ return get_or_init_context_map(ssr_context, 'getAllContexts');
79
58
  }
80
59
 
81
60
  /**
@@ -94,24 +73,6 @@ export function pop() {
94
73
  ssr_context = /** @type {SSRContext} */ (ssr_context).p;
95
74
  }
96
75
 
97
- /**
98
- * @param {SSRContext} ssr_context
99
- * @returns {Map<unknown, unknown> | null}
100
- */
101
- function get_parent_context(ssr_context) {
102
- let parent = ssr_context.p;
103
-
104
- while (parent !== null) {
105
- const context_map = parent.c;
106
- if (context_map !== null) {
107
- return context_map;
108
- }
109
- parent = parent.p;
110
- }
111
-
112
- return null;
113
- }
114
-
115
76
  /**
116
77
  * Wraps an `await` expression in such a way that the component context that was
117
78
  * active before the expression evaluated can be reapplied afterwards —
@@ -1,5 +1,5 @@
1
1
  /** @import { ComponentType, SvelteComponent, Component } from 'svelte' */
2
- /** @import { Csp, RenderOutput } from '#server' */
2
+ /** @import { Csp, RenderOutput } from '../../server/public.js' */
3
3
  /** @import { Store } from '#shared' */
4
4
  export { FILENAME, HMR } from '../../constants.js';
5
5
  import { attr, clsx, to_class, to_style } from '../shared/attributes.js';
@@ -1,8 +1,9 @@
1
1
  /** @import { Component } from 'svelte' */
2
- /** @import { Csp, HydratableContext, RenderOutput, SSRContext, SyncRenderOutput, Sha256Source } from './types.js' */
2
+ /** @import { HydratableContext, SSRContext } from './types.js' */
3
+ /** @import { Csp, RenderOutput, SyncRenderOutput, Sha256Source } from '../../server/public.js' */
3
4
  /** @import { MaybePromise } from '#shared' */
4
5
  import { async_mode_flag } from '../flags/index.js';
5
- import { abort } from './abort-signal.js';
6
+ import { STALE_REACTION } from '../client/constants.js';
6
7
  import { pop, push, set_ssr_context, ssr_context } from './context.js';
7
8
  import * as e from './errors.js';
8
9
  import * as w from './warnings.js';
@@ -12,7 +13,7 @@ import { attributes } from './index.js';
12
13
  import { get_render_context, with_render_context, init_render_context } from './render-context.js';
13
14
  import { sha256 } from './crypto.js';
14
15
  import * as devalue from 'devalue';
15
- import { has_own_property, noop } from '../shared/utils.js';
16
+ import { has_own_property, is_array, noop } from '../shared/utils.js';
16
17
  import { escape_html } from '../../escaping.js';
17
18
 
18
19
  /** @typedef {'head' | 'body'} RendererType */
@@ -88,7 +89,7 @@ export class Renderer {
88
89
  * State that is local to the branch it is declared in.
89
90
  * It will be shallow-copied to all children.
90
91
  *
91
- * @type {{ select_value: string | undefined }}
92
+ * @type {{ select_value: any, multiple: boolean }}
92
93
  */
93
94
  local;
94
95
 
@@ -100,7 +101,7 @@ export class Renderer {
100
101
  this.#parent = parent;
101
102
 
102
103
  this.global = global;
103
- this.local = parent ? { ...parent.local } : { select_value: undefined };
104
+ this.local = parent ? { ...parent.local } : { select_value: undefined, multiple: false };
104
105
  this.type = parent ? parent.type : 'body';
105
106
  }
106
107
 
@@ -179,7 +180,7 @@ export class Renderer {
179
180
  // prevent unhandled rejections, and attach the promise to the renderer instance
180
181
  // so that rejections correctly cause rendering to fail
181
182
  promise.catch(noop);
182
- this.promise = promise;
183
+ this.promise = this.global.track(promise);
183
184
 
184
185
  return promises;
185
186
  }
@@ -224,7 +225,7 @@ export class Renderer {
224
225
  e.await_invalid();
225
226
  }
226
227
 
227
- child.promise = result;
228
+ child.promise = child.global.track(result);
228
229
  }
229
230
 
230
231
  return child;
@@ -272,7 +273,7 @@ export class Renderer {
272
273
  e.await_invalid();
273
274
  }
274
275
  result.catch(noop);
275
- child.promise = result;
276
+ child.promise = child.global.track(result);
276
277
  }
277
278
  } catch (error) {
278
279
  // synchronous errors are handled here, async errors will be handled in #collect_content_async
@@ -292,12 +293,14 @@ export class Renderer {
292
293
  e.await_invalid();
293
294
  }
294
295
 
295
- child.promise = /** @type {Promise<unknown>} */ (result).then((transformed) => {
296
- set_ssr_context(parent_context);
297
- child.#out.push(Renderer.#serialize_failed_boundary(transformed));
298
- failed_snippet(child, transformed, noop);
299
- child.#out.push(BLOCK_CLOSE);
300
- });
296
+ child.promise = child.global.track(
297
+ /** @type {Promise<unknown>} */ (result).then((transformed) => {
298
+ set_ssr_context(parent_context);
299
+ child.#out.push(Renderer.#serialize_failed_boundary(transformed));
300
+ failed_snippet(child, transformed, noop);
301
+ child.#out.push(BLOCK_CLOSE);
302
+ })
303
+ );
301
304
  child.promise.catch(noop);
302
305
  } else {
303
306
  child.#out.push(Renderer.#serialize_failed_boundary(result));
@@ -316,8 +319,11 @@ export class Renderer {
316
319
  */
317
320
  component(fn, component_fn) {
318
321
  push(component_fn);
319
- const child = this.child(fn);
320
- child.#is_component_body = true;
322
+ // mark before running so `onDestroy` callbacks are still collected if `fn` throws
323
+ this.child((renderer) => {
324
+ renderer.#is_component_body = true;
325
+ return fn(renderer);
326
+ });
321
327
  pop();
322
328
  }
323
329
 
@@ -332,11 +338,13 @@ export class Renderer {
332
338
  * @returns {void}
333
339
  */
334
340
  select(attrs, fn, css_hash, classes, styles, flags, is_rich) {
335
- const { value, ...select_attrs } = attrs;
341
+ const { value, defaultValue, ...select_attrs } = attrs;
342
+ if (select_attrs.multiple === '') select_attrs.multiple = true;
336
343
 
337
344
  this.push(`<select${attributes(select_attrs, css_hash, classes, styles, flags)}>`);
338
345
  this.child((renderer) => {
339
- renderer.local.select_value = value;
346
+ renderer.local.select_value = value === undefined ? defaultValue : value;
347
+ renderer.local.multiple = !!select_attrs.multiple;
340
348
  fn(renderer);
341
349
  });
342
350
  this.push(`${is_rich ? '<!>' : ''}</select>`);
@@ -364,7 +372,15 @@ export class Renderer {
364
372
  value = attrs.value;
365
373
  }
366
374
 
367
- if (value === this.local.select_value) {
375
+ var select_value = this.local.select_value;
376
+
377
+ if (
378
+ // Super edge-case, but theoretically someone could use arrays with non-multiple selects,
379
+ // so we gotta check for the multiple attribute presence, too.
380
+ this.local.multiple && is_array(select_value)
381
+ ? select_value.includes(value)
382
+ : value === select_value
383
+ ) {
368
384
  renderer.#out.push(' selected=""');
369
385
  }
370
386
 
@@ -451,6 +467,7 @@ export class Renderer {
451
467
  */
452
468
  copy() {
453
469
  const copy = new Renderer(this.global, this.#parent);
470
+ copy.type = this.type;
454
471
  copy.#out = this.#out.map((item) => (item instanceof Renderer ? item.copy() : item));
455
472
  copy.promise = this.promise;
456
473
  return copy;
@@ -624,6 +641,49 @@ export class Renderer {
624
641
  }
625
642
  }
626
643
 
644
+ /**
645
+ * Runs every `onDestroy` callback in this renderer tree. On a failed render,
646
+ * cleanup errors are suppressed so they do not mask the render error.
647
+ * @param {boolean} suppress_errors
648
+ */
649
+ #run_on_destroy(suppress_errors) {
650
+ let first_error;
651
+ let has_error = false;
652
+
653
+ for (const cleanup of this.#collect_on_destroy()) {
654
+ try {
655
+ cleanup();
656
+ } catch (error) {
657
+ if (!suppress_errors && !has_error) {
658
+ first_error = error;
659
+ has_error = true;
660
+ }
661
+ }
662
+ }
663
+
664
+ if (has_error) throw first_error;
665
+ }
666
+
667
+ /**
668
+ * @param {'sync' | 'async'} mode
669
+ * @param {{ idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} options
670
+ * @returns {Renderer}
671
+ */
672
+ static #create(mode, options) {
673
+ if (options.idPrefix?.includes('--')) {
674
+ e.invalid_id_prefix();
675
+ }
676
+
677
+ return new Renderer(
678
+ new SSRState(
679
+ mode,
680
+ options.idPrefix ? options.idPrefix + '-' : '',
681
+ options.csp,
682
+ options.transformError
683
+ )
684
+ );
685
+ }
686
+
627
687
  /**
628
688
  * Render a component. Throws if any of the children are performing asynchronous work.
629
689
  *
@@ -634,13 +694,27 @@ export class Renderer {
634
694
  */
635
695
  static #render(component, options) {
636
696
  var previous_context = ssr_context;
697
+ const renderer = Renderer.#create('sync', options);
698
+ /** @type {AccumulatedContent | undefined} */
699
+ let result;
700
+ let render_error;
701
+ let failed = false;
702
+
637
703
  try {
638
- const renderer = Renderer.#open_render('sync', component, options);
704
+ try {
705
+ Renderer.#open_render(renderer, component, options);
706
+ result = Renderer.#close_render(renderer.#collect_content(), renderer);
707
+ } catch (error) {
708
+ render_error = error;
709
+ failed = true;
710
+ }
639
711
 
640
- const content = renderer.#collect_content();
641
- return Renderer.#close_render(content, renderer);
712
+ renderer.#run_on_destroy(failed);
713
+ if (failed) throw render_error;
714
+
715
+ return /** @type {AccumulatedContent} */ (result);
642
716
  } finally {
643
- abort();
717
+ renderer.global.abort();
644
718
  set_ssr_context(previous_context);
645
719
  }
646
720
  }
@@ -655,18 +729,35 @@ export class Renderer {
655
729
  */
656
730
  static async #render_async(component, options) {
657
731
  const previous_context = ssr_context;
732
+ const renderer = Renderer.#create('async', options);
733
+ /** @type {(AccumulatedContent & { hashes: { script: Sha256Source[] } }) | undefined} */
734
+ let result;
735
+ let render_error;
736
+ let failed = false;
658
737
 
659
738
  try {
660
- const renderer = Renderer.#open_render('async', component, options);
661
- const content = await renderer.#collect_content_async();
662
- const hydratables = await renderer.#collect_hydratables();
663
- if (hydratables !== null) {
664
- content.head = hydratables + content.head;
739
+ try {
740
+ Renderer.#open_render(renderer, component, options);
741
+ const content = await renderer.#collect_content_async();
742
+ const hydratables = await renderer.#collect_hydratables();
743
+ if (hydratables !== null) {
744
+ content.head = hydratables + content.head;
745
+ }
746
+ result = Renderer.#close_render(content, renderer);
747
+ } catch (error) {
748
+ render_error = error;
749
+ failed = true;
750
+ renderer.global.abort();
751
+ await renderer.global.settle();
665
752
  }
666
- return Renderer.#close_render(content, renderer);
753
+
754
+ renderer.#run_on_destroy(failed);
755
+ if (failed) throw render_error;
756
+
757
+ return /** @type {AccumulatedContent & { hashes: { script: Sha256Source[] } }} */ (result);
667
758
  } finally {
668
759
  set_ssr_context(previous_context);
669
- abort();
760
+ renderer.global.abort();
670
761
  }
671
762
  }
672
763
 
@@ -758,28 +849,15 @@ export class Renderer {
758
849
 
759
850
  /**
760
851
  * @template {Record<string, any>} Props
761
- * @param {'sync' | 'async'} mode
852
+ * @param {Renderer} renderer
762
853
  * @param {import('svelte').Component<Props>} component
763
854
  * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} options
764
- * @returns {Renderer}
855
+ * @returns {void}
765
856
  */
766
- static #open_render(mode, component, options) {
767
- if (options.idPrefix?.includes('--')) {
768
- e.invalid_id_prefix();
769
- }
770
-
857
+ static #open_render(renderer, component, options) {
771
858
  var previous_context = ssr_context;
772
859
 
773
860
  try {
774
- const renderer = new Renderer(
775
- new SSRState(
776
- mode,
777
- options.idPrefix ? options.idPrefix + '-' : '',
778
- options.csp,
779
- options.transformError
780
- )
781
- );
782
-
783
861
  /** @type {SSRContext} */
784
862
  const context = { p: null, c: options.context ?? null, r: renderer };
785
863
  set_ssr_context(context);
@@ -788,8 +866,6 @@ export class Renderer {
788
866
  // @ts-expect-error
789
867
  component(renderer, options.props ?? {});
790
868
  renderer.push(BLOCK_CLOSE);
791
-
792
- return renderer;
793
869
  } finally {
794
870
  set_ssr_context(previous_context);
795
871
  }
@@ -801,10 +877,6 @@ export class Renderer {
801
877
  * @returns {AccumulatedContent & { hashes: { script: Sha256Source[] } }}
802
878
  */
803
879
  static #close_render(content, renderer) {
804
- for (const cleanup of renderer.#collect_on_destroy()) {
805
- cleanup();
806
- }
807
-
808
880
  let head = content.head + renderer.global.get_title();
809
881
  let body = content.body;
810
882
 
@@ -888,6 +960,14 @@ export class SSRState {
888
960
  /** @readonly @type {Set<{ hash: string; code: string }>} */
889
961
  css = new Set();
890
962
 
963
+ /** @type {Set<Promise<unknown>>} */
964
+ #pending = new Set();
965
+
966
+ /** @type {AbortController | null} */
967
+ #controller = null;
968
+
969
+ #aborted = false;
970
+
891
971
  /**
892
972
  * `transformError` passed to `render`. Called when an error boundary catches an error.
893
973
  * Throws by default if unset in `render`.
@@ -918,6 +998,38 @@ export class SSRState {
918
998
  this.uid = () => `${id_prefix}s${uid++}`;
919
999
  }
920
1000
 
1001
+ /**
1002
+ * @template T
1003
+ * @param {Promise<T>} promise
1004
+ * @returns {Promise<T>}
1005
+ */
1006
+ track(promise) {
1007
+ this.#pending.add(promise);
1008
+ promise.then(
1009
+ () => this.#pending.delete(promise),
1010
+ () => this.#pending.delete(promise)
1011
+ );
1012
+ return promise;
1013
+ }
1014
+
1015
+ async settle() {
1016
+ while (this.#pending.size > 0) {
1017
+ await Promise.allSettled([...this.#pending]);
1018
+ }
1019
+ }
1020
+
1021
+ abort() {
1022
+ if (this.#aborted) return;
1023
+ this.#aborted = true;
1024
+ this.#controller?.abort(STALE_REACTION);
1025
+ }
1026
+
1027
+ get_abort_signal() {
1028
+ const controller = (this.#controller ??= new AbortController());
1029
+ if (this.#aborted) controller.abort(STALE_REACTION);
1030
+ return controller.signal;
1031
+ }
1032
+
921
1033
  get_title() {
922
1034
  return this.#title.value;
923
1035
  }
@@ -27,7 +27,8 @@ export function attr(name, value, is_boolean = false) {
27
27
  if (name === 'hidden' && value !== 'until-found') {
28
28
  is_boolean = true;
29
29
  }
30
- if (value == null || (!value && is_boolean)) return '';
30
+ // `''` is a present boolean attribute, as it is in markup and on the client
31
+ if (value == null || (is_boolean && !value && value !== '')) return '';
31
32
  const normalized =
32
33
  (has_own_property.call(replacements, name) && replacements[name].get(value)) || value;
33
34
  const assignment = is_boolean ? `=""` : `="${escape_html(normalized, true)}"`;
@@ -142,8 +143,9 @@ export function to_style(value, styles) {
142
143
  }
143
144
 
144
145
  if (value) {
146
+ // strip comments; surrounding whitespace is handled by the trims below (which is much faster than doing it through regex)
145
147
  value = String(value)
146
- .replaceAll(/\s*\/\*.*?\*\/\s*/g, '')
148
+ .replaceAll(/\/\*.*?\*\//g, '')
147
149
  .trim();
148
150
 
149
151
  /** @type {boolean | '"' | "'"} */
@@ -105,6 +105,8 @@ function clone(value, cloned, path, paths, original = null, no_tojson = false) {
105
105
  }
106
106
 
107
107
  if (value instanceof Date) {
108
+ // Ensure SvelteDate snapshots are tracked
109
+ value.getTime();
108
110
  return /** @type {Snapshot<T>} */ (structuredClone(value));
109
111
  }
110
112
 
@@ -0,0 +1,53 @@
1
+ import { lifecycle_outside_component, missing_context } from './errors.js';
2
+
3
+ /**
4
+ * @template T
5
+ * @param {(key: object) => T} get_context
6
+ * @param {(key: object, context: T) => T} set_context
7
+ * @param {(key: object) => boolean} has_context
8
+ * @returns {[() => T, (context: T) => T, () => boolean]}
9
+ */
10
+ export function create_context(get_context, set_context, has_context) {
11
+ const key = {};
12
+
13
+ return [
14
+ () => {
15
+ if (!has_context(key)) {
16
+ missing_context();
17
+ }
18
+
19
+ return get_context(key);
20
+ },
21
+ (context) => set_context(key, context),
22
+ () => has_context(key)
23
+ ];
24
+ }
25
+
26
+ /**
27
+ * @typedef {{ p: Context | null, c: Map<unknown, unknown> | null }} Context
28
+ */
29
+
30
+ /**
31
+ * @param {Context} context
32
+ * @returns {Map<unknown, unknown> | null}
33
+ */
34
+ function get_parent_context(context) {
35
+ let parent = context.p;
36
+ while (parent !== null && parent.c === null) {
37
+ parent = parent.p;
38
+ }
39
+ return parent?.c ?? null;
40
+ }
41
+
42
+ /**
43
+ * @param {Context | null} context
44
+ * @param {string} name
45
+ * @returns {Map<unknown, unknown>}
46
+ */
47
+ export function get_or_init_context_map(context, name) {
48
+ if (context === null) {
49
+ lifecycle_outside_component(name);
50
+ }
51
+
52
+ return (context.c ??= new Map(get_parent_context(context) || undefined));
53
+ }
@@ -86,12 +86,12 @@ export function lifecycle_outside_component(name) {
86
86
  }
87
87
 
88
88
  /**
89
- * Context was not set in a parent component
89
+ * Context was not set in the current component or any of its ancestors
90
90
  * @returns {never}
91
91
  */
92
92
  export function missing_context() {
93
93
  if (DEV) {
94
- const error = new Error(`missing_context\nContext was not set in a parent component\nhttps://svelte.dev/e/missing_context`);
94
+ const error = new Error(`missing_context\nContext was not set in the current component or any of its ancestors\nhttps://svelte.dev/e/missing_context`);
95
95
 
96
96
  error.name = 'Svelte error';
97
97
 
@@ -1,5 +1,5 @@
1
1
  /** @import { SvelteComponent } from '../index.js' */
2
- /** @import { Csp } from '#server' */
2
+ /** @import { Csp } from '../server/public.js' */
3
3
  import { asClassComponent as as_class_component, createClassComponent } from './legacy-client.js';
4
4
  import { render } from '../internal/server/index.js';
5
5
  import { async_mode_flag } from '../internal/flags/index.js';
@@ -153,6 +153,28 @@ export class SvelteMap extends Map {
153
153
  return super.get(key);
154
154
  }
155
155
 
156
+ /**
157
+ * @param {K} key
158
+ * @param {V} value
159
+ * */
160
+ getOrInsert(key, value) {
161
+ if (!super.has(key)) {
162
+ this.set(key, value);
163
+ }
164
+ return /** @type {V} */ (this.get(key));
165
+ }
166
+
167
+ /**
168
+ * @param {K} key
169
+ * @param {(key: K) => V} callbackFn
170
+ */
171
+ getOrInsertComputed(key, callbackFn) {
172
+ if (!super.has(key)) {
173
+ this.set(key, callbackFn(key));
174
+ }
175
+ return /** @type {V} */ (this.get(key));
176
+ }
177
+
156
178
  /**
157
179
  * @param {K} key
158
180
  * @param {V} value
@@ -163,6 +163,8 @@ export class SvelteURL extends URL {
163
163
  set protocol(value) {
164
164
  super.protocol = value;
165
165
  set(this.#protocol, super.protocol);
166
+ // changing the protocol can clear the port when it matches the new scheme's default
167
+ set(this.#port, super.port);
166
168
  }
167
169
 
168
170
  get search() {
@@ -28,6 +28,17 @@ function split_css_unit(value) {
28
28
  return split ? [parseFloat(split[1]), split[2] || 'px'] : [/** @type {number} */ (value), 'px'];
29
29
  }
30
30
 
31
+ /**
32
+ * Omits unresolved dimensions rather than passing invalid values to Web Animations.
33
+ *
34
+ * @param {string} property
35
+ * @param {number} value
36
+ * @param {number} t
37
+ */
38
+ function css_dimension(property, value, t) {
39
+ return Number.isNaN(value) ? '' : `${property}: ${t * value}px;`;
40
+ }
41
+
31
42
  /**
32
43
  * Animates a `blur` filter alongside an element's opacity.
33
44
  *
@@ -138,13 +149,13 @@ export function slide(node, { delay = 0, duration = 400, easing = cubic_out, axi
138
149
  css: (t) =>
139
150
  'overflow: hidden;' +
140
151
  `opacity: ${Math.min(t * 20, 1) * opacity};` +
141
- `${primary_property}: ${t * primary_property_value}px;` +
142
- `padding-${secondary_properties[0]}: ${t * padding_start_value}px;` +
143
- `padding-${secondary_properties[1]}: ${t * padding_end_value}px;` +
144
- `margin-${secondary_properties[0]}: ${t * margin_start_value}px;` +
145
- `margin-${secondary_properties[1]}: ${t * margin_end_value}px;` +
146
- `border-${secondary_properties[0]}-width: ${t * border_width_start_value}px;` +
147
- `border-${secondary_properties[1]}-width: ${t * border_width_end_value}px;` +
152
+ css_dimension(primary_property, primary_property_value, t) +
153
+ css_dimension(`padding-${secondary_properties[0]}`, padding_start_value, t) +
154
+ css_dimension(`padding-${secondary_properties[1]}`, padding_end_value, t) +
155
+ css_dimension(`margin-${secondary_properties[0]}`, margin_start_value, t) +
156
+ css_dimension(`margin-${secondary_properties[1]}`, margin_end_value, t) +
157
+ css_dimension(`border-${secondary_properties[0]}-width`, border_width_start_value, t) +
158
+ css_dimension(`border-${secondary_properties[1]}-width`, border_width_end_value, t) +
148
159
  `min-${primary_property}: 0`
149
160
  };
150
161
  }
package/src/version.js CHANGED
@@ -4,5 +4,5 @@
4
4
  * The current version, as set in package.json.
5
5
  * @type {string}
6
6
  */
7
- export const VERSION = '5.56.9';
7
+ export const VERSION = '5.57.0';
8
8
  export const PUBLIC_VERSION = '5';