Repository: sveltejs/svelte (JavaScript)
Date: 2018-05-25 20:37:59+00:00
Task type: Bug Fix

Issue:
Another nestedTransitions bug
[REPL](https://svelte.technology/repl?version=2.6.5&gist=eda1eecbbd0d218b3f0e17817e8348a2)
Another nestedTransitions bug
[REPL](https://svelte.technology/repl?version=2.6.5&gist=eda1eecbbd0d218b3f0e17817e8348a2)


Issue discussion:
That looks the same as mine, although my transition isn't nested https://svelte.technology/repl?version=2.6.0&gist=bddff497d8ec8f3bac0ca8001513b655, I think transition inside of a loop is what's borked.
That looks the same as mine, although my transition isn't nested https://svelte.technology/repl?version=2.6.0&gist=bddff497d8ec8f3bac0ca8001513b655, I think transition inside of a loop is what's borked.

Solution patch:
diff --git a/src/compile/nodes/AwaitBlock.ts b/src/compile/nodes/AwaitBlock.ts
--- a/src/compile/nodes/AwaitBlock.ts
+++ b/src/compile/nodes/AwaitBlock.ts
@@ -172,12 +172,13 @@ export default class AwaitBlock extends Node {
 		}
 
 		if (this.pending.block.hasOutroMethod && this.compiler.options.nestedTransitions) {
+			const countdown = block.getUniqueName('countdown');
 			block.builders.outro.addBlock(deindent`
-				#outrocallback = @callAfter(#outrocallback, 3);
+				const ${countdown} = @callAfter(#outrocallback, 3);
 				for (let #i = 0; #i < 3; #i += 1) {
 					const block = ${info}.blocks[#i];
-					if (block) block.o(#outrocallback);
-					else #outrocallback();
+					if (block) block.o(${countdown});
+					else ${countdown}();
 				}
 			`);
 		}
diff --git a/src/compile/nodes/EachBlock.ts b/src/compile/nodes/EachBlock.ts
--- a/src/compile/nodes/EachBlock.ts
+++ b/src/compile/nodes/EachBlock.ts
@@ -331,9 +331,10 @@ export default class EachBlock extends Node {
 		`);
 
 		if (this.compiler.options.nestedTransitions) {
+			const countdown = block.getUniqueName('countdown');
 			block.builders.outro.addBlock(deindent`
-				#outrocallback = @callAfter(#outrocallback, ${blocks}.length);
-				for (#i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].o(#outrocallback);
+				const ${countdown} = @callAfter(#outrocallback, ${blocks}.length);
+				for (#i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].o(${countdown});
 			`);
 		}
 
@@ -393,14 +394,16 @@ export default class EachBlock extends Node {
 			allDependencies.add(dependency);
 		});
 
-		const outro = this.block.hasOutros && block.getUniqueName('outro')
-		if (outro) {
+		const outroBlock = this.block.hasOutros && block.getUniqueName('outroBlock')
+		if (outroBlock) {
 			block.builders.init.addBlock(deindent`
-				function ${outro}(i, detach, fn) {
+				function ${outroBlock}(i, detach, fn) {
 					if (${iterations}[i]) {
 						${iterations}[i].o(() => {
-							${iterations}[i].d(detach);
-							if (detach) ${iterations}[i] = null;
+							if (detach) {
+								${iterations}[i].d(detach);
+								${iterations}[i] = null;
+							}
 							if (fn) fn();
 						});
 					}
@@ -447,7 +450,7 @@ export default class EachBlock extends Node {
 			if (this.block.hasOutros) {
 				destroy = deindent`
 					@transitionManager.groupOutros();
-					for (; #i < ${iterations}.length; #i += 1) ${outro}(#i, 1);
+					for (; #i < ${iterations}.length; #i += 1) ${outroBlock}(#i, 1);
 				`;
 			} else {
 				destroy = deindent`
@@ -473,10 +476,11 @@ export default class EachBlock extends Node {
 			`);
 		}
 
-		if (outro && this.compiler.options.nestedTransitions) {
+		if (outroBlock && this.compiler.options.nestedTransitions) {
+			const countdown = block.getUniqueName('countdown');
 			block.builders.outro.addBlock(deindent`
-				#outrocallback = @callAfter(#outrocallback, #i);
-				for (let #i = 0; #i < ${iterations}.length; #i += 1) ${outro}(#i, 0, #outrocallback);`
+				const ${countdown} = @callAfter(#outrocallback, ${iterations}.length);
+				for (let #i = 0; #i < ${iterations}.length; #i += 1) ${outroBlock}(#i, 0, ${countdown});`
 			);
 		}
 
diff --git a/src/compile/nodes/IfBlock.ts b/src/compile/nodes/IfBlock.ts
--- a/src/compile/nodes/IfBlock.ts
+++ b/src/compile/nodes/IfBlock.ts
@@ -147,9 +147,10 @@ export default class IfBlock extends Node {
 			this.buildSimple(block, parentNode, parentNodes, branches[0], dynamic, vars);
 
 			if (hasOutros && this.compiler.options.nestedTransitions) {
-				block.builders.outro.addLine(
-					`if (${name}) ${name}.o(#outrocallback);`
-				);
+				block.builders.outro.addBlock(deindent`
+					if (${name}) ${name}.o(#outrocallback);
+					else #outrocallback();
+				`);
 			}
 		}
 


Test patch:
diff --git a/test/runtime/samples/nested-transition-detach-each/_config.js b/test/runtime/samples/nested-transition-detach-each/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/nested-transition-detach-each/_config.js
@@ -0,0 +1,41 @@
+export default {
+	data: {
+		visible: false,
+		rows: [1, 2, 3],
+		cols: ['a', 'b', 'c']
+	},
+
+	html: ``,
+
+	compileOptions: {
+		dev: true
+	},
+	nestedTransitions: true,
+	skipIntroByDefault: true,
+
+	test(assert, component, target, window, raf) {
+		component.set({ visible: true });
+		assert.htmlEqual(target.innerHTML, `
+			<div class="row">
+				<div class="cell">1, a</div>
+				<div class="cell">1, b</div>
+				<div class="cell">1, c</div>
+			</div>
+			<div class="row">
+				<div class="cell">2, a</div>
+				<div class="cell">2, b</div>
+				<div class="cell">2, c</div>
+			</div>
+			<div class="row">
+				<div class="cell">3, a</div>
+				<div class="cell">3, b</div>
+				<div class="cell">3, c</div>
+			</div>
+		`);
+
+		component.set({ visible: false });
+		raf.tick(0);
+		raf.tick(100);
+		assert.htmlEqual(target.innerHTML, ``);
+	},
+};
diff --git a/test/runtime/samples/nested-transition-detach-each/main.html b/test/runtime/samples/nested-transition-detach-each/main.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/nested-transition-detach-each/main.html
@@ -0,0 +1,22 @@
+{#if visible}
+	{#each rows as row}
+		<div out:foo class="row">
+			{#each cols as col}
+				<div out:foo class="cell">{row}, {col}</div>
+			{/each}
+		</div>
+	{/each}
+{/if}
+
+<script>
+	export default {
+		transitions: {
+			foo(node) {
+				return {
+					duration: 100,
+					tick: t => node.foo = t
+				};
+			}
+		}
+	};
+</script>
\ No newline at end of file
diff --git a/test/runtime/samples/nested-transition-detach-if-false/Folder.html b/test/runtime/samples/nested-transition-detach-if-false/Folder.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/nested-transition-detach-if-false/Folder.html
@@ -0,0 +1,44 @@
+<li>
+	<span>{dir}</span>
+
+	{#if open}
+		<ul>
+			{#each items as item (item.filename)}
+				{#if item.isDir}
+					<svelte:self dir={item.filename}/>
+				{:else}
+					<li>{item.filename}</li>
+				{/if}
+			{/each}
+		</ul>
+	{/if}
+</li>
+
+<script>
+	export default {
+		data() {
+			return {
+				items: [],
+				open: true
+			};
+		},
+
+		computed: {
+			items: ({ dir }) => {
+				return dir === 'a'
+					? [
+						{
+							filename: 'a/b',
+							isDir: true
+						}
+					]
+					: [
+						{
+							filename: 'a/b/c',
+							isDir: false
+						}
+					];
+			}
+		}
+	};
+</script>
\ No newline at end of file
diff --git a/test/runtime/samples/nested-transition-detach-if-false/_config.js b/test/runtime/samples/nested-transition-detach-if-false/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/nested-transition-detach-if-false/_config.js
@@ -0,0 +1,26 @@
+export default {
+	html: `
+		<li>
+			<span>a</span>
+			<ul>
+				<li>
+					<span>a/b</span>
+					<ul>
+						<li>a/b/c</li>
+					</ul>
+				</li>
+			</ul>
+		</li>
+	`,
+
+	nestedTransitions: true,
+
+	test(assert, component, target, window, raf) {
+		component.refs.folder.set({ open: false });
+		assert.htmlEqual(target.innerHTML, `
+			<li>
+				<span>a</span>
+			</li>
+		`);
+	},
+};
diff --git a/test/runtime/samples/nested-transition-detach-if-false/main.html b/test/runtime/samples/nested-transition-detach-if-false/main.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/nested-transition-detach-if-false/main.html
@@ -0,0 +1,9 @@
+<Folder ref:folder dir="a"/>
+
+<script>
+	export default {
+		components: {
+			Folder: './Folder.html'
+		}
+	};
+</script>
\ No newline at end of file