svelte 5.41.0 → 5.41.2

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 (37) hide show
  1. package/compiler/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/compiler/phases/2-analyze/index.js +3 -3
  4. package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +13 -6
  5. package/src/compiler/phases/2-analyze/visitors/CallExpression.js +1 -1
  6. package/src/compiler/phases/2-analyze/visitors/ConstTag.js +3 -1
  7. package/src/compiler/phases/2-analyze/visitors/EachBlock.js +2 -1
  8. package/src/compiler/phases/2-analyze/visitors/VariableDeclarator.js +0 -6
  9. package/src/compiler/phases/3-transform/client/transform-client.js +4 -1
  10. package/src/compiler/phases/3-transform/client/visitors/CallExpression.js +21 -3
  11. package/src/compiler/phases/3-transform/client/visitors/Fragment.js +7 -2
  12. package/src/compiler/phases/3-transform/client/visitors/LetDirective.js +21 -17
  13. package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +1 -1
  14. package/src/compiler/phases/3-transform/client/visitors/SlotElement.js +1 -1
  15. package/src/compiler/phases/3-transform/client/visitors/SvelteFragment.js +1 -1
  16. package/src/compiler/phases/3-transform/client/visitors/shared/component.js +2 -2
  17. package/src/compiler/phases/3-transform/server/visitors/CallExpression.js +10 -4
  18. package/src/compiler/phases/3-transform/utils.js +14 -25
  19. package/src/internal/client/dev/inspect.js +14 -4
  20. package/src/internal/client/dev/tracing.js +10 -1
  21. package/src/internal/client/dom/blocks/async.js +5 -0
  22. package/src/internal/client/dom/blocks/await.js +71 -137
  23. package/src/internal/client/dom/blocks/boundary.js +7 -26
  24. package/src/internal/client/dom/blocks/branches.js +185 -0
  25. package/src/internal/client/dom/blocks/if.js +28 -107
  26. package/src/internal/client/dom/blocks/key.js +12 -58
  27. package/src/internal/client/dom/blocks/snippet.js +6 -22
  28. package/src/internal/client/dom/blocks/svelte-component.js +7 -63
  29. package/src/internal/client/dom/blocks/svelte-element.js +34 -45
  30. package/src/internal/client/reactivity/async.js +27 -16
  31. package/src/internal/client/reactivity/batch.js +134 -69
  32. package/src/internal/client/reactivity/deriveds.js +19 -10
  33. package/src/internal/client/reactivity/effects.js +20 -2
  34. package/src/internal/client/reactivity/sources.js +1 -1
  35. package/src/internal/server/index.js +0 -9
  36. package/src/version.js +1 -1
  37. package/types/index.d.ts.map +1 -1
@@ -1,12 +1,8 @@
1
- /** @import { Effect, TemplateNode } from '#client' */
2
- /** @import { Batch } from '../../reactivity/batch.js'; */
3
- import { UNINITIALIZED } from '../../../../constants.js';
4
- import { block, branch, pause_effect } from '../../reactivity/effects.js';
5
- import { not_equal, safe_not_equal } from '../../reactivity/equality.js';
1
+ /** @import { TemplateNode } from '#client' */
6
2
  import { is_runes } from '../../context.js';
7
- import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
8
- import { create_text, should_defer_append } from '../operations.js';
9
- import { current_batch } from '../../reactivity/batch.js';
3
+ import { block } from '../../reactivity/effects.js';
4
+ import { hydrate_next, hydrating } from '../hydration.js';
5
+ import { BranchManager } from './branches.js';
10
6
 
11
7
  /**
12
8
  * @template V
@@ -20,60 +16,18 @@ export function key(node, get_key, render_fn) {
20
16
  hydrate_next();
21
17
  }
22
18
 
23
- var anchor = node;
19
+ var branches = new BranchManager(node);
24
20
 
25
- /** @type {V | typeof UNINITIALIZED} */
26
- var key = UNINITIALIZED;
27
-
28
- /** @type {Effect} */
29
- var effect;
30
-
31
- /** @type {Effect} */
32
- var pending_effect;
33
-
34
- /** @type {DocumentFragment | null} */
35
- var offscreen_fragment = null;
36
-
37
- var changed = is_runes() ? not_equal : safe_not_equal;
38
-
39
- function commit() {
40
- if (effect) {
41
- pause_effect(effect);
42
- }
43
-
44
- if (offscreen_fragment !== null) {
45
- // remove the anchor
46
- /** @type {Text} */ (offscreen_fragment.lastChild).remove();
47
-
48
- anchor.before(offscreen_fragment);
49
- offscreen_fragment = null;
50
- }
51
-
52
- effect = pending_effect;
53
- }
21
+ var legacy = !is_runes();
54
22
 
55
23
  block(() => {
56
- if (changed(key, (key = get_key()))) {
57
- var target = anchor;
58
-
59
- var defer = should_defer_append();
60
-
61
- if (defer) {
62
- offscreen_fragment = document.createDocumentFragment();
63
- offscreen_fragment.append((target = create_text()));
64
- }
65
-
66
- pending_effect = branch(() => render_fn(target));
24
+ var key = get_key();
67
25
 
68
- if (defer) {
69
- /** @type {Batch} */ (current_batch).add_callback(commit);
70
- } else {
71
- commit();
72
- }
26
+ // key blocks in Svelte <5 had stupid semantics
27
+ if (legacy && key !== null && typeof key === 'object') {
28
+ key = /** @type {V} */ ({});
73
29
  }
74
- });
75
30
 
76
- if (hydrating) {
77
- anchor = hydrate_node;
78
- }
31
+ branches.ensure(key, render_fn);
32
+ });
79
33
  }
@@ -1,8 +1,8 @@
1
1
  /** @import { Snippet } from 'svelte' */
2
- /** @import { Effect, TemplateNode } from '#client' */
2
+ /** @import { TemplateNode } from '#client' */
3
3
  /** @import { Getters } from '#shared' */
4
4
  import { EFFECT_TRANSPARENT, ELEMENT_NODE } from '#client/constants';
5
- import { branch, block, destroy_effect, teardown } from '../../reactivity/effects.js';
5
+ import { block, teardown } from '../../reactivity/effects.js';
6
6
  import {
7
7
  dev_current_component_function,
8
8
  set_dev_current_component_function
@@ -14,8 +14,8 @@ import * as w from '../../warnings.js';
14
14
  import * as e from '../../errors.js';
15
15
  import { DEV } from 'esm-env';
16
16
  import { get_first_child, get_next_sibling } from '../operations.js';
17
- import { noop } from '../../../shared/utils.js';
18
17
  import { prevent_snippet_stringification } from '../../../shared/validate.js';
18
+ import { BranchManager } from './branches.js';
19
19
 
20
20
  /**
21
21
  * @template {(node: TemplateNode, ...args: any[]) => void} SnippetFn
@@ -25,33 +25,17 @@ import { prevent_snippet_stringification } from '../../../shared/validate.js';
25
25
  * @returns {void}
26
26
  */
27
27
  export function snippet(node, get_snippet, ...args) {
28
- var anchor = node;
29
-
30
- /** @type {SnippetFn | null | undefined} */
31
- // @ts-ignore
32
- var snippet = noop;
33
-
34
- /** @type {Effect | null} */
35
- var snippet_effect;
28
+ var branches = new BranchManager(node);
36
29
 
37
30
  block(() => {
38
- if (snippet === (snippet = get_snippet())) return;
39
-
40
- if (snippet_effect) {
41
- destroy_effect(snippet_effect);
42
- snippet_effect = null;
43
- }
31
+ const snippet = get_snippet() ?? null;
44
32
 
45
33
  if (DEV && snippet == null) {
46
34
  e.invalid_snippet();
47
35
  }
48
36
 
49
- snippet_effect = branch(() => /** @type {SnippetFn} */ (snippet)(anchor, ...args));
37
+ branches.ensure(snippet, snippet && ((anchor) => snippet(anchor, ...args)));
50
38
  }, EFFECT_TRANSPARENT);
51
-
52
- if (hydrating) {
53
- anchor = hydrate_node;
54
- }
55
39
  }
56
40
 
57
41
  /**
@@ -1,10 +1,8 @@
1
- /** @import { TemplateNode, Dom, Effect } from '#client' */
2
- /** @import { Batch } from '../../reactivity/batch.js'; */
1
+ /** @import { TemplateNode, Dom } from '#client' */
3
2
  import { EFFECT_TRANSPARENT } from '#client/constants';
4
- import { block, branch, pause_effect } from '../../reactivity/effects.js';
5
- import { current_batch } from '../../reactivity/batch.js';
6
- import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
7
- import { create_text, should_defer_append } from '../operations.js';
3
+ import { block } from '../../reactivity/effects.js';
4
+ import { hydrate_next, hydrating } from '../hydration.js';
5
+ import { BranchManager } from './branches.js';
8
6
 
9
7
  /**
10
8
  * @template P
@@ -19,64 +17,10 @@ export function component(node, get_component, render_fn) {
19
17
  hydrate_next();
20
18
  }
21
19
 
22
- var anchor = node;
23
-
24
- /** @type {C} */
25
- var component;
26
-
27
- /** @type {Effect | null} */
28
- var effect;
29
-
30
- /** @type {DocumentFragment | null} */
31
- var offscreen_fragment = null;
32
-
33
- /** @type {Effect | null} */
34
- var pending_effect = null;
35
-
36
- function commit() {
37
- if (effect) {
38
- pause_effect(effect);
39
- effect = null;
40
- }
41
-
42
- if (offscreen_fragment) {
43
- // remove the anchor
44
- /** @type {Text} */ (offscreen_fragment.lastChild).remove();
45
-
46
- anchor.before(offscreen_fragment);
47
- offscreen_fragment = null;
48
- }
49
-
50
- effect = pending_effect;
51
- pending_effect = null;
52
- }
20
+ var branches = new BranchManager(node);
53
21
 
54
22
  block(() => {
55
- if (component === (component = get_component())) return;
56
-
57
- var defer = should_defer_append();
58
-
59
- if (component) {
60
- var target = anchor;
61
-
62
- if (defer) {
63
- offscreen_fragment = document.createDocumentFragment();
64
- offscreen_fragment.append((target = create_text()));
65
- if (effect) {
66
- /** @type {Batch} */ (current_batch).skipped_effects.add(effect);
67
- }
68
- }
69
- pending_effect = branch(() => render_fn(target, component));
70
- }
71
-
72
- if (defer) {
73
- /** @type {Batch} */ (current_batch).add_callback(commit);
74
- } else {
75
- commit();
76
- }
23
+ var component = get_component() ?? null;
24
+ branches.ensure(component, component && ((target) => render_fn(target, component)));
77
25
  }, EFFECT_TRANSPARENT);
78
-
79
- if (hydrating) {
80
- anchor = hydrate_node;
81
- }
82
26
  }
@@ -8,13 +8,7 @@ import {
8
8
  set_hydrating
9
9
  } from '../hydration.js';
10
10
  import { create_text, get_first_child } from '../operations.js';
11
- import {
12
- block,
13
- branch,
14
- destroy_effect,
15
- pause_effect,
16
- resume_effect
17
- } from '../../reactivity/effects.js';
11
+ import { block, teardown } from '../../reactivity/effects.js';
18
12
  import { set_should_intro } from '../../render.js';
19
13
  import { current_each_item, set_current_each_item } from './each.js';
20
14
  import { active_effect } from '../../runtime.js';
@@ -23,6 +17,7 @@ import { DEV } from 'esm-env';
23
17
  import { EFFECT_TRANSPARENT, ELEMENT_NODE } from '#client/constants';
24
18
  import { assign_nodes } from '../template.js';
25
19
  import { is_raw_text_element } from '../../../../utils.js';
20
+ import { BranchManager } from './branches.js';
26
21
 
27
22
  /**
28
23
  * @param {Comment | Element} node
@@ -42,12 +37,6 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
42
37
 
43
38
  var filename = DEV && location && component_context?.function[FILENAME];
44
39
 
45
- /** @type {string | null} */
46
- var tag;
47
-
48
- /** @type {string | null} */
49
- var current_tag;
50
-
51
40
  /** @type {null | Element} */
52
41
  var element = null;
53
42
 
@@ -58,9 +47,6 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
58
47
 
59
48
  var anchor = /** @type {TemplateNode} */ (hydrating ? hydrate_node : node);
60
49
 
61
- /** @type {Effect | null} */
62
- var effect;
63
-
64
50
  /**
65
51
  * The keyed `{#each ...}` item block, if any, that this element is inside.
66
52
  * We track this so we can set it when changing the element, allowing any
@@ -68,36 +54,24 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
68
54
  */
69
55
  var each_item_block = current_each_item;
70
56
 
57
+ var branches = new BranchManager(anchor, false);
58
+
71
59
  block(() => {
72
60
  const next_tag = get_tag() || null;
73
61
  var ns = get_namespace ? get_namespace() : is_svg || next_tag === 'svg' ? NAMESPACE_SVG : null;
74
62
 
75
- // Assumption: Noone changes the namespace but not the tag (what would that even mean?)
76
- if (next_tag === tag) return;
77
-
78
- // See explanation of `each_item_block` above
79
- var previous_each_item = current_each_item;
80
- set_current_each_item(each_item_block);
81
-
82
- if (effect) {
83
- if (next_tag === null) {
84
- // start outro
85
- pause_effect(effect, () => {
86
- effect = null;
87
- current_tag = null;
88
- });
89
- } else if (next_tag === current_tag) {
90
- // same tag as is currently rendered — abort outro
91
- resume_effect(effect);
92
- } else {
93
- // tag is changing — destroy immediately, render contents without intro transitions
94
- destroy_effect(effect);
95
- set_should_intro(false);
96
- }
63
+ if (next_tag === null) {
64
+ branches.ensure(null, null);
65
+ set_should_intro(true);
66
+ return;
97
67
  }
98
68
 
99
- if (next_tag && next_tag !== current_tag) {
100
- effect = branch(() => {
69
+ branches.ensure(next_tag, (anchor) => {
70
+ // See explanation of `each_item_block` above
71
+ var previous_each_item = current_each_item;
72
+ set_current_each_item(each_item_block);
73
+
74
+ if (next_tag) {
101
75
  element = hydrating
102
76
  ? /** @type {Element} */ (element)
103
77
  : ns
@@ -149,16 +123,31 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
149
123
  /** @type {Effect} */ (active_effect).nodes_end = element;
150
124
 
151
125
  anchor.before(element);
152
- });
153
- }
126
+ }
127
+
128
+ set_current_each_item(previous_each_item);
129
+
130
+ if (hydrating) {
131
+ set_hydrate_node(anchor);
132
+ }
133
+ });
154
134
 
155
- tag = next_tag;
156
- if (tag) current_tag = tag;
135
+ // revert to the default state after the effect has been created
157
136
  set_should_intro(true);
158
137
 
159
- set_current_each_item(previous_each_item);
138
+ return () => {
139
+ if (next_tag) {
140
+ // if we're in this callback because we're re-running the effect,
141
+ // disable intros (unless no element is currently displayed)
142
+ set_should_intro(false);
143
+ }
144
+ };
160
145
  }, EFFECT_TRANSPARENT);
161
146
 
147
+ teardown(() => {
148
+ set_should_intro(true);
149
+ });
150
+
162
151
  if (was_hydrating) {
163
152
  set_hydrating(true);
164
153
  set_hydrate_node(anchor);
@@ -1,8 +1,13 @@
1
- /** @import { Effect, Value } from '#client' */
2
-
1
+ /** @import { Effect, TemplateNode, Value } from '#client' */
3
2
  import { DESTROYED } from '#client/constants';
4
3
  import { DEV } from 'esm-env';
5
- import { component_context, is_runes, set_component_context } from '../context.js';
4
+ import {
5
+ component_context,
6
+ dev_stack,
7
+ is_runes,
8
+ set_component_context,
9
+ set_dev_stack
10
+ } from '../context.js';
6
11
  import { get_boundary } from '../dom/blocks/boundary.js';
7
12
  import { invoke_error_boundary } from '../error-handling.js';
8
13
  import {
@@ -28,6 +33,7 @@ import {
28
33
  set_hydrating,
29
34
  skip_nodes
30
35
  } from '../dom/hydration.js';
36
+ import { create_text } from '../dom/operations.js';
31
37
 
32
38
  /**
33
39
  *
@@ -80,7 +86,7 @@ export function flatten(sync, async, fn) {
80
86
  * some asynchronous work has happened (so that e.g. `await a + b`
81
87
  * causes `b` to be registered as a dependency).
82
88
  */
83
- function capture() {
89
+ export function capture() {
84
90
  var previous_effect = active_effect;
85
91
  var previous_reaction = active_reaction;
86
92
  var previous_component_context = component_context;
@@ -92,6 +98,10 @@ function capture() {
92
98
  var previous_hydrate_node = hydrate_node;
93
99
  }
94
100
 
101
+ if (DEV) {
102
+ var previous_dev_stack = dev_stack;
103
+ }
104
+
95
105
  return function restore() {
96
106
  set_active_effect(previous_effect);
97
107
  set_active_reaction(previous_reaction);
@@ -105,6 +115,7 @@ function capture() {
105
115
 
106
116
  if (DEV) {
107
117
  set_from_async_derived(null);
118
+ set_dev_stack(previous_dev_stack);
108
119
  }
109
120
  };
110
121
  }
@@ -193,19 +204,24 @@ export function unset_context() {
193
204
  set_active_effect(null);
194
205
  set_active_reaction(null);
195
206
  set_component_context(null);
196
- if (DEV) set_from_async_derived(null);
207
+
208
+ if (DEV) {
209
+ set_from_async_derived(null);
210
+ set_dev_stack(null);
211
+ }
197
212
  }
198
213
 
199
214
  /**
200
- * @param {() => Promise<void>} fn
215
+ * @param {TemplateNode} anchor
216
+ * @param {(target: TemplateNode) => Promise<void>} fn
201
217
  */
202
- export async function async_body(fn) {
218
+ export async function async_body(anchor, fn) {
203
219
  var boundary = get_boundary();
204
220
  var batch = /** @type {Batch} */ (current_batch);
205
- var pending = boundary.is_pending();
221
+ var blocking = !boundary.is_pending();
206
222
 
207
223
  boundary.update_pending_count(1);
208
- if (!pending) batch.increment();
224
+ batch.increment(blocking);
209
225
 
210
226
  var active = /** @type {Effect} */ (active_effect);
211
227
 
@@ -218,7 +234,7 @@ export async function async_body(fn) {
218
234
  }
219
235
 
220
236
  try {
221
- var promise = fn();
237
+ var promise = fn(anchor);
222
238
  } finally {
223
239
  if (next_hydrate_node) {
224
240
  set_hydrate_node(next_hydrate_node);
@@ -238,12 +254,7 @@ export async function async_body(fn) {
238
254
  }
239
255
 
240
256
  boundary.update_pending_count(-1);
241
-
242
- if (pending) {
243
- batch.flush();
244
- } else {
245
- batch.decrement();
246
- }
257
+ batch.decrement(blocking);
247
258
 
248
259
  unset_context();
249
260
  }