Repository: sveltejs/svelte (JavaScript) — Bug Fix

Issue:
if/else regression in 3.9.2
**Describe the bug**
{#if "Eva".startsWith('E')}
   eee
{:else}
   rrr
{/if}

will throw an error

**To Reproduce**
https://svelte.dev/repl/60ba4405814042e4925edc98e3bcc797?version=3.9.2

**Expected behavior**
no error

**Stacktraces**
Rollup: Unexpected token (Note that you need

**Severity**
breaking change



Discussion:
This appeared in 3.9.1, and apparently #3478 in 3.9.2 was an incomplete fix.
This seems to be caused by an empty dependencies array in IfBlocks.ts file, generated code is 

```javascript
if ((show_if == null) || ) show_if = !!("Eva".startsWith('E') == true)
```

Updating line 246 to check the array length should fix this issue :

```javascript
${snippet && `if ((${condition} == null) || ${dependencies.length > 0 ? dependencies.map(n => `changed.${n}`).join(' || ') : false}) ${condition} = !!(${snippet})`}
```

generating code : 

```javascript
if ((show_if == null) || false) show_if = !!("Eva".startsWith('E') == true)
```

Resolving patch:
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
@@ -74,6 +74,7 @@ class IfBlockBranch extends Wrapper {
 export default class IfBlockWrapper extends Wrapper {
 	node: IfBlock;
 	branches: IfBlockBranch[];
+	needs_update = false;
 
 	var = 'if_block';
 
@@ -112,10 +113,16 @@ export default class IfBlockWrapper extends Wrapper {
 			block.add_dependencies(node.expression.dependencies);
 
 			if (branch.block.dependencies.size > 0) {
+				// the condition, or its contents, is dynamic
 				is_dynamic = true;
 				block.add_dependencies(branch.block.dependencies);
 			}
 
+			if (branch.dependencies && branch.dependencies.length > 0) {
+				// the condition itself is dynamic
+				this.needs_update = true;
+			}
+
 			if (branch.block.has_intros) has_intros = true;
 			if (branch.block.has_outros) has_outros = true;
 
@@ -239,15 +246,29 @@ export default class IfBlockWrapper extends Wrapper {
 		const current_block_type_and = has_else ? '' : `${current_block_type} && `;
 
 		/* eslint-disable @typescript-eslint/indent,indent */
-		block.builders.init.add_block(deindent`
-			function ${select_block_type}(changed, ctx) {
-				${this.branches.map(({ dependencies, condition, snippet, block }) => condition
-				? deindent`
-				${snippet && `if ((${condition} == null) || ${dependencies.map(n => `changed.${n}`).join(' || ')}) ${condition} = !!(${snippet})`}
-				if (${condition}) return ${block.name};`
-				: `return ${block.name};`)}
-			}
-		`);
+		if (this.needs_update) {
+			block.builders.init.add_block(deindent`
+				function ${select_block_type}(changed, ctx) {
+					${this.branches.map(({ dependencies, condition, snippet, block }) => condition
+					? deindent`
+					${snippet && (
+						dependencies.length > 0
+							? `if ((${condition} == null) || ${dependencies.map(n => `changed.${n}`).join(' || ')}) ${condition} = !!(${snippet})`
+							: `if (${condition} == null) ${condition} = !!(${snippet})`
+					)}
+					if (${condition}) return ${block.name};`
+					: `return ${block.name};`)}
+				}
+			`);
+		} else {
+			block.builders.init.add_block(deindent`
+				function ${select_block_type}(changed, ctx) {
+					${this.branches.map(({ condition, snippet, block }) => condition
+					? `if (${snippet || condition}) return ${block.name};`
+					: `return ${block.name};`)}
+				}
+			`);
+		}
 		/* eslint-enable @typescript-eslint/indent,indent */
 
 		block.builders.init.add_block(deindent`
@@ -261,32 +282,36 @@ export default class IfBlockWrapper extends Wrapper {
 			`${if_name}${name}.m(${initial_mount_node}, ${anchor_node});`
 		);
 
-		const update_mount_node = this.get_update_mount_node(anchor);
+		if (this.needs_update) {
+			const update_mount_node = this.get_update_mount_node(anchor);
 
-		const change_block = deindent`
-			${if_name}${name}.d(1);
-			${name} = ${current_block_type_and}${current_block_type}(ctx);
-			if (${name}) {
-				${name}.c();
-				${has_transitions && `@transition_in(${name}, 1);`}
-				${name}.m(${update_mount_node}, ${anchor});
-			}
-		`;
-
-		if (dynamic) {
-			block.builders.update.add_block(deindent`
-				if (${current_block_type} === (${current_block_type} = ${select_block_type}(changed, ctx)) && ${name}) {
-					${name}.p(changed, ctx);
-				} else {
-					${change_block}
-				}
-			`);
-		} else {
-			block.builders.update.add_block(deindent`
-				if (${current_block_type} !== (${current_block_type} = ${select_block_type}(changed, ctx))) {
-					${change_block}
+			const change_block = deindent`
+				${if_name}${name}.d(1);
+				${name} = ${current_block_type_and}${current_block_type}(ctx);
+				if (${name}) {
+					${name}.c();
+					${has_transitions && `@transition_in(${name}, 1);`}
+					${name}.m(${update_mount_node}, ${anchor});
 				}
-			`);
+			`;
+
+			if (dynamic) {
+				block.builders.update.add_block(deindent`
+					if (${current_block_type} === (${current_block_type} = ${select_block_type}(changed, ctx)) && ${name}) {
+						${name}.p(changed, ctx);
+					} else {
+						${change_block}
+					}
+				`);
+			} else {
+				block.builders.update.add_block(deindent`
+					if (${current_block_type} !== (${current_block_type} = ${select_block_type}(changed, ctx))) {
+						${change_block}
+					}
+				`);
+			}
+		} else if (dynamic) {
+			block.builders.update.add_line(`${name}.p(changed, ctx);`);
 		}
 
 		block.builders.destroy.add_line(`${if_name}${name}.d(${detaching});`);
@@ -323,14 +348,25 @@ export default class IfBlockWrapper extends Wrapper {
 
 			var ${if_blocks} = [];
 
-			function ${select_block_type}(changed, ctx) {
-				${this.branches.map(({ dependencies, condition, snippet }, i) => condition
+			${this.needs_update
 				? deindent`
-				${snippet && `if ((${condition} == null) || ${dependencies.map(n => `changed.${n}`).join(' || ')}) ${condition} = !!(${snippet})`}
-				if (${condition}) return ${String(i)};`
-				: `return ${i};`)}
-				${!has_else && `return -1;`}
-			}
+					function ${select_block_type}(changed, ctx) {
+						${this.branches.map(({ dependencies, condition, snippet }, i) => condition
+						? deindent`
+						${snippet && `if ((${condition} == null) || ${dependencies.map(n => `changed.${n}`).join(' || ')}) ${condition} = !!(${snippet})`}
+						if (${condition}) return ${String(i)};`
+						: `return ${i};`)}
+						${!has_else && `return -1;`}
+					}
+				`
+				: deindent`
+					function ${select_block_type}(changed, ctx) {
+						${this.branches.map(({ condition, snippet }, i) => condition
+						? `if (${snippet || condition}) return ${String(i)};`
+						: `return ${i};`)}
+						${!has_else && `return -1;`}
+					}
+				`}
 		`);
 		/* eslint-enable @typescript-eslint/indent,indent */
 
@@ -354,62 +390,66 @@ export default class IfBlockWrapper extends Wrapper {
 			`${if_current_block_type_index}${if_blocks}[${current_block_type_index}].m(${initial_mount_node}, ${anchor_node});`
 		);
 
-		const update_mount_node = this.get_update_mount_node(anchor);
-
-		const destroy_old_block = deindent`
-			@group_outros();
-			@transition_out(${if_blocks}[${previous_block_index}], 1, 1, () => {
-				${if_blocks}[${previous_block_index}] = null;
-			});
-			@check_outros();
-		`;
+		if (this.needs_update) {
+			const update_mount_node = this.get_update_mount_node(anchor);
 
-		const create_new_block = deindent`
-			${name} = ${if_blocks}[${current_block_type_index}];
-			if (!${name}) {
-				${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](ctx);
-				${name}.c();
-			}
-			${has_transitions && `@transition_in(${name}, 1);`}
-			${name}.m(${update_mount_node}, ${anchor});
-		`;
+			const destroy_old_block = deindent`
+				@group_outros();
+				@transition_out(${if_blocks}[${previous_block_index}], 1, 1, () => {
+					${if_blocks}[${previous_block_index}] = null;
+				});
+				@check_outros();
+			`;
 
-		const change_block = has_else
-			? deindent`
-				${destroy_old_block}
+			const create_new_block = deindent`
+				${name} = ${if_blocks}[${current_block_type_index}];
+				if (!${name}) {
+					${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](ctx);
+					${name}.c();
+				}
+				${has_transitions && `@transition_in(${name}, 1);`}
+				${name}.m(${update_mount_node}, ${anchor});
+			`;
 
-				${create_new_block}
-			`
-			: deindent`
-				if (${name}) {
+			const change_block = has_else
+				? deindent`
 					${destroy_old_block}
-				}
 
-				if (~${current_block_type_index}) {
 					${create_new_block}
-				} else {
-					${name} = null;
-				}
-			`;
+				`
+				: deindent`
+					if (${name}) {
+						${destroy_old_block}
+					}
 
-		if (dynamic) {
-			block.builders.update.add_block(deindent`
-				var ${previous_block_index} = ${current_block_type_index};
-				${current_block_type_index} = ${select_block_type}(changed, ctx);
-				if (${current_block_type_index} === ${previous_block_index}) {
-					${if_current_block_type_index}${if_blocks}[${current_block_type_index}].p(changed, ctx);
-				} else {
-					${change_block}
-				}
-			`);
-		} else {
-			block.builders.update.add_block(deindent`
-				var ${previous_block_index} = ${current_block_type_index};
-				${current_block_type_index} = ${select_block_type}(changed, ctx);
-				if (${current_block_type_index} !== ${previous_block_index}) {
-					${change_block}
-				}
-			`);
+					if (~${current_block_type_index}) {
+						${create_new_block}
+					} else {
+						${name} = null;
+					}
+				`;
+
+			if (dynamic) {
+				block.builders.update.add_block(deindent`
+					var ${previous_block_index} = ${current_block_type_index};
+					${current_block_type_index} = ${select_block_type}(changed, ctx);
+					if (${current_block_type_index} === ${previous_block_index}) {
+						${if_current_block_type_index}${if_blocks}[${current_block_type_index}].p(changed, ctx);
+					} else {
+						${change_block}
+					}
+				`);
+			} else {
+				block.builders.update.add_block(deindent`
+					var ${previous_block_index} = ${current_block_type_index};
+					${current_block_type_index} = ${select_block_type}(changed, ctx);
+					if (${current_block_type_index} !== ${previous_block_index}) {
+						${change_block}
+					}
+				`);
+			}
+		} else if (dynamic) {
+			block.builders.update.add_line(`${name}.p(changed, ctx);`);
 		}
 
 		block.builders.destroy.add_line(deindent`


Test patch:
diff --git a/test/runtime/samples/if-block-static-with-else-and-outros/EEE.svelte b/test/runtime/samples/if-block-static-with-else-and-outros/EEE.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/if-block-static-with-else-and-outros/EEE.svelte
@@ -0,0 +1 @@
+eee
\ No newline at end of file
diff --git a/test/runtime/samples/if-block-static-with-else-and-outros/RRR.svelte b/test/runtime/samples/if-block-static-with-else-and-outros/RRR.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/if-block-static-with-else-and-outros/RRR.svelte
@@ -0,0 +1 @@
+rrr
\ No newline at end of file
diff --git a/test/runtime/samples/if-block-static-with-else-and-outros/_config.js b/test/runtime/samples/if-block-static-with-else-and-outros/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/if-block-static-with-else-and-outros/_config.js
@@ -0,0 +1,3 @@
+export default {
+	html: 'eee'
+};
diff --git a/test/runtime/samples/if-block-static-with-else-and-outros/main.svelte b/test/runtime/samples/if-block-static-with-else-and-outros/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/if-block-static-with-else-and-outros/main.svelte
@@ -0,0 +1,10 @@
+<script>
+	import EEE from './EEE.svelte';
+	import RRR from './RRR.svelte';
+</script>
+
+{#if "Eva".startsWith('E')}
+	<EEE/>
+{:else}
+	<RRR/>
+{/if}
\ No newline at end of file
diff --git a/test/runtime/samples/if-block-static-with-else/_config.js b/test/runtime/samples/if-block-static-with-else/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/if-block-static-with-else/_config.js
@@ -0,0 +1,3 @@
+export default {
+	html: 'eee'
+};
diff --git a/test/runtime/samples/if-block-static-with-else/main.svelte b/test/runtime/samples/if-block-static-with-else/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/if-block-static-with-else/main.svelte
@@ -0,0 +1,5 @@
+{#if "Eva".startsWith('E')}
+	eee
+{:else}
+	rrr
+{/if}
\ No newline at end of file