fix: check boundary `pending` attribute at runtime on server

fix-pending-ssr
ComputerGuy 1 day ago
parent bb33c555b4
commit 456c8c4e64

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: check boundary `pending` attribute at runtime on server

@ -2,7 +2,13 @@
/** @import { AST } from '#compiler' */ /** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */ /** @import { ComponentContext } from '../types' */
import * as b from '#compiler/builders'; import * as b from '#compiler/builders';
import { block_close, block_open, block_open_else, build_attribute_value } from './shared/utils.js'; import {
block_close,
block_open,
block_open_else,
build_attribute_value,
build_template
} from './shared/utils.js';
/** /**
* @param {AST.SvelteBoundary} node * @param {AST.SvelteBoundary} node
@ -13,6 +19,11 @@ export function SvelteBoundary(node, context) {
const pending_attribute = /** @type {AST.Attribute} */ ( const pending_attribute = /** @type {AST.Attribute} */ (
node.attributes.find((node) => node.type === 'Attribute' && node.name === 'pending') node.attributes.find((node) => node.type === 'Attribute' && node.name === 'pending')
); );
const is_pending_attr_nullish =
pending_attribute &&
typeof pending_attribute.value === 'object' &&
!Array.isArray(pending_attribute.value) &&
!context.state.scope.evaluate(pending_attribute.value.expression).is_defined;
const pending_snippet = /** @type {AST.SnippetBlock} */ ( const pending_snippet = /** @type {AST.SnippetBlock} */ (
node.fragment.nodes.find( node.fragment.nodes.find(
@ -21,6 +32,24 @@ export function SvelteBoundary(node, context) {
); );
if (pending_attribute || pending_snippet) { if (pending_attribute || pending_snippet) {
if (pending_attribute && is_pending_attr_nullish && !pending_snippet) {
const callee = build_attribute_value(
pending_attribute.value,
context,
(expression) => expression,
false,
true
);
const pending = b.call(callee, b.id('$$renderer'));
const block = /** @type {BlockStatement} */ (context.visit(node.fragment));
context.state.template.push(
b.if(
callee,
b.block(build_template([block_open_else, pending, block_close])),
b.block(build_template([block_open, block, block_close]))
)
);
} else {
const pending = pending_attribute const pending = pending_attribute
? b.call( ? b.call(
build_attribute_value( build_attribute_value(
@ -33,8 +62,8 @@ export function SvelteBoundary(node, context) {
b.id('$$renderer') b.id('$$renderer')
) )
: /** @type {BlockStatement} */ (context.visit(pending_snippet.body)); : /** @type {BlockStatement} */ (context.visit(pending_snippet.body));
context.state.template.push(block_open_else, pending, block_close); context.state.template.push(block_open_else, pending, block_close);
}
} else { } else {
const block = /** @type {BlockStatement} */ (context.visit(node.fragment)); const block = /** @type {BlockStatement} */ (context.visit(node.fragment));
context.state.template.push(block_open, block, block_close); context.state.template.push(block_open, block, block_close);

@ -260,6 +260,11 @@ class Evaluation {
break; break;
} }
if (binding.initial?.type === 'SnippetBlock') {
this.is_defined = true;
break;
}
if (!binding.updated && binding.initial !== null && !is_prop) { if (!binding.updated && binding.initial !== null && !is_prop) {
binding.scope.evaluate(/** @type {Expression} */ (binding.initial), this.values); binding.scope.evaluate(/** @type {Expression} */ (binding.initial), this.values);
break; break;

@ -0,0 +1,6 @@
<script>
let pending = null;
</script>
<svelte:boundary {pending}>
{await 'awaited'}
</svelte:boundary>
Loading…
Cancel
Save