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 822dfe6e5b..a1041947a4 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 @@ -159,7 +159,9 @@ export function client_component(analysis, options) { template_contains_script_tag: false }, namespace: options.namespace, - bound_contenteditable: false + bound_contenteditable: false, + init_is_async: false, + update_is_async: false }, events: new Set(), preserve_whitespace: options.preserveWhitespace, diff --git a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts index 5c8476de3e..46a268d514 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts +++ b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts @@ -75,6 +75,9 @@ export interface ComponentClientTransformState extends ClientTransformState { */ template_contains_script_tag: boolean; }; + // TODO it would be nice if these were colocated with the arrays they pertain to + init_is_async: boolean; + update_is_async: boolean; }; readonly preserve_whitespace: boolean; 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 0e6ea29614..a3572b9b9c 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 @@ -74,7 +74,9 @@ export function Fragment(node, context) { template_contains_script_tag: false }, namespace, - bound_contenteditable: context.state.metadata.bound_contenteditable + bound_contenteditable: context.state.metadata.bound_contenteditable, + init_is_async: false, + update_is_async: false } }; @@ -190,7 +192,7 @@ export function Fragment(node, context) { } if (state.update.length > 0) { - body.push(build_render_statement(state.update)); + body.push(build_render_statement(state.update, state.metadata.update_is_async)); } body.push(...state.after_update); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index ffd06dfd86..5632d35b24 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -409,7 +409,9 @@ export function RegularElement(node, context) { b.block([ ...child_state.init, ...element_state.init, - child_state.update.length > 0 ? build_render_statement(child_state.update) : b.empty, + child_state.update.length > 0 + ? build_render_statement(child_state.update, child_state.metadata.update_is_async) + : b.empty, ...child_state.after_update, ...element_state.after_update ]) @@ -418,6 +420,9 @@ export function RegularElement(node, context) { context.state.init.push(...child_state.init, ...element_state.init); context.state.update.push(...child_state.update); context.state.after_update.push(...child_state.after_update, ...element_state.after_update); + + context.state.metadata.init_is_async ||= child_state.metadata.init_is_async; + context.state.metadata.update_is_async ||= child_state.metadata.update_is_async; } else { context.state.init.push(...element_state.init); context.state.after_update.push(...element_state.after_update); @@ -627,9 +632,10 @@ function build_element_attribute_update_assignment( if (attribute.metadata.expression.has_state) { if (has_call) { - state.init.push(build_update(update)); + state.init.push(build_update(update, attribute.metadata.expression.is_async)); } else { state.update.push(update); + state.metadata.update_is_async ||= attribute.metadata.expression.is_async; } return true; } else { @@ -662,12 +668,16 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co if (attribute.metadata.expression.has_state) { if (has_call) { - state.init.push(build_update(update)); + state.init.push(build_update(update, attribute.metadata.expression.is_async)); } else { state.update.push(update); + state.metadata.update_is_async ||= attribute.metadata.expression.is_async; } return true; } else { + if (attribute.metadata.expression.is_async) { + throw new Error('TODO top-level await'); + } state.init.push(update); return false; } 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 ba66fe29d6..c3d0360722 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 @@ -123,7 +123,12 @@ export function SvelteElement(node, context) { /** @type {Statement[]} */ const inner = inner_context.state.init; if (inner_context.state.update.length > 0) { - inner.push(build_render_statement(inner_context.state.update)); + inner.push( + build_render_statement( + inner_context.state.update, + inner_context.state.metadata.update_is_async + ) + ); } inner.push(...inner_context.state.after_update); inner.push( diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/TitleElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/TitleElement.js index 72cc57b068..05ae059ad2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/TitleElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/TitleElement.js @@ -8,7 +8,7 @@ import { build_template_chunk } from './shared/utils.js'; * @param {ComponentContext} context */ export function TitleElement(node, context) { - const { has_state, value } = build_template_chunk( + const { has_state, is_async, value } = build_template_chunk( /** @type {any} */ (node.fragment.nodes), context.visit, context.state @@ -18,7 +18,12 @@ export function TitleElement(node, context) { if (has_state) { context.state.update.push(statement); + context.state.metadata.update_is_async ||= is_async; } else { + if (is_async) { + throw new Error('TODO top-level await'); + } + context.state.init.push(statement); } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index 1b0737e31e..2e746cbf78 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -29,6 +29,7 @@ export function build_set_attributes( state ) { let has_state = false; + let is_async = false; /** @type {ObjectExpression['properties']} */ const values = []; @@ -63,6 +64,8 @@ export function build_set_attributes( } values.push(b.spread(value)); } + + is_async ||= attribute.metadata.expression.is_async; } const call = b.call( @@ -80,6 +83,7 @@ export function build_set_attributes( context.state.init.push(b.let(attributes_id)); const update = b.stmt(b.assignment('=', attributes_id, call)); context.state.update.push(update); + context.state.metadata.update_is_async ||= is_async; return true; } @@ -104,7 +108,7 @@ export function build_style_directives( const state = context.state; for (const directive of style_directives) { - const { has_state, has_call } = directive.metadata.expression; + const { has_state, has_call, is_async } = directive.metadata.expression; let value = directive.value === true @@ -129,10 +133,14 @@ export function build_style_directives( ); if (!is_attributes_reactive && has_call) { - state.init.push(build_update(update)); + state.init.push(build_update(update, is_async)); } else if (is_attributes_reactive || has_state || has_call) { state.update.push(update); + state.metadata.update_is_async ||= is_async; } else { + if (is_async) { + throw new Error('TODO top-level await'); + } state.init.push(update); } } @@ -154,7 +162,7 @@ export function build_class_directives( ) { const state = context.state; for (const directive of class_directives) { - const { has_state, has_call } = directive.metadata.expression; + const { has_state, has_call, is_async } = directive.metadata.expression; let value = /** @type {Expression} */ (context.visit(directive.expression)); if (has_call) { @@ -167,10 +175,14 @@ export function build_class_directives( const update = b.stmt(b.call('$.toggle_class', element_id, b.literal(directive.name), value)); if (!is_attributes_reactive && has_call) { - state.init.push(build_update(update)); + state.init.push(build_update(update, is_async)); } else if (is_attributes_reactive || has_state || has_call) { state.update.push(update); + state.metadata.update_is_async ||= is_async; } else { + if (is_async) { + throw new Error('TODO top-level await'); + } state.init.push(update); } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index f74fbfcf76..5744cd51aa 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -82,7 +82,11 @@ export function process_children(nodes, initial, is_element, { visit, state }) { state.init.push(build_update(update, is_async)); } else if (has_state && !within_bound_contenteditable) { state.update.push(update); + state.metadata.update_is_async ||= is_async; } else { + if (is_async) { + throw new Error('TODO top-level await'); + } state.init.push(b.stmt(b.assignment('=', b.member(id, 'nodeValue'), value))); } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index f5b1abce39..5d1aa7bad0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -102,11 +102,12 @@ export function build_update(statement, is_async) { /** * @param {Statement[]} update + * @param {boolean} is_async */ -export function build_render_statement(update) { +export function build_render_statement(update, is_async) { return update.length === 1 - ? build_update(update[0]) - : b.stmt(b.call('$.template_effect', b.thunk(b.block(update)))); + ? build_update(update[0], is_async) + : b.stmt(b.call('$.template_effect', b.thunk(b.block(update), is_async))); } /**