Repository: sveltejs/svelte (JavaScript)
Date: 2020-09-10 10:03:09+00:00
Task type: Bug Fix

Issue:
$$props not updating nested slot
**Describe the bug**
A wrapper component exposes a main slot. When providing it with content that itself includes a default slot on which it spreads `$$props`, those a not reactive.

```js
<Container>
  <div class="input">
    <slot>
      <input type="text" {...$$props} />	
    </slot>
  </div>
</Container>
```

**Logs**
No particular logs

**To Reproduce**
See [this REPL example](https://svelte.dev/repl/3760eaa9aec046d4976bcb35f42f2290?version=3.24.1)

**Expected behavior**
The props passed to the wrapper component should be transferred to the default slot.

**Information about your Svelte project:**
Not specific to browser or OS. Reproduced using latest Svelte 3.24.1.

**Severity**
Annoying, since I have to find a workaround or explicitely declare every prop that the default slot could get from its wrapper.

Thanks for considering this! I appreciate.



Issue discussion:

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@
 * Fix using `<svelte:component>` in `{:catch}` ([#5259](https://github.com/sveltejs/svelte/issues/5259))
 * Fix setting one-way bound `<input>` `value` to `undefined` when it has spread attributes ([#5270](https://github.com/sveltejs/svelte/issues/5270))
 * Fix deep two-way bindings inside an `{#each}` involving a store ([#5286](https://github.com/sveltejs/svelte/issues/5286))
+* Fix reactivity of `$$props` in slot fallback content ([#5367](https://github.com/sveltejs/svelte/issues/5367))
 
 ## 3.24.1
 
diff --git a/src/compiler/compile/render_dom/wrappers/shared/is_dynamic.ts b/src/compiler/compile/render_dom/wrappers/shared/is_dynamic.ts
--- a/src/compiler/compile/render_dom/wrappers/shared/is_dynamic.ts
+++ b/src/compiler/compile/render_dom/wrappers/shared/is_dynamic.ts
@@ -1,9 +1,11 @@
 import { Var } from '../../../../interfaces';
+import { is_reserved_keyword } from '../../../utils/reserved_keywords';
 
 export default function is_dynamic(variable: Var) {
 	if (variable) {
 		if (variable.mutated || variable.reassigned) return true; // dynamic internal state
 		if (!variable.module && variable.writable && variable.export_name) return true; // writable props
+		if (is_reserved_keyword(variable.name)) return true;
 	}
 
 	return false;


Test patch:
diff --git a/test/runtime/samples/component-slot-fallback-6/Foo.svelte b/test/runtime/samples/component-slot-fallback-6/Foo.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/component-slot-fallback-6/Foo.svelte
@@ -0,0 +1 @@
+<slot />
\ No newline at end of file
diff --git a/test/runtime/samples/component-slot-fallback-6/Inner.svelte b/test/runtime/samples/component-slot-fallback-6/Inner.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/component-slot-fallback-6/Inner.svelte
@@ -0,0 +1,9 @@
+<script>
+	import Foo from './Foo.svelte';
+</script>
+
+<Foo>
+	<slot>
+		{JSON.stringify($$props)}
+	</slot>
+</Foo>
diff --git a/test/runtime/samples/component-slot-fallback-6/_config.js b/test/runtime/samples/component-slot-fallback-6/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/component-slot-fallback-6/_config.js
@@ -0,0 +1,18 @@
+// $$props reactivity in slot fallback
+export default {
+	html: `
+		<input>
+		{"value":""}
+	`,
+
+	async test({ assert, target, window }) {
+		const input = target.querySelector("input");
+		input.value = "abc";
+		await input.dispatchEvent(new window.Event('input'));
+
+		assert.htmlEqual(target.innerHTML, `
+			<input>
+			{"value":"abc"}
+		`);
+	}
+};
diff --git a/test/runtime/samples/component-slot-fallback-6/main.svelte b/test/runtime/samples/component-slot-fallback-6/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/component-slot-fallback-6/main.svelte
@@ -0,0 +1,8 @@
+<script>
+	import Inner from "./Inner.svelte";
+	let value = '';
+</script>
+
+<input bind:value />
+
+<Inner {value} />