fix: place `let:` declarations before `{@const}` declarations (#16985)

* fix: place `let:` declarations before `{@const}` declarations

* lint

* fix
pull/16984/head
ComputerGuy 1 week ago committed by GitHub
parent c8ef540985
commit 9a488d6b25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: place `let:` declarations before `{@const}` declarations

@ -172,6 +172,7 @@ export function client_component(analysis, options) {
// these are set inside the `Fragment` visitor, and cannot be used until then
init: /** @type {any} */ (null),
consts: /** @type {any} */ (null),
let_directives: /** @type {any} */ (null),
update: /** @type {any} */ (null),
after_update: /** @type {any} */ (null),
template: /** @type {any} */ (null),

@ -54,6 +54,8 @@ export interface ComponentClientTransformState extends ClientTransformState {
readonly after_update: Statement[];
/** Transformed `{@const }` declarations */
readonly consts: Statement[];
/** Transformed `let:` directives */
readonly let_directives: Statement[];
/** Memoized expressions */
readonly memoizer: Memoizer;
/** The HTML template string */

@ -63,6 +63,7 @@ export function Fragment(node, context) {
...context.state,
init: [],
consts: [],
let_directives: [],
update: [],
after_update: [],
memoizer: new Memoizer(),
@ -150,7 +151,7 @@ export function Fragment(node, context) {
}
}
body.push(...state.consts);
body.push(...state.let_directives, ...state.consts);
if (has_await) {
body.push(b.if(b.call('$.aborted'), b.return()));

@ -21,22 +21,24 @@ export function LetDirective(node, context) {
};
}
return b.const(
name,
b.call(
'$.derived',
b.thunk(
b.block([
b.let(
/** @type {Expression} */ (node.expression).type === 'ObjectExpression'
? // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
b.object_pattern(node.expression.properties)
: // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
b.array_pattern(node.expression.elements),
b.member(b.id('$$slotProps'), node.name)
),
b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node))))
])
context.state.let_directives.push(
b.const(
name,
b.call(
'$.derived',
b.thunk(
b.block([
b.let(
/** @type {Expression} */ (node.expression).type === 'ObjectExpression'
? // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
b.object_pattern(node.expression.properties)
: // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
b.array_pattern(node.expression.elements),
b.member(b.id('$$slotProps'), node.name)
),
b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node))))
])
)
)
)
);
@ -46,6 +48,8 @@ export function LetDirective(node, context) {
read: (node) => b.call('$.get', node)
};
return b.const(name, create_derived(context.state, b.member(b.id('$$slotProps'), node.name)));
context.state.let_directives.push(
b.const(name, create_derived(context.state, b.member(b.id('$$slotProps'), node.name)))
);
}
}

@ -106,7 +106,7 @@ export function RegularElement(node, context) {
case 'LetDirective':
// visit let directives before everything else, to set state
lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute)));
context.visit(attribute, { ...context.state, let_directives: lets });
break;
case 'OnDirective':

@ -49,7 +49,7 @@ export function SlotElement(node, context) {
}
}
} else if (attribute.type === 'LetDirective') {
lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute)));
context.visit(attribute, { ...context.state, let_directives: lets });
}
}

@ -9,7 +9,7 @@
export function SvelteFragment(node, context) {
for (const attribute of node.attributes) {
if (attribute.type === 'LetDirective') {
context.state.init.push(/** @type {ExpressionStatement} */ (context.visit(attribute)));
context.visit(attribute);
}
}

@ -101,7 +101,7 @@ export function build_component(node, component_name, context) {
if (slot_scope_applies_to_itself) {
for (const attribute of node.attributes) {
if (attribute.type === 'LetDirective') {
lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute)));
context.visit(attribute, { ...context.state, let_directives: lets });
}
}
}
@ -109,7 +109,7 @@ export function build_component(node, component_name, context) {
for (const attribute of node.attributes) {
if (attribute.type === 'LetDirective') {
if (!slot_scope_applies_to_itself) {
lets.push(/** @type {ExpressionStatement} */ (context.visit(attribute, states.default)));
context.visit(attribute, { ...states.default, let_directives: lets });
}
} else if (attribute.type === 'OnDirective') {
if (!attribute.expression) {

@ -0,0 +1,5 @@
import { test } from '../../test';
export default test({
html: 'foo'
});

@ -0,0 +1,7 @@
<script>
import Component from './component.svelte';
</script>
<Component let:data>
{@const thing = data}
{thing}
</Component>
Loading…
Cancel
Save