replace render_tag_invalid_argument with invalid_default_snippet

pull/12507/head
Rich Harris 2 years ago
parent deae40476a
commit e76c7c7360

@ -1,10 +1,10 @@
## lifecycle_outside_component
## invalid_default_snippet
> `%name%(...)` can only be used during component initialisation
> Cannot use `{@render children(...)}` if the parent component uses `let:` directives. Consider using a named snippet instead
## render_tag_invalid_argument
## lifecycle_outside_component
> 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 ...}`
> `%name%(...)` can only be used during component initialisation
## store_invalid_shape

@ -884,23 +884,27 @@ function serialize_inline_component(node, component_name, context, anchor = cont
])
);
if (
slot_name === 'default' &&
!has_children_prop &&
lets.length === 0 &&
children.default.every((node) => node.type !== 'SvelteFragment')
) {
push_prop(
b.init(
'children',
context.state.options.dev
? b.call('$.wrap_snippet', b.id(context.state.analysis.name), slot_fn)
: slot_fn
)
);
// We additionally add the default slot as a boolean, so that the slot render function on the other
// side knows it should get the content to render from $$props.children
serialized_slots.push(b.init(slot_name, b.true));
if (slot_name === 'default' && !has_children_prop) {
if (lets.length === 0 && children.default.every((node) => node.type !== 'SvelteFragment')) {
// create `children` prop...
push_prop(
b.init(
'children',
context.state.options.dev
? b.call('$.wrap_snippet', b.id(context.state.analysis.name), slot_fn)
: slot_fn
)
);
// and `$$slots.default: true` so that `<slot>` on the child works
serialized_slots.push(b.init(slot_name, b.true));
} else {
// create `$$slots.default`...
serialized_slots.push(b.init(slot_name, slot_fn));
// and a `children` prop that errors
push_prop(b.init('children', b.id('$.invalid_children_snippet')));
}
} else {
serialized_slots.push(b.init(slot_name, slot_fn));
}
@ -1864,15 +1868,6 @@ export const template_visitors = {
}
let snippet_function = /** @type {Expression} */ (context.visit(callee));
if (context.state.options.dev) {
snippet_function = b.call(
'$.validate_snippet',
snippet_function,
args.length && callee.type === 'Identifier' && callee.name === 'children'
? b.id('$$props')
: undefined
);
}
if (node.metadata.dynamic) {
context.state.init.push(

@ -963,19 +963,22 @@ function serialize_inline_component(node, expression, context) {
])
);
if (
slot_name === 'default' &&
!has_children_prop &&
lets.length === 0 &&
children.default.every((node) => node.type !== 'SvelteFragment')
) {
push_prop(b.prop('init', b.id('children'), slot_fn));
// We additionally add the default slot as a boolean, so that the slot render function on the other
// side knows it should get the content to render from $$props.children
serialized_slots.push(b.init('default', b.true));
if (slot_name === 'default' && !has_children_prop) {
if (lets.length === 0 && children.default.every((node) => node.type !== 'SvelteFragment')) {
// create `children` prop...
push_prop(b.prop('init', b.id('children'), slot_fn));
// and `$$slots.default: true` so that `<slot>` on the child works
serialized_slots.push(b.init(slot_name, b.true));
} else {
// create `$$slots.default`...
serialized_slots.push(b.init(slot_name, slot_fn));
// and a `children` prop that errors
push_prop(b.init('children', b.id('$.invalid_children_snippet')));
}
} else {
const slot = b.prop('init', b.literal(slot_name), slot_fn);
serialized_slots.push(slot);
serialized_slots.push(b.init(slot_name, slot_fn));
}
}
@ -1203,16 +1206,7 @@ const template_visitors = {
const callee = unwrap_optional(node.expression).callee;
const raw_args = unwrap_optional(node.expression).arguments;
const expression = /** @type {import('estree').Expression} */ (context.visit(callee));
const snippet_function = context.state.options.dev
? b.call(
'$.validate_snippet',
expression,
raw_args.length && callee.type === 'Identifier' && callee.name === 'children'
? b.id('$$props')
: undefined
)
: expression;
const snippet_function = /** @type {import('estree').Expression} */ (context.visit(callee));
const snippet_args = raw_args.map((arg) => {
return /** @type {import('estree').Expression} */ (context.visit(arg));

@ -164,9 +164,9 @@ export {
export { snapshot } from '../shared/clone.js';
export { noop } from '../shared/utils.js';
export {
invalid_children_snippet,
validate_component,
validate_dynamic_element_tag,
validate_snippet,
validate_store,
validate_void_dynamic_element
} from '../shared/validate.js';

@ -555,9 +555,9 @@ export { push_element, pop_element } from './dev.js';
export { snapshot } from '../shared/clone.js';
export {
invalid_children_snippet,
validate_component,
validate_dynamic_element_tag,
validate_snippet,
validate_void_dynamic_element
} from '../shared/validate.js';

@ -35,6 +35,22 @@ export function render_tag_invalid_argument() {
}
}
/**
* Cannot use `{@render children(...)}` if the parent component uses `let:` directives. Consider using a named snippet instead
* @returns {never}
*/
export function invalid_default_snippet() {
if (DEV) {
const error = new Error(`invalid_default_snippet\nCannot use \`{@render children(...)}\` if the parent component uses \`let:\` directives. Consider using a named snippet instead`);
error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("invalid_default_snippet");
}
}
/**
* `%name%` is not a store with a `subscribe` method
* @param {string} name

@ -17,6 +17,10 @@ export function validate_snippet(snippet_fn, $$props) {
return snippet_fn;
}
export function invalid_children_snippet() {
e.invalid_default_snippet();
}
/**
* Validate that the function behind `<Component />` isn't a snippet.
* @param {any} component_fn

@ -4,5 +4,5 @@ export default test({
compileOptions: {
dev: true
},
runtime_error: 'render_tag_invalid_argument'
runtime_error: 'invalid_default_snippet'
});

Loading…
Cancel
Save