Repository: sveltejs/svelte (JavaScript)
Date: 2019-07-04 00:20:42+00:00
Task type: Bug Fix

Issue:
From 3.6.0: "TypeError: Cannot read property 'block' of null"
I'm using it with `svelte-apollo` but I cannot create now reproduction on codesandbox.

```log
Uncaught (in promise) TypeError: Cannot read property 'block' of null
    at Object.destroy [as d] (PlayersPage.svelte:14)
    at destroy_component (index.mjs:1185)
    at App.svelte:31
    at index.mjs:633
    at run (index.mjs:18)
    at Array.forEach (<anonymous>)
    at run_all (index.mjs:24)
    at check_outros (index.mjs:615)
    at Object.update [as p] (App.svelte:31)
    at update (index.mjs:586)
```

Maybe something related to `Object.destroy` from 3.6.0 (https://github.com/sveltejs/svelte/issues/3058)?


Issue discussion:
I can confirm this. Downgrading to 3.5.4 fixes the issue. I am using page.js for routing and 3.6.0 breaks it.
We'll need a repro to debug this. It doesn't have to be on codesandbox, it can be a git repo.

Is this still present on the latest version?
I cannot create gist now. The problem is still on `3.6.2` (I try every time new Svelte versions).

Downgrading to `3.5.4` fix the issue also for me.
I also caught this issue. Seems it somehow depends on https://github.com/sveltejs/svelte/issues/3058
After a small investigation, I'm sure it also depends on `{#await /}` block.
@Conduitry, I have a reproduction: https://codesandbox.io/s/vigilant-cache-4rtqy

Open browser console, not the codesandbox's one.

@radarsh, @PaulMaly can you confirm it is the same problem?
@frederikhors yup, same error.

![image](https://user-images.githubusercontent.com/534512/60583500-f5672e00-9d82-11e9-8f92-c2596936de93.png)

Reduced repro: A `<svelte:component>` that displays a component with an `{#await}` block.

**App.svelte**
```svelte
<script>
  let flag = false;
  import Widget from './Widget.svelte';
</script>

<input type='checkbox' bind:checked={flag}>

<svelte:component this={flag && Widget}/>
```

**Widget.svelte**
```svelte
{#await null}{/await}
```

Toggling the checkbox on and off to display and destroy the child component throws this exception.

Solution patch:
diff --git a/src/compiler/compile/render_dom/wrappers/EachBlock.ts b/src/compiler/compile/render_dom/wrappers/EachBlock.ts
--- a/src/compiler/compile/render_dom/wrappers/EachBlock.ts
+++ b/src/compiler/compile/render_dom/wrappers/EachBlock.ts
@@ -493,7 +493,7 @@ export default class EachBlockWrapper extends Wrapper {
 				const out = block.get_unique_name('out');
 
 				block.builders.init.add_block(deindent`
-					const ${out} = i => @transition_out(${iterations}[i], 1, () => {
+					const ${out} = i => @transition_out(${iterations}[i], 1, 1, () => {
 						${iterations}[i] = null;
 					});
 				`);
diff --git a/src/compiler/compile/render_dom/wrappers/IfBlock.ts b/src/compiler/compile/render_dom/wrappers/IfBlock.ts
--- a/src/compiler/compile/render_dom/wrappers/IfBlock.ts
+++ b/src/compiler/compile/render_dom/wrappers/IfBlock.ts
@@ -326,7 +326,7 @@ export default class IfBlockWrapper extends Wrapper {
 
 		const destroy_old_block = deindent`
 			@group_outros();
-			@transition_out(${if_blocks}[${previous_block_index}], 1, () => {
+			@transition_out(${if_blocks}[${previous_block_index}], 1, 1, () => {
 				${if_blocks}[${previous_block_index}] = null;
 			});
 			@check_outros();
@@ -439,7 +439,7 @@ export default class IfBlockWrapper extends Wrapper {
 					${enter}
 				} else if (${name}) {
 					@group_outros();
-					@transition_out(${name}, 1, () => {
+					@transition_out(${name}, 1, 1, () => {
 						${name} = null;
 					});
 					@check_outros();
diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
--- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
+++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts
@@ -388,8 +388,8 @@ export default class InlineComponentWrapper extends Wrapper {
 					if (${name}) {
 						@group_outros();
 						const old_component = ${name};
-						@transition_out(old_component.$$.fragment, 1, () => {
-							@destroy_component(old_component);
+						@transition_out(old_component.$$.fragment, 1, 0, () => {
+							@destroy_component(old_component, 1);
 						});
 						@check_outros();
 					}
diff --git a/src/runtime/internal/await_block.ts b/src/runtime/internal/await_block.ts
--- a/src/runtime/internal/await_block.ts
+++ b/src/runtime/internal/await_block.ts
@@ -18,7 +18,7 @@ export function handle_promise(promise, info) {
 				info.blocks.forEach((block, i) => {
 					if (i !== index && block) {
 						group_outros();
-						transition_out(block, 1, () => {
+						transition_out(block, 1, 1, () => {
 							info.blocks[i] = null;
 						});
 						check_outros();
diff --git a/src/runtime/internal/keyed_each.ts b/src/runtime/internal/keyed_each.ts
--- a/src/runtime/internal/keyed_each.ts
+++ b/src/runtime/internal/keyed_each.ts
@@ -6,7 +6,7 @@ export function destroy_block(block, lookup) {
 }
 
 export function outro_and_destroy_block(block, lookup) {
-	transition_out(block, 1, () => {
+	transition_out(block, 1, 1, () => {
 		lookup.delete(block.key);
 	});
 }
diff --git a/src/runtime/internal/transitions.ts b/src/runtime/internal/transitions.ts
--- a/src/runtime/internal/transitions.ts
+++ b/src/runtime/internal/transitions.ts
@@ -46,7 +46,7 @@ export function transition_in(block, local?: 0 | 1) {
 	}
 }
 
-export function transition_out(block, local: 0 | 1, callback) {
+export function transition_out(block, local: 0 | 1, detach: 0 | 1, callback) {
 	if (block && block.o) {
 		if (outroing.has(block)) return;
 		outroing.add(block);
@@ -54,7 +54,7 @@ export function transition_out(block, local: 0 | 1, callback) {
 		outros.callbacks.push(() => {
 			outroing.delete(block);
 			if (callback) {
-				block.d(1);
+				if (detach) block.d(1);
 				callback();
 			}
 		});


Test patch:
diff --git a/test/js/samples/transition-repeated-outro/expected.js b/test/js/samples/transition-repeated-outro/expected.js
--- a/test/js/samples/transition-repeated-outro/expected.js
+++ b/test/js/samples/transition-repeated-outro/expected.js
@@ -81,7 +81,7 @@ function create_fragment(ctx) {
 				}
 			} else if (if_block) {
 				group_outros();
-				transition_out(if_block, 1, () => {
+				transition_out(if_block, 1, 1, () => {
 					if_block = null;
 				});
 				check_outros();
diff --git a/test/runtime/samples/await-in-dynamic-component/Widget.svelte b/test/runtime/samples/await-in-dynamic-component/Widget.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/await-in-dynamic-component/Widget.svelte
@@ -0,0 +1 @@
+{#await null}{/await}
\ No newline at end of file
diff --git a/test/runtime/samples/await-in-dynamic-component/_config.js b/test/runtime/samples/await-in-dynamic-component/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/await-in-dynamic-component/_config.js
@@ -0,0 +1,5 @@
+export default {
+	test({ component }) {
+		component.flag = false;
+	}
+};
\ No newline at end of file
diff --git a/test/runtime/samples/await-in-dynamic-component/main.svelte b/test/runtime/samples/await-in-dynamic-component/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/await-in-dynamic-component/main.svelte
@@ -0,0 +1,6 @@
+<script>
+	export let flag = true;
+	import Widget from './Widget.svelte';
+</script>
+
+<svelte:component this={flag && Widget}/>
\ No newline at end of file