fix: disallow using `let:` directives with component render tags (#12400)

closes #12087
pull/12395/head
Simon H 6 months ago committed by GitHub
parent fd7b950e7d
commit f846cb4833
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: disallow using `let:` directives with component render tags

@ -4,7 +4,7 @@
## render_tag_invalid_argument ## render_tag_invalid_argument
> 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 ...}` > The argument to `{@render ...}` must be a snippet function, not a component or a slot with a `let:` directive or some other kind of function. If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`
## snippet_used_as_component ## snippet_used_as_component

@ -1864,7 +1864,11 @@ export const template_visitors = {
let snippet_function = /** @type {import('estree').Expression} */ (context.visit(callee)); let snippet_function = /** @type {import('estree').Expression} */ (context.visit(callee));
if (context.state.options.dev) { if (context.state.options.dev) {
snippet_function = b.call('$.validate_snippet', snippet_function); snippet_function = b.call(
'$.validate_snippet',
snippet_function,
args.length ? b.id('$$props') : undefined
);
} }
if (node.metadata.dynamic) { if (node.metadata.dynamic) {

@ -20,12 +20,12 @@ export function lifecycle_outside_component(name) {
} }
/** /**
* 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 ...}` * The argument to `{@render ...}` must be a snippet function, not a component or a slot with a `let:` directive or some other kind of function. If you want to dynamically render one snippet or another, use `$derived` and pass its result to `{@render ...}`
* @returns {never} * @returns {never}
*/ */
export function render_tag_invalid_argument() { export function render_tag_invalid_argument() {
if (DEV) { if (DEV) {
const error = new Error(`render_tag_invalid_argument\nThe 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 ...}\``); const error = new Error(`render_tag_invalid_argument\nThe argument to \`{@render ...}\` must be a snippet function, not a component or a slot with a \`let:\` directive or some other kind of function. If you want to dynamically render one snippet or another, use \`$derived\` and pass its result to \`{@render ...}\``);
error.name = 'Svelte error'; error.name = 'Svelte error';
throw error; throw error;

@ -15,9 +15,10 @@ export function add_snippet_symbol(fn) {
/** /**
* Validate that the function handed to `{@render ...}` is a snippet function, and not some other kind of function. * Validate that the function handed to `{@render ...}` is a snippet function, and not some other kind of function.
* @param {any} snippet_fn * @param {any} snippet_fn
* @param {Record<string, any> | undefined} $$props Only passed if render tag receives arguments
*/ */
export function validate_snippet(snippet_fn) { export function validate_snippet(snippet_fn, $$props) {
if (snippet_fn && snippet_fn[snippet_symbol] !== true) { if ($$props?.$$slots?.default || (snippet_fn && snippet_fn[snippet_symbol] !== true)) {
e.render_tag_invalid_argument(); e.render_tag_invalid_argument();
} }

@ -117,7 +117,9 @@ export function runtime_suite(runes: boolean) {
(!config.test_ssr && (!config.test_ssr &&
config.html === undefined && config.html === undefined &&
config.ssrHtml === undefined && config.ssrHtml === undefined &&
config.error === undefined) config.error === undefined &&
config.runtime_error === undefined &&
!config.mode?.includes('server'))
) { ) {
return 'no-test'; return 'no-test';
} }

@ -0,0 +1,8 @@
import { test } from '../../test';
export default test({
compileOptions: {
dev: true
},
runtime_error: 'render_tag_invalid_argument'
});

@ -0,0 +1,5 @@
<script>
let { children } = $props();
</script>
{@render children(true)}

@ -0,0 +1,7 @@
<script>
import Inner from './inner.svelte';
</script>
<Inner let:foo>
{foo}
</Inner>

@ -4,8 +4,5 @@ export default test({
compileOptions: { compileOptions: {
dev: true dev: true
}, },
error: error: 'render_tag_invalid_argument'
'render_tag_invalid_argument\n' +
'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 ...}`'
}); });

Loading…
Cancel
Save