svelte 5.45.2 → 5.45.4

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 (46) hide show
  1. package/compiler/index.js +1 -1
  2. package/package.json +2 -2
  3. package/src/compiler/phases/1-parse/index.js +32 -16
  4. package/src/compiler/phases/1-parse/read/context.js +3 -12
  5. package/src/compiler/phases/1-parse/state/element.js +79 -55
  6. package/src/compiler/phases/1-parse/state/tag.js +5 -16
  7. package/src/compiler/phases/2-analyze/index.js +7 -2
  8. package/src/compiler/phases/2-analyze/visitors/Identifier.js +2 -1
  9. package/src/compiler/phases/2-analyze/visitors/RegularElement.js +3 -2
  10. package/src/compiler/phases/3-transform/client/transform-template/index.js +1 -2
  11. package/src/compiler/phases/3-transform/client/visitors/BindDirective.js +27 -11
  12. package/src/compiler/phases/3-transform/client/visitors/CallExpression.js +2 -1
  13. package/src/compiler/phases/3-transform/client/visitors/Component.js +1 -2
  14. package/src/compiler/phases/3-transform/client/visitors/Fragment.js +1 -1
  15. package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +1 -0
  16. package/src/compiler/phases/3-transform/client/visitors/SvelteComponent.js +2 -1
  17. package/src/compiler/phases/3-transform/client/visitors/SvelteHead.js +1 -1
  18. package/src/compiler/phases/3-transform/client/visitors/SvelteSelf.js +2 -1
  19. package/src/compiler/phases/3-transform/client/visitors/TitleElement.js +1 -1
  20. package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +2 -1
  21. package/src/compiler/phases/3-transform/client/visitors/shared/component.js +27 -25
  22. package/src/compiler/phases/3-transform/client/visitors/shared/events.js +8 -2
  23. package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +10 -4
  24. package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +2 -2
  25. package/src/compiler/phases/3-transform/server/visitors/RegularElement.js +1 -1
  26. package/src/compiler/phases/3-transform/server/visitors/SvelteElement.js +1 -1
  27. package/src/compiler/phases/3-transform/server/visitors/shared/element.js +2 -2
  28. package/src/compiler/phases/nodes.js +4 -2
  29. package/src/compiler/state.js +12 -4
  30. package/src/compiler/utils/builders.js +6 -2
  31. package/src/internal/client/dev/debug.js +365 -7
  32. package/src/internal/client/dom/blocks/each.js +24 -44
  33. package/src/internal/client/dom/blocks/html.js +3 -3
  34. package/src/internal/client/dom/blocks/svelte-element.js +8 -11
  35. package/src/internal/client/dom/elements/transitions.js +15 -7
  36. package/src/internal/client/dom/template.js +12 -10
  37. package/src/internal/client/reactivity/async.js +1 -6
  38. package/src/internal/client/reactivity/batch.js +4 -1
  39. package/src/internal/client/reactivity/effects.js +20 -16
  40. package/src/internal/client/reactivity/sources.js +2 -0
  41. package/src/internal/client/render.js +2 -2
  42. package/src/internal/client/runtime.js +1 -1
  43. package/src/internal/server/hydratable.js +11 -1
  44. package/src/version.js +1 -1
  45. package/types/index.d.ts +18 -11
  46. package/types/index.d.ts.map +1 -1
@@ -1,12 +1,13 @@
1
1
  /** @import { AST } from '#compiler' */
2
2
  /** @import { ComponentContext } from '../types' */
3
3
  import { build_component } from './shared/component.js';
4
+ import * as b from '#compiler/builders';
4
5
 
5
6
  /**
6
7
  * @param {AST.SvelteComponent} node
7
8
  * @param {ComponentContext} context
8
9
  */
9
10
  export function SvelteComponent(node, context) {
10
- const component = build_component(node, '$$component', context);
11
+ const component = build_component(node, '$$component', null, context);
11
12
  context.state.init.push(component);
12
13
  }
@@ -14,7 +14,7 @@ export function SvelteHead(node, context) {
14
14
  context.state.init.push(
15
15
  b.stmt(
16
16
  b.call(
17
- '$.head',
17
+ b.id('$.head', node.name_loc),
18
18
  b.literal(hash(filename)),
19
19
  b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(node.fragment)))
20
20
  )
@@ -1,5 +1,6 @@
1
1
  /** @import { AST } from '#compiler' */
2
2
  /** @import { ComponentContext } from '../types' */
3
+ import { component_name } from '../../../../state.js';
3
4
  import { build_component } from './shared/component.js';
4
5
 
5
6
  /**
@@ -7,6 +8,6 @@ import { build_component } from './shared/component.js';
7
8
  * @param {ComponentContext} context
8
9
  */
9
10
  export function SvelteSelf(node, context) {
10
- const component = build_component(node, context.state.analysis.name, context);
11
+ const component = build_component(node, component_name, node.name_loc, context);
11
12
  context.state.init.push(component);
12
13
  }
@@ -20,7 +20,7 @@ export function TitleElement(node, context) {
20
20
  const statement = b.stmt(
21
21
  b.assignment(
22
22
  '=',
23
- b.id('$.document.title'),
23
+ b.member(b.id('$.document'), b.id('title', node.name_loc)),
24
24
  evaluated.is_known
25
25
  ? b.literal(evaluated.value)
26
26
  : evaluated.is_defined
@@ -136,7 +136,8 @@ export function VariableDeclaration(node, context) {
136
136
  }
137
137
 
138
138
  if (is_state) {
139
- value = b.call('$.state', value);
139
+ const callee = b.id('$.state', /** @type {CallExpression} */ (init).callee.loc);
140
+ value = b.call(callee, value);
140
141
 
141
142
  if (dev) {
142
143
  value = b.call('$.tag', value, b.literal(id.name));
@@ -1,4 +1,4 @@
1
- /** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, Pattern, Property, SequenceExpression, Statement } from 'estree' */
1
+ /** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, Pattern, Property, SequenceExpression, SourceLocation, Statement } from 'estree' */
2
2
  /** @import { AST } from '#compiler' */
3
3
  /** @import { ComponentContext } from '../../types.js' */
4
4
  import { dev, is_ignored } from '../../../../../state.js';
@@ -12,10 +12,11 @@ import { determine_slot } from '../../../../../utils/slot.js';
12
12
  /**
13
13
  * @param {AST.Component | AST.SvelteComponent | AST.SvelteSelf} node
14
14
  * @param {string} component_name
15
+ * @param {SourceLocation | null} loc
15
16
  * @param {ComponentContext} context
16
17
  * @returns {Statement}
17
18
  */
18
- export function build_component(node, component_name, context) {
19
+ export function build_component(node, component_name, loc, context) {
19
20
  /** @type {Expression} */
20
21
  const anchor = context.state.node;
21
22
 
@@ -259,15 +260,9 @@ export function build_component(node, component_name, context) {
259
260
  attribute.expression.type === 'Identifier' &&
260
261
  context.state.scope.get(attribute.expression.name)?.kind === 'store_sub';
261
262
 
262
- // Delay prop pushes so bindings come at the end, to avoid spreads overwriting them
263
- if (is_store_sub) {
264
- push_prop(
265
- b.get(attribute.name, [b.stmt(b.call('$.mark_store_binding')), b.return(expression)]),
266
- true
267
- );
268
- } else {
269
- push_prop(b.get(attribute.name, [b.return(expression)]), true);
270
- }
263
+ const get = is_store_sub
264
+ ? b.get(attribute.name, [b.stmt(b.call('$.mark_store_binding')), b.return(expression)])
265
+ : b.get(attribute.name, [b.return(expression)]);
271
266
 
272
267
  const assignment = b.assignment(
273
268
  '=',
@@ -275,10 +270,16 @@ export function build_component(node, component_name, context) {
275
270
  b.id('$$value')
276
271
  );
277
272
 
278
- push_prop(
279
- b.set(attribute.name, [b.stmt(/** @type {Expression} */ (context.visit(assignment)))]),
280
- true
281
- );
273
+ const set = b.set(attribute.name, [
274
+ b.stmt(/** @type {Expression} */ (context.visit(assignment)))
275
+ ]);
276
+
277
+ get.key.loc = attribute.name_loc;
278
+ set.key.loc = attribute.name_loc;
279
+
280
+ // Delay prop pushes so bindings come at the end, to avoid spreads overwriting them
281
+ push_prop(get, true);
282
+ push_prop(set, true);
282
283
  }
283
284
  }
284
285
  } else if (attribute.type === 'AttachTag') {
@@ -434,16 +435,17 @@ export function build_component(node, component_name, context) {
434
435
 
435
436
  /** @param {Expression} node_id */
436
437
  let fn = (node_id) => {
437
- return b.call(
438
- // TODO We can remove this ternary once we remove legacy mode, since in runes mode dynamic components
439
- // will be handled separately through the `$.component` function, and then the component name will
440
- // always be referenced through just the identifier here.
441
- is_component_dynamic
442
- ? intermediate_name
443
- : /** @type {Expression} */ (context.visit(b.member_id(component_name))),
444
- node_id,
445
- props_expression
446
- );
438
+ // TODO We can remove this ternary once we remove legacy mode, since in runes mode dynamic components
439
+ // will be handled separately through the `$.component` function, and then the component name will
440
+ // always be referenced through just the identifier here.
441
+ const callee = is_component_dynamic
442
+ ? b.id(intermediate_name)
443
+ : /** @type {Expression} */ (context.visit(b.member_id(component_name)));
444
+
445
+ // line up the `Foo` in `Foo(...)` and `<Foo>` for usable stack traces
446
+ callee.loc = loc;
447
+
448
+ return b.call(callee, node_id, props_expression);
447
449
  };
448
450
 
449
451
  if (bind_this !== null) {
@@ -32,7 +32,13 @@ export function visit_event_attribute(node, context) {
32
32
  }
33
33
 
34
34
  context.state.init.push(
35
- b.stmt(b.assignment('=', b.member(context.state.node, '__' + event_name), handler))
35
+ b.stmt(
36
+ b.assignment(
37
+ '=',
38
+ b.member(context.state.node, b.id('__' + event_name, node.name_loc)),
39
+ handler
40
+ )
41
+ )
36
42
  );
37
43
  } else {
38
44
  const statement = b.stmt(
@@ -140,7 +146,7 @@ export function build_event_handler(node, metadata, context) {
140
146
  b.this,
141
147
  b.id('$$args'),
142
148
  b.id(context.state.analysis.name),
143
- loc && b.array([b.literal(loc.line), b.literal(loc.column)]),
149
+ b.array([b.literal(loc.line), b.literal(loc.column)]),
144
150
  has_side_effects(node) && b.true,
145
151
  remove_parens && b.true
146
152
  );
@@ -1,4 +1,4 @@
1
- /** @import { Expression } from 'estree' */
1
+ /** @import { Expression, Identifier, SourceLocation } from 'estree' */
2
2
  /** @import { AST } from '#compiler' */
3
3
  /** @import { ComponentContext } from '../../types' */
4
4
  import { cannot_be_set_statically } from '../../../../../../utils.js';
@@ -42,13 +42,14 @@ export function process_children(nodes, initial, is_element, context) {
42
42
  /**
43
43
  * @param {boolean} is_text
44
44
  * @param {string} name
45
+ * @param {SourceLocation | null} [loc]
45
46
  */
46
- function flush_node(is_text, name) {
47
+ function flush_node(is_text, name, loc) {
47
48
  const expression = get_node(is_text);
48
49
  let id = expression;
49
50
 
50
51
  if (id.type !== 'Identifier') {
51
- id = b.id(context.state.scope.generate(name));
52
+ id = b.id(context.state.scope.generate(name), loc);
52
53
  context.state.init.push(b.var(id, expression));
53
54
  }
54
55
 
@@ -109,7 +110,12 @@ export function process_children(nodes, initial, is_element, context) {
109
110
  ) {
110
111
  node.metadata.is_controlled = true;
111
112
  } else {
112
- const id = flush_node(false, node.type === 'RegularElement' ? node.name : 'node');
113
+ const id = flush_node(
114
+ false,
115
+ node.type === 'RegularElement' ? node.name : 'node',
116
+ node.type === 'RegularElement' ? node.name_loc : null
117
+ );
118
+
113
119
  child_state = { ...context.state, node: id };
114
120
  }
115
121
 
@@ -366,8 +366,8 @@ export function validate_binding(state, binding, expression) {
366
366
  : b.literal(/** @type {Identifier} */ (expression.property).name)
367
367
  )
368
368
  ),
369
- loc && b.literal(loc.line),
370
- loc && b.literal(loc.column)
369
+ b.literal(loc.line),
370
+ b.literal(loc.column)
371
371
  )
372
372
  )
373
373
  );
@@ -99,7 +99,7 @@ export function RegularElement(node, context) {
99
99
  }
100
100
 
101
101
  if (dev) {
102
- const location = /** @type {Location} */ (locator(node.start));
102
+ const location = locator(node.start);
103
103
  state.template.push(
104
104
  b.stmt(
105
105
  b.call(
@@ -50,7 +50,7 @@ export function SvelteElement(node, context) {
50
50
  build_element_attributes(node, { ...context, state }, optimiser.transform);
51
51
 
52
52
  if (dev) {
53
- const location = /** @type {Location} */ (locator(node.start));
53
+ const location = locator(node.start);
54
54
  statements.push(
55
55
  b.stmt(
56
56
  b.call(
@@ -142,7 +142,7 @@ export function build_element_attributes(node, context, transform) {
142
142
  );
143
143
 
144
144
  attributes.push(
145
- create_attribute('checked', -1, -1, [
145
+ create_attribute('checked', null, -1, -1, [
146
146
  {
147
147
  type: 'ExpressionTag',
148
148
  start: -1,
@@ -165,7 +165,7 @@ export function build_element_attributes(node, context, transform) {
165
165
  );
166
166
  } else {
167
167
  attributes.push(
168
- create_attribute(attribute.name, -1, -1, [
168
+ create_attribute(attribute.name, null, -1, -1, [
169
169
  {
170
170
  type: 'ExpressionTag',
171
171
  start: -1,
@@ -1,4 +1,4 @@
1
- /** @import { Expression, PrivateIdentifier } from 'estree' */
1
+ /** @import { Expression, PrivateIdentifier, SourceLocation } from 'estree' */
2
2
  /** @import { AST, Binding } from '#compiler' */
3
3
  import * as b from '#compiler/builders';
4
4
 
@@ -47,17 +47,19 @@ export function is_custom_element_node(node) {
47
47
 
48
48
  /**
49
49
  * @param {string} name
50
+ * @param {SourceLocation | null} name_loc
50
51
  * @param {number} start
51
52
  * @param {number} end
52
53
  * @param {AST.Attribute['value']} value
53
54
  * @returns {AST.Attribute}
54
55
  */
55
- export function create_attribute(name, start, end, value) {
56
+ export function create_attribute(name, name_loc, start, end, value) {
56
57
  return {
57
58
  type: 'Attribute',
58
59
  start,
59
60
  end,
60
61
  name,
62
+ name_loc,
61
63
  value,
62
64
  metadata: {
63
65
  delegated: false,
@@ -40,19 +40,28 @@ export let dev;
40
40
 
41
41
  export let runes = false;
42
42
 
43
- export let locator = getLocator('', { offsetLine: 1 });
43
+ /** @type {(index: number) => Location} */
44
+ export let locator;
44
45
 
45
46
  /** @param {string} value */
46
47
  export function set_source(value) {
47
48
  source = value;
48
- locator = getLocator(source, { offsetLine: 1 });
49
+
50
+ const l = getLocator(source, { offsetLine: 1 });
51
+
52
+ locator = (i) => {
53
+ const loc = l(i);
54
+ if (!loc) throw new Error('An impossible situation occurred');
55
+
56
+ return loc;
57
+ };
49
58
  }
50
59
 
51
60
  /**
52
61
  * @param {AST.SvelteNode & { start?: number | undefined }} node
53
62
  */
54
63
  export function locate_node(node) {
55
- const loc = /** @type {Location} */ (locator(/** @type {number} */ (node.start)));
64
+ const loc = locator(/** @type {number} */ (node.start));
56
65
  return `${sanitize_location(filename)}:${loc?.line}:${loc.column}`;
57
66
  }
58
67
 
@@ -103,7 +112,6 @@ export function reset(state) {
103
112
  runes = false;
104
113
  component_name = UNKNOWN_FILENAME;
105
114
  source = '';
106
- locator = () => undefined;
107
115
  filename = (state.filename ?? UNKNOWN_FILENAME).replace(/\\/g, '/');
108
116
  warning_filter = state.warning ?? (() => true);
109
117
  warnings = [];
@@ -262,10 +262,14 @@ export function get(name, body) {
262
262
 
263
263
  /**
264
264
  * @param {string} name
265
+ * @param {ESTree.SourceLocation | null} [loc]
265
266
  * @returns {ESTree.Identifier}
266
267
  */
267
- export function id(name) {
268
- return { type: 'Identifier', name };
268
+ export function id(name, loc) {
269
+ const node = /** @type {ESTree.Identifier} */ ({ type: 'Identifier', name });
270
+ if (loc) node.loc = loc;
271
+
272
+ return node;
269
273
  }
270
274
 
271
275
  /**