svelte 5.55.8 → 5.55.10

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/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.8",
5
+ "version": "5.55.10",
6
6
  "type": "module",
7
7
  "types": "./types/index.d.ts",
8
8
  "engines": {
@@ -157,7 +157,7 @@
157
157
  "dependencies": {
158
158
  "@jridgewell/remapping": "^2.3.4",
159
159
  "@jridgewell/sourcemap-codec": "^1.5.0",
160
- "@sveltejs/acorn-typescript": "^1.0.5",
160
+ "@sveltejs/acorn-typescript": "^1.0.10",
161
161
  "@types/estree": "^1.0.5",
162
162
  "@types/trusted-types": "^2.0.7",
163
163
  "acorn": "^8.12.1",
@@ -166,14 +166,14 @@
166
166
  "clsx": "^2.1.1",
167
167
  "devalue": "^5.8.1",
168
168
  "esm-env": "^1.2.1",
169
- "esrap": "^2.2.4",
169
+ "esrap": "^2.2.9",
170
170
  "is-reference": "^3.0.3",
171
171
  "locate-character": "^3.0.0",
172
172
  "magic-string": "^0.30.11",
173
173
  "zimmerframe": "^1.1.2"
174
174
  },
175
175
  "scripts": {
176
- "build": "rollup -c && pnpm generate",
176
+ "build": "rollup -c && pnpm generate && node scripts/check-treeshakeability.js",
177
177
  "dev": "node scripts/process-messages -w & rollup -cw",
178
178
  "check": "tsc --project tsconfig.runtime.json && tsc && cd ./tests/types && tsc",
179
179
  "check:tsgo": "tsgo --project tsconfig.runtime.json --skipLibCheck && tsgo --skipLibCheck",
@@ -115,8 +115,7 @@ function is_last_evaluated_expression(path, node) {
115
115
  break;
116
116
 
117
117
  case 'MemberExpression':
118
- if (parent.computed && node === parent.object) return false;
119
- break;
118
+ return false;
120
119
 
121
120
  case 'ObjectExpression':
122
121
  if (node !== parent.properties.at(-1)) return false;
@@ -302,7 +302,7 @@ export function check_element(node, context) {
302
302
  const has_key_event =
303
303
  handlers.has('keydown') || handlers.has('keyup') || handlers.has('keypress');
304
304
  if (!has_key_event) {
305
- w.a11y_click_events_have_key_events(node);
305
+ w.a11y_click_events_have_key_events(node, node.name);
306
306
  }
307
307
  }
308
308
  }
@@ -71,14 +71,14 @@ export function AwaitBlock(node, context) {
71
71
  'await'
72
72
  );
73
73
 
74
- if (node.metadata.expression.has_blockers()) {
74
+ if (node.metadata.expression.has_blockers() || node.metadata.expression.has_await) {
75
75
  context.state.init.push(
76
76
  b.stmt(
77
77
  b.call(
78
78
  '$.async',
79
79
  context.state.node,
80
80
  node.metadata.expression.blockers(),
81
- b.array([]),
81
+ b.array([]), // {#await await ...} is special insofar that the await should not be waited on
82
82
  b.arrow([context.state.node], b.block([stmt]))
83
83
  )
84
84
  )
@@ -201,8 +201,8 @@ export function RegularElement(node, context) {
201
201
  }
202
202
  }
203
203
 
204
- // Let bindings first, they can be used on attributes
205
- context.state.init.push(...lets);
204
+ // Let bindings first, they can be used on attributes and `{@const}` declarations
205
+ context.state.let_directives.push(...lets);
206
206
 
207
207
  const node_id = context.state.node;
208
208
 
@@ -194,11 +194,6 @@ export function VariableDeclaration(node, context) {
194
194
  /** @type {CallExpression} */ (init)
195
195
  );
196
196
 
197
- // for now, only wrap async derived in $.save if it's not
198
- // a top-level instance derived. TODO in future maybe we
199
- // can dewaterfall all of them?
200
- const should_save = context.state.is_instance && context.state.scope.function_depth > 1;
201
-
202
197
  if (declarator.id.type === 'Identifier') {
203
198
  let expression = /** @type {Expression} */ (context.visit(value));
204
199
 
@@ -213,9 +208,7 @@ export function VariableDeclaration(node, context) {
213
208
  location ? b.literal(location) : undefined
214
209
  );
215
210
 
216
- call = should_save ? save(call) : b.await(call);
217
-
218
- declarations.push(b.declarator(declarator.id, call));
211
+ declarations.push(b.declarator(declarator.id, b.await(call)));
219
212
  } else {
220
213
  if (rune === '$derived') expression = b.thunk(expression);
221
214
 
@@ -251,7 +244,7 @@ export function VariableDeclaration(node, context) {
251
244
  location ? b.literal(location) : undefined
252
245
  );
253
246
 
254
- call = should_save ? save(call) : b.await(call);
247
+ call = b.await(call);
255
248
  }
256
249
 
257
250
  declarations.push(b.declarator(id, call));
@@ -386,13 +379,17 @@ export function VariableDeclaration(node, context) {
386
379
  * @param {Expression} value
387
380
  */
388
381
  function create_state_declarators(declarator, context, value) {
382
+ /**
383
+ * @param {Expression} value
384
+ * @param {string} name
385
+ */
386
+ const mutable_source = (value, name) => {
387
+ const call = b.call('$.mutable_source', value, context.state.analysis.immutable && b.true);
388
+ return dev ? b.call('$.tag', call, b.literal(name)) : call;
389
+ };
390
+
389
391
  if (declarator.id.type === 'Identifier') {
390
- return [
391
- b.declarator(
392
- declarator.id,
393
- b.call('$.mutable_source', value, context.state.analysis.immutable ? b.true : undefined)
394
- )
395
- ];
392
+ return [b.declarator(declarator.id, mutable_source(value, declarator.id.name))];
396
393
  }
397
394
 
398
395
  const tmp = b.id(context.state.scope.generate('tmp'));
@@ -414,7 +411,7 @@ function create_state_declarators(declarator, context, value) {
414
411
  return b.declarator(
415
412
  path.node,
416
413
  binding?.kind === 'state'
417
- ? b.call('$.mutable_source', value, context.state.analysis.immutable ? b.true : undefined)
414
+ ? mutable_source(value, /** @type {Identifier} */ (path.node).name)
418
415
  : value
419
416
  );
420
417
  })
@@ -52,7 +52,7 @@ export class Memoizer {
52
52
  * @param {ExpressionMetadata} metadata
53
53
  */
54
54
  check_blockers(metadata) {
55
- for (const binding of metadata.dependencies) {
55
+ for (const binding of metadata.references) {
56
56
  if (binding.blocker) {
57
57
  this.#blockers.add(binding.blocker);
58
58
  }
@@ -9,12 +9,19 @@ import { block_close, create_child_block } from './shared/utils.js';
9
9
  * @param {ComponentContext} context
10
10
  */
11
11
  export function AwaitBlock(node, context) {
12
+ let expression = /** @type {Expression} */ (context.visit(node.expression));
13
+ if (node.metadata.expression.has_await) {
14
+ // If this is an await expression, turn it into a IIFE so that the result is a promise.
15
+ // {#await await ...} is special insofar that the await should not be waited on.
16
+ expression = b.call(b.arrow([], expression, true));
17
+ }
18
+
12
19
  /** @type {Statement} */
13
20
  let statement = b.stmt(
14
21
  b.call(
15
22
  '$.await',
16
23
  b.id('$$renderer'),
17
- /** @type {Expression} */ (context.visit(node.expression)),
24
+ expression,
18
25
  b.thunk(
19
26
  node.pending ? /** @type {BlockStatement} */ (context.visit(node.pending)) : b.block([])
20
27
  ),
@@ -229,18 +229,25 @@ export function build_attribute_value(
229
229
  ? node.data.replace(regex_whitespaces_strict, ' ')
230
230
  : node.data;
231
231
  } else {
232
- expressions.push(
233
- b.call(
234
- '$.stringify',
235
- transform(
236
- /** @type {Expression} */ (context.visit(node.expression)),
237
- node.metadata.expression
238
- )
239
- )
240
- );
232
+ const evaluated = context.state.scope.evaluate(node.expression);
241
233
 
242
- quasi = b.quasi('', i + 1 === value.length);
243
- quasis.push(quasi);
234
+ if (evaluated.is_known) {
235
+ quasi.value.cooked += (evaluated.value ?? '') + '';
236
+ } else {
237
+ const expression = transform(
238
+ /** @type {Expression} */ (context.visit(node.expression)),
239
+ node.metadata.expression
240
+ );
241
+
242
+ expressions.push(
243
+ evaluated.is_string && evaluated.is_defined
244
+ ? expression
245
+ : b.call('$.stringify', expression)
246
+ );
247
+
248
+ quasi = b.quasi('', i + 1 === value.length);
249
+ quasis.push(quasi);
250
+ }
244
251
  }
245
252
  }
246
253
 
@@ -248,7 +255,9 @@ export function build_attribute_value(
248
255
  quasi.value.raw = sanitize_template_string(/** @type {string} */ (quasi.value.cooked));
249
256
  }
250
257
 
251
- return b.template(quasis, expressions);
258
+ return expressions.length > 0
259
+ ? b.template(quasis, expressions)
260
+ : b.literal(/** @type {string} */ (quasi.value.cooked));
252
261
  }
253
262
 
254
263
  /**
@@ -102,8 +102,8 @@ export class ExpressionMetadata {
102
102
  if (!this.#blockers) {
103
103
  this.#blockers = new Set();
104
104
 
105
- for (const d of this.dependencies) {
106
- if (d.blocker) this.#blockers.add(d.blocker);
105
+ for (const r of this.references) {
106
+ if (r.blocker) this.#blockers.add(r.blocker);
107
107
  }
108
108
  }
109
109
 
@@ -166,11 +166,12 @@ export function a11y_autofocus(node) {
166
166
  }
167
167
 
168
168
  /**
169
- * Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate
169
+ * Visible, non-interactive element `<%element%>` with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `<button type="button">` or `<a>` might be more appropriate
170
170
  * @param {null | NodeLike} node
171
+ * @param {string} element
171
172
  */
172
- export function a11y_click_events_have_key_events(node) {
173
- w(node, 'a11y_click_events_have_key_events', `Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as \`<button type="button">\` or \`<a>\` might be more appropriate\nhttps://svelte.dev/e/a11y_click_events_have_key_events`);
173
+ export function a11y_click_events_have_key_events(node, element) {
174
+ w(node, 'a11y_click_events_have_key_events', `Visible, non-interactive element \`<${element}>\` with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as \`<button type="button">\` or \`<a>\` might be more appropriate\nhttps://svelte.dev/e/a11y_click_events_have_key_events`);
174
175
  }
175
176
 
176
177
  /**
@@ -803,11 +804,11 @@ export function script_context_deprecated(node) {
803
804
  }
804
805
 
805
806
  /**
806
- * Unrecognized attribute — should be one of `generics`, `lang` or `module`. If this exists for a preprocessor, ensure that the preprocessor removes it
807
+ * Unrecognised attribute — should be one of `generics`, `lang` or `module`. If this exists for a preprocessor, ensure that the preprocessor removes it
807
808
  * @param {null | NodeLike} node
808
809
  */
809
810
  export function script_unknown_attribute(node) {
810
- w(node, 'script_unknown_attribute', `Unrecognized attribute — should be one of \`generics\`, \`lang\` or \`module\`. If this exists for a preprocessor, ensure that the preprocessor removes it\nhttps://svelte.dev/e/script_unknown_attribute`);
811
+ w(node, 'script_unknown_attribute', `Unrecognised attribute — should be one of \`generics\`, \`lang\` or \`module\`. If this exists for a preprocessor, ensure that the preprocessor removes it\nhttps://svelte.dev/e/script_unknown_attribute`);
811
812
  }
812
813
 
813
814
  /**
@@ -7,7 +7,8 @@ import {
7
7
  hydrating,
8
8
  skip_nodes,
9
9
  set_hydrate_node,
10
- set_hydrating
10
+ set_hydrating,
11
+ hydrate_node
11
12
  } from '../hydration.js';
12
13
  import { queue_micro_task } from '../task.js';
13
14
  import { HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js';
@@ -15,6 +16,7 @@ import { is_runes } from '../../context.js';
15
16
  import { Batch, current_batch, flushSync, is_flushing_sync } from '../../reactivity/batch.js';
16
17
  import { BranchManager } from './branches.js';
17
18
  import { capture, unset_context } from '../../reactivity/async.js';
19
+ import { DEV } from 'esm-env';
18
20
 
19
21
  const PENDING = 0;
20
22
  const THEN = 1;
@@ -42,16 +44,16 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
42
44
  var value = runes ? source(v) : mutable_source(v, false, false);
43
45
  var error = runes ? source(v) : mutable_source(v, false, false);
44
46
 
47
+ if (DEV) {
48
+ value.label = '{#await ...} value';
49
+ error.label = '{#await ...} error';
50
+ }
51
+
45
52
  var branches = new BranchManager(node);
46
53
 
47
54
  block(() => {
48
55
  var batch = /** @type {Batch} */ (current_batch);
49
-
50
- // we null out `current_batch` because otherwise `save(...)` will incorrectly restore it —
51
- // the batch will already have been committed by the time it resolves
52
- batch.deactivate();
53
56
  var input = get_input();
54
- batch.activate();
55
57
 
56
58
  var destroyed = false;
57
59
 
@@ -79,15 +81,17 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
79
81
  // We don't want to restore the previous batch here; {#await} blocks don't follow the async logic
80
82
  // we have elsewhere, instead pending/resolve/fail states are each their own batch so to speak.
81
83
  restore(false);
84
+ // ...but it might still be set here. That means a `save(...)` has restored it — but that batch will
85
+ // likely already have been committed by the time it resolves, and this resolve should be processed
86
+ // in a separate batch. We're not using batch.deactivate()/activate() above because get_input()
87
+ // could write to sources, which would then incorrectly create a new batch or could mess with
88
+ // async_derived expecting a current_batch to exist.
89
+ if (current_batch === batch) {
90
+ batch.deactivate();
91
+ }
82
92
  // Make sure we have a batch, since the branch manager expects one to exist
83
93
  Batch.ensure();
84
94
 
85
- if (hydrating) {
86
- // `restore()` could set `hydrating` to `true`, which we very much
87
- // don't want — we want to restore everything _except_ this
88
- set_hydrating(false);
89
- }
90
-
91
95
  try {
92
96
  fn();
93
97
  } finally {
@@ -90,6 +90,8 @@ export class BranchManager {
90
90
  var offscreen = this.#offscreen.get(key);
91
91
 
92
92
  if (offscreen) {
93
+ // effect could have been outro'ed before through a prior batch — resume if necessary
94
+ resume_effect(offscreen.effect);
93
95
  this.#onscreen.set(key, offscreen.effect);
94
96
  this.#offscreen.delete(key);
95
97
 
@@ -88,9 +88,11 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
88
88
  assign_nodes(element, element);
89
89
 
90
90
  if (render_fn) {
91
+ var tmp_comment = null;
92
+
91
93
  if (hydrating && is_raw_text_element(next_tag)) {
92
- // prevent hydration glitches
93
- element.append(document.createComment(''));
94
+ // prevent hydration glitches (code just below expects an anchor)
95
+ element.append((tmp_comment = document.createComment('')));
94
96
  }
95
97
 
96
98
  // If hydrating, use the existing ssr comment as the anchor so that the
@@ -114,7 +116,7 @@ export function element(node, get_tag, is_svg, render_fn, get_namespace, locatio
114
116
  // contains children, it's a user error (which is warned on elsewhere)
115
117
  // and the DOM will be silently discarded
116
118
  render_fn(element, child_anchor);
117
-
119
+ tmp_comment?.remove();
118
120
  set_animation_effect_override(null);
119
121
  }
120
122
 
@@ -257,12 +257,7 @@ export function handle_event_propagation(event) {
257
257
  var other_errors = [];
258
258
 
259
259
  while (current_target !== null) {
260
- /** @type {null | Element} */
261
- var parent_element =
262
- current_target.assignedSlot ||
263
- current_target.parentNode ||
264
- /** @type {any} */ (current_target).host ||
265
- null;
260
+ if (current_target === handler_element) break;
266
261
 
267
262
  try {
268
263
  // @ts-expect-error
@@ -284,10 +279,10 @@ export function handle_event_propagation(event) {
284
279
  throw_error = error;
285
280
  }
286
281
  }
287
- if (event.cancelBubble || parent_element === handler_element || parent_element === null) {
288
- break;
289
- }
290
- current_target = parent_element;
282
+ if (event.cancelBubble) break;
283
+
284
+ path_idx++;
285
+ current_target = path_idx < path.length ? /** @type {Element} */ (path[path_idx]) : null;
291
286
  }
292
287
 
293
288
  if (throw_error) {
@@ -352,15 +352,15 @@ export function wait(blockers) {
352
352
  */
353
353
  export function increment_pending() {
354
354
  var effect = /** @type {Effect} */ (active_effect);
355
- var boundary = /** @type {Boundary} */ (effect.b);
355
+ var boundary = effect.b; // undefined if called outside the render tree, e.g. a standalone $effect.root
356
356
  var batch = /** @type {Batch} */ (current_batch);
357
- var blocking = boundary.is_rendered();
357
+ var blocking = !!boundary?.is_rendered();
358
358
 
359
- boundary.update_pending_count(1, batch);
359
+ boundary?.update_pending_count(1, batch);
360
360
  batch.increment(blocking, effect);
361
361
 
362
362
  return () => {
363
- boundary.update_pending_count(-1, batch);
363
+ boundary?.update_pending_count(-1, batch);
364
364
  batch.decrement(blocking, effect);
365
365
  };
366
366
  }