svelte 5.41.0 → 5.41.1

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.
@@ -1,19 +1,16 @@
1
- /** @import { Effect, TemplateNode } from '#client' */
2
- /** @import { Batch } from '../../reactivity/batch.js'; */
1
+ /** @import { TemplateNode } from '#client' */
3
2
  import { EFFECT_TRANSPARENT } from '#client/constants';
4
3
  import {
5
4
  hydrate_next,
6
- hydrate_node,
7
5
  hydrating,
8
6
  read_hydration_instruction,
9
7
  skip_nodes,
10
8
  set_hydrate_node,
11
9
  set_hydrating
12
10
  } from '../hydration.js';
13
- import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js';
14
- import { HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js';
15
- import { create_text, should_defer_append } from '../operations.js';
16
- import { current_batch } from '../../reactivity/batch.js';
11
+ import { block } from '../../reactivity/effects.js';
12
+ import { HYDRATION_START_ELSE } from '../../../../constants.js';
13
+ import { BranchManager } from './branches.js';
17
14
 
18
15
  // TODO reinstate https://github.com/sveltejs/svelte/pull/15250
19
16
 
@@ -28,122 +25,46 @@ export function if_block(node, fn, elseif = false) {
28
25
  hydrate_next();
29
26
  }
30
27
 
31
- var anchor = node;
32
-
33
- /** @type {Effect | null} */
34
- var consequent_effect = null;
35
-
36
- /** @type {Effect | null} */
37
- var alternate_effect = null;
38
-
39
- /** @type {typeof UNINITIALIZED | boolean | null} */
40
- var condition = UNINITIALIZED;
41
-
28
+ var branches = new BranchManager(node);
42
29
  var flags = elseif ? EFFECT_TRANSPARENT : 0;
43
30
 
44
- var has_branch = false;
45
-
46
- const set_branch = (/** @type {(anchor: Node) => void} */ fn, flag = true) => {
47
- has_branch = true;
48
- update_branch(flag, fn);
49
- };
50
-
51
- /** @type {DocumentFragment | null} */
52
- var offscreen_fragment = null;
53
-
54
- function commit() {
55
- if (offscreen_fragment !== null) {
56
- // remove the anchor
57
- /** @type {Text} */ (offscreen_fragment.lastChild).remove();
58
-
59
- anchor.before(offscreen_fragment);
60
- offscreen_fragment = null;
61
- }
62
-
63
- var active = condition ? consequent_effect : alternate_effect;
64
- var inactive = condition ? alternate_effect : consequent_effect;
65
-
66
- if (active) {
67
- resume_effect(active);
68
- }
69
-
70
- if (inactive) {
71
- pause_effect(inactive, () => {
72
- if (condition) {
73
- alternate_effect = null;
74
- } else {
75
- consequent_effect = null;
76
- }
77
- });
78
- }
79
- }
80
-
81
- const update_branch = (
82
- /** @type {boolean | null} */ new_condition,
83
- /** @type {null | ((anchor: Node) => void)} */ fn
84
- ) => {
85
- if (condition === (condition = new_condition)) return;
86
-
87
- /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
88
- let mismatch = false;
89
-
31
+ /**
32
+ * @param {boolean} condition,
33
+ * @param {null | ((anchor: Node) => void)} fn
34
+ */
35
+ function update_branch(condition, fn) {
90
36
  if (hydrating) {
91
- const is_else = read_hydration_instruction(anchor) === HYDRATION_START_ELSE;
37
+ const is_else = read_hydration_instruction(node) === HYDRATION_START_ELSE;
92
38
 
93
- if (!!condition === is_else) {
39
+ if (condition === is_else) {
94
40
  // Hydration mismatch: remove everything inside the anchor and start fresh.
95
41
  // This could happen with `{#if browser}...{/if}`, for example
96
- anchor = skip_nodes();
42
+ var anchor = skip_nodes();
97
43
 
98
44
  set_hydrate_node(anchor);
99
- set_hydrating(false);
100
- mismatch = true;
101
- }
102
- }
45
+ branches.anchor = anchor;
103
46
 
104
- var defer = should_defer_append();
105
- var target = anchor;
106
-
107
- if (defer) {
108
- offscreen_fragment = document.createDocumentFragment();
109
- offscreen_fragment.append((target = create_text()));
110
- }
47
+ set_hydrating(false);
48
+ branches.ensure(condition, fn);
49
+ set_hydrating(true);
111
50
 
112
- if (condition) {
113
- consequent_effect ??= fn && branch(() => fn(target));
114
- } else {
115
- alternate_effect ??= fn && branch(() => fn(target));
51
+ return;
52
+ }
116
53
  }
117
54
 
118
- if (defer) {
119
- var batch = /** @type {Batch} */ (current_batch);
120
-
121
- var active = condition ? consequent_effect : alternate_effect;
122
- var inactive = condition ? alternate_effect : consequent_effect;
123
-
124
- if (active) batch.skipped_effects.delete(active);
125
- if (inactive) batch.skipped_effects.add(inactive);
55
+ branches.ensure(condition, fn);
56
+ }
126
57
 
127
- batch.add_callback(commit);
128
- } else {
129
- commit();
130
- }
58
+ block(() => {
59
+ var has_branch = false;
131
60
 
132
- if (mismatch) {
133
- // continue in hydration mode
134
- set_hydrating(true);
135
- }
136
- };
61
+ fn((fn, flag = true) => {
62
+ has_branch = true;
63
+ update_branch(flag, fn);
64
+ });
137
65
 
138
- block(() => {
139
- has_branch = false;
140
- fn(set_branch);
141
66
  if (!has_branch) {
142
- update_branch(null, null);
67
+ update_branch(false, null);
143
68
  }
144
69
  }, flags);
145
-
146
- if (hydrating) {
147
- anchor = hydrate_node;
148
- }
149
70
  }
@@ -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,13 +204,18 @@ 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
221
  var pending = boundary.is_pending();
@@ -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);
@@ -553,15 +553,16 @@ export function unlink_effect(effect) {
553
553
  * A paused effect does not update, and the DOM subtree becomes inert.
554
554
  * @param {Effect} effect
555
555
  * @param {() => void} [callback]
556
+ * @param {boolean} [destroy]
556
557
  */
557
- export function pause_effect(effect, callback) {
558
+ export function pause_effect(effect, callback, destroy = true) {
558
559
  /** @type {TransitionManager[]} */
559
560
  var transitions = [];
560
561
 
561
562
  pause_children(effect, transitions, true);
562
563
 
563
564
  run_out_transitions(transitions, () => {
564
- destroy_effect(effect);
565
+ if (destroy) destroy_effect(effect);
565
566
  if (callback) callback();
566
567
  });
567
568
  }
@@ -662,3 +663,20 @@ function resume_children(effect, local) {
662
663
  export function aborted(effect = /** @type {Effect} */ (active_effect)) {
663
664
  return (effect.f & DESTROYED) !== 0;
664
665
  }
666
+
667
+ /**
668
+ * @param {Effect} effect
669
+ * @param {DocumentFragment} fragment
670
+ */
671
+ export function move_effect(effect, fragment) {
672
+ var node = effect.nodes_start;
673
+ var end = effect.nodes_end;
674
+
675
+ while (node !== null) {
676
+ /** @type {TemplateNode | null} */
677
+ var next = node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node));
678
+
679
+ fragment.append(node);
680
+ node = next;
681
+ }
682
+ }
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.41.0';
7
+ export const VERSION = '5.41.1';
8
8
  export const PUBLIC_VERSION = '5';