diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index bebf196bdd..8898af0a8c 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -1,3 +1,11 @@ +## bind_invalid_export + +> Component %component% has an export named `%key%` that a consumer component is trying to access using `bind:%key%`, which is disallowed. Instead, use `bind:this` (e.g. `<%name% bind:this={component} />`) and then access the property on the bound component instance (e.g. `component.%key%`) + +## bind_not_bindable + +> A component is attempting to bind to a non-bindable property `%key%` belonging to %component% (i.e. `<%name% bind:%key%={...}>`). To mark a property as bindable: `let { %key% = $bindable() } = $props()` + ## each_key_duplicate > Keyed each block has duplicate key at indexes %a% and %b% diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 1ecee03786..81a736100b 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -2,6 +2,44 @@ import { DEV } from 'esm-env'; +/** + * Component %component% has an export named `%key%` that a consumer component is trying to access using `bind:%key%`, which is disallowed. Instead, use `bind:this` (e.g. `<%name% bind:this={component} />`) and then access the property on the bound component instance (e.g. `component.%key%`) + * @param {string} component + * @param {string} key + * @param {string} name + * @returns {never} + */ +export function bind_invalid_export(component, key, name) { + if (DEV) { + const error = new Error(`${"bind_invalid_export"}\n${`Component ${component} has an export named \`${key}\` that a consumer component is trying to access using \`bind:${key}\`, which is disallowed. Instead, use \`bind:this\` (e.g. \`<${name} bind:this={component} />\`) and then access the property on the bound component instance (e.g. \`component.${key}\`)`}`); + + error.name = 'Svelte error'; + throw error; + } else { + // TODO print a link to the documentation + throw new Error("bind_invalid_export"); + } +} + +/** + * A component is attempting to bind to a non-bindable property `%key%` belonging to %component% (i.e. `<%name% bind:%key%={...}>`). To mark a property as bindable: `let { %key% = $bindable() } = $props()` + * @param {string} key + * @param {string} component + * @param {string} name + * @returns {never} + */ +export function bind_not_bindable(key, component, name) { + if (DEV) { + const error = new Error(`${"bind_not_bindable"}\n${`A component is attempting to bind to a non-bindable property \`${key}\` belonging to ${component} (i.e. \`<${name} bind:${key}={...}>\`). To mark a property as bindable: \`let { ${key} = $bindable() } = $props()\``}`); + + error.name = 'Svelte error'; + throw error; + } else { + // TODO print a link to the documentation + throw new Error("bind_not_bindable"); + } +} + /** * Keyed each block has duplicate key `%value%` at indexes %a% and %b% * @param {string} a diff --git a/packages/svelte/src/internal/client/validate.js b/packages/svelte/src/internal/client/validate.js index a1799d42f9..2685e0c1ad 100644 --- a/packages/svelte/src/internal/client/validate.js +++ b/packages/svelte/src/internal/client/validate.js @@ -78,17 +78,11 @@ export function validate_prop_bindings($$props, bindable, exports, component) { if (setter) { if (exports.includes(key)) { - throw new Error( - `Component ${component.filename} has an export named ${key} that a consumer component is trying to access using bind:${key}, which is disallowed. ` + - `Instead, use bind:this (e.g. <${name} bind:this={component} />) ` + - `and then access the property on the bound component instance (e.g. component.${key}).` - ); + e.bind_invalid_export(component.filename, key, name); } + if (!bindable.includes(key)) { - throw new Error( - `A component is binding to property ${key} of ${name}.svelte (i.e. <${name} bind:${key} />). This is disallowed because the property was not declared as bindable inside ${component.filename}. ` + - `To mark a property as bindable, use the $bindable() rune in ${name}.svelte like this: \`let { ${key} = $bindable() } = $props()\`` - ); + e.bind_not_bindable(key, component.filename, name); } } } diff --git a/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js b/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js index 0f64998059..5f02769634 100644 --- a/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js @@ -5,6 +5,6 @@ export default test({ dev: true // to ensure we we catch the error }, error: - 'Component .../export-binding/counter/index.svelte has an export named increment that a consumer component is trying to access using bind:increment, which is disallowed. ' + - 'Instead, use bind:this (e.g. ) and then access the property on the bound component instance (e.g. component.increment).' + 'bind_invalid_export\n' + + 'Component .../export-binding/counter/index.svelte has an export named `increment` that a consumer component is trying to access using `bind:increment`, which is disallowed. Instead, use `bind:this` (e.g. ``) and then access the property on the bound component instance (e.g. `component.increment`)' }); diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js index fc6b46d488..3cc17bd7b3 100644 --- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js @@ -4,9 +4,10 @@ export default test({ compileOptions: { dev: true }, + + html: '0', + error: - 'A component is binding to property count of Counter.svelte (i.e. ). This is disallowed because the property was ' + - 'not declared as bindable inside .../samples/props-not-bindable-spread/Counter.svelte. To mark a property as bindable, use the $bindable() rune ' + - 'in Counter.svelte like this: `let { count = $bindable() } = $props()`', - html: `0` + 'bind_not_bindable\n' + + 'A component is attempting to bind to a non-bindable property `count` belonging to .../samples/props-not-bindable-spread/Counter.svelte (i.e. ``). To mark a property as bindable: `let { count = $bindable() } = $props()`' }); diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js b/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js index b3dcc7f23e..34ab163829 100644 --- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js @@ -4,9 +4,10 @@ export default test({ compileOptions: { dev: true }, + + html: '0', + error: - 'A component is binding to property count of Counter.svelte (i.e. ). This is disallowed because the property was ' + - 'not declared as bindable inside .../samples/props-not-bindable/Counter.svelte. To mark a property as bindable, use the $bindable() rune ' + - 'in Counter.svelte like this: `let { count = $bindable() } = $props()`', - html: `0` + 'bind_not_bindable\n' + + 'A component is attempting to bind to a non-bindable property `count` belonging to .../samples/props-not-bindable/Counter.svelte (i.e. ``). To mark a property as bindable: `let { count = $bindable() } = $props()`' });