fix: check that snippet is not rendered as a component (#9423)

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/9438/head
Simon H 1 year ago committed by GitHub
parent 1fd77d7494
commit 9aa06bdf9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: check that snippet is not rendered as a component

@ -850,7 +850,14 @@ function serialize_inline_component(node, component_name, context) {
b.thunk(b.array(props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p)))) b.thunk(b.array(props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p))))
); );
/** @param {import('estree').Identifier} node_id */ /** @param {import('estree').Identifier} node_id */
let fn = (node_id) => b.call(component_name, node_id, props_expression); let fn = (node_id) =>
b.call(
context.state.options.dev
? b.call('$.validate_component', b.id(component_name))
: component_name,
node_id,
props_expression
);
if (bind_this !== null) { if (bind_this !== null) {
const prev = fn; const prev = fn;

@ -882,7 +882,12 @@ function serialize_inline_component(node, component_name, context) {
/** @type {import('estree').Statement} */ /** @type {import('estree').Statement} */
let statement = b.stmt( let statement = b.stmt(
(typeof component_name === 'string' ? b.call : b.maybe_call)( (typeof component_name === 'string' ? b.call : b.maybe_call)(
component_name, context.state.options.dev
? b.call(
'$.validate_component',
typeof component_name === 'string' ? b.id(component_name) : component_name
)
: component_name,
b.id('$$payload'), b.id('$$payload'),
props_expression props_expression
) )

@ -102,13 +102,13 @@ export function loop_guard(timeout) {
}; };
} }
const symbol = Symbol.for('svelte.snippet'); const snippet_symbol = Symbol.for('svelte.snippet');
/** /**
* @param {any} fn * @param {any} fn
*/ */
export function add_snippet_symbol(fn) { export function add_snippet_symbol(fn) {
fn[symbol] = true; fn[snippet_symbol] = true;
return fn; return fn;
} }
@ -117,7 +117,7 @@ export function add_snippet_symbol(fn) {
* @param {any} snippet_fn * @param {any} snippet_fn
*/ */
export function validate_snippet(snippet_fn) { export function validate_snippet(snippet_fn) {
if (snippet_fn[symbol] !== true) { if (snippet_fn[snippet_symbol] !== true) {
throw new Error( throw new Error(
'The argument to `{@render ...}` must be a snippet function, not a component or some other kind of function. ' + 'The argument to `{@render ...}` must be a snippet function, not a component or some other kind of function. ' +
'If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`.' 'If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`.'
@ -125,3 +125,14 @@ export function validate_snippet(snippet_fn) {
} }
return snippet_fn; return snippet_fn;
} }
/**
* Validate that the function behind `<Component />` isn't a snippet.
* @param {any} component_fn
*/
export function validate_component(component_fn) {
if (component_fn?.[snippet_symbol] === true) {
throw new Error('A snippet must be rendered with `{@render ...}`');
}
return component_fn;
}

@ -0,0 +1,8 @@
import { test } from '../../test';
export default test({
compileOptions: {
dev: true
},
error: 'A snippet must be rendered with `{@render ...}`'
});
Loading…
Cancel
Save