From 351b3e7aac4096e56b645d73803a04c8a8c55b2a Mon Sep 17 00:00:00 2001 From: Oscar Dominguez Date: Fri, 27 Sep 2024 21:53:32 +0200 Subject: [PATCH 1/2] fix: warn instead of error when binding a non-existent prop Co-authored-by: Paolo Ricciuti --- .changeset/nervous-pans-call.md | 5 +++++ .../svelte/messages/client-warnings/warnings.md | 4 ++++ .../3-transform/client/transform-client.js | 16 ++++++++++++++-- packages/svelte/src/internal/client/validate.js | 16 ++++++++++++++-- packages/svelte/src/internal/client/warnings.js | 15 +++++++++++++++ .../props-bindable-non-existent/Input.svelte | 7 +++++++ .../InputEditable.svelte | 9 +++++++++ .../props-bindable-non-existent/_config.js | 11 +++++++++++ .../props-bindable-non-existent/main.svelte | 15 +++++++++++++++ 9 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 .changeset/nervous-pans-call.md create mode 100644 packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/Input.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/InputEditable.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/main.svelte diff --git a/.changeset/nervous-pans-call.md b/.changeset/nervous-pans-call.md new file mode 100644 index 000000000000..99a08c747385 --- /dev/null +++ b/.changeset/nervous-pans-call.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: warn instead of error when binding a non-existent prop diff --git a/packages/svelte/messages/client-warnings/warnings.md b/packages/svelte/messages/client-warnings/warnings.md index 24355cae546e..ac8a1c07f0ce 100644 --- a/packages/svelte/messages/client-warnings/warnings.md +++ b/packages/svelte/messages/client-warnings/warnings.md @@ -1,3 +1,7 @@ +## binding_non_existent_prop + +> A component is attempting to bind to a non-existent property `%key%` belonging to %component% (i.e. `<%name% bind:%key%={...}>`) + ## binding_property_non_reactive > `%binding%` is binding to a non-reactive property 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..6f98bf1ae166 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,10 @@ 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); + } + w.binding_non_existent_prop(key, component[FILENAME], name); } } } diff --git a/packages/svelte/src/internal/client/warnings.js b/packages/svelte/src/internal/client/warnings.js index c45a92cdde95..2205825d83af 100644 --- a/packages/svelte/src/internal/client/warnings.js +++ b/packages/svelte/src/internal/client/warnings.js @@ -5,6 +5,21 @@ import { DEV } from 'esm-env'; var bold = 'font-weight: bold'; var normal = 'font-weight: normal'; +/** + * A component is attempting to bind to a non-existent property `%key%` belonging to %component% (i.e. `<%name% bind:%key%={...}>`) + * @param {string} key + * @param {string} component + * @param {string} name + */ +export function binding_non_existent_prop(key, component, name) { + if (DEV) { + console.warn(`%c[svelte] binding_non_existent_prop\n%cA component is attempting to bind to a non-existent property \`${key}\` belonging to ${component} (i.e. \`<${name} bind:${key}={...}>\`)`, bold, normal); + } else { + // TODO print a link to the documentation + console.warn("binding_non_existent_prop"); + } +} + /** * `%binding%` (%location%) is binding to a non-reactive property * @param {string} binding 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..cc5c13e36781 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-bindable-non-existent/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + warnings: [ + 'A component is attempting to bind to a non-existent property `content` belonging to Input.svelte (i.e. ``)' + ] +}); 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} From c6dc740f8f773d0d1b8f54a018ab6b1150a65fa1 Mon Sep 17 00:00:00 2001 From: Oscar Dominguez Date: Sun, 20 Oct 2024 11:55:06 +0200 Subject: [PATCH 2/2] feat: do nothing instead of warning when binding a non-existent prop --- .changeset/nervous-pans-call.md | 2 +- .../svelte/messages/client-warnings/warnings.md | 4 ---- packages/svelte/src/internal/client/validate.js | 1 - packages/svelte/src/internal/client/warnings.js | 15 --------------- .../props-bindable-non-existent/_config.js | 6 +----- 5 files changed, 2 insertions(+), 26 deletions(-) diff --git a/.changeset/nervous-pans-call.md b/.changeset/nervous-pans-call.md index 99a08c747385..224e0fe61994 100644 --- a/.changeset/nervous-pans-call.md +++ b/.changeset/nervous-pans-call.md @@ -2,4 +2,4 @@ 'svelte': patch --- -fix: warn instead of error when binding a non-existent prop +fix: do not error when binding a non-existent prop diff --git a/packages/svelte/messages/client-warnings/warnings.md b/packages/svelte/messages/client-warnings/warnings.md index ac8a1c07f0ce..24355cae546e 100644 --- a/packages/svelte/messages/client-warnings/warnings.md +++ b/packages/svelte/messages/client-warnings/warnings.md @@ -1,7 +1,3 @@ -## binding_non_existent_prop - -> A component is attempting to bind to a non-existent property `%key%` belonging to %component% (i.e. `<%name% bind:%key%={...}>`) - ## binding_property_non_reactive > `%binding%` is binding to a non-reactive property diff --git a/packages/svelte/src/internal/client/validate.js b/packages/svelte/src/internal/client/validate.js index 6f98bf1ae166..3ad15ca73f61 100644 --- a/packages/svelte/src/internal/client/validate.js +++ b/packages/svelte/src/internal/client/validate.js @@ -75,7 +75,6 @@ export function validate_prop_bindings( if (has_rest || non_bindable.includes(key)) { e.bind_not_bindable(key, component[FILENAME], name); } - w.binding_non_existent_prop(key, component[FILENAME], name); } } } diff --git a/packages/svelte/src/internal/client/warnings.js b/packages/svelte/src/internal/client/warnings.js index 2205825d83af..c45a92cdde95 100644 --- a/packages/svelte/src/internal/client/warnings.js +++ b/packages/svelte/src/internal/client/warnings.js @@ -5,21 +5,6 @@ import { DEV } from 'esm-env'; var bold = 'font-weight: bold'; var normal = 'font-weight: normal'; -/** - * A component is attempting to bind to a non-existent property `%key%` belonging to %component% (i.e. `<%name% bind:%key%={...}>`) - * @param {string} key - * @param {string} component - * @param {string} name - */ -export function binding_non_existent_prop(key, component, name) { - if (DEV) { - console.warn(`%c[svelte] binding_non_existent_prop\n%cA component is attempting to bind to a non-existent property \`${key}\` belonging to ${component} (i.e. \`<${name} bind:${key}={...}>\`)`, bold, normal); - } else { - // TODO print a link to the documentation - console.warn("binding_non_existent_prop"); - } -} - /** * `%binding%` (%location%) is binding to a non-reactive property * @param {string} binding 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 index cc5c13e36781..ed0ead960bdb 100644 --- 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 @@ -3,9 +3,5 @@ import { test } from '../../test'; export default test({ compileOptions: { dev: true - }, - - warnings: [ - 'A component is attempting to bind to a non-existent property `content` belonging to Input.svelte (i.e. ``)' - ] + } });