diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index f30d5d65cb..a9451135ca 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -131,9 +131,6 @@ const visitors = { ignore_map.set(node, structuredClone(ignore_stack)); const scope = state.scopes.get(node); - if (node.type === 'Fragment') { - node.metadata.hoisted_promises.name = state.scope.generate('promises'); - } next(scope !== undefined && scope !== state.scope ? { ...state, scope } : state); if (ignores.length > 0) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Fragment.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Fragment.js index 02d780dc0d..3631468233 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Fragment.js @@ -7,4 +7,18 @@ */ export function Fragment(node, context) { context.next({ ...context.state, fragment: node }); + + // TODO this indicates whether the fragment contains an `await` expression (not inside + // a child fragment), which is necessary for ensuring that a `SnippetBlock` creates an + // async function in SSR. It feels like this is probably duplicative, but it's late + // and it works, so for now I'm doing it like this + node.metadata.is_async = node.metadata.hoisted_promises.promises.length > 0; + + if (node.metadata.hoisted_promises.promises.length > 1) { + node.metadata.hoisted_promises.name = context.state.scope.generate('promises'); + } else { + // if there's only one promise in this fragment, we don't need to de-waterfall it + context.state.analysis.hoisted_promises.delete(node.metadata.hoisted_promises.promises[0]); + node.metadata.hoisted_promises.promises.length = 0; + } } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js index 238485e665..a27c3e00a2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js @@ -12,7 +12,8 @@ export function SnippetBlock(node, context) { let fn = b.function_declaration( node.expression, [b.id('$$payload'), ...node.parameters], - /** @type {BlockStatement} */ (context.visit(node.body)) + /** @type {BlockStatement} */ (context.visit(node.body)), + node.body.metadata.is_async ); // @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone diff --git a/packages/svelte/tests/snapshot/samples/async-each-fallback-hoisting/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-each-fallback-hoisting/_expected/server/index.svelte.js index 91b8b7e2d1..5da41c4161 100644 --- a/packages/svelte/tests/snapshot/samples/async-each-fallback-hoisting/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/async-each-fallback-hoisting/_expected/server/index.svelte.js @@ -2,32 +2,21 @@ import * as $ from 'svelte/internal/server'; export default function Async_each_fallback_hoisting($$payload) { $$payload.child(async ($$payload) => { - const promises = [Promise.resolve([])]; - const each_array = $.ensure_array_like(await promises[0]); + const each_array = $.ensure_array_like(await Promise.resolve([])); - $$payload.child(async ($$payload) => { - if (each_array.length !== 0) { - $$payload.push(''); + if (each_array.length !== 0) { + $$payload.push(''); - for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { - let item = each_array[$$index]; - const promises_1 = [Promise.reject('This should never be reached')]; + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let item = each_array[$$index]; - $$payload.child(async ($$payload) => { - $$payload.push(`${$.escape(await promises_1[0])}`); - }); - } - } else { - $$payload.push(''); - - const promises_2 = [Promise.resolve(4)]; - - $$payload.child(async ($$payload) => { - $$payload.push(`${$.escape(await promises_2[0])}`); - }); + $$payload.push(`${$.escape(await Promise.reject('This should never be reached'))}`); } + } else { + $$payload.push(''); + $$payload.push(`${$.escape(await Promise.resolve(4))}`); + } - $$payload.push(``); - }); + $$payload.push(``); }); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/async-each-hoisting/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-each-hoisting/_expected/server/index.svelte.js index 99dfc7c8ef..633efbc2be 100644 --- a/packages/svelte/tests/snapshot/samples/async-each-hoisting/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/async-each-hoisting/_expected/server/index.svelte.js @@ -5,22 +5,16 @@ export default function Async_each_hoisting($$payload) { const first = Promise.resolve(1); const second = Promise.resolve(2); const third = Promise.resolve(3); - const promises = [Promise.resolve([first, second, third])]; - const each_array = $.ensure_array_like(await promises[0]); + const each_array = $.ensure_array_like(await Promise.resolve([first, second, third])); - $$payload.child(async ($$payload) => { - $$payload.push(``); + $$payload.push(``); - for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { - let item = each_array[$$index]; - const promises_1 = [item]; + for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) { + let item = each_array[$$index]; - $$payload.child(async ($$payload) => { - $$payload.push(`${$.escape(await promises_1[0])}`); - }); - } + $$payload.push(`${$.escape(await item)}`); + } - $$payload.push(``); - }); + $$payload.push(``); }); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/async-if-alternate-hoisting/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-if-alternate-hoisting/_expected/server/index.svelte.js index ee5cc89ac5..149219ad8b 100644 --- a/packages/svelte/tests/snapshot/samples/async-if-alternate-hoisting/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/async-if-alternate-hoisting/_expected/server/index.svelte.js @@ -2,28 +2,14 @@ import * as $ from 'svelte/internal/server'; export default function Async_if_alternate_hoisting($$payload) { $$payload.child(async ($$payload) => { - const promises = [Promise.resolve(false)]; - - $$payload.child(async ($$payload) => { - if (await promises[0]) { - $$payload.push(''); - - const promises_1 = [Promise.reject('no no no')]; - - $$payload.child(async ($$payload) => { - $$payload.push(`${$.escape(await promises_1[0])}`); - }); - } else { - $$payload.push(''); - - const promises_2 = [Promise.resolve('yes yes yes')]; - - $$payload.child(async ($$payload) => { - $$payload.push(`${$.escape(await promises_2[0])}`); - }); - } - - $$payload.push(``); - }); + if (await Promise.resolve(false)) { + $$payload.push(''); + $$payload.push(`${$.escape(await Promise.reject('no no no'))}`); + } else { + $$payload.push(''); + $$payload.push(`${$.escape(await Promise.resolve('yes yes yes'))}`); + } + + $$payload.push(``); }); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/async-if-hoisting/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-if-hoisting/_expected/server/index.svelte.js index 04d076a59b..6e72fb6be0 100644 --- a/packages/svelte/tests/snapshot/samples/async-if-hoisting/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/async-if-hoisting/_expected/server/index.svelte.js @@ -2,28 +2,14 @@ import * as $ from 'svelte/internal/server'; export default function Async_if_hoisting($$payload) { $$payload.child(async ($$payload) => { - const promises = [Promise.resolve(true)]; - - $$payload.child(async ($$payload) => { - if (await promises[0]) { - $$payload.push(''); - - const promises_1 = [Promise.resolve('yes yes yes')]; - - $$payload.child(async ($$payload) => { - $$payload.push(`${$.escape(await promises_1[0])}`); - }); - } else { - $$payload.push(''); - - const promises_2 = [Promise.reject('no no no')]; - - $$payload.child(async ($$payload) => { - $$payload.push(`${$.escape(await promises_2[0])}`); - }); - } - - $$payload.push(``); - }); + if (await Promise.resolve(true)) { + $$payload.push(''); + $$payload.push(`${$.escape(await Promise.resolve('yes yes yes'))}`); + } else { + $$payload.push(''); + $$payload.push(`${$.escape(await Promise.reject('no no no'))}`); + } + + $$payload.push(``); }); } \ No newline at end of file