From f4b94e57a349b22d012fa27563fae520463ec1de Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 25 Oct 2025 17:24:40 -0400 Subject: [PATCH] WIP --- .../3-transform/client/visitors/EachBlock.js | 10 ++++--- .../3-transform/client/visitors/HtmlTag.js | 3 ++- .../3-transform/client/visitors/KeyBlock.js | 9 ++++--- .../3-transform/client/visitors/Program.js | 15 ++++++++++- .../client/visitors/SvelteElement.js | 9 ++++--- .../src/internal/client/reactivity/async.js | 26 +++++++++++++------ .../src/internal/client/reactivity/sources.js | 2 +- 7 files changed, 51 insertions(+), 23 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js index 225a4f617c..c0bfe272e5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js @@ -312,9 +312,10 @@ export function EachBlock(node, context) { declarations.push(b.let(node.index, index)); } - const { has_await } = node.metadata.expression; - const get_collection = b.thunk(collection, has_await); - const thunk = has_await ? b.thunk(b.call('$.get', b.id('$$collection'))) : get_collection; + const is_async = node.metadata.expression.is_async(); + + const get_collection = b.thunk(collection, node.metadata.expression.has_await); + const thunk = is_async ? b.thunk(b.call('$.get', b.id('$$collection'))) : get_collection; const render_args = [b.id('$$anchor'), item]; if (uses_index || collection_id) render_args.push(index); @@ -341,12 +342,13 @@ export function EachBlock(node, context) { statements.unshift(b.stmt(b.call('$.validate_each_keys', thunk, key_function))); } - if (has_await) { + if (is_async) { context.state.init.push( b.stmt( b.call( '$.async', context.state.node, + node.metadata.expression.blockers(), b.array([get_collection]), b.arrow([context.state.node, b.id('$$collection')], b.block(statements)) ) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js index b395e1191d..0567edc610 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js @@ -37,7 +37,8 @@ export function HtmlTag(node, context) { b.call( '$.async', context.state.node, - b.array([b.thunk(expression, true)]), + node.metadata.expression.blockers(), + b.array([b.thunk(expression, node.metadata.expression.has_await)]), b.arrow([context.state.node, b.id('$$html')], b.block([statement])) ) ) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js index 52850090e3..d050155e8b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js @@ -11,10 +11,10 @@ import { build_expression, add_svelte_meta } from './shared/utils.js'; export function KeyBlock(node, context) { context.state.template.push_comment(); - const { has_await } = node.metadata.expression; + const is_async = node.metadata.expression.is_async(); const expression = build_expression(context, node.expression, node.metadata.expression); - const key = b.thunk(has_await ? b.call('$.get', b.id('$$key')) : expression); + const key = b.thunk(is_async ? b.call('$.get', b.id('$$key')) : expression); const body = /** @type {Expression} */ (context.visit(node.fragment)); let statement = add_svelte_meta( @@ -23,12 +23,13 @@ export function KeyBlock(node, context) { 'key' ); - if (has_await) { + if (is_async) { statement = b.stmt( b.call( '$.async', context.state.node, - b.array([b.thunk(expression, true)]), + node.metadata.expression.blockers(), + b.array([b.thunk(expression, node.metadata.expression.has_await)]), b.arrow([context.state.node, b.id('$$key')], b.block([statement])) ) ); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js index a585765afb..a4cd4d0eda 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js @@ -272,8 +272,21 @@ function transform_body(program, context) { context.visit(b.var(s.node.id, s.node.init)) ); + if (visited.declarations.length === 1) { + return b.thunk( + b.assignment('=', s.node.id, visited.declarations[0].init ?? b.void0), + s.has_await + ); + } + + // if we have multiple declarations, it indicates destructuring return b.thunk( - b.assignment('=', s.node.id, visited.declarations[0].init ?? b.void0), + b.block([ + b.var(visited.declarations[0].id, visited.declarations[0].init), + ...visited.declarations + .slice(1) + .map((d) => b.stmt(b.assignment('=', d.id, d.init ?? b.void0))) + ]), s.has_await ); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index 2062519bb6..cfa79b73ca 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -93,10 +93,10 @@ export function SvelteElement(node, context) { ); } - const { has_await } = node.metadata.expression; + const is_async = node.metadata.expression.is_async(); const expression = /** @type {Expression} */ (context.visit(node.tag)); - const get_tag = b.thunk(has_await ? b.call('$.get', b.id('$$tag')) : expression); + const get_tag = b.thunk(is_async ? b.call('$.get', b.id('$$tag')) : expression); /** @type {Statement[]} */ const inner = inner_context.state.init; @@ -139,13 +139,14 @@ export function SvelteElement(node, context) { ) ); - if (has_await) { + if (is_async) { context.state.init.push( b.stmt( b.call( '$.async', context.state.node, - b.array([b.thunk(expression, true)]), + node.metadata.expression.blockers(), + b.array([b.thunk(expression, node.metadata.expression.has_await)]), b.arrow([context.state.node, b.id('$$tag')], b.block(statements)) ) ) diff --git a/packages/svelte/src/internal/client/reactivity/async.js b/packages/svelte/src/internal/client/reactivity/async.js index b045084c8c..02b3f54c36 100644 --- a/packages/svelte/src/internal/client/reactivity/async.js +++ b/packages/svelte/src/internal/client/reactivity/async.js @@ -56,10 +56,8 @@ export function flatten(blockers, sync, async, fn) { var was_hydrating = hydrating; - Promise.all(blockers).then(() => { - restore(); - - const result = Promise.all(async.map((expression) => async_derived(expression))) + function run() { + Promise.all(async.map((expression) => async_derived(expression))) .then((result) => { restore(); @@ -82,11 +80,22 @@ export function flatten(blockers, sync, async, fn) { .catch((error) => { invoke_error_boundary(error, parent); }); + } - unset_context(); + if (blockers.length > 0) { + Promise.all(blockers).then(() => { + restore(); - return result; - }); + try { + return run(); + } finally { + batch?.deactivate(); + unset_context(); + } + }); + } else { + run(); + } } /** @@ -274,7 +283,8 @@ export async function async_body(anchor, fn) { export function run(thunks) { const restore = capture(); - let promise = Promise.resolve(); + // let promise = Promise.resolve(); + let promise = new Promise((f) => Batch.enqueue(() => f(undefined))); var boundary = get_boundary(); var batch = /** @type {Batch} */ (current_batch); diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 9534e718a5..5b13426233 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -34,7 +34,7 @@ import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { get_stack, tag_proxy } from '../dev/tracing.js'; import { component_context, is_runes } from '../context.js'; -import { Batch, eager_block_effects, schedule_effect } from './batch.js'; +import { Batch, current_batch, eager_block_effects, schedule_effect } from './batch.js'; import { proxy } from '../proxy.js'; import { execute_derived } from './deriveds.js';