diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 3f7876144fb5..13eebb36fa83 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.33.1 + +### Patch Changes + +- fix: make deriveds on the server lazy again ([#15964](https://github.com/sveltejs/svelte/pull/15964)) + ## 5.33.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0a992d1901ce..1580f5853330 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.33.0", + "version": "5.33.1", "type": "module", "types": "./types/index.d.ts", "engines": { 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 3498b2636b15..7a3d6bef6c31 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/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js index 280c16dbd26d..466682fb82b8 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,11 @@ function build_assignment(operator, left, right, context) { /** @type {Expression} */ (context.visit(right)) ); } + } else if (field && (field.type === '$derived' || field.type === '$derived.by')) { + let value = /** @type {Expression} */ ( + context.visit(build_assignment_value(operator, left, right)) + ); + return b.call(b.member(b.this, name), value); } } 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 e36dc820b3ef..35c79988b08b 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 6797b0beffd2..432d0142cdbf 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 @@ -36,7 +36,8 @@ export function ClassBody(node, context) { body.push( b.prop_def(field.key, null), - b.method('get', b.key(name), [], [b.return(b.call(member))]) + b.method('get', b.key(name), [], [b.return(b.call(member))]), + b.method('set', b.key(name), [b.id('$$value')], [b.return(b.call(member, b.id('$$value')))]) ); } } @@ -61,6 +62,7 @@ export function ClassBody(node, context) { if (name[0] === '#' || field.type === '$state' || field.type === '$state.raw') { body.push(/** @type {PropertyDefinition} */ (context.visit(definition, child_state))); } else if (field.node === definition) { + // $derived / $derived.by const member = b.member(b.this, field.key); body.push( @@ -69,7 +71,8 @@ export function ClassBody(node, context) { /** @type {CallExpression} */ (context.visit(field.value, child_state)) ), - b.method('get', definition.key, [], [b.return(b.call(member))]) + b.method('get', definition.key, [], [b.return(b.call(member))]), + b.method('set', b.key(name), [b.id('$$value')], [b.return(b.call(member, b.id('$$value')))]) ); } } 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 000000000000..50b5ae793fb4 --- /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 c9225bb8da6f..498f90703c7b 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('$.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 b58a1d4372a6..29e09fe4dd07 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -514,3 +514,24 @@ 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) { + const get_value = once(fn); + /** + * @type {T | undefined} + */ + let updated_value; + + return function (new_value) { + if (arguments.length === 0) { + return updated_value ?? get_value(); + } + updated_value = new_value; + return updated_value; + }; +} diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 20c84d001e6c..336e5fcb4179 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.33.0'; +export const VERSION = '5.33.1'; export const PUBLIC_VERSION = '5'; 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 141d994a2f85..40ef84a2e6f5 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

+

tripled: 0

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

doubled: 2

+

tripled: 3

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

doubled: 4

+

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 2c4c8f183970..d971566396d6 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); + #tripled = $derived.by(() => this.count * this.by); - get embiggened() { + constructor(by) { + this.by = by; + } + + get embiggened1() { return this.#doubled; } + + get embiggened2() { + return this.#tripled; + } } - const counter = new Counter(); + const counter = new Counter(3); -

doubled: {counter.embiggened}

+

doubled: {counter.embiggened1}

+

tripled: {counter.embiggened2}

diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived-3/_config.js b/packages/svelte/tests/runtime-runes/samples/writable-derived-3/_config.js new file mode 100644 index 000000000000..999e4ad6e05c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/writable-derived-3/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `3 3 3 3` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/writable-derived-3/main.svelte new file mode 100644 index 000000000000..0b20f811c338 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/writable-derived-3/main.svelte @@ -0,0 +1,29 @@ + + +{x.on_class} {x.in_constructor} {x.on_class_private} {x.in_constructor_private} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy