Skip to content

Commit 883d194

Browse files
committed
fix: improve ssr derived output to ensure memoization remains
1 parent b2e9be2 commit 883d194

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
@@ -541,11 +541,88 @@ const javascript_visitors = {
541541

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

548-
if (rune === '$state' || rune === '$state.frozen') {
625+
if (rune === '$state' || rune === '$state.frozen' || rune === '$derived') {
549626
return {
550627
...node,
551628
value:
@@ -554,26 +631,6 @@ const javascript_visitors_runes = {
554631
: /** @type {import('estree').Expression} */ (visit(node.value.arguments[0]))
555632
};
556633
}
557-
if (rune === '$derived') {
558-
return {
559-
type: 'MethodDefinition',
560-
kind: 'get',
561-
key: node.key,
562-
computed: false,
563-
static: false,
564-
value: b.function(
565-
null,
566-
[],
567-
b.block([
568-
b.return(
569-
node.value.arguments.length === 0
570-
? null
571-
: /** @type {import('estree').Expression} */ (visit(node.value.arguments[0]))
572-
)
573-
])
574-
)
575-
};
576-
}
577634
if (rune === '$derived.by') {
578635
return {
579636
...node,

0 commit comments

Comments
 (0)