From 3cd934416d80fd24f7e4bafb41e807abd6bff827 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 23 Jul 2024 18:37:10 +0200 Subject: [PATCH 1/2] fix: exclude readonly bindings from reactive state validation `bind:this` etc only go upwards, and depending on the use case it's fine to connect it to a non-reactive variable --- .changeset/silent-rocks-yell.md | 5 +++ .../3-transform/client/visitors/template.js | 5 ++- .../svelte/src/compiler/phases/bindings.js | 42 ++++++++++++------- .../binding-property-static/main.svelte | 30 ++++++++++++- 4 files changed, 65 insertions(+), 17 deletions(-) create mode 100644 .changeset/silent-rocks-yell.md diff --git a/.changeset/silent-rocks-yell.md b/.changeset/silent-rocks-yell.md new file mode 100644 index 000000000000..115e7724b193 --- /dev/null +++ b/.changeset/silent-rocks-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: exclude `bind:this` from reactive state validation diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index af10f0a4cd6f..d80546fbb10b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -2826,9 +2826,11 @@ export const template_visitors = { BindDirective(node, context) { const { state, path, visit } = context; const expression = node.expression; + const property = binding_properties[node.name]; if ( expression.type === 'MemberExpression' && + (!property || property.bidirectional) && context.state.options.dev && context.state.analysis.runes ) { @@ -2859,8 +2861,7 @@ export const template_visitors = { /** @type {CallExpression} */ let call_expr; - const property = binding_properties[node.name]; - if (property && property.event) { + if (property?.event) { call_expr = b.call( '$.bind_property', b.literal(node.name), diff --git a/packages/svelte/src/compiler/phases/bindings.js b/packages/svelte/src/compiler/phases/bindings.js index eee84473a0c3..6714a6e77d35 100644 --- a/packages/svelte/src/compiler/phases/bindings.js +++ b/packages/svelte/src/compiler/phases/bindings.js @@ -15,7 +15,8 @@ export const binding_properties = { // media currentTime: { valid_elements: ['audio', 'video'], - omit_in_ssr: true + omit_in_ssr: true, + bidirectional: true }, duration: { valid_elements: ['audio', 'video'], @@ -25,7 +26,8 @@ export const binding_properties = { focused: {}, paused: { valid_elements: ['audio', 'video'], - omit_in_ssr: true + omit_in_ssr: true, + bidirectional: true }, buffered: { valid_elements: ['audio', 'video'], @@ -41,15 +43,18 @@ export const binding_properties = { }, volume: { valid_elements: ['audio', 'video'], - omit_in_ssr: true + omit_in_ssr: true, + bidirectional: true }, muted: { valid_elements: ['audio', 'video'], - omit_in_ssr: true + omit_in_ssr: true, + bidirectional: true }, playbackRate: { valid_elements: ['audio', 'video'], - omit_in_ssr: true + omit_in_ssr: true, + bidirectional: true }, seeking: { valid_elements: ['audio', 'video'], @@ -124,11 +129,13 @@ export const binding_properties = { }, scrollX: { valid_elements: ['svelte:window'], - omit_in_ssr: true + omit_in_ssr: true, + bidirectional: true }, scrollY: { valid_elements: ['svelte:window'], - omit_in_ssr: true + omit_in_ssr: true, + bidirectional: true }, online: { valid_elements: ['svelte:window'], @@ -180,23 +187,28 @@ export const binding_properties = { omit_in_ssr: true // no corresponding attribute }, checked: { - valid_elements: ['input'] + valid_elements: ['input'], + bidirectional: true }, group: { - valid_elements: ['input'] + valid_elements: ['input'], + bidirectional: true }, // various this: { omit_in_ssr: true }, innerText: { - invalid_elements: ['svelte:window', 'svelte:document'] + invalid_elements: ['svelte:window', 'svelte:document'], + bidirectional: true }, innerHTML: { - invalid_elements: ['svelte:window', 'svelte:document'] + invalid_elements: ['svelte:window', 'svelte:document'], + bidirectional: true }, textContent: { - invalid_elements: ['svelte:window', 'svelte:document'] + invalid_elements: ['svelte:window', 'svelte:document'], + bidirectional: true }, open: { event: 'toggle', @@ -204,10 +216,12 @@ export const binding_properties = { valid_elements: ['details'] }, value: { - valid_elements: ['input', 'textarea', 'select'] + valid_elements: ['input', 'textarea', 'select'], + bidirectional: true }, files: { valid_elements: ['input'], - omit_in_ssr: true + omit_in_ssr: true, + bidirectional: true } }; diff --git a/packages/svelte/tests/runtime-runes/samples/binding-property-static/main.svelte b/packages/svelte/tests/runtime-runes/samples/binding-property-static/main.svelte index c07062e3b27d..fb73244e5884 100644 --- a/packages/svelte/tests/runtime-runes/samples/binding-property-static/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/binding-property-static/main.svelte @@ -52,10 +52,38 @@ - + + + + + + +
+ + From be331d793962c4ed57834fc56ba23063dc730bb4 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Wed, 24 Jul 2024 16:54:26 +0200 Subject: [PATCH 2/2] only do it for bind:this --- .../3-transform/client/visitors/template.js | 9 +++++- .../binding-property-static/_config.js | 9 +++--- .../binding-property-static/main.svelte | 32 +++---------------- 3 files changed, 17 insertions(+), 33 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index d80546fbb10b..7e6501efe488 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -2830,7 +2830,14 @@ export const template_visitors = { if ( expression.type === 'MemberExpression' && - (!property || property.bidirectional) && + (node.name !== 'this' || + path.some( + ({ type }) => + type === 'IfBlock' || + type === 'EachBlock' || + type === 'AwaitBlock' || + type === 'KeyBlock' + )) && context.state.options.dev && context.state.analysis.runes ) { diff --git a/packages/svelte/tests/runtime-runes/samples/binding-property-static/_config.js b/packages/svelte/tests/runtime-runes/samples/binding-property-static/_config.js index 67bdc497a4d8..7202aadfa431 100644 --- a/packages/svelte/tests/runtime-runes/samples/binding-property-static/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/binding-property-static/_config.js @@ -7,10 +7,11 @@ export default test({ async test({ assert, warnings }) { assert.deepEqual(warnings, [ - `\`bind:value={pojo.value}\` (main.svelte:50:7) is binding to a non-reactive property`, - `\`bind:value={frozen.value}\` (main.svelte:51:7) is binding to a non-reactive property`, - `\`bind:value={pojo.value}\` (main.svelte:52:7) is binding to a non-reactive property`, - `\`bind:value={frozen.value}\` (main.svelte:53:7) is binding to a non-reactive property` + '`bind:value={pojo.value}` (main.svelte:50:7) is binding to a non-reactive property', + '`bind:value={frozen.value}` (main.svelte:51:7) is binding to a non-reactive property', + '`bind:value={pojo.value}` (main.svelte:52:7) is binding to a non-reactive property', + '`bind:value={frozen.value}` (main.svelte:53:7) is binding to a non-reactive property', + '`bind:this={pojo.value}` (main.svelte:55:6) is binding to a non-reactive property' ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/binding-property-static/main.svelte b/packages/svelte/tests/runtime-runes/samples/binding-property-static/main.svelte index fb73244e5884..3195aa35eaf5 100644 --- a/packages/svelte/tests/runtime-runes/samples/binding-property-static/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/binding-property-static/main.svelte @@ -51,39 +51,15 @@ +{#if value} +
+{/if} - + - - - - -
- -