Skip to content

Commit 9aa06bd

Browse files
fix: check that snippet is not rendered as a component (#9423)
Co-authored-by: Rich Harris <[email protected]>
1 parent 1fd77d7 commit 9aa06bd

File tree

8 files changed

+46
-5
lines changed

8 files changed

+46
-5
lines changed

.changeset/good-pianos-jump.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: check that snippet is not rendered as a component

packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,14 @@ function serialize_inline_component(node, component_name, context) {
850850
b.thunk(b.array(props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p))))
851851
);
852852
/** @param {import('estree').Identifier} node_id */
853-
let fn = (node_id) => b.call(component_name, node_id, props_expression);
853+
let fn = (node_id) =>
854+
b.call(
855+
context.state.options.dev
856+
? b.call('$.validate_component', b.id(component_name))
857+
: component_name,
858+
node_id,
859+
props_expression
860+
);
854861

855862
if (bind_this !== null) {
856863
const prev = fn;

packages/svelte/src/compiler/phases/3-transform/server/transform-server.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -882,7 +882,12 @@ function serialize_inline_component(node, component_name, context) {
882882
/** @type {import('estree').Statement} */
883883
let statement = b.stmt(
884884
(typeof component_name === 'string' ? b.call : b.maybe_call)(
885-
component_name,
885+
context.state.options.dev
886+
? b.call(
887+
'$.validate_component',
888+
typeof component_name === 'string' ? b.id(component_name) : component_name
889+
)
890+
: component_name,
886891
b.id('$$payload'),
887892
props_expression
888893
)

packages/svelte/src/internal/client/validate.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,13 @@ export function loop_guard(timeout) {
102102
};
103103
}
104104

105-
const symbol = Symbol.for('svelte.snippet');
105+
const snippet_symbol = Symbol.for('svelte.snippet');
106106

107107
/**
108108
* @param {any} fn
109109
*/
110110
export function add_snippet_symbol(fn) {
111-
fn[symbol] = true;
111+
fn[snippet_symbol] = true;
112112
return fn;
113113
}
114114

@@ -117,11 +117,22 @@ export function add_snippet_symbol(fn) {
117117
* @param {any} snippet_fn
118118
*/
119119
export function validate_snippet(snippet_fn) {
120-
if (snippet_fn[symbol] !== true) {
120+
if (snippet_fn[snippet_symbol] !== true) {
121121
throw new Error(
122122
'The argument to `{@render ...}` must be a snippet function, not a component or some other kind of function. ' +
123123
'If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`.'
124124
);
125125
}
126126
return snippet_fn;
127127
}
128+
129+
/**
130+
* Validate that the function behind `<Component />` isn't a snippet.
131+
* @param {any} component_fn
132+
*/
133+
export function validate_component(component_fn) {
134+
if (component_fn?.[snippet_symbol] === true) {
135+
throw new Error('A snippet must be rendered with `{@render ...}`');
136+
}
137+
return component_fn;
138+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
compileOptions: {
5+
dev: true
6+
},
7+
error: 'A snippet must be rendered with `{@render ...}`'
8+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{#snippet Foo()}
2+
<p>hello</p>
3+
{/snippet}
4+
5+
<Foo />

0 commit comments

Comments
 (0)