svelte 5.41.3 → 5.42.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 (38) hide show
  1. package/compiler/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/compiler/phases/2-analyze/index.js +1 -1
  4. package/src/compiler/phases/2-analyze/visitors/Attribute.js +5 -183
  5. package/src/compiler/phases/2-analyze/visitors/shared/function.js +0 -7
  6. package/src/compiler/phases/3-transform/client/utils.js +2 -124
  7. package/src/compiler/phases/3-transform/client/visitors/FunctionDeclaration.js +0 -11
  8. package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +0 -18
  9. package/src/compiler/phases/3-transform/client/visitors/shared/events.js +1 -29
  10. package/src/compiler/phases/3-transform/client/visitors/shared/function.js +0 -13
  11. package/src/compiler/phases/3-transform/server/visitors/shared/utils.js +2 -2
  12. package/src/compiler/phases/3-transform/utils.js +0 -19
  13. package/src/compiler/phases/nodes.js +1 -1
  14. package/src/compiler/utils/ast.js +8 -4
  15. package/src/compiler/utils/builders.js +5 -8
  16. package/src/index-client.js +1 -1
  17. package/src/index-server.js +4 -0
  18. package/src/internal/client/constants.js +6 -2
  19. package/src/internal/client/context.js +8 -1
  20. package/src/internal/client/dev/inspect.js +12 -3
  21. package/src/internal/client/dev/tracing.js +1 -2
  22. package/src/internal/client/dom/blocks/boundary.js +29 -3
  23. package/src/internal/client/dom/blocks/branches.js +18 -3
  24. package/src/internal/client/dom/blocks/each.js +1 -1
  25. package/src/internal/client/dom/elements/attributes.js +2 -2
  26. package/src/internal/client/dom/elements/events.js +2 -7
  27. package/src/internal/client/error-handling.js +2 -2
  28. package/src/internal/client/errors.js +48 -0
  29. package/src/internal/client/proxy.js +5 -5
  30. package/src/internal/client/reactivity/batch.js +160 -22
  31. package/src/internal/client/reactivity/deriveds.js +5 -5
  32. package/src/internal/client/reactivity/effects.js +13 -5
  33. package/src/internal/client/reactivity/sources.js +19 -19
  34. package/src/internal/client/runtime.js +2 -2
  35. package/src/utils.js +1 -1
  36. package/src/version.js +1 -1
  37. package/types/index.d.ts +39 -5
  38. package/types/index.d.ts.map +5 -3
@@ -13,10 +13,14 @@ export const INERT = 1 << 13;
13
13
  export const DESTROYED = 1 << 14;
14
14
 
15
15
  // Flags exclusive to effects
16
+ /** Set once an effect that should run synchronously has run */
16
17
  export const EFFECT_RAN = 1 << 15;
17
- /** 'Transparent' effects do not create a transition boundary */
18
+ /**
19
+ * 'Transparent' effects do not create a transition boundary.
20
+ * This is on a block effect 99% of the time but may also be on a branch effect if its parent block effect was pruned
21
+ */
18
22
  export const EFFECT_TRANSPARENT = 1 << 16;
19
- export const INSPECT_EFFECT = 1 << 17;
23
+ export const EAGER_EFFECT = 1 << 17;
20
24
  export const HEAD_EFFECT = 1 << 18;
21
25
  export const EFFECT_PRESERVED = 1 << 19;
22
26
  export const USER_EFFECT = 1 << 20;
@@ -128,7 +128,11 @@ export function setContext(key, context) {
128
128
 
129
129
  if (async_mode_flag) {
130
130
  var flags = /** @type {Effect} */ (active_effect).f;
131
- var valid = !active_reaction && (flags & BRANCH_EFFECT) !== 0 && (flags & EFFECT_RAN) === 0;
131
+ var valid =
132
+ !active_reaction &&
133
+ (flags & BRANCH_EFFECT) !== 0 &&
134
+ // pop() runs synchronously, so this indicates we're setting context after an await
135
+ !(/** @type {ComponentContext} */ (component_context).i);
132
136
 
133
137
  if (!valid) {
134
138
  e.set_context_after_init();
@@ -173,6 +177,7 @@ export function getAllContexts() {
173
177
  export function push(props, runes = false, fn) {
174
178
  component_context = {
175
179
  p: component_context,
180
+ i: false,
176
181
  c: null,
177
182
  e: null,
178
183
  s: props,
@@ -208,6 +213,8 @@ export function pop(component) {
208
213
  context.x = component;
209
214
  }
210
215
 
216
+ context.i = true;
217
+
211
218
  component_context = context.p;
212
219
 
213
220
  if (DEV) {
@@ -1,6 +1,6 @@
1
1
  import { UNINITIALIZED } from '../../../constants.js';
2
2
  import { snapshot } from '../../shared/clone.js';
3
- import { inspect_effect, render_effect, validate_effect } from '../reactivity/effects.js';
3
+ import { eager_effect, render_effect, validate_effect } from '../reactivity/effects.js';
4
4
  import { untrack } from '../runtime.js';
5
5
  import { get_stack } from './tracing.js';
6
6
 
@@ -19,7 +19,7 @@ export function inspect(get_value, inspector, show_stack = false) {
19
19
  // stack traces. As a consequence, reading the value might result
20
20
  // in an error (an `$inspect(object.property)` will run before the
21
21
  // `{#if object}...{/if}` that contains it)
22
- inspect_effect(() => {
22
+ eager_effect(() => {
23
23
  try {
24
24
  var value = get_value();
25
25
  } catch (e) {
@@ -33,8 +33,17 @@ export function inspect(get_value, inspector, show_stack = false) {
33
33
  inspector(...snap);
34
34
 
35
35
  if (!initial) {
36
+ const stack = get_stack('$inspect(...)');
36
37
  // eslint-disable-next-line no-console
37
- console.log(get_stack('UpdatedAt'));
38
+
39
+ if (stack) {
40
+ // eslint-disable-next-line no-console
41
+ console.groupCollapsed('stack trace');
42
+ // eslint-disable-next-line no-console
43
+ console.log(stack);
44
+ // eslint-disable-next-line no-console
45
+ console.groupEnd();
46
+ }
38
47
  }
39
48
  } else {
40
49
  inspector(initial ? 'init' : 'update', ...snap);
@@ -179,8 +179,7 @@ export function get_stack(label) {
179
179
  });
180
180
 
181
181
  define_property(error, 'name', {
182
- // 'Error' suffix is required for stack traces to be rendered properly
183
- value: `${label}Error`
182
+ value: label
184
183
  });
185
184
 
186
185
  return /** @type {Error & { stack: string }} */ (error);
@@ -30,7 +30,6 @@ import {
30
30
  skip_nodes,
31
31
  set_hydrate_node
32
32
  } from '../hydration.js';
33
- import { get_next_sibling } from '../operations.js';
34
33
  import { queue_micro_task } from '../task.js';
35
34
  import * as e from '../../errors.js';
36
35
  import * as w from '../../warnings.js';
@@ -39,6 +38,7 @@ import { Batch, effect_pending_updates } from '../../reactivity/batch.js';
39
38
  import { internal_set, source } from '../../reactivity/sources.js';
40
39
  import { tag } from '../../dev/tracing.js';
41
40
  import { createSubscriber } from '../../../../reactivity/create-subscriber.js';
41
+ import { create_text } from '../operations.js';
42
42
 
43
43
  /**
44
44
  * @typedef {{
@@ -93,6 +93,9 @@ export class Boundary {
93
93
  /** @type {DocumentFragment | null} */
94
94
  #offscreen_fragment = null;
95
95
 
96
+ /** @type {TemplateNode | null} */
97
+ #pending_anchor = null;
98
+
96
99
  #local_pending_count = 0;
97
100
  #pending_count = 0;
98
101
 
@@ -156,8 +159,10 @@ export class Boundary {
156
159
  this.#hydrate_resolved_content();
157
160
  }
158
161
  } else {
162
+ var anchor = this.#get_anchor();
163
+
159
164
  try {
160
- this.#main_effect = branch(() => children(this.#anchor));
165
+ this.#main_effect = branch(() => children(anchor));
161
166
  } catch (error) {
162
167
  this.error(error);
163
168
  }
@@ -168,6 +173,10 @@ export class Boundary {
168
173
  this.#pending = false;
169
174
  }
170
175
  }
176
+
177
+ return () => {
178
+ this.#pending_anchor?.remove();
179
+ };
171
180
  }, flags);
172
181
 
173
182
  if (hydrating) {
@@ -195,9 +204,11 @@ export class Boundary {
195
204
  this.#pending_effect = branch(() => pending(this.#anchor));
196
205
 
197
206
  Batch.enqueue(() => {
207
+ var anchor = this.#get_anchor();
208
+
198
209
  this.#main_effect = this.#run(() => {
199
210
  Batch.ensure();
200
- return branch(() => this.#children(this.#anchor));
211
+ return branch(() => this.#children(anchor));
201
212
  });
202
213
 
203
214
  if (this.#pending_count > 0) {
@@ -212,6 +223,19 @@ export class Boundary {
212
223
  });
213
224
  }
214
225
 
226
+ #get_anchor() {
227
+ var anchor = this.#anchor;
228
+
229
+ if (this.#pending) {
230
+ this.#pending_anchor = create_text();
231
+ this.#anchor.before(this.#pending_anchor);
232
+
233
+ anchor = this.#pending_anchor;
234
+ }
235
+
236
+ return anchor;
237
+ }
238
+
215
239
  /**
216
240
  * Returns `true` if the effect exists inside a boundary whose pending snippet is shown
217
241
  * @returns {boolean}
@@ -253,6 +277,7 @@ export class Boundary {
253
277
 
254
278
  if (this.#main_effect !== null) {
255
279
  this.#offscreen_fragment = document.createDocumentFragment();
280
+ this.#offscreen_fragment.append(/** @type {TemplateNode} */ (this.#pending_anchor));
256
281
  move_effect(this.#main_effect, this.#offscreen_fragment);
257
282
  }
258
283
 
@@ -402,6 +427,7 @@ export class Boundary {
402
427
  if (failed) {
403
428
  queue_micro_task(() => {
404
429
  this.#failed_effect = this.#run(() => {
430
+ Batch.ensure();
405
431
  this.#is_creating_fallback = true;
406
432
 
407
433
  try {
@@ -1,5 +1,4 @@
1
1
  /** @import { Effect, TemplateNode } from '#client' */
2
- import { is_runes } from '../../context.js';
3
2
  import { Batch, current_batch } from '../../reactivity/batch.js';
4
3
  import {
5
4
  branch,
@@ -8,7 +7,6 @@ import {
8
7
  pause_effect,
9
8
  resume_effect
10
9
  } from '../../reactivity/effects.js';
11
- import { set_should_intro, should_intro } from '../../render.js';
12
10
  import { hydrate_node, hydrating } from '../hydration.js';
13
11
  import { create_text, should_defer_append } from '../operations.js';
14
12
 
@@ -126,6 +124,22 @@ export class BranchManager {
126
124
  }
127
125
  };
128
126
 
127
+ /**
128
+ * @param {Batch} batch
129
+ */
130
+ #discard = (batch) => {
131
+ this.#batches.delete(batch);
132
+
133
+ const keys = Array.from(this.#batches.values());
134
+
135
+ for (const [k, branch] of this.#offscreen) {
136
+ if (!keys.includes(k)) {
137
+ destroy_effect(branch.effect);
138
+ this.#offscreen.delete(k);
139
+ }
140
+ }
141
+ };
142
+
129
143
  /**
130
144
  *
131
145
  * @param {any} key
@@ -173,7 +187,8 @@ export class BranchManager {
173
187
  }
174
188
  }
175
189
 
176
- batch.add_callback(this.#commit);
190
+ batch.oncommit(this.#commit);
191
+ batch.ondiscard(this.#discard);
177
192
  } else {
178
193
  if (hydrating) {
179
194
  this.anchor = hydrate_node;
@@ -310,7 +310,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
310
310
  }
311
311
  }
312
312
 
313
- batch.add_callback(commit);
313
+ batch.oncommit(commit);
314
314
  } else {
315
315
  commit();
316
316
  }
@@ -7,7 +7,7 @@ import { add_form_reset_listener, autofocus } from './misc.js';
7
7
  import * as w from '../../warnings.js';
8
8
  import { LOADING_ATTR_SYMBOL } from '#client/constants';
9
9
  import { queue_micro_task } from '../task.js';
10
- import { is_capture_event, is_delegated, normalize_attribute } from '../../../../utils.js';
10
+ import { is_capture_event, can_delegate_event, normalize_attribute } from '../../../../utils.js';
11
11
  import {
12
12
  active_effect,
13
13
  active_reaction,
@@ -378,7 +378,7 @@ function set_attributes(
378
378
  const opts = {};
379
379
  const event_handle_key = '$$' + key;
380
380
  let event_name = key.slice(2);
381
- var delegated = is_delegated(event_name);
381
+ var delegated = can_delegate_event(event_name);
382
382
 
383
383
  if (is_capture_event(event_name)) {
384
384
  event_name = event_name.slice(0, -7);
@@ -1,5 +1,5 @@
1
1
  import { teardown } from '../../reactivity/effects.js';
2
- import { define_property, is_array } from '../../../shared/utils.js';
2
+ import { define_property } from '../../../shared/utils.js';
3
3
  import { hydrating } from '../hydration.js';
4
4
  import { queue_micro_task } from '../task.js';
5
5
  import { FILENAME } from '../../../../constants.js';
@@ -258,12 +258,7 @@ export function handle_event_propagation(event) {
258
258
  // -> the target could not have been disabled because it emits the event in the first place
259
259
  event.target === current_target)
260
260
  ) {
261
- if (is_array(delegated)) {
262
- var [fn, ...data] = delegated;
263
- fn.apply(current_target, [event, ...data]);
264
- } else {
265
- delegated.call(current_target, event);
266
- }
261
+ delegated.call(current_target, event);
267
262
  }
268
263
  } catch (error) {
269
264
  if (throw_error) {
@@ -29,7 +29,7 @@ export function handle_error(error) {
29
29
  // if the error occurred while creating this subtree, we let it
30
30
  // bubble up until it hits a boundary that can handle it
31
31
  if ((effect.f & BOUNDARY_EFFECT) === 0) {
32
- if (!effect.parent && error instanceof Error) {
32
+ if (DEV && !effect.parent && error instanceof Error) {
33
33
  apply_adjustments(error);
34
34
  }
35
35
 
@@ -61,7 +61,7 @@ export function invoke_error_boundary(error, effect) {
61
61
  effect = effect.parent;
62
62
  }
63
63
 
64
- if (error instanceof Error) {
64
+ if (DEV && error instanceof Error) {
65
65
  apply_adjustments(error);
66
66
  }
67
67
 
@@ -229,6 +229,22 @@ export function effect_update_depth_exceeded() {
229
229
  }
230
230
  }
231
231
 
232
+ /**
233
+ * Cannot use `fork(...)` unless the `experimental.async` compiler option is `true`
234
+ * @returns {never}
235
+ */
236
+ export function experimental_async_fork() {
237
+ if (DEV) {
238
+ const error = new Error(`experimental_async_fork\nCannot use \`fork(...)\` unless the \`experimental.async\` compiler option is \`true\`\nhttps://svelte.dev/e/experimental_async_fork`);
239
+
240
+ error.name = 'Svelte error';
241
+
242
+ throw error;
243
+ } else {
244
+ throw new Error(`https://svelte.dev/e/experimental_async_fork`);
245
+ }
246
+ }
247
+
232
248
  /**
233
249
  * Cannot use `flushSync` inside an effect
234
250
  * @returns {never}
@@ -245,6 +261,38 @@ export function flush_sync_in_effect() {
245
261
  }
246
262
  }
247
263
 
264
+ /**
265
+ * Cannot commit a fork that was already committed or discarded
266
+ * @returns {never}
267
+ */
268
+ export function fork_discarded() {
269
+ if (DEV) {
270
+ const error = new Error(`fork_discarded\nCannot commit a fork that was already committed or discarded\nhttps://svelte.dev/e/fork_discarded`);
271
+
272
+ error.name = 'Svelte error';
273
+
274
+ throw error;
275
+ } else {
276
+ throw new Error(`https://svelte.dev/e/fork_discarded`);
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Cannot create a fork inside an effect or when state changes are pending
282
+ * @returns {never}
283
+ */
284
+ export function fork_timing() {
285
+ if (DEV) {
286
+ const error = new Error(`fork_timing\nCannot create a fork inside an effect or when state changes are pending\nhttps://svelte.dev/e/fork_timing`);
287
+
288
+ error.name = 'Svelte error';
289
+
290
+ throw error;
291
+ } else {
292
+ throw new Error(`https://svelte.dev/e/fork_timing`);
293
+ }
294
+ }
295
+
248
296
  /**
249
297
  * `getAbortSignal()` can only be called inside an effect or derived
250
298
  * @returns {never}
@@ -19,8 +19,8 @@ import {
19
19
  state as source,
20
20
  set,
21
21
  increment,
22
- flush_inspect_effects,
23
- set_inspect_effects_deferred
22
+ flush_eager_effects,
23
+ set_eager_effects_deferred
24
24
  } from './reactivity/sources.js';
25
25
  import { PROXY_PATH_SYMBOL, STATE_SYMBOL } from '#client/constants';
26
26
  import { UNINITIALIZED } from '../../constants.js';
@@ -53,7 +53,7 @@ export function proxy(value) {
53
53
  var is_proxied_array = is_array(value);
54
54
  var version = source(0);
55
55
 
56
- var stack = DEV && tracing_mode_flag ? get_stack('CreatedAt') : null;
56
+ var stack = DEV && tracing_mode_flag ? get_stack('created at') : null;
57
57
  var parent_version = update_version;
58
58
 
59
59
  /**
@@ -421,9 +421,9 @@ function inspectable_array(array) {
421
421
  * @param {any[]} args
422
422
  */
423
423
  return function (...args) {
424
- set_inspect_effects_deferred();
424
+ set_eager_effects_deferred();
425
425
  var result = value.apply(this, args);
426
- flush_inspect_effects();
426
+ flush_eager_effects();
427
427
  return result;
428
428
  };
429
429
  }