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:
parent
4d0b743918
commit
27891cb2dd
5
.changeset/gold-tools-nail.md
Normal file
5
.changeset/gold-tools-nail.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: ensure top level snippets are defined when binding to component prop
|
@ -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(
|
||||
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
@ -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}
|
Loading…
Reference in New Issue
Block a user