diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js index 1bccfaec5e..a89ae00f70 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js @@ -211,10 +211,9 @@ export const javascript_visitors_runes = { property.value.type === 'AssignmentPattern' ? property.value.left : property.value; assert.equal(id.type, 'Identifier'); const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(id.name)); - let initial = /** @type {import('estree').Expression | null} */ (binding.initial); - if (initial) { - initial = /** @type {import('estree').Expression} */ (visit(initial)); - } + const initial = + binding.initial && + /** @type {import('estree').Expression} */ (visit(binding.initial)); if (binding.reassigned || state.analysis.accessors || initial) { declarations.push(b.declarator(id, get_prop_source(binding, state, name, initial))); @@ -223,6 +222,14 @@ export const javascript_visitors_runes = { // RestElement /** @type {import('estree').Expression[]} */ const args = [b.id('$$props'), b.array(seen.map((name) => b.literal(name)))]; + + if (state.options.dev) { + // include rest name, so we can provide informative error messages + args.push( + b.literal(/** @type {import('estree').Identifier} */ (property.argument).name) + ); + } + declarations.push(b.declarator(property.argument, b.call('$.rest_props', ...args))); } } diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index 9a87893516..994ba894bd 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -36,19 +36,18 @@ export function update_pre_prop(fn, d = 1) { /** * The proxy handler for rest props (i.e. `const { x, ...rest } = $props()`). * Is passed the full `$$props` object and excludes the named props. - * @type {ProxyHandler<{ props: Record, exclude: Array }>}} + * @type {ProxyHandler<{ props: Record, exclude: Array, name: string }>}} */ const rest_props_handler = { get(target, key) { if (target.exclude.includes(key)) return; return target.props[key]; }, - set(_, key) { + set(target, key) { if (DEV) { - throw new Error( - `Cannot write to property '${String(key)}' of rest element of $props(). It is always readonly.` - ); + throw new Error(`${target.name}.${String(key)} is readonly`); } + return false; }, getOwnPropertyDescriptor(target, key) { @@ -73,10 +72,11 @@ const rest_props_handler = { /** * @param {Record} props * @param {string[]} rest + * @param {string} [name] * @returns {Record} */ -export function rest_props(props, rest) { - return new Proxy({ props, exclude: rest }, rest_props_handler); +export function rest_props(props, rest, name) { + return new Proxy({ props, exclude: rest, name }, rest_props_handler); } /**