From ed348c6cab3c2f70edfb9b3a821a8bb50a395230 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 22 Jan 2025 16:07:28 -0500 Subject: [PATCH] if blocks --- .../src/compiler/phases/1-parse/state/tag.js | 10 ++++- .../phases/2-analyze/visitors/IfBlock.js | 8 +++- .../3-transform/client/visitors/IfBlock.js | 22 ++++++++++- .../svelte/src/compiler/types/template.d.ts | 3 ++ .../runtime-runes/samples/async-if/_config.js | 37 +++++++++++++++++++ .../samples/async-if/main.svelte | 15 ++++++++ 6 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 packages/svelte/tests/runtime-runes/samples/async-if/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-if/main.svelte diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index 95d7d00677..0d0176ac85 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -60,7 +60,10 @@ function open(parser) { end: -1, test: read_expression(parser), consequent: create_fragment(), - alternate: null + alternate: null, + metadata: { + expression: create_expression_metadata() + } }); parser.allow_whitespace(); @@ -441,7 +444,10 @@ function next(parser) { elseif: true, test: expression, consequent: create_fragment(), - alternate: null + alternate: null, + metadata: { + expression: create_expression_metadata() + } }); parser.stack.push(child); diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js index a65771bcfc..dcdae3587f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/IfBlock.js @@ -17,5 +17,11 @@ export function IfBlock(node, context) { mark_subtree_dynamic(context.path); - context.next(); + context.visit(node.test, { + ...context.state, + expression: node.metadata.expression + }); + + context.visit(node.consequent); + if (node.alternate) context.visit(node.alternate); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js index d658f9eaf8..b354a8877b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js @@ -24,6 +24,11 @@ export function IfBlock(node, context) { statements.push(b.var(b.id(alternate_id), b.arrow([b.id('$$anchor')], alternate))); } + const { is_async } = node.metadata.expression; + + const expression = /** @type {Expression} */ (context.visit(node.test)); + const test = is_async ? b.call('$.get', b.id('$$condition')) : expression; + /** @type {Expression[]} */ const args = [ context.state.node, @@ -31,7 +36,7 @@ export function IfBlock(node, context) { [b.id('$$render')], b.block([ b.if( - /** @type {Expression} */ (context.visit(node.test)), + test, b.stmt(b.call(b.id('$$render'), b.id(consequent_id))), alternate_id ? b.stmt( @@ -74,5 +79,18 @@ export function IfBlock(node, context) { statements.push(b.stmt(b.call('$.if', ...args))); - context.state.init.push(b.block(statements)); + if (is_async) { + context.state.init.push( + b.stmt( + b.call( + '$.async', + context.state.node, + b.array([b.thunk(expression, true)]), + b.arrow([context.state.node, b.id('$$condition')], b.block(statements)) + ) + ) + ); + } else { + context.state.init.push(b.block(statements)); + } } diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index fb60966895..f2b2c4629a 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -434,6 +434,9 @@ export namespace AST { test: Expression; consequent: Fragment; alternate: Fragment | null; + metadata: { + expression: ExpressionMetadata; + }; } /** An `{#await ...}` block */ diff --git a/packages/svelte/tests/runtime-runes/samples/async-if/_config.js b/packages/svelte/tests/runtime-runes/samples/async-if/_config.js new file mode 100644 index 0000000000..286595a977 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-if/_config.js @@ -0,0 +1,37 @@ +import { tick } from 'svelte'; +import { deferred } from '../../../../src/internal/shared/utils.js'; +import { test } from '../../test'; + +/** @type {ReturnType} */ +let d; + +export default test({ + html: `

pending

`, + + get props() { + d = deferred(); + + return { + promise: d.promise + }; + }, + + async test({ assert, target, component }) { + d.resolve(true); + await Promise.resolve(); + await Promise.resolve(); + await Promise.resolve(); + await tick(); + assert.htmlEqual(target.innerHTML, '

yes

'); + + d = deferred(); + component.promise = d.promise; + await tick(); + assert.htmlEqual(target.innerHTML, '

pending

'); + + d.resolve(false); + await Promise.resolve(); + await tick(); + assert.htmlEqual(target.innerHTML, '

no

'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-if/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-if/main.svelte new file mode 100644 index 0000000000..baed33a76e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-if/main.svelte @@ -0,0 +1,15 @@ + + + + {#if await promise} +

yes

+ {:else} +

no

+ {/if} + + {#snippet pending()} +

pending

+ {/snippet} +