0
0
mirror of https://github.com/sveltejs/svelte.git synced 2024-12-01 09:21:21 +01:00

fix: ensure top level snippets are defined when binding to component prop (#11104)

...by hoisting top level snippets out of the binding loop in ssr mode
fixes #11086
This commit is contained in:
Simon H 2024-04-10 19:52:58 +02:00 committed by GitHub
parent 4d0b743918
commit 27891cb2dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 122 additions and 8 deletions

View File

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: ensure top level snippets are defined when binding to component prop

View File

@ -1614,14 +1614,15 @@ const template_visitors = {
state.template.push(block_close);
},
SnippetBlock(node, context) {
// TODO hoist where possible
context.state.init.push(
b.function_declaration(
node.expression,
[b.id('$$payload'), ...node.parameters],
/** @type {import('estree').BlockStatement} */ (context.visit(node.body))
)
const fn = b.function_declaration(
node.expression,
[b.id('$$payload'), ...node.parameters],
/** @type {import('estree').BlockStatement} */ (context.visit(node.body))
);
// @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone
fn.___snippet = true;
// TODO hoist where possible
context.state.init.push(fn);
if (context.state.options.dev) {
context.state.init.push(b.stmt(b.call('$.add_snippet_symbol', node.expression)));
@ -2221,14 +2222,27 @@ export function server_component(analysis, options) {
// If the component binds to a child, we need to put the template in a loop and repeat until legacy bindings are stable.
// We can remove this once the legacy syntax is gone.
if (analysis.uses_component_bindings) {
const snippets = template.body.filter(
(node) =>
node.type === 'FunctionDeclaration' &&
// @ts-expect-error
node.___snippet
);
const rest = template.body.filter(
(node) =>
node.type !== 'FunctionDeclaration' ||
// @ts-expect-error
!node.___snippet
);
template.body = [
...snippets,
b.let('$$settled', b.true),
b.let('$$inner_payload'),
b.stmt(
b.function(
b.id('$$render_inner'),
[b.id('$$payload')],
b.block(/** @type {import('estree').Statement[]} */ (template.body))
b.block(/** @type {import('estree').Statement[]} */ (rest))
)
),
b.do_while(

View File

@ -0,0 +1,39 @@
// index.svelte (Svelte VERSION)
// Note: compiler output will change before 5.0 is released!
import "svelte/internal/disclose-version";
import * as $ from "svelte/internal/client";
import TextInput from './Child.svelte';
var root_1 = $.template(`Something`, 1);
var root = $.template(`<!> `, 1);
export default function Bind_component_snippet($$anchor, $$props) {
$.push($$props, true);
let value = $.source('');
const _snippet = snippet;
var fragment_1 = root();
function snippet($$anchor) {
var fragment = root_1();
$.append($$anchor, fragment);
}
var node = $.first_child(fragment_1);
TextInput(node, {
get value() {
return $.get(value);
},
set value($$value) {
$.set(value, $.proxy($$value));
}
});
var text = $.sibling(node, true);
$.render_effect(() => $.set_text(text, ` value: ${$.stringify($.get(value))}`));
$.append($$anchor, fragment_1);
$.pop();
}

View File

@ -0,0 +1,43 @@
// index.svelte (Svelte VERSION)
// Note: compiler output will change before 5.0 is released!
import * as $ from "svelte/internal/server";
import TextInput from './Child.svelte';
export default function Bind_component_snippet($$payload, $$props) {
$.push(true);
let value = '';
const _snippet = snippet;
function snippet($$payload) {
$$payload.out += `Something`;
}
let $$settled = true;
let $$inner_payload;
function $$render_inner($$payload) {
$$payload.out += `<!--[-->`;
TextInput($$payload, {
get value() {
return value;
},
set value($$value) {
value = $$value;
$$settled = false;
}
});
$$payload.out += `<!--]--> value: ${$.escape(value)}`;
};
do {
$$settled = true;
$$inner_payload = $.copy_payload($$payload);
$$render_inner($$inner_payload);
} while (!$$settled);
$.assign_payload($$payload, $$inner_payload);
$.pop();
}

View File

@ -0,0 +1,13 @@
<script>
import TextInput from './Child.svelte';
let value = $state('');
const _snippet = snippet;
</script>
{#snippet snippet()}
Something
{/snippet}
<TextInput bind:value />
value: {value}