Repository: sveltejs/svelte (JavaScript)
Date: 2019-08-19 15:42:01+00:00
Task type: Bug Fix

Issue:
Confusing SSR error when `let:foo` has no corresponding `<slot foo={...}>`
**Describe the bug**

A component with slotted contents will fail to render if it has a `let:` directive but the corresponding `<slot>` has no properties.

**To Reproduce**

[This REPL demo](https://svelte.dev/repl/49963e1550274855a2ad2f45ca786a59?version=3.6.10) contains the bug, though it's not directly visible since it's SSR-only. The offending code is this:

```js
	return `${$$slots.default ? $$slots.default() : ``}`;
```

`$$slots.default` corresponds to this:

```js
	return `${validate_component(Foo, 'Foo').$$render($$result, {}, {}, {
		default: ({ id }) => `
		${validate_component(Bar, 'Bar').$$render($$result, { thing: things[id] }, {}, {})}
	`
	})}`
```

This fails with 

> TypeError: Cannot destructure property `id` of 'undefined' or 'null'.

**Expected behavior**

It might be nice if a warning was printed about the missing property. But the easy immediate fix is to always pass an object, i.e. `$$slots.default({})`. This way there's no disparity between `<slot>` and `<slot someOtherProperty="whatever">`.

**Severity**

Minor annoyance


Issue discussion:

Solution patch:
diff --git a/src/compiler/compile/render_ssr/handlers/Slot.ts b/src/compiler/compile/render_ssr/handlers/Slot.ts
--- a/src/compiler/compile/render_ssr/handlers/Slot.ts
+++ b/src/compiler/compile/render_ssr/handlers/Slot.ts
@@ -8,7 +8,7 @@ export default function(node: Slot, renderer: Renderer, options: RenderOptions)
 
 	const slot_data = get_slot_data(node.values, true);
 
-	const arg = slot_data.length > 0 ? `{ ${slot_data.join(', ')} }` : '';
+	const arg = slot_data.length > 0 ? `{ ${slot_data.join(', ')} }` : '{}';
 
 	renderer.append(`\${$$slots${prop} ? $$slots${prop}(${arg}) : \``);
 


Test patch:
diff --git a/test/runtime/samples/component-slot-let-missing-prop/Bar.svelte b/test/runtime/samples/component-slot-let-missing-prop/Bar.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/component-slot-let-missing-prop/Bar.svelte
@@ -0,0 +1,5 @@
+<script>
+	export let thing;
+</script>
+
+<p>{thing}</p>
\ No newline at end of file
diff --git a/test/runtime/samples/component-slot-let-missing-prop/Foo.svelte b/test/runtime/samples/component-slot-let-missing-prop/Foo.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/component-slot-let-missing-prop/Foo.svelte
@@ -0,0 +1 @@
+<slot></slot>
\ No newline at end of file
diff --git a/test/runtime/samples/component-slot-let-missing-prop/_config.js b/test/runtime/samples/component-slot-let-missing-prop/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/component-slot-let-missing-prop/_config.js
@@ -0,0 +1,5 @@
+export default {
+	html: `
+		<p>undefined</p>
+	`
+};
diff --git a/test/runtime/samples/component-slot-let-missing-prop/main.svelte b/test/runtime/samples/component-slot-let-missing-prop/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/component-slot-let-missing-prop/main.svelte
@@ -0,0 +1,10 @@
+<script>
+	import Foo from './Foo.svelte';
+	import Bar from './Bar.svelte';
+
+	const things = { '1': 'one' };
+</script>
+
+<Foo let:id>
+	<Bar thing={things[id]}/>
+</Foo>
\ No newline at end of file