diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index d591dbe4e1..e7a5e024af 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -355,6 +355,12 @@ export function client_component(analysis, options) { const push_args = [b.id('$$props'), b.literal(analysis.runes)]; if (dev) push_args.push(b.id(analysis.name)); + if (analysis.is_async) { + const body = /** @type {ESTree.FunctionDeclaration} */ (template.body[0]); + body.body.body.unshift(...instance.body); + instance.body.length = 0; + } + let component_block = b.block([ ...store_setup, ...legacy_reactive_declarations, @@ -367,24 +373,6 @@ export function client_component(analysis, options) { .../** @type {ESTree.Statement[]} */ (template.body) ]); - if (analysis.is_async) { - const body = b.function_declaration( - b.id('$$body'), - [b.id('$$anchor'), b.id('$$props')], - component_block - ); - body.async = true; - - state.hoisted.push(body); - - component_block = b.block([ - b.var('fragment', b.call('$.comment')), - b.var('node', b.call('$.first_child', b.id('fragment'))), - b.stmt(b.call(body.id, b.id('node'), b.id('$$props'))), - b.stmt(b.call('$.append', b.id('$$anchor'), b.id('fragment'))) - ]); - } - if (!analysis.runes) { // Bind static exports to props so that people can access them with bind:x for (const { name, alias } of analysis.exports) { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js index a3572b9b9c..e69243e9d7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js @@ -204,6 +204,21 @@ export function Fragment(node, context) { body.push(close); } + const async = + state.metadata.init_is_async || (state.analysis.is_async && context.path.length === 0); + + if (async) { + // TODO need to create bookends for hydration to work + return b.block([ + b.function_declaration(b.id('$$body'), [b.id('$$anchor')], b.block(body), true), + + b.var('fragment', b.call('$.comment')), + b.var('node', b.call('$.first_child', b.id('fragment'))), + b.stmt(b.call(b.id('$$body'), b.id('node'))), + b.stmt(b.call('$.append', b.id('$$anchor'), b.id('fragment'))) + ]); + } + return b.block(body); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index e79fa931b0..644c0478d2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -167,8 +167,17 @@ export function build_component(node, component_name, context, anchor = context. if (should_wrap_in_derived) { const id = b.id(context.state.scope.generate(attribute.name)); - context.state.init.push(b.var(id, create_derived(context.state, b.thunk(value)))); - arg = b.call('$.get', id); + + if (attribute.metadata.expression.is_async) { + // TODO parallelise these + context.state.init.push( + b.var(id, b.await(b.call('$.async_derived', b.thunk(arg, true)))) + ); + arg = b.call(id); + } else { + context.state.init.push(b.var(id, create_derived(context.state, b.thunk(value)))); + arg = b.call('$.get', id); + } } push_prop(b.get(attribute.name, [b.return(arg)])); diff --git a/packages/svelte/src/compiler/utils/builders.js b/packages/svelte/src/compiler/utils/builders.js index f79028a947..42c0a46788 100644 --- a/packages/svelte/src/compiler/utils/builders.js +++ b/packages/svelte/src/compiler/utils/builders.js @@ -30,16 +30,17 @@ export function assignment_pattern(left, right) { /** * @param {Array} params * @param {ESTree.BlockStatement | ESTree.Expression} body + * @param {boolean} async * @returns {ESTree.ArrowFunctionExpression} */ -export function arrow(params, body) { +export function arrow(params, body, async = false) { return { type: 'ArrowFunctionExpression', params, body, expression: body.type !== 'BlockStatement', generator: false, - async: false, + async, metadata: /** @type {any} */ (null) // should not be used by codegen }; } @@ -214,16 +215,17 @@ export function export_default(declaration) { * @param {ESTree.Identifier} id * @param {ESTree.Pattern[]} params * @param {ESTree.BlockStatement} body + * @param {boolean} async * @returns {ESTree.FunctionDeclaration} */ -export function function_declaration(id, params, body) { +export function function_declaration(id, params, body, async = false) { return { type: 'FunctionDeclaration', id, params, body, generator: false, - async: false, + async, metadata: /** @type {any} */ (null) // should not be used by codegen }; } @@ -419,9 +421,7 @@ export function template(elements, expressions) { * @returns {ESTree.Expression} */ export function thunk(expression, async = false) { - const fn = arrow([], expression); - if (async) fn.async = true; - return unthunk(fn); + return unthunk(arrow([], expression, async)); } /**