Skip to content

Commit 9a488d6

Browse files
authored
fix: place let: declarations before {@const} declarations (#16985)
* fix: place `let:` declarations before `{@const}` declarations * lint * fix
1 parent c8ef540 commit 9a488d6

File tree

12 files changed

+49
-23
lines changed

12 files changed

+49
-23
lines changed

.changeset/brown-insects-burn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: place `let:` declarations before `{@const}` declarations

packages/svelte/src/compiler/phases/3-transform/client/transform-client.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ export function client_component(analysis, options) {
172172
// these are set inside the `Fragment` visitor, and cannot be used until then
173173
init: /** @type {any} */ (null),
174174
consts: /** @type {any} */ (null),
175+
let_directives: /** @type {any} */ (null),
175176
update: /** @type {any} */ (null),
176177
after_update: /** @type {any} */ (null),
177178
template: /** @type {any} */ (null),

packages/svelte/src/compiler/phases/3-transform/client/types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ export interface ComponentClientTransformState extends ClientTransformState {
5454
readonly after_update: Statement[];
5555
/** Transformed `{@const }` declarations */
5656
readonly consts: Statement[];
57+
/** Transformed `let:` directives */
58+
readonly let_directives: Statement[];
5759
/** Memoized expressions */
5860
readonly memoizer: Memoizer;
5961
/** The HTML template string */

packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export function Fragment(node, context) {
6363
...context.state,
6464
init: [],
6565
consts: [],
66+
let_directives: [],
6667
update: [],
6768
after_update: [],
6869
memoizer: new Memoizer(),
@@ -150,7 +151,7 @@ export function Fragment(node, context) {
150151
}
151152
}
152153

153-
body.push(...state.consts);
154+
body.push(...state.let_directives, ...state.consts);
154155

155156
if (has_await) {
156157
body.push(b.if(b.call('$.aborted'), b.return()));

packages/svelte/src/compiler/phases/3-transform/client/visitors/LetDirective.js

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,24 @@ export function LetDirective(node, context) {
2121
};
2222
}
2323

24-
return b.const(
25-
name,
26-
b.call(
27-
'$.derived',
28-
b.thunk(
29-
b.block([
30-
b.let(
31-
/** @type {Expression} */ (node.expression).type === 'ObjectExpression'
32-
? // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
33-
b.object_pattern(node.expression.properties)
34-
: // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
35-
b.array_pattern(node.expression.elements),
36-
b.member(b.id('$$slotProps'), node.name)
37-
),
38-
b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node))))
39-
])
24+
context.state.let_directives.push(
25+
b.const(
26+
name,
27+
b.call(
28+
'$.derived',
29+
b.thunk(
30+
b.block([
31+
b.let(
32+
/** @type {Expression} */ (node.expression).type === 'ObjectExpression'
33+
? // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
34+
b.object_pattern(node.expression.properties)
35+
: // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
36+
b.array_pattern(node.expression.elements),
37+
b.member(b.id('$$slotProps'), node.name)
38+
),
39+
b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node))))
40+
])
41+
)
4042
)
4143
)
4244
);
@@ -46,6 +48,8 @@ export function LetDirective(node, context) {
4648
read: (node) => b.call('$.get', node)
4749
};
4850

49-
return b.const(name, create_derived(context.state, b.member(b.id('$$slotProps'), node.name)));
51+
context.state.let_directives.push(
52+
b.const(name, create_derived(context.state, b.member(b.id('$$slotProps'), node.name)))
53+
);
5054
}
5155
}

packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export function RegularElement(node, context) {
106106

107107
case 'LetDirective':
108108
// visit let directives before everything else, to set state
109-
lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute)));
109+
context.visit(attribute, { ...context.state, let_directives: lets });
110110
break;
111111

112112
case 'OnDirective':

packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export function SlotElement(node, context) {
4949
}
5050
}
5151
} else if (attribute.type === 'LetDirective') {
52-
lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute)));
52+
context.visit(attribute, { ...context.state, let_directives: lets });
5353
}
5454
}
5555

packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteFragment.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
export function SvelteFragment(node, context) {
1010
for (const attribute of node.attributes) {
1111
if (attribute.type === 'LetDirective') {
12-
context.state.init.push(/** @type {ExpressionStatement} */ (context.visit(attribute)));
12+
context.visit(attribute);
1313
}
1414
}
1515

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,15 @@ export function build_component(node, component_name, context) {
101101
if (slot_scope_applies_to_itself) {
102102
for (const attribute of node.attributes) {
103103
if (attribute.type === 'LetDirective') {
104-
lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute)));
104+
context.visit(attribute, { ...context.state, let_directives: lets });
105105
}
106106
}
107107
}
108108

109109
for (const attribute of node.attributes) {
110110
if (attribute.type === 'LetDirective') {
111111
if (!slot_scope_applies_to_itself) {
112-
lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute, states.default)));
112+
context.visit(attribute, { ...states.default, let_directives: lets });
113113
}
114114
} else if (attribute.type === 'OnDirective') {
115115
if (!attribute.expression) {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
html: 'foo'
5+
});

0 commit comments

Comments
 (0)