diff --git a/.changeset/khaki-carrots-destroy.md b/.changeset/khaki-carrots-destroy.md new file mode 100644 index 0000000000..677a83643d --- /dev/null +++ b/.changeset/khaki-carrots-destroy.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: make deriveds on the server lazy again diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index 3498b2636b..7a3d6bef6c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -23,6 +23,7 @@ import { Identifier } from './visitors/Identifier.js'; import { IfBlock } from './visitors/IfBlock.js'; import { KeyBlock } from './visitors/KeyBlock.js'; import { LabeledStatement } from './visitors/LabeledStatement.js'; +import { MemberExpression } from './visitors/MemberExpression.js'; import { PropertyDefinition } from './visitors/PropertyDefinition.js'; import { RegularElement } from './visitors/RegularElement.js'; import { RenderTag } from './visitors/RenderTag.js'; @@ -48,6 +49,7 @@ const global_visitors = { ExpressionStatement, Identifier, LabeledStatement, + MemberExpression, PropertyDefinition, UpdateExpression, VariableDeclaration diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ClassBody.js index 6797b0beff..0f65375670 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ClassBody.js @@ -22,10 +22,6 @@ export function ClassBody(node, context) { const child_state = { ...context.state, state_fields }; for (const [name, field] of state_fields) { - if (name[0] === '#') { - continue; - } - // insert backing fields for stuff declared in the constructor if ( field && diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/MemberExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/MemberExpression.js new file mode 100644 index 0000000000..50b5ae793f --- /dev/null +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/MemberExpression.js @@ -0,0 +1,23 @@ +/** @import { ClassBody, MemberExpression } from 'estree' */ +/** @import { Context } from '../types.js' */ +import * as b from '#compiler/builders'; + +/** + * @param {MemberExpression} node + * @param {Context} context + */ +export function MemberExpression(node, context) { + if ( + context.state.analysis.runes && + node.object.type === 'ThisExpression' && + node.property.type === 'PrivateIdentifier' + ) { + const field = context.state.state_fields?.get(`#${node.property.name}`); + + if (field?.type === '$derived' || field?.type === '$derived.by') { + return b.call(node); + } + } + + context.next(); +} diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/PropertyDefinition.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/PropertyDefinition.js index c9225bb8da..d83dc783b6 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/PropertyDefinition.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/PropertyDefinition.js @@ -11,7 +11,7 @@ export function PropertyDefinition(node, context) { if (context.state.analysis.runes && node.value != null && node.value.type === 'CallExpression') { const rune = get_rune(node.value, context.state.scope); - if (rune === '$state' || rune === '$state.raw' || rune === '$derived') { + if (rune === '$state' || rune === '$state.raw') { return { ...node, value: @@ -21,13 +21,14 @@ export function PropertyDefinition(node, context) { }; } - if (rune === '$derived.by') { + if (rune === '$derived.by' || rune === '$derived') { + const fn = /** @type {Expression} */ (context.visit(node.value.arguments[0])); return { ...node, value: node.value.arguments.length === 0 ? null - : b.call(/** @type {Expression} */ (context.visit(node.value.arguments[0]))) + : b.call('$.once', rune === '$derived' ? b.thunk(fn) : fn) }; } } diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-derived-private/_config.js b/packages/svelte/tests/runtime-runes/samples/class-state-derived-private/_config.js index 141d994a2f..a6b42605ed 100644 --- a/packages/svelte/tests/runtime-runes/samples/class-state-derived-private/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/class-state-derived-private/_config.js @@ -5,6 +5,7 @@ export default test({ html: `

doubled: 0

+

trippled: 0

`, test({ assert, target }) { @@ -17,6 +18,7 @@ export default test({ `

doubled: 2

+

trippled: 3

` ); @@ -27,6 +29,7 @@ export default test({ `

doubled: 4

+

trippled: 9

` ); } diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-derived-private/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-derived-private/main.svelte index 2c4c8f1839..5188dfd8ba 100644 --- a/packages/svelte/tests/runtime-runes/samples/class-state-derived-private/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/class-state-derived-private/main.svelte @@ -2,14 +2,24 @@ class Counter { count = $state(0); #doubled = $derived(this.count * 2); + #trippled = $derived.by(() => this.count * this.by); - get embiggened() { + constructor(by) { + this.by = by; + } + + get embiggened1() { return this.#doubled; } + + get embiggened2() { + return this.#trippled; + } } - const counter = new Counter(); + const counter = new Counter(3); -

doubled: {counter.embiggened}

+

doubled: {counter.embiggened1}

+

trippled: {counter.embiggened2}