From eaf15c5f00835d87fb6a42a67a32a71d44396182 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 10 Feb 2026 20:47:20 -0500 Subject: [PATCH] WIP --- .../server/visitors/VariableDeclaration.js | 20 ++++++++++--------- .../server/visitors/shared/utils.js | 4 ++++ .../svelte/src/internal/server/bindings.js | 14 +++++++++++++ .../_expected/server/index.svelte.js | 6 +++--- .../_expected/server/index.svelte.js | 12 +++++------ .../_expected/server/index.svelte.js | 4 ++-- 6 files changed, 40 insertions(+), 20 deletions(-) create mode 100644 packages/svelte/src/internal/server/bindings.js diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js index 58fde5964f..2718ca2138 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js @@ -84,10 +84,19 @@ export function VariableDeclaration(node, context) { const args = /** @type {CallExpression} */ (init).arguments; const value = args.length > 0 ? /** @type {Expression} */ (context.visit(args[0])) : b.void0; - if (rune === '$derived.by') { + if (rune === '$derived' || rune === '$derived.by') { + const is_async = + rune === '$derived' && + context.state.analysis.async_deriveds.has( + /** @type {CallExpression} */ (declarator.init) + ); + + let init = b.call('$.derived', rune === '$derived' ? b.thunk(value, is_async) : value); + declarations.push( - b.declarator(/** @type {Pattern} */ (context.visit(declarator.id)), b.call(value)) + b.declarator(/** @type {Pattern} */ (context.visit(declarator.id)), init) ); + continue; } @@ -96,13 +105,6 @@ export function VariableDeclaration(node, context) { continue; } - if (rune === '$derived') { - declarations.push( - b.declarator(/** @type {Pattern} */ (context.visit(declarator.id)), value) - ); - continue; - } - declarations.push(...create_state_declarators(declarator, context.state.scope, value)); } } else { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js index ee14a4d135..47108f5fc7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js @@ -270,6 +270,10 @@ export function build_getter(node, state) { ); } + if (binding.kind === 'derived') { + return (binding.declaration_kind === 'var' ? b.maybe_call : b.call)(binding.node); + } + return node; } diff --git a/packages/svelte/src/internal/server/bindings.js b/packages/svelte/src/internal/server/bindings.js new file mode 100644 index 0000000000..d3223f4de2 --- /dev/null +++ b/packages/svelte/src/internal/server/bindings.js @@ -0,0 +1,14 @@ +import { ssr_context } from './context.js'; +import { once } from './index.js'; + +/** + * @template V + * @param {() => V} fn + */ +export function derived(fn) { + if (ssr_context !== null) { + return once(fn); + } + + return fn; +} diff --git a/packages/svelte/tests/snapshot/samples/async-if-chain/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-if-chain/_expected/server/index.svelte.js index 91315edb94..37966fb964 100644 --- a/packages/svelte/tests/snapshot/samples/async-if-chain/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/async-if-chain/_expected/server/index.svelte.js @@ -8,7 +8,7 @@ export default function Async_if_chain($$renderer) { let foo = true; var blocking; - var $$promises = $$renderer.run([async () => blocking = await foo]); + var $$promises = $$renderer.run([async () => blocking = $.derived(() => foo)]); $$renderer.async_block([$$promises[0]], ($$renderer) => { if (foo) { @@ -94,10 +94,10 @@ export default function Async_if_chain($$renderer) { $$renderer.push(` `); $$renderer.async_block([$$promises[0]], ($$renderer) => { - if (blocking > 10) { + if (blocking() > 10) { $$renderer.push(''); $$renderer.push(`foo`); - } else if (blocking > 5) { + } else if (blocking() > 5) { $$renderer.push(''); $$renderer.push(`bar`); } else { diff --git a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js index 58358b3ded..264ba1de36 100644 --- a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js @@ -6,15 +6,15 @@ export default function Async_in_derived($$renderer, $$props) { var yes1, yes2, no1, no2; var $$promises = $$renderer.run([ - async () => yes1 = await 1, - async () => yes2 = foo(await 1), - () => no1 = (async () => { + async () => yes1 = $.derived(() => 1), + async () => yes2 = $.derived(async () => foo(await 1)), + () => no1 = $.derived(async () => { return await 1; - })(), + }), - () => no2 = async () => { + () => no2 = $.derived(() => async () => { return await 1; - } + }) ]); if (true) { diff --git a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js index e9bf215dcd..a41b58051c 100644 --- a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js @@ -2,13 +2,13 @@ import * as $ from 'svelte/internal/server'; export default function Await_block_scope($$renderer) { let counter = { count: 0 }; - const promise = Promise.resolve(counter); + const promise = $.derived(() => Promise.resolve(counter)); function increment() { counter.count += 1; } $$renderer.push(` `); - $.await($$renderer, promise, () => {}, (counter) => {}); + $.await($$renderer, promise(), () => {}, (counter) => {}); $$renderer.push(` ${$.escape(counter.count)}`); } \ No newline at end of file