diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js index 280c16dbd2..b4d738eaa1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js @@ -44,6 +44,8 @@ function build_assignment(operator, left, right, context) { /** @type {Expression} */ (context.visit(right)) ); } + } else if (field && (field.type === '$derived' || field.type === '$derived.by')) { + return b.call(b.member(b.this, name), right); } } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js index e36dc820b3..35c79988b0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js @@ -31,7 +31,7 @@ export function CallExpression(node, context) { if (rune === '$derived' || rune === '$derived.by') { const fn = /** @type {Expression} */ (context.visit(node.arguments[0])); - return b.call('$.once', rune === '$derived' ? b.thunk(fn) : fn); + return b.call('$.derived', rune === '$derived' ? b.thunk(fn) : fn); } if (rune === '$state.snapshot') { 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 0f65375670..6797b0beff 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,6 +22,10 @@ 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/PropertyDefinition.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/PropertyDefinition.js index d83dc783b6..498f90703c 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 @@ -28,7 +28,7 @@ export function PropertyDefinition(node, context) { value: node.value.arguments.length === 0 ? null - : b.call('$.once', rune === '$derived' ? b.thunk(fn) : fn) + : b.call('$.derived', rune === '$derived' ? b.thunk(fn) : fn) }; } } diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index b58a1d4372..fa3f9a0f28 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -514,3 +514,23 @@ export { } from '../shared/validate.js'; export { escape_html as escape }; + +/** + * @template T + * @param {()=>T} fn + * @returns {(new_value?: T) => (T | void)} + */ +export function derived(fn) { + /** + * @type {T | undefined} + */ + let updated_value; + + return function (new_value) { + if (arguments.length === 0) { + return updated_value ?? fn(); + } + updated_value = new_value; + return updated_value; + }; +} 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 a6b42605ed..40ef84a2e6 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,7 +5,7 @@ export default test({ html: `

doubled: 0

-

trippled: 0

+

tripled: 0

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

doubled: 2

-

trippled: 3

+

tripled: 3

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

doubled: 4

-

trippled: 9

+

tripled: 6

` ); } 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 5188dfd8ba..d971566396 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,7 +2,7 @@ class Counter { count = $state(0); #doubled = $derived(this.count * 2); - #trippled = $derived.by(() => this.count * this.by); + #tripled = $derived.by(() => this.count * this.by); constructor(by) { this.by = by; @@ -13,7 +13,7 @@ } get embiggened2() { - return this.#trippled; + return this.#tripled; } } @@ -22,4 +22,4 @@

doubled: {counter.embiggened1}

-

trippled: {counter.embiggened2}

+

tripled: {counter.embiggened2}