svelte 5.43.15 → 5.44.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.
Files changed (36) hide show
  1. package/compiler/index.js +1 -1
  2. package/package.json +2 -1
  3. package/src/compiler/migrate/index.js +2 -2
  4. package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +11 -5
  5. package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +1 -1
  6. package/src/compiler/phases/3-transform/server/visitors/ConstTag.js +8 -1
  7. package/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +1 -1
  8. package/src/compiler/phases/3-transform/shared/transform-async.js +15 -16
  9. package/src/index-client.js +1 -0
  10. package/src/index-server.js +2 -0
  11. package/src/internal/client/dev/debug.js +25 -2
  12. package/src/internal/client/dev/inspect.js +2 -2
  13. package/src/internal/client/dev/tracing.js +0 -57
  14. package/src/internal/client/dom/blocks/each.js +14 -2
  15. package/src/internal/client/dom/elements/bindings/this.js +1 -1
  16. package/src/internal/client/dom/elements/misc.js +1 -1
  17. package/src/internal/client/errors.js +21 -4
  18. package/src/internal/client/hydratable.js +33 -0
  19. package/src/internal/client/proxy.js +3 -2
  20. package/src/internal/client/reactivity/batch.js +1 -1
  21. package/src/internal/client/reactivity/deriveds.js +2 -2
  22. package/src/internal/client/reactivity/sources.js +4 -3
  23. package/src/internal/client/runtime.js +4 -3
  24. package/src/internal/client/warnings.js +12 -0
  25. package/src/internal/server/dev.js +10 -0
  26. package/src/internal/server/errors.js +66 -0
  27. package/src/internal/server/hydratable.js +136 -0
  28. package/src/internal/server/render-context.js +74 -0
  29. package/src/internal/server/renderer.js +76 -10
  30. package/src/internal/server/warnings.js +24 -1
  31. package/src/internal/shared/dev.js +65 -0
  32. package/src/internal/shared/errors.js +17 -0
  33. package/src/internal/shared/utils.js +1 -1
  34. package/src/version.js +1 -1
  35. package/types/index.d.ts +1 -0
  36. package/types/index.d.ts.map +4 -1
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.43.15",
5
+ "version": "5.44.1",
6
6
  "type": "module",
7
7
  "types": "./types/index.d.ts",
8
8
  "engines": {
@@ -163,6 +163,7 @@
163
163
  "aria-query": "^5.3.1",
164
164
  "axobject-query": "^4.1.0",
165
165
  "clsx": "^2.1.1",
166
+ "devalue": "^5.5.0",
166
167
  "esm-env": "^1.2.1",
167
168
  "esrap": "^2.1.0",
168
169
  "is-reference": "^3.0.3",
@@ -604,7 +604,7 @@ const instance_script = {
604
604
  'Encountered an export declaration pattern that is not supported for automigration.'
605
605
  );
606
606
  // Turn export let into props. It's really really weird because export let { x: foo, z: [bar]} = ..
607
- // means that foo and bar are the props (i.e. the leafs are the prop names), not x and z.
607
+ // means that foo and bar are the props (i.e. the leaves are the prop names), not x and z.
608
608
  // const tmp = b.id(state.scope.generate('tmp'));
609
609
  // const paths = extract_paths(declarator.id, tmp);
610
610
  // state.props_pre.push(
@@ -1812,7 +1812,7 @@ function handle_events(element, state) {
1812
1812
  }
1813
1813
 
1814
1814
  /**
1815
- * Returns start and end of the node. If the start is preceeded with white-space-only before a line break,
1815
+ * Returns start and end of the node. If the start is preceded with white-space-only before a line break,
1816
1816
  * the start will be the start of the line.
1817
1817
  * @param {string} source
1818
1818
  * @param {LabeledStatement} node
@@ -1,6 +1,7 @@
1
1
  /** @import { Pattern } from 'estree' */
2
2
  /** @import { AST } from '#compiler' */
3
3
  /** @import { ComponentContext } from '../types' */
4
+ /** @import { ExpressionMetadata } from '../../../nodes.js' */
4
5
  import { dev } from '../../../../state.js';
5
6
  import { extract_identifiers } from '../../../../utils/ast.js';
6
7
  import * as b from '#compiler/builders';
@@ -30,7 +31,7 @@ export function ConstTag(node, context) {
30
31
  context.state,
31
32
  declaration.id,
32
33
  expression,
33
- node.metadata.expression.has_await,
34
+ node.metadata.expression,
34
35
  context.state.scope.get_bindings(declaration)
35
36
  );
36
37
  } else {
@@ -73,7 +74,7 @@ export function ConstTag(node, context) {
73
74
  context.state,
74
75
  tmp,
75
76
  expression,
76
- node.metadata.expression.has_await,
77
+ node.metadata.expression,
77
78
  context.state.scope.get_bindings(declaration)
78
79
  );
79
80
 
@@ -89,15 +90,18 @@ export function ConstTag(node, context) {
89
90
  * @param {ComponentContext['state']} state
90
91
  * @param {import('estree').Identifier} id
91
92
  * @param {import('estree').Expression} expression
92
- * @param {boolean} has_await
93
+ * @param {ExpressionMetadata} metadata
93
94
  * @param {import('#compiler').Binding[]} bindings
94
95
  */
95
- function add_const_declaration(state, id, expression, has_await, bindings) {
96
+ function add_const_declaration(state, id, expression, metadata, bindings) {
96
97
  // we need to eagerly evaluate the expression in order to hit any
97
98
  // 'Cannot access x before initialization' errors
98
99
  const after = dev ? [b.stmt(b.call('$.get', id))] : [];
99
100
 
100
- if (has_await || state.async_consts) {
101
+ const has_await = metadata.has_await;
102
+ const blockers = [...metadata.dependencies].map((dep) => dep.blocker).filter((b) => b !== null);
103
+
104
+ if (has_await || state.async_consts || blockers.length > 0) {
101
105
  const run = (state.async_consts ??= {
102
106
  id: b.id(state.scope.generate('promises')),
103
107
  thunks: []
@@ -108,6 +112,8 @@ function add_const_declaration(state, id, expression, has_await, bindings) {
108
112
  const assignment = b.assignment('=', id, expression);
109
113
  const body = after.length === 0 ? assignment : b.block([b.stmt(assignment), ...after]);
110
114
 
115
+ if (blockers.length > 0) run.thunks.push(b.thunk(b.call('Promise.all', b.array(blockers))));
116
+
111
117
  run.thunks.push(b.thunk(body, has_await));
112
118
 
113
119
  const blocker = b.member(run.id, b.literal(run.thunks.length - 1), true);
@@ -305,7 +305,7 @@ export function VariableDeclaration(node, context) {
305
305
  if (has_props) {
306
306
  if (declarator.id.type !== 'Identifier') {
307
307
  // Turn export let into props. It's really really weird because export let { x: foo, z: [bar]} = ..
308
- // means that foo and bar are the props (i.e. the leafs are the prop names), not x and z.
308
+ // means that foo and bar are the props (i.e. the leaves are the prop names), not x and z.
309
309
  const tmp = b.id(context.state.scope.generate('tmp'));
310
310
  const { inserts, paths } = extract_paths(declarator.id, tmp);
311
311
 
@@ -13,8 +13,11 @@ export function ConstTag(node, context) {
13
13
  const id = /** @type {Pattern} */ (context.visit(declaration.id));
14
14
  const init = /** @type {Expression} */ (context.visit(declaration.init));
15
15
  const has_await = node.metadata.expression.has_await;
16
+ const blockers = [...node.metadata.expression.dependencies]
17
+ .map((dep) => dep.blocker)
18
+ .filter((b) => b !== null);
16
19
 
17
- if (has_await || context.state.async_consts) {
20
+ if (has_await || context.state.async_consts || blockers.length > 0) {
18
21
  const run = (context.state.async_consts ??= {
19
22
  id: b.id(context.state.scope.generate('promises')),
20
23
  thunks: []
@@ -27,6 +30,10 @@ export function ConstTag(node, context) {
27
30
  context.state.init.push(b.let(identifier.name));
28
31
  }
29
32
 
33
+ if (blockers.length > 0) {
34
+ run.thunks.push(b.thunk(b.call('Promise.all', b.array(blockers))));
35
+ }
36
+
30
37
  const assignment = b.assignment('=', id, init);
31
38
  run.thunks.push(b.thunk(b.block([b.stmt(assignment)]), has_await));
32
39
 
@@ -119,7 +119,7 @@ export function VariableDeclaration(node, context) {
119
119
  if (has_props) {
120
120
  if (declarator.id.type !== 'Identifier') {
121
121
  // Turn export let into props. It's really really weird because export let { x: foo, z: [bar]} = ..
122
- // means that foo and bar are the props (i.e. the leafs are the prop names), not x and z.
122
+ // means that foo and bar are the props (i.e. the leaves are the prop names), not x and z.
123
123
  const tmp = b.id(context.state.scope.generate('tmp'));
124
124
  const { inserts, paths } = extract_paths(declarator.id, tmp);
125
125
 
@@ -35,7 +35,7 @@ export function transform_body(instance_body, runner, transform) {
35
35
  (node) => /** @type {ESTree.Statement | ESTree.VariableDeclaration} */ (transform(node))
36
36
  );
37
37
 
38
- // Declarations for the await expressions (they will asign to them; need to be hoisted to be available in whole instance scope)
38
+ // Declarations for the await expressions (they will assign to them; need to be hoisted to be available in whole instance scope)
39
39
  if (instance_body.declarations.length > 0) {
40
40
  statements.push(
41
41
  b.declaration(
@@ -53,23 +53,22 @@ export function transform_body(instance_body, runner, transform) {
53
53
  transform(b.var(s.node.id, s.node.init))
54
54
  );
55
55
 
56
- if (visited.declarations.length === 1) {
57
- return b.thunk(
58
- b.assignment('=', visited.declarations[0].id, visited.declarations[0].init ?? b.void0),
59
- s.has_await
60
- );
56
+ const statements = visited.declarations.map((node) => {
57
+ if (node.id.type === 'Identifier' && node.id.name.startsWith('$$d')) {
58
+ // this is an intermediate declaration created in VariableDeclaration.js;
59
+ // subsequent statements depend on it
60
+ return b.var(node.id, node.init);
61
+ }
62
+
63
+ return b.stmt(b.assignment('=', node.id, node.init ?? b.void0));
64
+ });
65
+
66
+ if (statements.length === 1) {
67
+ const statement = /** @type {ESTree.ExpressionStatement} */ (statements[0]);
68
+ return b.thunk(statement.expression, s.has_await);
61
69
  }
62
70
 
63
- // if we have multiple declarations, it indicates destructuring
64
- return b.thunk(
65
- b.block([
66
- b.var(visited.declarations[0].id, visited.declarations[0].init),
67
- ...visited.declarations
68
- .slice(1)
69
- .map((d) => b.stmt(b.assignment('=', d.id, d.init ?? b.void0)))
70
- ]),
71
- s.has_await
72
- );
71
+ return b.thunk(b.block(statements), s.has_await);
73
72
  }
74
73
 
75
74
  if (s.node.type === 'ClassDeclaration') {
@@ -249,6 +249,7 @@ export {
249
249
  hasContext,
250
250
  setContext
251
251
  } from './internal/client/context.js';
252
+ export { hydratable } from './internal/client/hydratable.js';
252
253
  export { hydrate, mount, unmount } from './internal/client/render.js';
253
254
  export { tick, untrack, settled } from './internal/client/runtime.js';
254
255
  export { createRawSnippet } from './internal/client/dom/blocks/snippet.js';
@@ -51,4 +51,6 @@ export {
51
51
  setContext
52
52
  } from './internal/server/context.js';
53
53
 
54
+ export { hydratable } from './internal/server/hydratable.js';
55
+
54
56
  export { createRawSnippet } from './internal/server/blocks/snippet.js';
@@ -12,6 +12,8 @@ import {
12
12
  RENDER_EFFECT,
13
13
  ROOT_EFFECT
14
14
  } from '#client/constants';
15
+ import { snapshot } from '../../shared/clone.js';
16
+ import { untrack } from '../runtime.js';
15
17
 
16
18
  /**
17
19
  *
@@ -84,6 +86,16 @@ export function log_effect_tree(effect, depth = 0) {
84
86
  console.groupEnd();
85
87
  }
86
88
 
89
+ if (effect.nodes_start && effect.nodes_end) {
90
+ // eslint-disable-next-line no-console
91
+ console.log(effect.nodes_start);
92
+
93
+ if (effect.nodes_start !== effect.nodes_end) {
94
+ // eslint-disable-next-line no-console
95
+ console.log(effect.nodes_end);
96
+ }
97
+ }
98
+
87
99
  let child = effect.first;
88
100
  while (child !== null) {
89
101
  log_effect_tree(child, depth + 1);
@@ -103,7 +115,13 @@ function log_dep(dep) {
103
115
  const derived = /** @type {Derived} */ (dep);
104
116
 
105
117
  // eslint-disable-next-line no-console
106
- console.groupCollapsed('%cderived', 'font-weight: normal', derived.v);
118
+ console.groupCollapsed(
119
+ `%c$derived %c${dep.label ?? '<unknown>'}`,
120
+ 'font-weight: bold; color: CornflowerBlue',
121
+ 'font-weight: normal',
122
+ untrack(() => snapshot(derived.v))
123
+ );
124
+
107
125
  if (derived.deps) {
108
126
  for (const d of derived.deps) {
109
127
  log_dep(d);
@@ -114,6 +132,11 @@ function log_dep(dep) {
114
132
  console.groupEnd();
115
133
  } else {
116
134
  // eslint-disable-next-line no-console
117
- console.log('state', dep.v);
135
+ console.log(
136
+ `%c$state %c${dep.label ?? '<unknown>'}`,
137
+ 'font-weight: bold; color: CornflowerBlue',
138
+ 'font-weight: normal',
139
+ untrack(() => snapshot(dep.v))
140
+ );
118
141
  }
119
142
  }
@@ -2,7 +2,7 @@ import { UNINITIALIZED } from '../../../constants.js';
2
2
  import { snapshot } from '../../shared/clone.js';
3
3
  import { eager_effect, render_effect, validate_effect } from '../reactivity/effects.js';
4
4
  import { untrack } from '../runtime.js';
5
- import { get_stack } from './tracing.js';
5
+ import { get_error } from '../../shared/dev.js';
6
6
 
7
7
  /**
8
8
  * @param {() => any[]} get_value
@@ -33,7 +33,7 @@ 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
+ const stack = get_error('$inspect(...)');
37
37
  // eslint-disable-next-line no-console
38
38
 
39
39
  if (stack) {
@@ -1,7 +1,6 @@
1
1
  /** @import { Derived, Reaction, Value } from '#client' */
2
2
  import { UNINITIALIZED } from '../../../constants.js';
3
3
  import { snapshot } from '../../shared/clone.js';
4
- import { define_property } from '../../shared/utils.js';
5
4
  import { DERIVED, ASYNC, PROXY_PATH_SYMBOL, STATE_SYMBOL } from '#client/constants';
6
5
  import { effect_tracking } from '../reactivity/effects.js';
7
6
  import { active_reaction, untrack } from '../runtime.js';
@@ -131,62 +130,6 @@ export function trace(label, fn) {
131
130
  }
132
131
  }
133
132
 
134
- /**
135
- * @param {string} label
136
- * @returns {Error & { stack: string } | null}
137
- */
138
- export function get_stack(label) {
139
- // @ts-ignore stackTraceLimit doesn't exist everywhere
140
- const limit = Error.stackTraceLimit;
141
-
142
- // @ts-ignore
143
- Error.stackTraceLimit = Infinity;
144
- let error = Error();
145
-
146
- // @ts-ignore
147
- Error.stackTraceLimit = limit;
148
-
149
- const stack = error.stack;
150
-
151
- if (!stack) return null;
152
-
153
- const lines = stack.split('\n');
154
- const new_lines = ['\n'];
155
-
156
- for (let i = 0; i < lines.length; i++) {
157
- const line = lines[i];
158
- const posixified = line.replaceAll('\\', '/');
159
-
160
- if (line === 'Error') {
161
- continue;
162
- }
163
-
164
- if (line.includes('validate_each_keys')) {
165
- return null;
166
- }
167
-
168
- if (posixified.includes('svelte/src/internal') || posixified.includes('node_modules/.vite')) {
169
- continue;
170
- }
171
-
172
- new_lines.push(line);
173
- }
174
-
175
- if (new_lines.length === 1) {
176
- return null;
177
- }
178
-
179
- define_property(error, 'stack', {
180
- value: new_lines.join('\n')
181
- });
182
-
183
- define_property(error, 'name', {
184
- value: label
185
- });
186
-
187
- return /** @type {Error & { stack: string }} */ (error);
188
- }
189
-
190
133
  /**
191
134
  * @param {Value} source
192
135
  * @param {string} label
@@ -394,8 +394,12 @@ function reconcile(state, array, anchor, flags, get_key) {
394
394
  key = get_key(value, i);
395
395
  item = /** @type {EachItem} */ (items.get(key));
396
396
 
397
- item.a?.measure();
398
- (to_animate ??= new Set()).add(item);
397
+ // offscreen == coming in now, no animation in that case,
398
+ // else this would happen https://github.com/sveltejs/svelte/issues/17181
399
+ if (item.o) {
400
+ item.a?.measure();
401
+ (to_animate ??= new Set()).add(item);
402
+ }
399
403
  }
400
404
  }
401
405
 
@@ -637,6 +641,10 @@ function link(state, prev, next) {
637
641
  state.first = next;
638
642
  state.effect.first = next && next.e;
639
643
  } else {
644
+ if (prev.e === state.effect.last && next !== null) {
645
+ state.effect.last = next.e;
646
+ }
647
+
640
648
  if (prev.e.next) {
641
649
  prev.e.next.prev = null;
642
650
  }
@@ -648,6 +656,10 @@ function link(state, prev, next) {
648
656
  if (next === null) {
649
657
  state.effect.last = prev && prev.e;
650
658
  } else {
659
+ if (next.e === state.effect.last && prev === null) {
660
+ state.effect.last = next.e.prev;
661
+ }
662
+
651
663
  if (next.e.prev) {
652
664
  next.e.prev.next = null;
653
665
  }
@@ -38,7 +38,7 @@ export function bind_this(element_or_component = {}, update, get_value, get_part
38
38
  untrack(() => {
39
39
  if (element_or_component !== get_value(...parts)) {
40
40
  update(element_or_component, ...parts);
41
- // If this is an effect rerun (cause: each block context changes), then nullfiy the binding at
41
+ // If this is an effect rerun (cause: each block context changes), then nullify the binding at
42
42
  // the previous position if it isn't already taken over by a different effect.
43
43
  if (old_parts && is_bound_this(get_value(...old_parts), element_or_component)) {
44
44
  update(null, ...old_parts);
@@ -51,7 +51,7 @@ export function add_form_reset_listener() {
51
51
  }
52
52
  });
53
53
  },
54
- // In the capture phase to guarantee we get noticed of it (no possiblity of stopPropagation)
54
+ // In the capture phase to guarantee we get noticed of it (no possibility of stopPropagation)
55
55
  { capture: true }
56
56
  );
57
57
  }
@@ -230,18 +230,18 @@ export function effect_update_depth_exceeded() {
230
230
  }
231
231
 
232
232
  /**
233
- * Cannot use `fork(...)` unless the `experimental.async` compiler option is `true`
233
+ * Cannot use `flushSync` inside an effect
234
234
  * @returns {never}
235
235
  */
236
- export function experimental_async_fork() {
236
+ export function flush_sync_in_effect() {
237
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`);
238
+ const error = new Error(`flush_sync_in_effect\nCannot use \`flushSync\` inside an effect\nhttps://svelte.dev/e/flush_sync_in_effect`);
239
239
 
240
240
  error.name = 'Svelte error';
241
241
 
242
242
  throw error;
243
243
  } else {
244
- throw new Error(`https://svelte.dev/e/experimental_async_fork`);
244
+ throw new Error(`https://svelte.dev/e/flush_sync_in_effect`);
245
245
  }
246
246
  }
247
247
 
@@ -293,6 +293,23 @@ export function get_abort_signal_outside_reaction() {
293
293
  }
294
294
  }
295
295
 
296
+ /**
297
+ * Expected to find a hydratable with key `%key%` during hydration, but did not.
298
+ * @param {string} key
299
+ * @returns {never}
300
+ */
301
+ export function hydratable_missing_but_required(key) {
302
+ if (DEV) {
303
+ const error = new Error(`hydratable_missing_but_required\nExpected to find a hydratable with key \`${key}\` during hydration, but did not.\nhttps://svelte.dev/e/hydratable_missing_but_required`);
304
+
305
+ error.name = 'Svelte error';
306
+
307
+ throw error;
308
+ } else {
309
+ throw new Error(`https://svelte.dev/e/hydratable_missing_but_required`);
310
+ }
311
+ }
312
+
296
313
  /**
297
314
  * Failed to hydrate the application
298
315
  * @returns {never}
@@ -0,0 +1,33 @@
1
+ import { async_mode_flag } from '../flags/index.js';
2
+ import { hydrating } from './dom/hydration.js';
3
+ import * as w from './warnings.js';
4
+ import * as e from './errors.js';
5
+ import { DEV } from 'esm-env';
6
+
7
+ /**
8
+ * @template T
9
+ * @param {string} key
10
+ * @param {() => T} fn
11
+ * @returns {T}
12
+ */
13
+ export function hydratable(key, fn) {
14
+ if (!async_mode_flag) {
15
+ e.experimental_async_required('hydratable');
16
+ }
17
+
18
+ if (hydrating) {
19
+ const store = window.__svelte?.h;
20
+
21
+ if (store?.has(key)) {
22
+ return /** @type {T} */ (store.get(key));
23
+ }
24
+
25
+ if (DEV) {
26
+ e.hydratable_missing_but_required(key);
27
+ } else {
28
+ w.hydratable_missing_but_expected(key);
29
+ }
30
+ }
31
+
32
+ return fn();
33
+ }
@@ -25,7 +25,8 @@ import {
25
25
  import { PROXY_PATH_SYMBOL, STATE_SYMBOL } from '#client/constants';
26
26
  import { UNINITIALIZED } from '../../constants.js';
27
27
  import * as e from './errors.js';
28
- import { get_stack, tag } from './dev/tracing.js';
28
+ import { tag } from './dev/tracing.js';
29
+ import { get_error } from '../shared/dev.js';
29
30
  import { tracing_mode_flag } from '../flags/index.js';
30
31
 
31
32
  // TODO move all regexes into shared module?
@@ -53,7 +54,7 @@ export function proxy(value) {
53
54
  var is_proxied_array = is_array(value);
54
55
  var version = source(0);
55
56
 
56
- var stack = DEV && tracing_mode_flag ? get_stack('created at') : null;
57
+ var stack = DEV && tracing_mode_flag ? get_error('created at') : null;
57
58
  var parent_version = update_version;
58
59
 
59
60
  /**
@@ -945,7 +945,7 @@ export function eager(fn) {
945
945
  */
946
946
  export function fork(fn) {
947
947
  if (!async_mode_flag) {
948
- e.experimental_async_fork();
948
+ e.experimental_async_required('fork');
949
949
  }
950
950
 
951
951
  if (current_batch !== null) {
@@ -29,7 +29,7 @@ import * as e from '../errors.js';
29
29
  import * as w from '../warnings.js';
30
30
  import { async_effect, destroy_effect, effect_tracking, teardown } from './effects.js';
31
31
  import { eager_effects, internal_set, set_eager_effects, source } from './sources.js';
32
- import { get_stack } from '../dev/tracing.js';
32
+ import { get_error } from '../../shared/dev.js';
33
33
  import { async_mode_flag, tracing_mode_flag } from '../../flags/index.js';
34
34
  import { Boundary } from '../dom/blocks/boundary.js';
35
35
  import { component_context } from '../context.js';
@@ -84,7 +84,7 @@ export function derived(fn) {
84
84
  };
85
85
 
86
86
  if (DEV && tracing_mode_flag) {
87
- signal.created = get_stack('created at');
87
+ signal.created = get_error('created at');
88
88
  }
89
89
 
90
90
  return signal;
@@ -34,7 +34,8 @@ import {
34
34
  } from '#client/constants';
35
35
  import * as e from '../errors.js';
36
36
  import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js';
37
- import { get_stack, tag_proxy } from '../dev/tracing.js';
37
+ import { tag_proxy } from '../dev/tracing.js';
38
+ import { get_error } from '../../shared/dev.js';
38
39
  import { component_context, is_runes } from '../context.js';
39
40
  import { Batch, batch_values, eager_block_effects, schedule_effect } from './batch.js';
40
41
  import { proxy } from '../proxy.js';
@@ -78,7 +79,7 @@ export function source(v, stack) {
78
79
  };
79
80
 
80
81
  if (DEV && tracing_mode_flag) {
81
- signal.created = stack ?? get_stack('created at');
82
+ signal.created = stack ?? get_error('created at');
82
83
  signal.updated = null;
83
84
  signal.set_during_effect = false;
84
85
  signal.trace = null;
@@ -196,7 +197,7 @@ export function internal_set(source, value) {
196
197
  source.updated.set('', { error: /** @type {any} */ (null), count });
197
198
 
198
199
  if (tracing_mode_flag || count > 5) {
199
- const error = get_stack('updated at');
200
+ const error = get_error('updated at');
200
201
 
201
202
  if (error !== null) {
202
203
  let entry = source.updated.get(error.stack);
@@ -33,7 +33,8 @@ import {
33
33
  update_derived
34
34
  } from './reactivity/deriveds.js';
35
35
  import { async_mode_flag, tracing_mode_flag } from '../flags/index.js';
36
- import { tracing_expressions, get_stack } from './dev/tracing.js';
36
+ import { tracing_expressions } from './dev/tracing.js';
37
+ import { get_error } from '../shared/dev.js';
37
38
  import {
38
39
  component_context,
39
40
  dev_current_component_function,
@@ -554,7 +555,7 @@ export function get(signal) {
554
555
  // if (!tracking && !untracking && !was_read) {
555
556
  // w.await_reactivity_loss(/** @type {string} */ (signal.label));
556
557
 
557
- // var trace = get_stack('traced at');
558
+ // var trace = get_error('traced at');
558
559
  // // eslint-disable-next-line no-console
559
560
  // if (trace) console.warn(trace);
560
561
  // }
@@ -573,7 +574,7 @@ export function get(signal) {
573
574
  if (signal.trace) {
574
575
  signal.trace();
575
576
  } else {
576
- var trace = get_stack('traced at');
577
+ var trace = get_error('traced at');
577
578
 
578
579
  if (trace) {
579
580
  var entry = tracing_expressions.entries.get(signal);
@@ -87,6 +87,18 @@ export function event_handler_invalid(handler, suggestion) {
87
87
  }
88
88
  }
89
89
 
90
+ /**
91
+ * Expected to find a hydratable with key `%key%` during hydration, but did not.
92
+ * @param {string} key
93
+ */
94
+ export function hydratable_missing_but_expected(key) {
95
+ if (DEV) {
96
+ console.warn(`%c[svelte] hydratable_missing_but_expected\n%cExpected to find a hydratable with key \`${key}\` during hydration, but did not.\nhttps://svelte.dev/e/hydratable_missing_but_expected`, bold, normal);
97
+ } else {
98
+ console.warn(`https://svelte.dev/e/hydratable_missing_but_expected`);
99
+ }
100
+ }
101
+
90
102
  /**
91
103
  * The `%attribute%` attribute on `%html%` changed its value between server and client renders. The client value, `%value%`, will be ignored in favour of the server value
92
104
  * @param {string} attribute
@@ -4,6 +4,7 @@ import {
4
4
  is_tag_valid_with_ancestor,
5
5
  is_tag_valid_with_parent
6
6
  } from '../../html-tree-validation.js';
7
+ import { get_stack } from '../shared/dev.js';
7
8
  import { set_ssr_context, ssr_context } from './context.js';
8
9
  import * as e from './errors.js';
9
10
  import { Renderer } from './renderer.js';
@@ -98,3 +99,12 @@ export function validate_snippet_args(renderer) {
98
99
  e.invalid_snippet_arguments();
99
100
  }
100
101
  }
102
+
103
+ export function get_user_code_location() {
104
+ const stack = get_stack();
105
+
106
+ return stack
107
+ .filter((line) => line.trim().startsWith('at '))
108
+ .map((line) => line.replace(/\((.*):\d+:\d+\)$/, (_, file) => `(${file})`))
109
+ .join('\n');
110
+ }