Skip to content

Commit 61e7442

Browse files
authored
fix: improve ssr derived output to ensure memoization remains (#10664)
1 parent 56315df commit 61e7442

File tree

1 file changed

+78
-21
lines changed

1 file changed

+78
-21
lines changed

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

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -546,11 +546,88 @@ const javascript_visitors = {
546546

547547
/** @type {import('./types').Visitors} */
548548
const javascript_visitors_runes = {
549+
ClassBody(node, { state, visit, next }) {
550+
if (!state.analysis.runes) {
551+
next();
552+
}
553+
/** @type {import('estree').PropertyDefinition[]} */
554+
const deriveds = [];
555+
/** @type {import('estree').MethodDefinition | null} */
556+
let constructor = null;
557+
// Get the constructor
558+
for (const definition of node.body) {
559+
if (definition.type === 'MethodDefinition' && definition.kind === 'constructor') {
560+
constructor = /** @type {import('estree').MethodDefinition} */ (visit(definition));
561+
}
562+
}
563+
// Move $derived() runes to the end of the body if there is a constructor
564+
if (constructor !== null) {
565+
const body = [];
566+
for (const definition of node.body) {
567+
if (
568+
definition.type === 'PropertyDefinition' &&
569+
(definition.key.type === 'Identifier' || definition.key.type === 'PrivateIdentifier')
570+
) {
571+
const is_private = definition.key.type === 'PrivateIdentifier';
572+
573+
if (definition.value?.type === 'CallExpression') {
574+
const rune = get_rune(definition.value, state.scope);
575+
576+
if (rune === '$derived') {
577+
deriveds.push(/** @type {import('estree').PropertyDefinition} */ (visit(definition)));
578+
if (is_private) {
579+
// Keep the private #name initializer if private, but remove initial value
580+
body.push({
581+
...definition,
582+
value: null
583+
});
584+
}
585+
continue;
586+
}
587+
}
588+
}
589+
if (definition.type !== 'MethodDefinition' || definition.kind !== 'constructor') {
590+
body.push(
591+
/** @type {import('estree').PropertyDefinition | import('estree').MethodDefinition | import('estree').StaticBlock} */ (
592+
visit(definition)
593+
)
594+
);
595+
}
596+
}
597+
if (deriveds.length > 0) {
598+
body.push({
599+
...constructor,
600+
value: {
601+
...constructor.value,
602+
body: b.block([
603+
...constructor.value.body.body,
604+
...deriveds.map((d) => {
605+
return b.stmt(
606+
b.assignment(
607+
'=',
608+
b.member(b.this, d.key),
609+
/** @type {import('estree').Expression} */ (d.value)
610+
)
611+
);
612+
})
613+
])
614+
}
615+
});
616+
} else {
617+
body.push(constructor);
618+
}
619+
return {
620+
...node,
621+
body
622+
};
623+
}
624+
next();
625+
},
549626
PropertyDefinition(node, { state, next, visit }) {
550627
if (node.value != null && node.value.type === 'CallExpression') {
551628
const rune = get_rune(node.value, state.scope);
552629

553-
if (rune === '$state' || rune === '$state.frozen') {
630+
if (rune === '$state' || rune === '$state.frozen' || rune === '$derived') {
554631
return {
555632
...node,
556633
value:
@@ -559,26 +636,6 @@ const javascript_visitors_runes = {
559636
: /** @type {import('estree').Expression} */ (visit(node.value.arguments[0]))
560637
};
561638
}
562-
if (rune === '$derived') {
563-
return {
564-
type: 'MethodDefinition',
565-
kind: 'get',
566-
key: node.key,
567-
computed: false,
568-
static: false,
569-
value: b.function(
570-
null,
571-
[],
572-
b.block([
573-
b.return(
574-
node.value.arguments.length === 0
575-
? null
576-
: /** @type {import('estree').Expression} */ (visit(node.value.arguments[0]))
577-
)
578-
])
579-
)
580-
};
581-
}
582639
if (rune === '$derived.by') {
583640
return {
584641
...node,

0 commit comments

Comments
 (0)