diff --git a/.changeset/nervous-pans-call.md b/.changeset/nervous-pans-call.md new file mode 100644 index 000000000000..224e0fe61994 --- /dev/null +++ b/.changeset/nervous-pans-call.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: do not error when binding a non-existent prop diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index d0e6efc6d8f8..f5a564aba32b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -286,16 +286,26 @@ export function client_component(analysis, options) { const properties = [...analysis.instance.scope.declarations].filter( ([name, binding]) => - (binding.kind === 'prop' || binding.kind === 'bindable_prop') && !name.startsWith('$$') + (binding.kind === 'prop' || + binding.kind === 'bindable_prop' || + binding.kind === 'rest_prop') && + !name.startsWith('$$') ); if (dev && analysis.runes) { const exports = analysis.exports.map(({ name, alias }) => b.literal(alias ?? name)); /** @type {ESTree.Literal[]} */ const bindable = []; + const non_bindable = []; + let has_rest = false; + for (const [name, binding] of properties) { if (binding.kind === 'bindable_prop') { bindable.push(b.literal(binding.prop_alias ?? name)); + } else if (binding.kind === 'rest_prop') { + has_rest = true; + } else { + non_bindable.push(b.literal(binding.prop_alias ?? name)); } } instance.body.unshift( @@ -304,8 +314,10 @@ export function client_component(analysis, options) { '$.validate_prop_bindings', b.id('$$props'), b.array(bindable), + b.array(non_bindable), b.array(exports), - b.id(`${analysis.name}`) + b.id(`${analysis.name}`), + b.literal(has_rest) ) ) ); diff --git a/packages/svelte/src/internal/client/validate.js b/packages/svelte/src/internal/client/validate.js index 0666e5f87bb0..3ad15ca73f61 100644 --- a/packages/svelte/src/internal/client/validate.js +++ b/packages/svelte/src/internal/client/validate.js @@ -49,10 +49,19 @@ export function validate_each_keys(collection, key_fn) { /** * @param {Record} $$props * @param {string[]} bindable + * @param {string[]} non_bindable * @param {string[]} exports * @param {Function & { [FILENAME]: string }} component + * @param {boolean} has_rest */ -export function validate_prop_bindings($$props, bindable, exports, component) { +export function validate_prop_bindings( + $$props, + bindable, + non_bindable, + exports, + component, + has_rest +) { for (const key in $$props) { var setter = get_descriptor($$props, key)?.set; var name = component.name; @@ -63,7 +72,9 @@ export function validate_prop_bindings($$props, bindable, exports, component) { } if (!bindable.includes(key)) { - e.bind_not_bindable(key, component[FILENAME], name); + if (has_rest || non_bindable.includes(key)) { + e.bind_not_bindable(key, component[FILENAME], name); + } } } } diff --git a/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/Input.svelte b/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/Input.svelte new file mode 100644 index 000000000000..7450e6b61f99 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/Input.svelte @@ -0,0 +1,7 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/InputEditable.svelte b/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/InputEditable.svelte new file mode 100644 index 000000000000..b57ba767490f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/InputEditable.svelte @@ -0,0 +1,9 @@ + +
+
+ diff --git a/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/_config.js b/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/_config.js new file mode 100644 index 000000000000..ed0ead960bdb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/main.svelte new file mode 100644 index 000000000000..34e06083eae3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/main.svelte @@ -0,0 +1,15 @@ + + +{#each components as Component, i} + +{/each}